From e001c9156f76fbfd708d9b225609b92c85218216 Mon Sep 17 00:00:00 2001 From: Craig Taverner Date: Wed, 18 Oct 2023 18:20:16 +0200 Subject: [PATCH 001/203] Removed flaky test for unsupported metric type position (#100978) This test failed about 1% of the time in mixed clusters. We never figured out why, but since it was a test that really only needed to be run a few times to verify that the correct error is raised in mixed clusters, it was seen as very low priority, and considered better to just delete it. --- .../rest-api-spec/test/tsdb/20_mapping.yml | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/20_mapping.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/20_mapping.yml index 3d297e2181970..7edae8f264c76 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/20_mapping.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/20_mapping.yml @@ -574,37 +574,6 @@ source include/exclude: type: keyword time_series_dimension: true ---- -Unsupported metric type position: - - skip: - version: "all, - 8.0.99, 8.8.0 -" - reason: AwaitsFix https://github.com/elastic/elasticsearch/issues/94239, index.mode and routing_path introduced in 8.1.0 and time series metric position introduced in 8.8.0 - - - do: - catch: '/unknown parameter \[time_series_metric\] on mapper \[location\] of type \[geo_point\]/' - indices.create: - index: test_position - body: - settings: - number_of_shards: 1 - number_of_replicas: 0 - index: - mode: time_series - routing_path: [metricset] - time_series: - start_time: 2021-04-28T00:00:00Z - end_time: 2021-04-29T00:00:00Z - mappings: - properties: - "@timestamp": - type: date - metricset: - type: keyword - time_series_dimension: true - location: - type: geo_point - time_series_metric: position - --- Supported metric type position: - skip: From aada7d5eab619c15f874e8e07cc6c30555d6dd47 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 18 Oct 2023 10:34:53 -0600 Subject: [PATCH 002/203] Fix build randomization to account for null strings (#101019) Since adding qualifier as a nullable string, the build utils which mutates Build objects for tests may experience an NPE. This commit handles null for string values. closes #100994 --- .../src/main/java/org/elasticsearch/test/BuildUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/BuildUtils.java b/test/framework/src/main/java/org/elasticsearch/test/BuildUtils.java index 8b846a0488ab1..8823d725a6506 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/BuildUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/BuildUtils.java @@ -100,6 +100,7 @@ public static Build mutateBuild(Build existing) { } private static String randomStringExcept(final String s) { - return randomAlphaOfLength(13 - s.length()); + int len = s == null ? 0 : s.length(); + return randomAlphaOfLength(13 - len); } } From 3b8825c5d324cd4989c5dd61de81732021d61171 Mon Sep 17 00:00:00 2001 From: Mike Pellegrini Date: Wed, 18 Oct 2023 13:00:38 -0400 Subject: [PATCH 003/203] Clarify search application search API documentation (#100930) Clarify the Search Application Search API & Render Search Application Query API documentation --- .../apis/put-search-application.asciidoc | 1 + .../search-application-render-query.asciidoc | 66 +++++++----- .../apis/search-application-search.asciidoc | 101 ++++++++++++++---- 3 files changed, 123 insertions(+), 45 deletions(-) diff --git a/docs/reference/search-application/apis/put-search-application.asciidoc b/docs/reference/search-application/apis/put-search-application.asciidoc index 9cd7ee37aaa02..eb559acc8cdc7 100644 --- a/docs/reference/search-application/apis/put-search-application.asciidoc +++ b/docs/reference/search-application/apis/put-search-application.asciidoc @@ -59,6 +59,7 @@ The <> associated with this search application. (Required, object) The associated mustache template. +[[put-search-application-dictionary-param]] `dictionary`:: (Optional, object) The dictionary used to validate the parameters used with the <> API. The dictionary must be a valid JSON schema. diff --git a/docs/reference/search-application/apis/search-application-render-query.asciidoc b/docs/reference/search-application/apis/search-application-render-query.asciidoc index 326de9e38f420..687176b4fb070 100644 --- a/docs/reference/search-application/apis/search-application-render-query.asciidoc +++ b/docs/reference/search-application/apis/search-application-render-query.asciidoc @@ -8,9 +8,11 @@ preview::[] Render Search Application Query ++++ -Given specified query parameters, creates an Elasticsearch query to run. Any unspecified template parameters will be -assigned their default values if applicable. Returns the specific Elasticsearch query that would be generated and -run by calling <>. +Given specified query parameters, generates an {es} query using the search template associated with the search +application or a default template if none is specified. +Unspecified template parameters will be assigned their default values (if applicable). +Returns the specific {es} query that would be generated and executed by calling +<>. [[search-application-render-query-request]] ==== {api-request-title} @@ -27,19 +29,35 @@ Requires read privileges on the backing alias of the search application. `params`:: (Optional, map of strings to objects) -Query parameters specific to this request, which will override any defaults specified in the template. +Query parameters used to generate the {es} query from the search template associated with the search application. +If a parameter used in the search template is not specified in `params`, the parameter's default value will be used. + +[NOTE] +==== +The search application can be configured to validate search template parameters. +See the `dictionary` parameter in the <> API for more +information. +==== [[search-application-render-query-response-codes]] ==== {api-response-codes-title} +`400`:: +Invalid parameter passed to search template. +Examples include: + +- Missing required parameter +- Invalid parameter data type +- Invalid parameter value + `404`:: Search Application `` does not exist. [[search-application-render-query-example]] ==== {api-examples-title} -The following example renders a query for a search application called `my-app`. In this case, the `from` and `size` -parameters are not specified, so default values are pulled from the search application template. +The following example generates a query for a search application called `my-app` that uses the search template from +the <>: //// [source,console] @@ -99,14 +117,8 @@ POST _application/search_application/my-app/_render_query "params": { "query_string": "my first query", "text_fields": [ - { - "name": "title", - "boost": 10 - }, - { - "name": "text", - "boost": 1 - } + {"name": "title", "boost": 5}, + {"name": "description", "boost": 1} ] } } @@ -117,19 +129,21 @@ A sample response: [source,console-result] ---- { - "from": 0, - "size": 10, - "query": { - "multi_match": { - "query": "my first query", - "fields": [ - "text^1.0", - "title^10.0" - ] - } - }, - "explain": false + "from": 0, + "size": 10, + "query": { + "multi_match": { + "query": "my first query", + "fields": [ + "description^1.0", + "title^5.0" + ] + } + }, + "explain": false } ---- // TEST[continued] +In this case, the `from`, `size`, and `explain` parameters are not specified in the request, so the default values +specified in the search template are used. diff --git a/docs/reference/search-application/apis/search-application-search.asciidoc b/docs/reference/search-application/apis/search-application-search.asciidoc index 02755f1896496..b166c8aae04d0 100644 --- a/docs/reference/search-application/apis/search-application-search.asciidoc +++ b/docs/reference/search-application/apis/search-application-search.asciidoc @@ -8,8 +8,9 @@ beta::[] Search Application Search ++++ -Given specified query parameters, creates an Elasticsearch query to run. Any unspecified template parameters will be -assigned their default values if applicable. +Given specified query parameters, generates and executes an {es} query using the search template associated +with the search application or a default template if none is specified. +Unspecified template parameters will be assigned their default values (if applicable). [[search-application-search-request]] ==== {api-request-title} @@ -28,24 +29,47 @@ Requires read privileges on the backing alias of the search application. `params`:: (Optional, map of strings to objects) -Query parameters specific to this request, which will override any defaults specified in the template. +Query parameters used to generate the {es} query from the search template associated with the search application. +If a parameter used in the search template is not specified in `params`, the parameter's default value will be used. + +[NOTE] +==== +The search application can be configured to validate search template parameters. +See the `dictionary` parameter in the <> API for more +information. +==== [[search-application-search-response-codes]] ==== {api-response-codes-title} +`400`:: +Invalid parameter passed to search template. +Examples include: + +- Missing required parameter +- Invalid parameter data type +- Invalid parameter value + `404`:: Search Application `` does not exist. [[search-application-search-example]] ==== {api-examples-title} -The following example performs a search against a search application called `my-app`: +The following example executes a search against a search application called `my-app` that uses the search template from +the <>: //// [source,console] ---- PUT /index1 +PUT /index1/_doc/1?refresh=true +{ + "title": "Sample document", + "description": "A sample document that matches my first query" +} + PUT _application/search_application/my-app { "indices": ["index1"], @@ -57,7 +81,7 @@ PUT _application/search_application/my-app "query": { "multi_match": { "query": "{{query_string}}", - "fields": [{{#text_fields}}"{{name}}^{{boost}}"{{^last}},{{/last}}{{/text_fields}}] + "fields": [{{#text_fields}}"{{name}}^{{boost}}",{{/text_fields}}] } }, "explain": "{{explain}}", @@ -68,8 +92,8 @@ PUT _application/search_application/my-app "params": { "query_string": "*", "text_fields": [ - {"name": "title", "boost": 10, "last": false}, - {"name": "description", "boost": 5, "last": true} + {"name": "title", "boost": 10}, + {"name": "description", "boost": 5} ], "explain": false, "from": 0, @@ -97,23 +121,62 @@ DELETE /index1 POST _application/search_application/my-app/_search { "params": { - "value": "my first query", - "size": 10, - "from": 0, + "query_string": "my first query", "text_fields": [ - { - "name": "title", - "boost": 10 - }, - { - "name": "text", - "boost": 1 - } + {"name": "title", "boost": 5}, + {"name": "description", "boost": 1} ] } } ---- -The expected results are search results from the query that was run. +The generated {es} query would look like: +[source,console-result] +---- +{ + "from": 0, + "size": 10, + "query": { + "multi_match": { + "query": "my first query", + "fields": [ + "description^1.0", + "title^5.0" + ] + } + }, + "explain": false +} +---- +// TESTRESPONSE[skip:result of request not run in this document] + +In this case, the `from`, `size`, and `explain` parameters are not specified in the request, so the default values +specified in the search template are used. +The expected response is the search results from the {es} query that was generated & executed. +The response format is the same as that used by the <>: + +[source,console-result] +---- +{ + "took": 5, + "timed_out": false, + "_shards": { + "total": 1, + "successful": 1, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": { + "value": 1, + "relation": "eq" + }, + "max_score": 0.8630463, + "hits": ... + } +} +---- +// TESTRESPONSE[s/"took": 5/"took": $body.$_path/] +// TESTRESPONSE[s/"hits": \.\.\./"hits": $body.$_path/] From 03ea4bbe6e78a1202fa94521265bfaca494071e0 Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Wed, 18 Oct 2023 20:27:52 +0200 Subject: [PATCH 004/203] Remove more explicit references to SearchResponse in tests (#101052) Follow up to #100966 introducing new combined assertion `assertSearchHitsWithoutFailures` to combine no-failure, count, and id assertions into one block. --- .../elasticsearch/join/query/InnerHitsIT.java | 25 +- .../PercolatorQuerySearchTests.java | 47 ++- .../index/WaitUntilRefreshIT.java | 6 +- .../index/shard/SearchIdleIT.java | 2 +- .../elasticsearch/recovery/RelocationIT.java | 7 +- .../search/fetch/subphase/InnerHitsIT.java | 24 +- .../search/query/MultiMatchQueryIT.java | 25 +- .../search/query/SearchQueryIT.java | 285 ++++++++---------- .../search/query/SimpleQueryStringIT.java | 216 +++++++------ .../hamcrest/ElasticsearchAssertions.java | 20 ++ .../DocumentLevelSecurityTests.java | 101 ++++--- .../integration/FieldLevelSecurityTests.java | 29 +- .../security/authz/ReadActionsTests.java | 72 +++-- 13 files changed, 411 insertions(+), 448 deletions(-) diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java index 2e647e6ea08e5..0f8bf4e701e09 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java @@ -54,7 +54,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; @@ -612,18 +612,17 @@ public void testInnerHitsWithIgnoreUnmapped() { createIndexRequest("index1", "child_type", "2", "1").get(); client().prepareIndex("index2").setId("3").setSource("key", "value").get(); refresh(); - - SearchResponse response = client().prepareSearch("index1", "index2") - .setQuery( - boolQuery().should( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true)) - ).should(termQuery("key", "value")) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 2); - assertSearchHits(response, "1", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch("index1", "index2") + .setQuery( + boolQuery().should( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true)) + ).should(termQuery("key", "value")) + ), + "1", + "3" + ); } public void testTooHighResultWindow() { diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchTests.java index bce98ec90d527..05c2c27de40fc 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorQuerySearchTests.java @@ -9,7 +9,6 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.join.ScoreMode; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; @@ -47,7 +46,7 @@ import static org.elasticsearch.index.query.QueryBuilders.scriptQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; @@ -87,17 +86,17 @@ public void testPercolateScriptQuery() throws IOException { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .execute() .actionGet(); - SearchResponse response = client().prepareSearch("index") - .setQuery( - new PercolateQueryBuilder( - "query", - BytesReference.bytes(jsonBuilder().startObject().field("field1", "b").endObject()), - XContentType.JSON - ) - ) - .get(); - assertHitCount(response, 1); - assertSearchHits(response, "1"); + assertSearchHitsWithoutFailures( + client().prepareSearch("index") + .setQuery( + new PercolateQueryBuilder( + "query", + BytesReference.bytes(jsonBuilder().startObject().field("field1", "b").endObject()), + XContentType.JSON + ) + ), + "1" + ); } public void testPercolateQueryWithNestedDocuments_doNotLeakBitsetCacheEntries() throws Exception { @@ -265,17 +264,17 @@ public void testMapUnmappedFieldAsText() throws IOException { .get(); indicesAdmin().prepareRefresh().get(); - SearchResponse response = client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "query", - BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()), - XContentType.JSON - ) - ) - .get(); - assertHitCount(response, 1); - assertSearchHits(response, "1"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test") + .setQuery( + new PercolateQueryBuilder( + "query", + BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()), + XContentType.JSON + ) + ), + "1" + ); } public void testRangeQueriesWithNow() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java index 6970f73e591fc..09290f40d6c29 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java @@ -75,7 +75,7 @@ public void testDelete() throws InterruptedException, ExecutionException { DeleteResponse delete = client().prepareDelete("test", "1").setRefreshPolicy(RefreshPolicy.WAIT_UNTIL).get(); assertEquals(DocWriteResponse.Result.DELETED, delete.getResult()); assertFalse("request shouldn't have forced a refresh", delete.forcedRefresh()); - assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")).get()); + assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar"))); } public void testUpdate() throws InterruptedException, ExecutionException { @@ -109,7 +109,7 @@ public void testUpdate() throws InterruptedException, ExecutionException { .get(); assertEquals(2, update.getVersion()); assertFalse("request shouldn't have forced a refresh", update.forcedRefresh()); - assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "cat")).get()); + assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "cat"))); } public void testBulk() { @@ -129,7 +129,7 @@ public void testBulk() { bulk = client().prepareBulk().setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); bulk.add(client().prepareDelete("test", "1")); assertBulkSuccess(bulk.get()); - assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")).get()); + assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar"))); // Update makes a noop bulk = client().prepareBulk().setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/SearchIdleIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/SearchIdleIT.java index 3a10539d3c451..22bb5974ad550 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/SearchIdleIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/SearchIdleIT.java @@ -88,7 +88,7 @@ private void runTestAutomaticRefresh(final IntToLongFunction count) throws Inter assertFalse(indexService.getIndexSettings().isExplicitRefresh()); ensureGreen(); AtomicInteger totalNumDocs = new AtomicInteger(Integer.MAX_VALUE); - assertNoSearchHits(client().prepareSearch().get()); + assertNoSearchHits(client().prepareSearch()); int numDocs = scaledRandomIntBetween(25, 100); totalNumDocs.set(numDocs); CountDownLatch indexingDone = new CountDownLatch(numDocs); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java index a18015da0737a..6a8de644b00cf 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java @@ -27,6 +27,7 @@ import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; @@ -79,7 +80,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.in; @@ -522,9 +523,7 @@ public void testIndexSearchAndRelocateConcurrently() throws Exception { final int numIters = randomIntBetween(10, 20); for (int i = 0; i < numIters; i++) { logger.info(" --> checking iteration {}", i); - SearchResponse afterRelocation = client().prepareSearch().setSize(ids.size()).get(); - assertNoFailures(afterRelocation); - assertSearchHits(afterRelocation, ids.toArray(new String[ids.size()])); + assertSearchHitsWithoutFailures(client().prepareSearch().setSize(ids.size()), ids.toArray(Strings.EMPTY_ARRAY)); } stopped.set(true); for (Thread searchThread : searchThreads) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index 4041bbc431f75..183f925ced9d3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -52,7 +52,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; @@ -922,17 +922,17 @@ public void testInnerHitsWithIgnoreUnmapped() throws Exception { client().prepareIndex("index2").setId("3").setSource("key", "value").get(); refresh(); - SearchResponse response = client().prepareSearch("index1", "index2") - .setQuery( - boolQuery().should( - nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true)) - ).should(termQuery("key", "value")) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 2); - assertSearchHits(response, "1", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch("index1", "index2") + .setQuery( + boolQuery().should( + nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true)) + ).should(termQuery("key", "value")) + ), + "1", + "3" + ); } public void testUseMaxDocInsteadOfSize() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java index 9e55c38336c1b..3af7502cefead 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java @@ -53,7 +53,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFirstHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSecondHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.hamcrest.Matchers.anyOf; @@ -348,17 +348,18 @@ public void testPhraseType() { .get(); assertThat(searchResponse.getHits().getTotalHits().value, greaterThan(1L)); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("the Ul", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase").operator( - Operator.OR - ).type(MatchQueryParser.Type.PHRASE_PREFIX) - ) - ) - .get(); - assertSearchHits(searchResponse, "ultimate2", "ultimate1"); - assertHitCount(searchResponse, 2L); + assertSearchHitsWithoutFailures( + client().prepareSearch("test") + .setQuery( + randomizeType( + multiMatchQuery("the Ul", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase").operator( + Operator.OR + ).type(MatchQueryParser.Type.PHRASE_PREFIX) + ) + ), + "ultimate2", + "ultimate1" + ); } public void testSingleField() throws NoSuchFieldException, IllegalAccessException { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java index 18a5ad78995da..be265cb3985e6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -106,6 +106,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSecondHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; @@ -414,20 +415,13 @@ public void testIdsQueryTestsIdIndexed() throws Exception { client().prepareIndex("test").setId("3").setSource("field1", "value3") ); - SearchResponse searchResponse = client().prepareSearch().setQuery(constantScoreQuery(idsQuery().addIds("1", "3"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); - - searchResponse = client().prepareSearch().setQuery(idsQuery().addIds("1", "3")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(constantScoreQuery(idsQuery().addIds("1", "3"))), "1", "3"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(idsQuery().addIds("1", "3")), "1", "3"); assertHitCount(client().prepareSearch().setQuery(idsQuery().addIds("7", "10")), 0L); // repeat..., with terms - searchResponse = client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_id", "1", "3"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_id", "1", "3"))), "1", "3"); } public void testTermIndexQuery() throws Exception { @@ -440,22 +434,22 @@ public void testTermIndexQuery() throws Exception { } for (String indexName : indexNames) { - SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termQuery("_index", indexName))).get(); - SearchResponse searchResponse = assertSearchResponse(request); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, indexName + "1"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(constantScoreQuery(termQuery("_index", indexName))), + indexName + "1" + ); } for (String indexName : indexNames) { - SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexName))).get(); - SearchResponse searchResponse = assertSearchResponse(request); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, indexName + "1"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexName))), + indexName + "1" + ); } for (String indexName : indexNames) { - SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(matchQuery("_index", indexName))).get(); - SearchResponse searchResponse = assertSearchResponse(request); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, indexName + "1"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(constantScoreQuery(matchQuery("_index", indexName))), + indexName + "1" + ); } { SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexNames))).get(); @@ -516,35 +510,15 @@ public void testFilterExistsMissing() throws Exception { ) ); - SearchResponse searchResponse = client().prepareSearch().setQuery(existsQuery("field1")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); - - searchResponse = client().prepareSearch().setQuery(constantScoreQuery(existsQuery("field1"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); - - searchResponse = client().prepareSearch().setQuery(queryStringQuery("_exists_:field1")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); - - searchResponse = client().prepareSearch().setQuery(existsQuery("field2")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); - - searchResponse = client().prepareSearch().setQuery(existsQuery("field3")).get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("4")); - + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("field1")), "1", "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(constantScoreQuery(existsQuery("field1"))), "1", "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(queryStringQuery("_exists_:field1")), "1", "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("field2")), "1", "3"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("field3")), "4"); // wildcard check - searchResponse = client().prepareSearch().setQuery(existsQuery("x*")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); - + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("x*")), "1", "2"); // object check - searchResponse = client().prepareSearch().setQuery(existsQuery("obj1")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("obj1")), "1", "2"); } public void testPassQueryOrFilterAsJSONString() throws Exception { @@ -603,21 +577,22 @@ public void testMatchQueryFuzzy() throws Exception { ); assertHitCount(client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(0))), 0L); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(1))) - .get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); - - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(1))), + "1", + "2" + ); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO"))), + "1", + "2" + ); assertHitCount(client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO:5,7"))), 0L); - - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness(Fuzziness.fromString("AUTO:5,7"))).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "2"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness(Fuzziness.fromString("AUTO:5,7"))), + "2" + ); } public void testMultiMatchQuery() throws Exception { @@ -631,40 +606,31 @@ public void testMultiMatchQuery() throws Exception { ); MultiMatchQueryBuilder builder = multiMatchQuery("value1 value2 value4", "field1", "field2"); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(builder) - .addAggregation(AggregationBuilders.terms("field1").field("field1.keyword")) - .get(); - - assertHitCount(searchResponse, 2L); // this uses dismax so scores are equal and the order can be arbitrary - assertSearchHits(searchResponse, "1", "2"); - - searchResponse = client().prepareSearch().setQuery(builder).get(); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(builder).addAggregation(AggregationBuilders.terms("field1").field("field1.keyword")), + "1", + "2" + ); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "1", "2"); indicesAdmin().prepareRefresh("test").get(); builder = multiMatchQuery("value1", "field1", "field2").operator(Operator.AND); // Operator only applies on terms inside a field! // Fields are always OR-ed together. - searchResponse = client().prepareSearch().setQuery(builder).get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("1")); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "1"); refresh(); builder = multiMatchQuery("value1", "field1").field("field3", 1.5f).operator(Operator.AND); // Operator only applies on terms inside // a field! Fields are always OR-ed // together. - searchResponse = client().prepareSearch().setQuery(builder).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "3", "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "3", "1"); indicesAdmin().prepareRefresh("test").get(); builder = multiMatchQuery("value1").field("field1").field("field3", 1.5f).operator(Operator.AND); // Operator only applies on terms // inside a field! Fields are // always OR-ed together. - searchResponse = client().prepareSearch().setQuery(builder).get(); + SearchResponse searchResponse = client().prepareSearch().setQuery(builder).get(); assertHitCount(searchResponse, 2L); assertSearchHits(searchResponse, "3", "1"); @@ -693,9 +659,7 @@ public void testMultiMatchQuery() throws Exception { } builder.lenient(true); - searchResponse = client().prepareSearch().setQuery(builder).get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("1")); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "1"); } public void testMatchQueryZeroTermsQuery() { @@ -894,46 +858,39 @@ public void testTermsQuery() throws Exception { client().prepareIndex("test").setId("3").setSource("str", "3", "lng", 3L, "dbl", 3.0d), client().prepareIndex("test").setId("4").setSource("str", "4", "lng", 4L, "dbl", 4.0d) ); - - SearchResponse searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "1", "4"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "4"); - - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 3 }))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "2", "3"); - - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 3 }))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "2", "3"); - - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new int[] { 1, 3 }))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); - - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new float[] { 2, 4 }))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "2", "4"); - + assertSearchHitsWithoutFailures(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "1", "4"))), "1", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 3 }))), + "2", + "3" + ); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 3 }))), + "2", + "3" + ); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new int[] { 1, 3 }))), + "1", + "3" + ); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new float[] { 2, 4 }))), + "2", + "4" + ); // test partial matching - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "2", "5"))).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("2")); - - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 5 }))).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("2")); - - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 5 }))).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("2")); - + assertSearchHitsWithoutFailures(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "2", "5"))), "2"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 5 }))), + "2" + ); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 5 }))), + "2" + ); // test valid type, but no matching terms assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "5", "6"))), 0L); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 5, 6 }))), 0L); assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 5, 6 }))), 0L); } @@ -1014,50 +971,55 @@ public void testTermsLookupFilter() throws Exception { client().prepareIndex("test").setId("3").setSource("term", "3"), client().prepareIndex("test").setId("4").setSource("term", "4") ); - - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))) - .get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))), + "1", + "3" + ); // same as above, just on the _id... - searchResponse = client().prepareSearch("test").setQuery(termsLookupQuery("_id", new TermsLookup("lookup", "1", "terms"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("_id", new TermsLookup("lookup", "1", "terms"))), + "1", + "3" + ); // another search with same parameters... - searchResponse = client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))), + "1", + "3" + ); - searchResponse = client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "2", "terms"))).get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("2")); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "2", "terms"))), + "2" + ); - searchResponse = client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "3", "terms"))).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "2", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "3", "terms"))), + "2", + "4" + ); assertHitCount(client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "4", "terms"))), 0L); - searchResponse = client().prepareSearch("test") - .setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "1", "arr.term"))) - .get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "1", "arr.term"))), + "1", + "3" + ); - searchResponse = client().prepareSearch("test") - .setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "2", "arr.term"))) - .get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("2")); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "2", "arr.term"))), + "2" + ); - searchResponse = client().prepareSearch("test") - .setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "3", "arr.term"))) - .get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "2", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "3", "arr.term"))), + "2", + "4" + ); assertHitCount( client().prepareSearch("test").setQuery(termsLookupQuery("not_exists", new TermsLookup("lookup2", "3", "arr.term"))), @@ -1737,17 +1699,12 @@ public void testMatchPhrasePrefixQuery() throws ExecutionException, InterruptedE client().prepareIndex("test1").setId("2").setSource("field", "trying out Elasticsearch") ); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(matchPhrasePrefixQuery("field", "Johnnie la").slop(between(2, 5))) - .get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); - searchResponse = client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "trying")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "2"); - searchResponse = client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "try")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "2"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "Johnnie la").slop(between(2, 5))), + "1" + ); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "trying")), "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "try")), "2"); } public void testQueryStringParserCache() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java index 930bc565969cc..80c3e73e76bd5 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java @@ -51,6 +51,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -84,29 +85,30 @@ public void testSimpleQueryString() throws ExecutionException, InterruptedExcept client().prepareIndex("test").setId("6").setSource("otherbody", "spaghetti") ); - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar")).get(); - assertHitCount(searchResponse, 3L); - assertSearchHits(searchResponse, "1", "2", "3"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar")), "1", "2", "3"); // Tests boost value setting. In this case doc 1 should always be ranked above the other // two matches. - searchResponse = client().prepareSearch() + SearchResponse searchResponse = client().prepareSearch() .setQuery(boolQuery().should(simpleQueryStringQuery("\"foo bar\"").boost(10.0f)).should(termQuery("body", "eggplant"))) .get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("3")); - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").defaultOperator(Operator.AND)).get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("3")); - - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("\"quux baz\" +(eggplant | spaghetti)")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "4", "5"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").defaultOperator(Operator.AND)), + "3" + ); - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("eggplants").analyzer("mock_snowball")).get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("4")); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("\"quux baz\" +(eggplant | spaghetti)")), + "4", + "5" + ); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("eggplants").analyzer("mock_snowball")), + "4" + ); searchResponse = client().prepareSearch() .setQuery(simpleQueryStringQuery("spaghetti").field("body", 1000.0f).field("otherbody", 2.0f).queryName("myquery")) @@ -116,9 +118,7 @@ public void testSimpleQueryString() throws ExecutionException, InterruptedExcept assertSearchHits(searchResponse, "5", "6"); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("myquery")); - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("spaghetti").field("*body")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "5", "6"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("spaghetti").field("*body")), "5", "6"); } public void testSimpleQueryStringMinimumShouldMatch() throws Exception { @@ -134,31 +134,35 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { ); logger.info("--> query 1"); - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "3", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")), + "3", + "4" + ); logger.info("--> query 2"); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")) - .get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "3", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")), + "3", + "4" + ); // test case from #13884 logger.info("--> query 3"); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo").field("body").field("body2").field("body3").minimumShouldMatch("-50%")) - .get(); - assertHitCount(searchResponse, 3L); - assertSearchHits(searchResponse, "1", "3", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch() + .setQuery(simpleQueryStringQuery("foo").field("body").field("body2").field("body3").minimumShouldMatch("-50%")), + "1", + "3", + "4" + ); logger.info("--> query 4"); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo bar baz").field("body").field("body2").minimumShouldMatch("70%")) - .get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "3", "4"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body").field("body2").minimumShouldMatch("70%")), + "3", + "4" + ); indexRandom( true, @@ -170,23 +174,32 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { ); logger.info("--> query 5"); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")) - .get(); - assertHitCount(searchResponse, 4L); - assertSearchHits(searchResponse, "3", "4", "7", "8"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")), + "3", + "4", + "7", + "8" + ); logger.info("--> query 6"); - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")).get(); - assertHitCount(searchResponse, 5L); - assertSearchHits(searchResponse, "3", "4", "6", "7", "8"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")), + "3", + "4", + "6", + "7", + "8" + ); logger.info("--> query 7"); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo bar baz").field("body2").field("other").minimumShouldMatch("70%")) - .get(); - assertHitCount(searchResponse, 3L); - assertSearchHits(searchResponse, "6", "7", "8"); + assertSearchHitsWithoutFailures( + client().prepareSearch() + .setQuery(simpleQueryStringQuery("foo bar baz").field("body2").field("other").minimumShouldMatch("70%")), + "6", + "7", + "8" + ); } public void testNestedFieldSimpleQueryString() throws IOException { @@ -211,21 +224,10 @@ public void testNestedFieldSimpleQueryString() throws IOException { client().prepareIndex("test").setId("1").setSource("body", "foo bar baz").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); - - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); - - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); - - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")), "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")), "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")), "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")), "1"); } public void testSimpleQueryStringFlags() throws ExecutionException, InterruptedException { @@ -240,23 +242,26 @@ public void testSimpleQueryStringFlags() throws ExecutionException, InterruptedE client().prepareIndex("test").setId("6").setSource("otherbody", "spaghetti") ); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo bar").flags(SimpleQueryStringFlag.ALL)) - .get(); - assertHitCount(searchResponse, 3L); - assertSearchHits(searchResponse, "1", "2", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").flags(SimpleQueryStringFlag.ALL)), + "1", + "2", + "3" + ); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.OR)) - .get(); - assertHitCount(searchResponse, 3L); - assertSearchHits(searchResponse, "1", "2", "3"); + assertSearchHitsWithoutFailures( + client().prepareSearch() + .setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.OR)), + "1", + "2", + "3" + ); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE)) - .get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("3")); + assertSearchHitsWithoutFailures( + client().prepareSearch() + .setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE)), + "3" + ); assertHitCount( client().prepareSearch() @@ -274,18 +279,18 @@ public void testSimpleQueryStringFlags() throws ExecutionException, InterruptedE 1L ); - searchResponse = client().prepareSearch() - .setQuery( - simpleQueryStringQuery("quuz~1 + egg*").flags( - SimpleQueryStringFlag.WHITESPACE, - SimpleQueryStringFlag.AND, - SimpleQueryStringFlag.FUZZY, - SimpleQueryStringFlag.PREFIX - ) - ) - .get(); - assertHitCount(searchResponse, 1L); - assertFirstHit(searchResponse, hasId("4")); + assertSearchHitsWithoutFailures( + client().prepareSearch() + .setQuery( + simpleQueryStringQuery("quuz~1 + egg*").flags( + SimpleQueryStringFlag.WHITESPACE, + SimpleQueryStringFlag.AND, + SimpleQueryStringFlag.FUZZY, + SimpleQueryStringFlag.PREFIX + ) + ), + "4" + ); } public void testSimpleQueryStringLenient() throws ExecutionException, InterruptedException { @@ -305,10 +310,7 @@ public void testSimpleQueryStringLenient() throws ExecutionException, Interrupte assertHitCount(searchResponse, 1L); assertSearchHits(searchResponse, "1"); - searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("foo").field("field").lenient(true)).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo").field("field").lenient(true)), "1"); } // Issue #7967 @@ -320,12 +322,9 @@ public void testLenientFlagBeingTooLenient() throws Exception { ); BoolQueryBuilder q = boolQuery().should(simpleQueryStringQuery("bar").field("num").field("body").lenient(true)); - SearchResponse resp = client().prepareSearch("test").setQuery(q).get(); - assertNoFailures(resp); // the bug is that this would be parsed into basically a match_all // query and this would match both documents - assertHitCount(resp, 1); - assertSearchHits(resp, "1"); + assertSearchHitsWithoutFailures(client().prepareSearch("test").setQuery(q), "1"); } public void testSimpleQueryStringAnalyzeWildcard() throws ExecutionException, InterruptedException, IOException { @@ -346,32 +345,21 @@ public void testSimpleQueryStringAnalyzeWildcard() throws ExecutionException, In indexRandom(true, client().prepareIndex("test1").setId("1").setSource("location", "Köln")); refresh(); - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("Köln*").field("location")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("Köln*").field("location")), "1"); } public void testSimpleQueryStringUsesFieldAnalyzer() throws Exception { client().prepareIndex("test").setId("1").setSource("foo", 123, "bar", "abc").get(); client().prepareIndex("test").setId("2").setSource("foo", 234, "bar", "bcd").get(); - refresh(); - - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("123").field("foo").field("bar")).get(); - assertHitCount(searchResponse, 1L); - assertSearchHits(searchResponse, "1"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("123").field("foo").field("bar")), "1"); } public void testSimpleQueryStringOnIndexMetaField() throws Exception { client().prepareIndex("test").setId("1").setSource("foo", 123, "bar", "abc").get(); client().prepareIndex("test").setId("2").setSource("foo", 234, "bar", "bcd").get(); - refresh(); - - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("test").field("_index")).get(); - assertHitCount(searchResponse, 2L); - assertSearchHits(searchResponse, "1", "2"); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("test").field("_index")), "1", "2"); } public void testEmptySimpleQueryStringWithAnalysis() throws Exception { @@ -393,9 +381,7 @@ public void testEmptySimpleQueryStringWithAnalysis() throws Exception { indexRandom(true, client().prepareIndex("test1").setId("1").setSource("body", "Some Text")); refresh(); - SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryStringQuery("the*").field("body")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 0L); + assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("the*").field("body"))); } public void testBasicAllQuery() throws Exception { diff --git a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java index 7868043ac61b3..885a1aafd89b8 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java +++ b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java @@ -223,6 +223,15 @@ public static String formatShardStatus(SearchResponse response) { return msg.toString(); } + public static void assertNoSearchHits(SearchRequestBuilder searchRequestBuilder) { + var searchResponse = searchRequestBuilder.get(); + try { + assertNoSearchHits(searchResponse); + } finally { + searchResponse.decRef(); + } + } + public static void assertNoSearchHits(SearchResponse searchResponse) { assertThat(searchResponse.getHits().getHits(), emptyArray()); } @@ -244,6 +253,17 @@ public static void assertSearchHits(SearchResponse searchResponse, String... ids ); } + public static void assertSearchHitsWithoutFailures(SearchRequestBuilder requestBuilder, String... ids) { + var res = requestBuilder.get(); + try { + assertNoFailures(res); + assertHitCount(res, ids.length); + assertSearchHits(res, ids); + } finally { + res.decRef(); + } + } + public static void assertSortValues(SearchRequestBuilder searchRequestBuilder, Object[]... sortValues) { var searchResponse = searchRequestBuilder.get(); try { diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java index 80f2ed881bbad..3baec135d107f 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java @@ -95,6 +95,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER; @@ -208,32 +209,29 @@ public void testSimpleQuery() throws Exception { client().prepareIndex("test").setId("2").setSource("field2", "value2").setRefreshPolicy(IMMEDIATE).get(); client().prepareIndex("test").setId("3").setSource("field3", "value3").setRefreshPolicy(IMMEDIATE).get(); - SearchResponse response = client().filterWithHeader( - Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD)) - ) - .prepareSearch("test") - .setQuery(randomBoolean() ? QueryBuilders.termQuery("field1", "value1") : QueryBuilders.matchAllQuery()) - .get(); - assertHitCount(response, 1); - assertSearchHits(response, "1"); - - response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) - .prepareSearch("test") - .setQuery(randomBoolean() ? QueryBuilders.termQuery("field2", "value2") : QueryBuilders.matchAllQuery()) - .get(); - assertHitCount(response, 1); - assertSearchHits(response, "2"); - + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD))) + .prepareSearch("test") + .setQuery(randomBoolean() ? QueryBuilders.termQuery("field1", "value1") : QueryBuilders.matchAllQuery()), + "1" + ); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) + .prepareSearch("test") + .setQuery(randomBoolean() ? QueryBuilders.termQuery("field2", "value2") : QueryBuilders.matchAllQuery()), + "2" + ); QueryBuilder combined = QueryBuilders.boolQuery() .should(QueryBuilders.termQuery("field2", "value2")) .should(QueryBuilders.termQuery("field1", "value1")) .minimumShouldMatch(1); - response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) - .prepareSearch("test") - .setQuery(randomBoolean() ? combined : QueryBuilders.matchAllQuery()) - .get(); - assertHitCount(response, 2); - assertSearchHits(response, "1", "2"); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) + .prepareSearch("test") + .setQuery(randomBoolean() ? combined : QueryBuilders.matchAllQuery()), + "1", + "2" + ); } public void testGetApi() throws Exception { @@ -676,11 +674,14 @@ public void testTermsLookupOnIndexWithDLS() { // Lookup doc#1 is: visible to user1 and user3, but hidden from user2 TermsQueryBuilder lookup = QueryBuilders.termsLookupQuery("search_field", new TermsLookup("lookup_index", "1", "lookup_field")); - SearchResponse response = client().filterWithHeader( - Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD)) - ).prepareSearch("search_index").setQuery(lookup).get(); - assertHitCount(response, 3); - assertSearchHits(response, "1", "2", "3"); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD))) + .prepareSearch("search_index") + .setQuery(lookup), + "1", + "2", + "3" + ); assertHitCount( client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) @@ -688,13 +689,16 @@ public void testTermsLookupOnIndexWithDLS() { .setQuery(lookup), 0 ); - - response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) - .prepareSearch("search_index") - .setQuery(lookup) - .get(); - assertHitCount(response, 5); - assertSearchHits(response, "1", "2", "3", "4", "5"); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) + .prepareSearch("search_index") + .setQuery(lookup), + "1", + "2", + "3", + "4", + "5" + ); // Lookup doc#2 is: hidden from user1, visible to user2 and user3 lookup = QueryBuilders.termsLookupQuery("search_field", new TermsLookup("lookup_index", "2", "lookup_field")); assertHitCount( @@ -704,20 +708,21 @@ public void testTermsLookupOnIndexWithDLS() { .get(), 0 ); - - response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) - .prepareSearch("search_index") - .setQuery(lookup) - .get(); - assertHitCount(response, 2); - assertSearchHits(response, "2", "5"); - - response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) - .prepareSearch("search_index") - .setQuery(lookup) - .get(); - assertHitCount(response, 3); - assertSearchHits(response, "1", "2", "5"); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) + .prepareSearch("search_index") + .setQuery(lookup), + "2", + "5" + ); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) + .prepareSearch("search_index") + .setQuery(lookup), + "1", + "2", + "5" + ); } public void testTVApi() throws Exception { diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java index 1aee3e445ab94..27b6a10e0619c 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java @@ -81,7 +81,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; @@ -633,20 +633,19 @@ public void testTermsLookupOnIndexWithFLS() { client().prepareIndex("lookup_index").setId("2").setSource("other", "value2", "field", "value2").setRefreshPolicy(IMMEDIATE).get(); // user sees the terms doc field - SearchResponse response = client().filterWithHeader( - Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user6", USERS_PASSWD)) - ) - .prepareSearch("search_index") - .setQuery(QueryBuilders.termsLookupQuery("field", new TermsLookup("lookup_index", "1", "field"))) - .get(); - assertHitCount(response, 2); - assertSearchHits(response, "1", "2"); - response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user6", USERS_PASSWD))) - .prepareSearch("search_index") - .setQuery(QueryBuilders.termsLookupQuery("field", new TermsLookup("lookup_index", "2", "field"))) - .get(); - assertHitCount(response, 1); - assertSearchHits(response, "1"); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user6", USERS_PASSWD))) + .prepareSearch("search_index") + .setQuery(QueryBuilders.termsLookupQuery("field", new TermsLookup("lookup_index", "1", "field"))), + "1", + "2" + ); + assertSearchHitsWithoutFailures( + client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user6", USERS_PASSWD))) + .prepareSearch("search_index") + .setQuery(QueryBuilders.termsLookupQuery("field", new TermsLookup("lookup_index", "2", "field"))), + "1" + ); // user does not see the terms doc field assertHitCount( client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user6", USERS_PASSWD))) diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java index d90434ea7d9a8..f48a6eaf2655f 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java @@ -13,13 +13,13 @@ import org.elasticsearch.action.search.MultiSearchResponse; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.termvectors.MultiTermVectorsAction; import org.elasticsearch.action.termvectors.MultiTermVectorsResponse; import org.elasticsearch.action.termvectors.TermVectorsAction; import org.elasticsearch.core.Strings; -import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.search.SearchHit; import org.elasticsearch.test.SecurityIntegTestCase; @@ -54,17 +54,13 @@ protected String configRoles() { public void testSearchForAll() { // index1 is not authorized and referred to through wildcard createIndicesWithRandomAliases("test1", "test2", "test3", "index1"); - - SearchResponse searchResponse = trySearch(); - assertReturnedIndices(searchResponse, "test1", "test2", "test3"); + assertReturnedIndices(trySearch(), "test1", "test2", "test3"); } public void testSearchForWildcard() { // index1 is not authorized and referred to through wildcard createIndicesWithRandomAliases("test1", "test2", "test3", "index1"); - - SearchResponse searchResponse = trySearch("*"); - assertReturnedIndices(searchResponse, "test1", "test2", "test3"); + assertReturnedIndices(trySearch("*"), "test1", "test2", "test3"); } public void testSearchNonAuthorizedWildcard() { @@ -78,7 +74,7 @@ public void testSearchNonAuthorizedWildcardDisallowNoIndices() { createIndicesWithRandomAliases("test1", "test2", "index1", "index2"); IndexNotFoundException e = expectThrows( IndexNotFoundException.class, - () -> trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean()), "index*") + trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean()), "index*") ); assertEquals("no such index [index*]", e.getMessage()); } @@ -90,20 +86,19 @@ public void testEmptyClusterSearchForAll() { public void testEmptyClusterSearchForAllDisallowNoIndices() { IndexNotFoundException e = expectThrows( IndexNotFoundException.class, - () -> trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean())) + trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean())) ); assertEquals("no such index [[]]", e.getMessage()); } public void testEmptyClusterSearchForWildcard() { - SearchResponse searchResponse = trySearch("*"); - assertNoSearchHits(searchResponse); + assertNoSearchHits(trySearch("*")); } public void testEmptyClusterSearchForWildcardDisallowNoIndices() { IndexNotFoundException e = expectThrows( IndexNotFoundException.class, - () -> trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean()), "*") + trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean()), "*") ); assertEquals("no such index [*]", e.getMessage()); } @@ -117,7 +112,7 @@ public void testEmptyAuthorizedIndicesSearchForAllDisallowNoIndices() { createIndicesWithRandomAliases("index1", "index2"); IndexNotFoundException e = expectThrows( IndexNotFoundException.class, - () -> trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean())) + trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean())) ); assertEquals("no such index [[]]", e.getMessage()); } @@ -131,19 +126,19 @@ public void testEmptyAuthorizedIndicesSearchForWildcardDisallowNoIndices() { createIndicesWithRandomAliases("index1", "index2"); IndexNotFoundException e = expectThrows( IndexNotFoundException.class, - () -> trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean()), "*") + trySearch(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean()), "*") ); assertEquals("no such index [*]", e.getMessage()); } public void testExplicitNonAuthorizedIndex() { createIndicesWithRandomAliases("test1", "test2", "index1"); - assertThrowsAuthorizationExceptionDefaultUsers(() -> trySearch("test*", "index1"), SearchAction.NAME); + assertThrowsAuthorizationExceptionDefaultUsers(() -> trySearch("test*", "index1").get(), SearchAction.NAME); } public void testIndexNotFound() { createIndicesWithRandomAliases("test1", "test2", "index1"); - assertThrowsAuthorizationExceptionDefaultUsers(() -> trySearch("missing"), SearchAction.NAME); + assertThrowsAuthorizationExceptionDefaultUsers(() -> trySearch("missing").get(), SearchAction.NAME); } public void testIndexNotFoundIgnoreUnavailable() { @@ -176,46 +171,36 @@ public void testIndexNotFoundIgnoreUnavailable() { public void testExplicitExclusion() { // index1 is not authorized and referred to through wildcard, test2 is excluded createIndicesWithRandomAliases("test1", "test2", "test3", "index1"); - - SearchResponse searchResponse = trySearch("*", "-test2"); - assertReturnedIndices(searchResponse, "test1", "test3"); + assertReturnedIndices(trySearch("*", "-test2"), "test1", "test3"); } public void testWildcardExclusion() { // index1 is not authorized and referred to through wildcard, test2 is excluded createIndicesWithRandomAliases("test1", "test2", "test21", "test3", "index1"); - - SearchResponse searchResponse = trySearch("*", "-test2*"); - assertReturnedIndices(searchResponse, "test1", "test3"); + assertReturnedIndices(trySearch("*", "-test2*"), "test1", "test3"); } public void testInclusionAndWildcardsExclusion() { // index1 is not authorized and referred to through wildcard, test111 and test112 are excluded createIndicesWithRandomAliases("test1", "test10", "test111", "test112", "test2", "index1"); - - SearchResponse searchResponse = trySearch("test1*", "index*", "-test11*"); - assertReturnedIndices(searchResponse, "test1", "test10"); + assertReturnedIndices(trySearch("test1*", "index*", "-test11*"), "test1", "test10"); } public void testExplicitAndWildcardsInclusionAndWildcardExclusion() { // index1 is not authorized and referred to through wildcard, test111 and test112 are excluded createIndicesWithRandomAliases("test1", "test10", "test111", "test112", "test2", "index1"); - - SearchResponse searchResponse = trySearch("test2", "test11*", "index*", "-test2*"); - assertReturnedIndices(searchResponse, "test111", "test112"); + assertReturnedIndices(trySearch("test2", "test11*", "index*", "-test2*"), "test111", "test112"); } public void testExplicitAndWildcardInclusionAndExplicitExclusions() { // index1 is not authorized and referred to through wildcard, test111 and test112 are excluded createIndicesWithRandomAliases("test1", "test10", "test111", "test112", "test2", "index1"); - - SearchResponse searchResponse = trySearch("test10", "test11*", "index*", "-test111", "-test112"); - assertReturnedIndices(searchResponse, "test10"); + assertReturnedIndices(trySearch("test10", "test11*", "index*", "-test111", "-test112"), "test10"); } public void testMissingDateMath() { - expectThrows(ElasticsearchSecurityException.class, () -> trySearch("")); - expectThrows(IndexNotFoundException.class, () -> trySearch("")); + expectThrows(ElasticsearchSecurityException.class, trySearch("")); + expectThrows(IndexNotFoundException.class, trySearch("")); } public void testMultiSearchUnauthorizedIndex() { @@ -424,12 +409,25 @@ public void testMultiTermVectors() { assertThat(response.getResponses()[4].getFailure().getCause(), instanceOf(IndexNotFoundException.class)); } - private SearchResponse trySearch(String... indices) { - return client().prepareSearch(indices).get(TimeValue.timeValueSeconds(20)); + private SearchRequestBuilder trySearch(String... indices) { + return client().prepareSearch(indices); + } + + private SearchRequestBuilder trySearch(IndicesOptions options, String... indices) { + return client().prepareSearch(indices).setIndicesOptions(options); } - private SearchResponse trySearch(IndicesOptions options, String... indices) { - return client().prepareSearch(indices).setIndicesOptions(options).get(TimeValue.timeValueSeconds(20)); + private static T expectThrows(Class expectedType, SearchRequestBuilder searchRequestBuilder) { + return expectThrows(expectedType, searchRequestBuilder::get); + } + + private static void assertReturnedIndices(SearchRequestBuilder searchRequestBuilder, String... indices) { + var searchResponse = searchRequestBuilder.get(); + try { + assertReturnedIndices(searchResponse, indices); + } finally { + searchResponse.decRef(); + } } private static void assertReturnedIndices(SearchResponse searchResponse, String... indices) { From baf251eb63b6c936643701a8fd95404cd9499eb4 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 18 Oct 2023 21:44:51 +0300 Subject: [PATCH 005/203] ESQL: Alias duplicated aggregations in a stats (#100642) Replace duplicated aggregations in a stat with an alias (through a synthetic eval). Additionally introduce agg normalization which replaces field aliases inside agg and the literals in Count with "*". This improves the query: eval x = salary stats c = count(), m = min(x), m1 = min(salary), c1 = count(1) to stats c = count(*), m = min(x) eval m1 = m, c1 = c keep c, m, m1, c1 Fix #100544 --- docs/changelog/100642.yaml | 6 + .../src/main/resources/stats.csv-spec | 16 ++ .../esql/optimizer/LogicalPlanOptimizer.java | 129 +++++++++++++++- .../LocalPhysicalPlanOptimizerTests.java | 23 ++- .../optimizer/LogicalPlanOptimizerTests.java | 145 +++++++++++++++++- 5 files changed, 314 insertions(+), 5 deletions(-) create mode 100644 docs/changelog/100642.yaml diff --git a/docs/changelog/100642.yaml b/docs/changelog/100642.yaml new file mode 100644 index 0000000000000..805a20174e11d --- /dev/null +++ b/docs/changelog/100642.yaml @@ -0,0 +1,6 @@ +pr: 100642 +summary: "ESQL: Alias duplicated aggregations in a stats" +area: ES|QL +type: enhancement +issues: + - 100544 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec index d671ba6ec13b1..acf42d908ed66 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec @@ -681,3 +681,19 @@ c:l | job_positions:s 4 |Reporting Analyst 4 |Tech Lead ; + +duplicateAggregationsWithoutGrouping +from employees | eval x = salary | stats c = count(), m = min(x), m1 = min(salary), c1 = count(1); + +c:l | m:i | m1:i | c1:l +100 | 25324 | 25324 | 100 +; + +duplicateAggregationsWithGrouping +from employees | eval x = salary | stats c = count(), m = min(x), m1 = min(salary), c1 = count(1) by gender | sort gender; + +c:l| m:i | m1:i | c1:l| gender:s +33 | 25976 | 25976 | 33 | F +57 | 25945 | 25945 | 57 | M +10 | 25324 | 25324 | 10 | null +; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java index 23fa051c1d7a2..ed215efc4c066 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.esql.optimizer; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.util.Maps; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BlockUtils; @@ -57,8 +58,10 @@ import org.elasticsearch.xpack.ql.plan.logical.UnaryPlan; import org.elasticsearch.xpack.ql.rule.Rule; import org.elasticsearch.xpack.ql.rule.RuleExecutor; +import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.util.CollectionUtils; import org.elasticsearch.xpack.ql.util.Holder; +import org.elasticsearch.xpack.ql.util.StringUtils; import java.time.ZoneId; import java.util.ArrayList; @@ -95,13 +98,14 @@ protected static List> rules() { new SubstituteSurrogates(), new ReplaceRegexMatch(), new ReplaceAliasingEvalWithProject() - // new ReplaceTextFieldAttributesWithTheKeywordSubfield() + // new NormalizeAggregate(), - waits on https://github.com/elastic/elasticsearch/issues/100634 ); var operators = new Batch<>( "Operator Optimization", new CombineProjections(), new CombineEvals(), + new ReplaceDuplicateAggWithEval(), new PruneEmptyPlans(), new PropagateEmptyRelation(), new ConvertStringToByteRef(), @@ -947,4 +951,127 @@ private LogicalPlan rule(Eval eval) { } } + + /** + * Normalize aggregation functions by: + * 1. replaces reference to field attributes with their source + * 2. in case of Count, aligns the various forms (Count(1), Count(0), Count(), Count(*)) to Count(*) + */ + // TODO waiting on https://github.com/elastic/elasticsearch/issues/100634 + static class NormalizeAggregate extends Rule { + + @Override + public LogicalPlan apply(LogicalPlan plan) { + AttributeMap aliases = new AttributeMap<>(); + + // traverse the tree bottom-up + // 1. if it's Aggregate, normalize the aggregates + // regardless, collect the attributes but only if they refer to an attribute or literal + plan = plan.transformUp(p -> { + if (p instanceof Aggregate agg) { + p = normalize(agg, aliases); + } + p.forEachExpression(Alias.class, a -> { + var child = a.child(); + if (child.foldable() || child instanceof NamedExpression) { + aliases.putIfAbsent(a.toAttribute(), child); + } + }); + + return p; + }); + return plan; + } + + private static LogicalPlan normalize(Aggregate aggregate, AttributeMap aliases) { + var aggs = aggregate.aggregates(); + List newAggs = new ArrayList<>(aggs.size()); + boolean changed = false; + + for (NamedExpression agg : aggs) { + if (agg instanceof Alias as && as.child() instanceof AggregateFunction af) { + // replace field reference + if (af.field() instanceof NamedExpression ne) { + Attribute attr = ne.toAttribute(); + var resolved = aliases.resolve(attr, attr); + if (resolved != attr) { + changed = true; + var newChildren = CollectionUtils.combine(Collections.singletonList(resolved), af.parameters()); + // update the reference so Count can pick it up + af = (AggregateFunction) af.replaceChildren(newChildren); + agg = as.replaceChild(af); + } + } + // handle Count(*) + if (af instanceof Count count) { + var field = af.field(); + if (field.foldable()) { + var fold = field.fold(); + if (fold != null && StringUtils.WILDCARD.equals(fold) == false) { + changed = true; + var source = count.source(); + agg = as.replaceChild(new Count(source, new Literal(source, StringUtils.WILDCARD, DataTypes.KEYWORD))); + } + } + } + } + newAggs.add(agg); + } + return changed ? new Aggregate(aggregate.source(), aggregate.child(), aggregate.groupings(), newAggs) : aggregate; + } + } + + /** + * Replace aggregations that are duplicated inside an Aggregate with an Eval to avoid duplicated compute. + * stats a = min(x), b = min(x), c = count(*), d = count() by g + * becomes + * stats a = min(x), c = count(*) by g + * eval b = a, d = c + * keep a, b, c, d, g + */ + static class ReplaceDuplicateAggWithEval extends OptimizerRules.OptimizerRule { + + ReplaceDuplicateAggWithEval() { + super(TransformDirection.UP); + } + + @Override + protected LogicalPlan rule(Aggregate aggregate) { + LogicalPlan plan = aggregate; + + boolean foundDuplicate = false; + var aggs = aggregate.aggregates(); + Map seenAggs = Maps.newMapWithExpectedSize(aggs.size()); + List projections = new ArrayList<>(); + List keptAggs = new ArrayList<>(aggs.size()); + + for (NamedExpression agg : aggs) { + var attr = agg.toAttribute(); + if (agg instanceof Alias as && as.child() instanceof AggregateFunction af) { + var seen = seenAggs.putIfAbsent(af, attr); + if (seen != null) { + foundDuplicate = true; + projections.add(as.replaceChild(seen)); + } + // otherwise keep the agg in place + else { + keptAggs.add(agg); + projections.add(attr); + } + } else { + keptAggs.add(agg); + projections.add(attr); + } + } + + // at least one duplicate found - add the projection (to keep the output in place) + if (foundDuplicate) { + var source = aggregate.source(); + var newAggregate = new Aggregate(source, aggregate.child(), aggregate.groupings(), keptAggs); + plan = new Project(source, newAggregate, projections); + } + + return plan; + } + } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java index 511a7ee08b5e1..72e12697488df 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.xpack.esql.plan.physical.LimitExec; import org.elasticsearch.xpack.esql.plan.physical.LocalSourceExec; import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan; +import org.elasticsearch.xpack.esql.plan.physical.ProjectExec; import org.elasticsearch.xpack.esql.planner.FilterTests; import org.elasticsearch.xpack.esql.planner.Mapper; import org.elasticsearch.xpack.esql.planner.PlannerUtils; @@ -41,6 +42,7 @@ import org.elasticsearch.xpack.esql.stats.Metrics; import org.elasticsearch.xpack.esql.stats.SearchStats; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; +import org.elasticsearch.xpack.ql.expression.Alias; import org.elasticsearch.xpack.ql.expression.Expressions; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; import org.elasticsearch.xpack.ql.index.EsIndex; @@ -299,6 +301,16 @@ public void testAnotherCountAllWithFilter() { assertThat(expected.toString(), is(esStatsQuery.query().toString())); } + /** + * Expected + * ProjectExec[[c{r}#3, c{r}#3 AS call, c_literal{r}#7]] + * \_LimitExec[500[INTEGER]] + * \_AggregateExec[[],[COUNT([2a][KEYWORD]) AS c, COUNT(1[INTEGER]) AS c_literal],FINAL,null] + * \_ExchangeExec[[count{r}#18, seen{r}#19, count{r}#20, seen{r}#21],true] + * \_EsStatsQueryExec[test], stats[Stat[name=*, type=COUNT, query=null], Stat[name=*, type=COUNT, query=null]]], + * query[{"esql_single_value":{"field":"emp_no","next":{"range":{"emp_no":{"gt":10010,"boost":1.0}}}}}] + * [count{r}#23, seen{r}#24, count{r}#25, seen{r}#26], limit[], + */ public void testMultiCountAllWithFilter() { var plan = plan(""" from test @@ -306,14 +318,19 @@ public void testMultiCountAllWithFilter() { | stats c = count(), call = count(*), c_literal = count(1) """, IS_SV_STATS); - var limit = as(plan, LimitExec.class); + var project = as(plan, ProjectExec.class); + var projections = project.projections(); + assertThat(Expressions.names(projections), contains("c", "call", "c_literal")); + var alias = as(projections.get(1), Alias.class); + assertThat(Expressions.name(alias.child()), is("c")); + var limit = as(project.child(), LimitExec.class); var agg = as(limit.child(), AggregateExec.class); assertThat(agg.getMode(), is(FINAL)); - assertThat(Expressions.names(agg.aggregates()), contains("c", "call", "c_literal")); + assertThat(Expressions.names(agg.aggregates()), contains("c", "c_literal")); var exchange = as(agg.child(), ExchangeExec.class); var esStatsQuery = as(exchange.child(), EsStatsQueryExec.class); assertThat(esStatsQuery.limit(), is(nullValue())); - assertThat(Expressions.names(esStatsQuery.output()), contains("count", "seen", "count", "seen", "count", "seen")); + assertThat(Expressions.names(esStatsQuery.output()), contains("count", "seen", "count", "seen")); var expected = wrapWithSingleQuery(QueryBuilders.rangeQuery("emp_no").gt(10010), "emp_no"); assertThat(expected.toString(), is(esStatsQuery.query().toString())); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index a22bb3b91ff0b..285ad7021e83f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.xpack.esql.expression.Order; import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry; import org.elasticsearch.xpack.esql.expression.function.aggregate.Count; +import org.elasticsearch.xpack.esql.expression.function.aggregate.Max; import org.elasticsearch.xpack.esql.expression.function.aggregate.Min; import org.elasticsearch.xpack.esql.expression.function.aggregate.Percentile; import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum; @@ -1923,6 +1924,147 @@ public void testPruneRenameOnAggBy() { var source = as(agg.child(), EsRelation.class); } + /** + * Expects + * Project[[c1{r}#2, c2{r}#4, cs{r}#6, cm{r}#8, cexp{r}#10]] + * \_Eval[[c1{r}#2 AS c2, c1{r}#2 AS cs, c1{r}#2 AS cm, c1{r}#2 AS cexp]] + * \_Limit[500[INTEGER]] + * \_Aggregate[[],[COUNT([2a][KEYWORD]) AS c1]] + * \_EsRelation[test][_meta_field{f}#17, emp_no{f}#11, first_name{f}#12, ..] + */ + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100634") + public void testEliminateDuplicateAggsCountAll() { + var plan = plan(""" + from test + | stats c1 = count(1), c2 = count(2), cs = count(*), cm = count(), cexp = count("123") + """); + + var project = as(plan, Project.class); + assertThat(Expressions.names(project.projections()), contains("c1", "c2", "cs", "cm", "cexp")); + var eval = as(project.child(), Eval.class); + var fields = eval.fields(); + assertThat(Expressions.names(fields), contains("c2", "cs", "cm", "cexp")); + for (Alias field : fields) { + assertThat(Expressions.name(field.child()), is("c1")); + } + var limit = as(eval.child(), Limit.class); + var agg = as(limit.child(), Aggregate.class); + var aggs = agg.aggregates(); + assertThat(Expressions.names(aggs), contains("c1")); + aggFieldName(aggs.get(0), Count.class, "*"); + var source = as(agg.child(), EsRelation.class); + } + + /** + * Expects + * Project[[c1{r}#7, cx{r}#10, cs{r}#12, cy{r}#15]] + * \_Eval[[c1{r}#7 AS cx, c1{r}#7 AS cs, c1{r}#7 AS cy]] + * \_Limit[500[INTEGER]] + * \_Aggregate[[],[COUNT([2a][KEYWORD]) AS c1]] + * \_EsRelation[test][_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, ..] + */ + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100634") + public void testEliminateDuplicateAggsWithAliasedFields() { + var plan = plan(""" + from test + | eval x = 1 + | eval y = x + | stats c1 = count(1), cx = count(x), cs = count(*), cy = count(y) + """); + + var project = as(plan, Project.class); + assertThat(Expressions.names(project.projections()), contains("c1", "cx", "cs", "cy")); + var eval = as(project.child(), Eval.class); + var fields = eval.fields(); + assertThat(Expressions.names(fields), contains("cx", "cs", "cy")); + for (Alias field : fields) { + assertThat(Expressions.name(field.child()), is("c1")); + } + var limit = as(eval.child(), Limit.class); + var agg = as(limit.child(), Aggregate.class); + var aggs = agg.aggregates(); + assertThat(Expressions.names(aggs), contains("c1")); + aggFieldName(aggs.get(0), Count.class, "*"); + var source = as(agg.child(), EsRelation.class); + } + + /** + * Expects + * Project[[min{r}#1385, max{r}#1388, min{r}#1385 AS min2, max{r}#1388 AS max2, gender{f}#1398]] + * \_Limit[500[INTEGER]] + * \_Aggregate[[gender{f}#1398],[MIN(salary{f}#1401) AS min, MAX(salary{f}#1401) AS max, gender{f}#1398]] + * \_EsRelation[test][_meta_field{f}#1402, emp_no{f}#1396, first_name{f}#..] + */ + public void testEliminateDuplicateAggsMixed() { + var plan = plan(""" + from test + | stats min = min(salary), max = max(salary), min2 = min(salary), max2 = max(salary) by gender + """); + + var project = as(plan, Project.class); + var projections = project.projections(); + assertThat(Expressions.names(projections), contains("min", "max", "min2", "max2", "gender")); + as(projections.get(0), ReferenceAttribute.class); + as(projections.get(1), ReferenceAttribute.class); + assertThat(Expressions.name(aliased(projections.get(2), ReferenceAttribute.class)), is("min")); + assertThat(Expressions.name(aliased(projections.get(3), ReferenceAttribute.class)), is("max")); + + var limit = as(project.child(), Limit.class); + var agg = as(limit.child(), Aggregate.class); + var aggs = agg.aggregates(); + assertThat(Expressions.names(aggs), contains("min", "max", "gender")); + aggFieldName(aggs.get(0), Min.class, "salary"); + aggFieldName(aggs.get(1), Max.class, "salary"); + var source = as(agg.child(), EsRelation.class); + } + + /** + * Expects + * EsqlProject[[a{r}#5, c{r}#8]] + * \_Eval[[null[INTEGER] AS x]] + * \_EsRelation[test][_meta_field{f}#15, emp_no{f}#9, first_name{f}#10, g..] + */ + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100634") + public void testEliminateDuplicateAggWithNull() { + var plan = plan(""" + from test + | eval x = null + 1 + | stats a = avg(x), c = count(x) + """); + fail("Awaits fix"); + } + + /** + * Expects + * Project[[max(x){r}#11, max(x){r}#11 AS max(y), max(x){r}#11 AS max(z)]] + * \_Limit[500[INTEGER]] + * \_Aggregate[[],[MAX(salary{f}#21) AS max(x)]] + * \_EsRelation[test][_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, ..] + */ + public void testEliminateDuplicateAggsNonCount() { + var plan = plan(""" + from test + | eval x = salary + | eval y = x + | eval z = y + | stats max(x), max(y), max(z) + """); + + var project = as(plan, Project.class); + var projections = project.projections(); + assertThat(Expressions.names(projections), contains("max(x)", "max(y)", "max(z)")); + as(projections.get(0), ReferenceAttribute.class); + assertThat(Expressions.name(aliased(projections.get(1), ReferenceAttribute.class)), is("max(x)")); + assertThat(Expressions.name(aliased(projections.get(2), ReferenceAttribute.class)), is("max(x)")); + + var limit = as(project.child(), Limit.class); + var agg = as(limit.child(), Aggregate.class); + var aggs = agg.aggregates(); + assertThat(Expressions.names(aggs), contains("max(x)")); + aggFieldName(aggs.get(0), Max.class, "salary"); + var source = as(agg.child(), EsRelation.class); + } + private T aliased(Expression exp, Class clazz) { var alias = as(exp, Alias.class); return as(alias.child(), clazz); @@ -1932,7 +2074,8 @@ private void aggFieldName(Expression exp, Class var alias = as(exp, Alias.class); var af = as(alias.child(), aggType); var field = af.field(); - assertThat(Expressions.name(field), is(fieldName)); + var name = field.foldable() ? BytesRefs.toString(field.fold()) : Expressions.name(field); + assertThat(name, is(fieldName)); } private LogicalPlan optimizedPlan(String query) { From 65f91cfa71d47be7cb40d17e9cd6593b37318d57 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Wed, 18 Oct 2023 16:16:08 -0400 Subject: [PATCH 006/203] Ensure mappings are fully updated before reading for all 60_dense_vector_dynamic_mapping (#101086) other tests rely on getting the mapping and dynamically updated it, not just ones creating for vectors (specifically there are dynamic `float` mappings in this yaml test suite too). --- .../60_dense_vector_dynamic_mapping.yml | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml index af09c56f0cfca..ffc7f749b8fd3 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml @@ -41,7 +41,9 @@ teardown: refresh: true body: my_field: [ 230.0, 300.33, -34.8988, 15.555, -200.0 ] - + - do: + cluster.health: + wait_for_events: languid - do: indices.get_mapping: index: test-too-short-still-float @@ -60,6 +62,9 @@ teardown: my_field: [ -457.1953,259.6788,271.9127,-26.8833,403.0915,-56.9197,-445.8869,-108.8167,417.8988,13.4232,-281.765,-405.8573,262.7831,-279.493,328.5591,-453.3941,-116.0368,435.4734,-439.0927,-332.9565,355.4955,324.9878,33.3519,-165.0182,188.1811,467.3455,185.1057,-233.8598,-17.6827,283.4271,-329.1247,-402.9721,404.7866,-358.7031,-267.4074,441.8363,320.2389,-128.0179,339.544,196.2018,-60.2688,336.0228,-440.1943,318.6882,-158.2596,277.0925,-487.4971,-338.9865,-275.716,136.8547,-253.6206,-40.2807,-357.0971,188.0344,-203.0674,449.9618,-223.2508,468.1441,302.4002,-65.0044,342.4431,205.6774,-118.636,-29.9706,183.9825,223.956,314.0691,137.0129,-8.0452,-15.131,-269.8643,-12.691,228.9777,-147.8384,-347.1117,-283.1905,459.2004,296.1321,-483.1799,414.3423,383.0187,-408.5525,-286.8169,482.5853,9.5232,-459.4968,-333.2521,109.0969,129.5107,43.4369,455.8283,-4.0423,-318.5019,339.1641,416.3581,-309.0429,84.2325,-355.8753,264.7671,43.8922,-298.6039,412.4413,19.4198,-251.279,-191.157,-478.2058,251.5709,-178.9633,479.293,188.399,380.9755,268.6575,120.3467,-322.0305,-255.4894,-377.515,56.9153,-133.9486,156.2546,-428.9581,-54.994,28.2146,158.7121,-426.7307,491.0086,-150.7205,-233.1005,244.5174,45.911,-406.1181,233.1636,175.9334,414.2805,421.7396,-322.8029,-252.2412,35.7622,318.5223,-141.5121,-375.4407,380.3081,222.1228,443.7844,367.377,-202.9594,-493.6231,-184.2242,-253.9838,463.1952,-416.3887,252.0867,-63.5317,411.0727,98.6261,330.7369,363.5685,-498.1848,-413.7246,-2.5996,-238.3547,-355.6041,-303.698,43.6266,383.1105,-72.3066,274.7491,321.9322,220.9543,-30.5578,400.0891,-181.7069,-386.4403,497.2206,-408.9611,138.485,-133.5666,-340.2569,-223.6313,270.884,-215.9399,74.3931,-244.1364,353.4219,-156.9905,488.3148,96.352,401.8525,-468.8344,129.9715,-27.1953,-168.631,187.7049,-336.5255,331.0652,204.3538,36.0182,366.8502,-468.6579,478.1409,-332.6136,-281.8499,63.7165,-458.8161,14.8894,-145.6397,267.1499,85.2025,326.3764,-419.6361,-133.9626,102.0618,443.3099,-207.9032,132.7032,234.001,-26.0754,105.6478,174.1252,-403.3511,-164.9714,-262.9344,-58.9668,357.6414,355.7508,-331.8443,153.5733,417.5712,260.7394,-150.1053,-435.6525,-364.1558,328.6183,-270.0863,107.1746,345.7998,480.8749,206.3896,-498.237,495.0835,481.9384,418.5571,-246.5213,-363.7304,311.7076,-53.1664,-297.3839,122.3105,-13.9226,-145.9754,-189.1748,460.9375,194.5417,-28.1346,-261.2177,-88.8396,-254.6407,-465.3148,-169.5377,24.3113,-116.2323,-420.3526,317.2107,-231.6227,-270.8239,387.8598,412.4251,428.1373,308.2044,275.2082,402.3663,-209.9843,-492.7269,225.1948,326.469,207.3557,-131.7677,371.9408,-139.3098,324.205,-126.6204,-335.0853,-248.2587,-344.907,307.2109,-441.3296,-318.027,414.6535,172.0537,-280.4991,331.0475,-158.0178,-285.1951,12.3632,149.9347,282.8302,-91.5624,-180.6097,496.0881,368.2567,357.6875,-194.2106,48.9213,-479.2956,-165.139,238.7811,302.7007,297.2805,208.7099,-5.5755,-85.7911,-358.1111,344.6131,415.7199,-219.1525,490.5003,-46.0096,498.2818,-91.8067,384.0104,396.1107,408.2827,-5.3919,-333.7992,-168.985,273.72,359.7125,227.7621,158.3406,-366.9722,3.7709,27.2728,71.9754,269.5792,-365.281,117.9152,-184.3682,356.9013,-142.6579,-496.7598,122.0194,89.1247,4.1914,-81.9905,465.0841,115.4727,169.6116,-199.9951,-223.3149,-447.3022,11.831,320.2368,105.1316,344.2462,8.6333,62.2285,-70.3944,-284.6694,-482.4229,-448.1569,-237.7858,222.3921,-172.1386,-312.5756,-390.0565,398.951,119.9784,-419.6537,121.3186,481.3011,-181.6662,-56.0219,424.1359,7.1461,138.8567,-307.0606,334.066,254.0897,473.7227,45.5936,133.7268,49.5334,-283.3406,179.4466,105.6191,-30.4162,271.5774,6.1156,110.4732,286.4325,13.3431,494.0139,-371.7624,283.3652,272.0558,-302.343,122.7245,-463.9261,299.9807,282.4502,-262.4911,183.4289,222.7474,-229.5973,141.6188,262.5468,278.1155,-331.0891,-393.6027,-230.1461,201.6657,-93.3604,-395.8877,-125.2013,-222.973,368.3759,234.6628,-28.6809,-151.0703,432.0315,253.1214,430.7065,-143.6963,499.84,85.1683,280.4354,196.6013,139.0476,120.8148,-398.8155,-335.5504,229.0516,403.8604,-383.9868,-79.975,-152.77,220.4036,135.0355,238.2176,-242.3085,-177.0743,381.8202,411.167,378.0153,456.5976,364.013,24.2316,-395.4659,-210.2581,138.7539,479.7398,-291.7797,-123.0491,188.9817,42.8931,-354.4479,358.853,-43.6168,-190.6656,-103.3037,47.8915,-358.5402,374.9758,493.9951,-427.2376,-119.1142,-453.2975,-326.2696,-212.8273,-142.2931,-179.795,355.77,-156.2903,331.2006,451.9252,185.2944,-96.1941,173.0447,345.2744,43.0151,381.7845,-143.4125,84.654,-208.7053,-293.141,333.6349,-80.472,-376.9817,214.6298,-43.0931,-254.7834,-421.6961,-368.844,467.5544,-418.61,-66.6824,-350.2671,348.8241,252.3495,41.8677,-128.869,90.0391,-136.7405,-136.7822,489.8074,-396.8204,63.8355,323.9557,-83.6674,451.263,152.8955,-291.7497,410.0787,-299.7468,51.34,-298.6066,-58.853,325.911,-281.9541,-15.3457,299.1325,-347.4959,388.407,343.1096,28.1816,24.3013,-111.3312,190.5583,279.9848,-479.8894,123.2182,233.8425,-466.2128,-134.7122,217.8674,432.9523,-186.799,-477.2512,-223.5514,64.274,141.5251,-161.2187,150.2791,-228.1087,81.172,451.0879,-230.3818,-304.9398,402.1081,199.1266,275.3423,-123.9548,-21.1815,-384.544,446.9626,208.9692,-337.4827,-58.1011,344.2642,230.2868,44.9176,245.9885,-284.1875,-351.6104,108.1289,459.649,191.4334,53.591,136.7139,10.5912,-15.8411,62.8305,448.5256,194.7705,-356.3214,84.4996,-133.2502,-358.6308,262.7949,219.8741,-355.3985,468.2922,243.7227,-408.3166,188.6111,-221.7264,-286.8234,-340.3046,-224.5375,332.2615,73.2788,-24.7857,-485.2204,-136.7196,-162.9693,92.6017,-99.611,-186.5203,495.5483,240.8051,409.6493,-58.1321,-154.1239,-335.9719,-82.4408,-471.3057,-43.373,301.0884,-96.6359,-236.6906,435.7313,-227.7263,-406.8904,-392.3187,169.0043,-371.0852,-271.3652,-57.4466,-196.8455,52.741,361.7395,-117.8599,190.5339,276.6457,-321.9851,425.881,-473.2662,-74.2968,221.3612,-465.4429,181.723,-78.4508,21.6152,148.8107,-166.1687,-281.6391,-462.3636,-420.5255,-161.4143,98.8383,-374.5345,-366.2851,187.1506,-405.1865,239.4847,-246.8352,33.1748,-344.1211,477.9759,-294.1354,-359.5015,-44.8454,151.7072,-22.7324,-260.3293,99.1414,-20.5536,173.3766,-422.6692,458.3853,-199.7898,-236.3929,365.2599,-66.4191,388.3472,283.0336,-268.9463,269.5704,360.9679,-322.102,-407.0705,-93.0994,338.9108,-189.1359,-216.9102,-249.0153,122.6058,-254.8318,-112.2771,-279.0506,-168.4431,392.888,394.7607,468.0544,340.1852,-293.1288,-8.2912,-419.2608,323.3382,-93.8793,-242.0672,427.7716,-441.6906,128.3229,424.4679,-71.8586,134.5411,-74.5205,18.4141,17.7277,126.9123,-137.6119,33.3783,222.9912,-279.3582,89.1226,-90.031,12.7221,98.7767,-80.2372,-485.9212,-481.6575,-325.9729,318.8005,-433.786,-296.6337,421.6515,-27.2786,-445.2456,451.8876,-482.1014,-143.1098,186.1258,-90.2432,-297.7479,-351.0026,-423.7518,-219.6096,-269.2043,33.5767,-325.4335,392.4866,-418.243,112.5852,-248.1306,451.2154,-419.2995,154.5752,483.6323,-315.962,-196.872,406.1769,-356.9868,67.5251,-255.6475,103.5181,-450.4418,386.9518,456.4057,99.4591,-166.636,275.5374,200.4925,99.7623,292.6794,-422.3998,419.4837,-466.548,-462.8519,-381.4489,472.8356,-129.9563,441.4941,-376.1232,-114.1945,233.5531,313.6963,394.9503,-278.7558,350.7515,47.9427,220.7074,-178.9789,-346.0485,-128.5665,8.9461,159.9838,-57.3637,351.9478,-65.9411,-258.1788,498.9494,-472.613,-428.5678,17.3981,-435.3682,-421.155,-54.9177,-490.2348,178.3777,-31.9618,-242.1805,362.3736,380.8179,446.4272,-23.9142,61.3588,-489.5704,363.6446,-186.1519,-351.8684,-322.2791,-226.0431,404.6996,203.9824,306.0958,234.0145,-180.4996,452.0633,257.171,-83.6197,-393.152,396.6934,32.156,-428.7645,183.7886,494.767,68.3905,278.9785,-40.4759,261.7298,236.5778,4.5577,-130.9582,433.2837,-298.1139,-107.9822,-196.8446,-121.1765,-292.5509,-246.4546,-258.6038,280.1334,-52.6511,483.2928,-185.7577,-75.3705,351.3411,179.1282,-479.3838,166.2733,-197.9043,282.6848,-50.4744,-492.7178,183.6435,-127.2379,483.646,433.0805,-228.5488,139.8314,-145.1337,-403.1749,306.2704,122.7149,479.6928,85.3866,108.095,-224.152,494.6848,-368.4504,-180.7579,61.7136,51.2045,-383.0103,-376.4816,-292.8217,-201.118,332.1516,425.2758,138.1284,-229.4302,432.9081,2.9898,-437.7631,-448.2151,129.9126,-170.2405,499.0396,-48.2137,363.8046,-423.2511,-28.0804,-267.826,-356.6288,-99.9371,-409.8465,170.4902,-269.2584,-277.4098,300.8819,-142.5889,339.0952,16.2275,-310.8646,201.0733,-495.5905,341.9279,-149.1184,-494.4928,-81.7343,209.9762,273.4892,380.3163,359.2424,-242.5,-42.1268,-303.9792,11.6018,361.5483,416.4178,10.3282,195.9796,148.8096,-60.9724,-205.5221,-145.4574,-341.5913,426.8996,-19.5843,60.6265,-133.4191,-139.8737,281.7465,461.2854,-270.8902,61.0182,-58.6791,-254.0193,-234.1206,-208.7334,39.7498,-14.337,-68.2319,-342.2756,403.6834,401.6122,-166.1637,47.3592,-325.7,274.5459,343.4873,328.3783,-370.1657,-122.8967,-231.3182,122.6609,119.2685,-223.5437,-210.8076,116.5022,340.2814,256.1852,-217.3487,-150.9598,331.1343,-453.8182,-448.0842,-95.2475,-340.9942,-416.7835,-96.7226,-328.7212,-373.4337,472.2214,-484.522,-465.1583,330.0712,73.2052,-55.1266,-352.8984,341.0742,-230.4845,321.0752,236.2116,35.1902,75.3489,-469.4042,110.2036,35.1156,454.7224,103.0685,-221.7499,-23.6898,-259.2362,-110.509,-261.0039,219.2391,-139.9404,155.7723,377.9713,434.0318,-365.1397,459.1471,-318.5774,323.4256,194.325,-311.9529,-153.9019,-346.5811,76.4069,443.2121,-199.407,495.6636,-138.5213,-145.3432,-151.7758,-365.3547,263.6507,-491.1686,-183.5585,-12.6044,318.5346,-443.8639,-179.0338,477.9093,-355.5118,-423.0035,-229.1166,-96.7782,-479.2384,192.9085,223.3407,-302.9472,297.3847,477.584,-297.5958,168.6023,-80.6912,-89.8717,87.1476,-129.7807,346.5576,-253.9729,-399.6858,-389.5785,35.1648,-180.451,-49.6084,83.9582,-185.2329,97.283,195.5249,-91.6969,199.202,-449.792,333.4825,-113.7558,443.434,394.3587,-94.9074,71.2092,-251.1774,-85.047,-46.4004,20.2595,341.1073,-91.2527,86.3775,303.1247,-336.9011,343.9894,-384.1261,154.4411,-465.2493,-63.3249,488.0231,348.6725,458.2093,322.401,220.2532,283.3734,-386.4252,-256.5262,-87.2205,96.8199,47.6908,-399.6307,214.7716,-19.9177,-458.513,-194.3218,-320.5342,-275.857,-301.6955,-84.9038,358.3475,-88.9271,499.7721,-161.7403,355.4894,313.6211,-176.1703,61.8427,107.603,-176.063,-426.5408,292.3612,58.3331,-115.8853,471.4131,-76.4815,-309.6263,361.4518,192.4763,-145.7968,256.3888,133.335,-474.0901,-366.9793,-495.223,457.2366,170.056,285.0152,89.8213,225.2251,354.1822,-298.374,-332.9164,-55.2409,306.9283,25.9392,218.0624,7.5085,-151.8768,-155.4932,6.0001,201.4506,-259.9874,485.1078,-362.8516,-230.1434,-398.2512,243.0012,32.302,-197.91,144.1195,-89.4196,-44.0399,-371.7866,227.6007,492.7526,499.3824,162.2475,279.0325,177.0781,341.0137,199.6009,108.1678,312.2319,-211.5001,-92.675,357.0513,-337.924,-348.984,-350.3677,173.3473,-193.7346,-318.5609,-2.0928,46.6287,-346.8513,36.634,-277.4949,-149.325,481.1378,370.3864,-139.6689,-332.2805,48.0292,109.8363,494.6994,373.6992,495.7442,400.4998,-26.2276,-308.7669,188.9497,257.9182,-116.6944,269.8932,197.005,123.1139,-356.2058,485.1982,-4.0119,397.8434,-204.67,-494.5133,-414.1299,142.1512,-36.5446,390.0718,6.9876,263.1216,457.5598,89.6086,-266.3804,17.3457,88.8182,236.6271,81.175,-170.2249,-5.7664,422.7852,180.3349,-135.2642,149.2285,-70.6607,-46.169,-389.3313,230.6125,388.4853,-438.3426,111.8034,300.0416,37.5604,-437.3868,-114.1336,312.7777,-99.1161,-312.9015,-147.3787,-434.0536,19.5034,141.706,-281.4504,-208.9608,281.4619,-361.0596,-464.2757,77.8205,232.5575,165.4104,424.8738,124.5555,342.038,86.7543,278.0216,311.2686,337.834,-90.0545,-210.1143,-488.4095,-80.7535,92.3731,-122.622,-288.0571,1.7285,-5.2998,100.0717,-395.0571,-477.5587,-160.5642,-119.4214,-232.233,415.7276,-204.3216,-436.7766,-103.4644,-427.0939,-31.0927,-440.2919,120.5971,-223.3623,-199.0988,304.8697,432.5731,-231.5791,-397.696,306.4134,330.1018,32.4345,-175.719,464.6091,-291.5686,300.1631,-167.4592,238.9574,104.5893,-187.2215,-294.0111,-361.9094,480.6847,-304.2133,-448.7144,67.7235,-255.9669,254.7379,464.5465,6.8909,-368.7554,337.5993,39.1928,-376.0625,433.4224,-109.1488,341.7731,377.843,446.839,-192.283,251.1592,437.6812,-478.3409,345.7668,377.965,125.6188,-462.0904,-235.3324,316.8892,-460.7371,248.9306,418.7082,-333.7257,-104.5062,-408.1356,148.6624,-158.4929,-477.0664,80.4926,-214.6292,211.3377,322.7854,-312.851,403.0215,-213.3089,-71.3355,-276.1068,-293.0902,-277.4559,54.2176,-119.1285,-479.4361,-492.6072,8.3732,42.4988,-5.576,-198.6151,-357.0952,-331.5667,186.6195,317.3075,201.267,-37.1731,-278.3164,-467.7796,-163.3909,-117.305,-233.9266,277.7969,181.9723,178.8292,-168.7152,-436.041,171.345,369.0302,423.7144,434.0961,-428.1816,23.7334,-136.6735,-222.4486,180.8461,57.5968,129.2984,127.1866,-109.3928,-143.6253,-385.9948,127.9867,-8.8096,-239.844,66.6491,-50.7301,-309.1113,-474.6991,212.1767,-444.4596,-211.3601,351.3551,335.0507,-128.6226,-98.5249,-257.454,489.8014,-378.8622,311.0304,-4.9107,362.7586,-458.8825,373.2779,-103.29,-5.6216,122.0183,76.9731,17.8771,289.8893,-56.4338,375.9665,-83.9991,440.0823,142.2309,-471.0813,-59.4847,-400.4217,91.4892,374.4009,486.8697,414.5213,-0.3535,-278.2345,-231.206,-238.479,389.3143,-276.9742,-33.9869,349.1201,127.3928,-410.7213,337.3789,36.4048,333.4291,-12.4075,483.8778,311.4489,-74.0628,-379.6051,463.234,157.5614,-140.9455,120.7926,-161.2341,194.162,-412.6181,-9.1258,-194.5065,441.1572,255.5455,-73.8086,-119.4013,-486.4792,-27.4352,98.9738,-119.002,-75.5589,261.7675,156.0993,89.6457,-190.6318,429.9325,195.9536,-172.6155,-22.7976,438.9412,-246.4661,447.7281,434.5346,405.8957,217.3324,392.6129,-158.604,15.8632,483.0414,334.7693,-307.2482,302.1267,-7.4125,3.8081,-405.7316,377.5069,51.2307,235.0695,269.737,-389.3487,186.4225,-36.8521,401.2051,-59.0378,-190.8023,-182.8076,-362.6136,-124.8064,362.4142,45.3344,-330.1214,-162.5452,-434.4411,219.1143,-374.1038,364.5639,-268.582,-22.9247,-73.8849,-54.5258,-23.0882,167.9233,-181.9807,-207.1173,300.2193,206.5903,-72.013,-244.4396,-435.5389,10.3523,-435.3545,-138.8392,449.8426,-244.8971,229.7666,267.5225,-401.6021,466.3278,418.3623,-317.8205,28.5192,384.5628,-79.6177,469.4532,-395.1986,-353.4477,-93.6914,70.3999,-441.0627,-201.1221,141.2748,433.3389,82.413,-394.0046,-438.6836,453.4704,-160.6535,353.0374,-238.0377,236.5195,497.9019,202.9472,-421.6417,-382.042,84.6308,430.1599,-390.9918,-195.0401,255.6526,-86.5964,-491.667,-199.1557,-102.7114,474.877,-292.9154,-77.3163,143.5625,58.8126,-284.8908,-457.6457,212.5317,480.4032,-324.0829,491.0165,-494.7934,267.4311,-142.2401,-368.9058,-370.4955,498.803,-6.7377,-395.373,177.8868,306.9761,80.4185,-239.1253,-435.1349,7.6298,-157.6242,348.6095,475.7845,317.7116,-353.7336,-40.2881,353.7096,-60.9783,-385.5816,243.8071,-398.8341,62.343,340.0251,-24.8105,-343.4186,189.6737,-467.3026,104.7127,159.5467,-482.5496,71.6951,-163.5304,-321.8438,185.2875,-331.6885,-102.6817,-242.7548,-259.4407,220.6898,231.6571,-297.1145,-186.9472,-316.9286,-36.2392,-293.964,296.3878,467.7409,-277.6389,493.2143,417.1244,12.241,-343.7893,-33.7207,457.2978,-248.9726,-409.5439,-92.4779,-173.7584,400.8483,59.7439,13.3265,-175.617,37.333,-307.6469,-82.3687,332.578,-412.0079,144.7037,350.6506,423.3235,-53.2147,67.9581,-447.3845,-461.0187,371.1702,386.2045,352.2722,-119.098,123.9178,-52.0535,465.2626,474.0272,402.9961,491.4763,-33.1373,-228.8607,-383.3299,408.8192,-275.155,489.8633,-349.5073,346.9781,129.3929,282.1868,-77.3384,277.3026,412.3277,263.6705,473.3756,-437.9988,114.1686,-452.3331,-167.8898,-193.6217,444.6168,-354.3223,-238.0967,432.0883,-349.7249,-42.3659,-304.7343,296.2192,-136.5386,-121.7774,450.4678,140.5384,-450.8993,93.8942,-54.4945,498.521,-461.7182,111.5166,-397.6007,-397.959,-20.9331,-19.7068,78.551,161.9472,-24.8682,-434.4537,102.9447,214.298,-494.3813,211.6782,64.8196,372.6962,-399.8337,114.5476,-191.0045,-369.6465,-391.7201,-204.9951,-201.7654,475.898,-262.3247,-348.6974,79.4062,-112.4281,-102.266,67.3008,335.485,68.4289,-433.9104,-392.963,-73.3788,276.5766,-105.2219,422.6201,192.915,-388.3541,242.3915,479.5633,42.5998,259.6189,-316.5861,390.1121,-216.0274,-373.296,103.7169,321.9107,19.0023,487.2627,151.6922,276.7424,461.6928,24.4758,133.263,-47.289,-413.9538,435.2414,-466.9724,-270.6602,238.9442,-110.5389,403.5151,-395.4393,-208.2219,-53.0773,-26.5792,-387.6534,-120.5566,143.2237,-305.3778,442.0665,417.9523,460.3337,254.8689,-375.9436,-101.0153,232.4727,-35.5285,-470.3007,-423.9161,-108.9997,-29.6555,233.1043,240.4766,404.763,276.8465,-354.4058,74.0678,-343.244,332.9786,361.2964,-322.0828,-41.1861,-122.8074,-299.5682,-481.218,-157.3994,310.6317,-261.176,310.2644,-239.9855,255.1004,-311.3351,437.9486,78.1311,-133.9261,-176.2119,45.9943,492.3169,266.5795,16.8553,-470.9413,-331.2718,218.4122,369.7118,-179.3201,-165.7277,-87.9832,357.6499,-261.0345,442.1609,113.2997,-112.5643,481.2426,-365.4958,400.5374,-395.085,303.8103,-292.0268,167.0744,-199.013,174.9283,498.3585,-337.466,303.9078,-326.0901,-331.7143,6.7189,-277.1371,-204.9097,-313.4259,-462.7296,437.8485,267.2872,157.752,143.8784,60.1304,-492.991,326.0132,-123.3415,390.8461,-293.0175,483.4759,240.4338,271.6879,483.4801,391.2687,238.3995,-246.607,-411.7722,-257.9864,238.0949,494.3455,-489.0838,-26.7283,317.1161,-264.0242,-16.6819,-141.4839,429.101,252.2336,-325.1541,471.044,452.352,7.4546,343.3004,-336.4424,489.6317,307.1831,-139.2075,153.572,-332.5617,-361.892,110.6459,-384.8117,-423.0834,-277.9929,44.5303,167.9458,364.1204,-222.5008,-148.7923,198.4694,-74.0043,-458.4327,-227.5346,272.4441,-477.2587,303.1998,72.3129,112.9422,-98.2577,296.903,-489.0569,-461.4503,-381.6239,-440.6212,-354.1834,356.1583,-220.6533,192.5295,-409.0818,-264.2973,498.2192,-306.675,-313.6103,-124.9266,-436.5922,297.9051,121.9351,425.3888,-283.9925,-360.441,-347.4517,8.6814,477.4163,-344.6926,-311.574,-199.9541,-272.862,-360.8642,-306.0856,-218.9529,200.1938,-187.9337,-149.341,-431.5156,-135.3958,131.1299,262.0532,-210.162,353.4392,-249.2969,216.4223,499.6139,215.8176,-346.1569,177.2202,-173.1132,-466.9007,-310.9848,463.485,6.516,-334.8823,-282.7409,-375.2367,-127.4937,257.2427,384.9285,206.4053,-283.9167,369.6312,-325.1146,452.7523,-103.9792,-51.036,153.325,-344.1749,289.4824,109.8308,375.2284,-249.8481,367.8478,71.0143,471.6136,-265.6336,12.9061,-470.1288,-113.547,38.8925,-205.7232,418.6063,475.6095,-18.8731,-431.5545,-288.6452,-406.8928,79.4828,-152.1474,345.565,-200.8038,174.7789,379.2991,-385.1188,-217.6888,241.9077,-449.1824,467.832,186.0095,-82.8376,-450.7827,-32.2903,-288.132,169.8581,-275.3198,-388.1222,-431.3601,64.9652,368.9351,107.4999,408.8666,267.7858,-462.4349,-198.4615,378.1182,252.7529,-344.883,-364.0161,-124.6144,-222.8902,-103.7114,387.1701,-363.7944,-237.934,230.2082,-63.1276,-456.8188,361.9248,461.0643,160.8127,305.6079,81.2236,-322.0002,-273.4727,-356.9758,227.4751,278.5386,-10.8627,49.6988,-495.2527,428.0901,393.6169,-360.5547,-137.0244,26.962,-326.3379,-399.4972,449.7645,-238.7444,-69.8461,222.6126,-68.7657,132.7567,255.7355,-190.3762,271.6129,405.5764,115.8834,0.9645,331.1665,396.4585,217.4435,-323.6914,39.5915,282.4489,411.3888,-219.2131,240.8913,-109.5264,-438.3067,-157.3961,-180.7485,-258.9153,61.7008,483.4718,-386.0406,-499.1824,-90.2675,-358.5152,-79.3051,-97.4094,-91.7246,63.539,-307.0526,226.416,-454.475,-375.7449,300.532,409.7526,7.7042,-320.297,-244.9896,-282.6645,-414.9866,-331.4623,316.162,348.8361,-342.8609,477.2374,6.5636,-483.931,341.3556,498.2318,-46.3428,203.981,101.2793,128.4547,-285.068,56.5149,-407.6478,-151.4672,116.6673,-115.0498,-491.7974,-151.9475,474.7827,-288.4179,286.4447,-430.6331,-279.1458,318.721,-276.8375,157.9586,-9.2346,398.8374,380.2256,61.1557,13.0746,-80.139,-134.8798,-37.6466,-209.7381,236.1511,388.5629,-196.1123,-481.5887,327.8334,408.2074,479.1439,85.082,227.7623,250.2644,-47.8238,464.8471,-431.5099,489.9794,452.9999,-50.8695,-429.0862,-138.8555,-395.3346,391.3405,-249.4682,-280.6761,-460.5297,1.0129,199.1008,-97.4134,-235.0172,-466.1287,-302.7993,298.4108,-22.478,173.9936,122.8033,-235.0353,231.5057,-97.2265,-203.8224,457.6806,484.1385,-309.3619,-168.3588,-177.2797,-3.9408,-279.2997,104.4862,-139.4921,-450.2539,402.541,-437.1151,-337.4914,-200.3446,-164.484,-293.7216,471.7414,192.6153,233.1926,-122.8377,356.5476,450.1361,-400.0941,61.0466,441.7145,189.7192,-69.6348,252.5418,-246.5242,-344.0219,14.2904,87.2185,-119.2684,205.422,-374.4802,33.4042,81.2271,-2.5025,-138.6816,8.1989,-439.7698,-446.1887,-374.9012,160.9795,49.3705,72.7925,245.9454,-138.7558,11.9923,414.9421,5.9535,-142.9589,396.2571,-222.2068,-2.6172,-90.5871,346.7415,-337.3213,-372.4473,91.8271,310.6442,263.7468,-357.0433,-246.0827,25.4967,55.8069,-64.7183,-342.7375,-356.7083,70.0885,-79.026,-346.3906,206.2687,-440.6602,321.8775,223.3025,159.6939,292.4308,241.077,-219.0901,495.9946,0.3506,-166.4262,475.1836,-272.5527,118.8711,458.2456,353.3839,-82.5653,37.2834,-92.4387,146.5082,233.4743,-408.0537,-469.9263,148.8959,-324.352,498.608,-324.5319,-114.6779,-200.4192,404.8448,-289.7989,400.6151,-372.9065,359.7581,141.4237,-304.6837,314.3738,-302.4693,442.6138,-224.0818,270.1887,-477.1098,429.0239,264.1871,26.84,283.4518,129.5215,6.6673,-91.4464,75.821,261.5692,-403.0782,-213.9284,-356.8221,-232.4484,33.5696,99.1931,344.0097,187.4695,-264.0572,-199.6103,342.5485,187.058,31.5948,-275.4046,215.9846,425.1114,327.1992,437.8426,-281.2049,71.7953,393.346,-339.9023,-78.8502,314.1866,-120.7207,-416.0802,-327.1001,413.6143,-236.2051,247.1197,318.5011,-194.295,486.3421,409.0831,252.6212,-452.654,-215.7497,-464.1643,61.9033,66.4139,-425.8918,-401.3522,-395.1639,427.7052,-264.1728,131.9144,258.4416,-442.2357,68.3167,441.5518,138.4774,470.7538,-14.6434,-436.2225,385.0708,286.1155,323.9014,137.4596,-352.5503,1.9307,-314.7656,449.5639,-468.3008,81.2499,487.4562,270.1387,-445.3627,460.1174,-205.2539,-32.6044,359.0438,-115.5841,-268.6624,-495.8554,-474.4781,337.9834,-281.4488,252.1636,-33.645,-26.6636,193.8834,287.2377,6.9748,414.4343,-211.7143,-23.0035,-226.5275,-400.285,-336.3935,28.1908,244.27,21.9938,-222.3759,-103.1418,464.7943,-256.0156,46.7511,-487.2509,-321.3631,479.2142,328.166,-481.2039,253.4962,100.2875,-399.98,-81.5868,289.7597,-318.7266,-264.2078,129.4063,407.6828,222.8346,370.0391,46.9838,-356.4992,-305.9992,-258.4048,-410.7736,-245.9092,32.9185,-237.9085,-403.8853,12.0239,-164.6252,107.369,8.0379,-139.3796,365.9266,-448.5863,314.1141,-280.0686,-463.4747,2.6092,-376.8811,96.7462,242.419,-480.9968,345.3697,328.281,39.0387,-342.3026,469.0461,-103.9411,381.0458,-141.6771,-4.7988,289.4799,-55.0671,-292.4788,364.1267,-395.9876,-232.5859,-285.7012,-444.7762,79.5454,251.5539,359.3705,467.2154,273.1778,-373.8216,299.611,-464.32,-106.0638,491.2626,-39.3721,-110.1154,383.4063,45.0848,262.2361,-111.754,249.0826,-305.9751,22.9663,-120.4794,484.0797,151.9063,388.5088,105.9067,444.0361,-45.5696,243.9313,303.4003,-27.795,-7.2151,411.6561,-100.6193,-207.3277,-6.4576,-300.3722,118.2638,342.3654,66.7861,104.0615,180.5752,281.6788,-342.7549,-65.8778,140.9091,-169.8935,-437.2435,-392.4147,-348.2217,202.3684,440.4071,-276.2247,129.5096,-43.4059,-456.876,-445.1126,-193.8847,-156.3408,274.7116,-129.6168,-484.7027,214.0806,375.6649,444.5303,-71.8577,-474.5957,-342.2716,-322.7281,205.6087,-14.3469,-283.0586,-86.2198,-420.3924,182.3599,22.7485,452.8141,-286.5839,155.1115,-316.4854,-28.3824,56.4873,-146.001,378.2396,473.2566,380.2417,-399.6208,-347.9016,206.5985,-145.9688,-219.9708,-216.6865,404.4334,324.8516,55.3154,-119.4645,-79.2847,-191.5158,-136.3728,413.3355,356.7344,-437.7335,404.9099,-494.6143,135.9107,151.2158,-161.0672,451.0975,-93.0876,495.7659,321.2577,-451.6211,-311.9214,-432.4626,496.8637,382.6126,97.7431,245.2208,-462.5156,-274.939,116.6882,80.6219,315.5602,-342.4345,274.387,-418.7591,53.5711,-96.2339,271.8546,-46.8098,150.3864,206.6682,311.9593,174.7625,-198.5948,105.6143,212.7571,237.4211,-21.2842,-383.0439,285.4973,-80.4955,105.5129,-158.8626,-156.2353,98.5192,-308.2654,-92.7883,45.686,-380.6921,140.1508,365.9526,108.1565,-140.4508,-246.5095,133.3693,-4.6582,-20.843,339.374,-99.2908,17.8824,242.8291,75.8953,-441.8762,-352.3943,-484.0549,-401.3674,321.6953,213.7102,261.1824,-41.5899,65.2736,-26.9977,152.9615,308.5357,-211.4979,477.2073,-414.7828,-330.2034,-123.7898,-261.1105,-328.6632,-15.1514,438.4531,-323.3771,-173.6672,-293.5578,459.1075,-18.34,-270.1311,-315.6445,348.4226,-435.2806,-419.9553,-106.1863,-283.0003,43.5508,-18.0891,224.808,406.4155,-163.6988,-129.2904,207.8322,474.5666,-60.1079,9.563,44.705,118.7999,-301.6795,-38.2161,410.4003,-190.4926,-430.6086,1.2693,312.7535,-455.5725,-271.7346,-159.4378,-227.9918,312.9331,166.2825,-31.7905,-227.9038,-421.644,296.5264,-335.4129,413.344,48.8782,217.3682,434.8719,-387.0484,170.5191,201.0157,127.1522,474.5561,-100.6847,-434.2549,29.5853,-467.6037,184.2936,116.9028,124.6507,-497.3002,-86.4991,59.6243,-104.9888,-294.6228,223.8354,-97.9298,64.2283,203.7397,186.3586,64.5045,122.1795,439.3753,464.9225,434.9882,85.5836,259.4985,70.5414,-117.1196,198.2037,-127.745,-200.2022,-386.0653,1.6688,272.3237,211.4442,445.0575,479.2069,-354.0842,-211.1788,160.3409,258.6131,-71.1154,-196.203,-95.1323,-398.3867,70.6868,15.5394,333.5079,187.8193,-393.7479,269.1152,-336.0885,339.4546,-147.6351,186.847,-126.4872,-108.1731,-70.3962,-389.0454,135.3408,-51.5671,4.6139,-3.1587,-274.941,-208.586,171.0845,-277.1015,-104.1653,-260.934,-310.5456,290.0738,-38.1867,-254.3353,31.6405,433.6526,86.9343,48.5563,137.4622,-34.6388,-1.5028,-452.3147,349.1007,-347.9019,70.4255,-201.5194,-430.2517,177.8199,-391.6226,20.1876,-287.8148,-190.1158,-356.0897,-319.7011,87.2696,-141.1962,-137.9268,-70.4841,95.4435,16.2261,191.5316,-214.8942,142.0224,209.0575,180.5105,26.1511,-497.0902,-186.2708,441.5505,-7.6379,23.9577,-401.2169,-339.3474,16.9572,269.8157,178.6692,299.5455,-367.3993,-413.7073,-96.9188,-472.0939,-327.975,129.6294,446.5669,-32.714,-120.6079,71.7334,190.4871,436.6714,110.0289,-108.4299,8.0033,-341.055,77.7304,-196.1335,-343.1391,-152.6897,-378.0097,-106.9584,395.4607,-98.6717,-131.0531,-140.8907,-185.3101,-68.8474,-478.2088,-18.3317,256.0313,-119.4212,334.7436,318.1335,-20.8287,-147.7622,118.1926,-218.2094,-478.7367,217.0914,219.1878,75.2151,231.5097,-410.8572,-46.2061,153.4654,264.0178,144.8928,-115.1857,-369.8591,126.6643,-122.1998,480.7727,-85.4362,134.3245,-34.403,124.6945,12.1795,-184.8116,390.6826,87.9712,367.0822,-233.2724,-245.9838,104.6339,-53.7753,-264.3381,50.9031,-122.0604,136.6276,465.3429,288.8934,5.7445,-325.7759,53.493,-441.8264,-271.3847,-371.3886,-272.7637,-102.4757,-358.4499,-143.2793,-64.6363,499.8284,-155.8017,-37.8801,63.5318,-377.6101,125.3457,57.231,49.3608,-245.5766,-47.9802,383.4127,-114.1047,-30.258,-479.6988,-194.4846,368.4079,466.1545,-26.7084,8.2433,74.9479,-155.4871,494.9634,-196.3082,-206.8022,423.2288,-494.5835,-291.7666,-204.8478,396.6,-418.9048,-130.0584,-137.5258,-440.7922,73.1423,-251.5694,356.1615,-34.088,-23.3318,43.2522,-297.3896,409.686,-305.5675,424.8321,-154.9096,181.7696,-87.5939,-151.7475,-319.3074,227.2369,-113.0086,-68.1299,368.0398,-20.3706,-296.0095,-269.9336,-250.5127,-56.5895,188.9818,82.7481,488.6398,-151.2088,11.8563,320.4209,316.3155,317.2716,-185.4569,128.2219,108.4381,-453.2648,-406.1359,-414.2863,36.6919,-160.1338,188.7767,364.4688,-13.3882,233.621,11.2764,-154.8894,424.1841,-128.4954,23.1408,183.1928,382.2918,-464.2506,234.1366,-447.21,-425.1161,66.1712,424.058,299.3596,372.7703,-162.3764,-37.8575,-468.5142,189.9036,172.0345,310.1368,-459.7659,-219.5317,-68.9306,211.4315,-408.8232,215.1716,-134.0617,367.326,385.2393,453.6431,-258.6041,194.9712,-266.8576,145.4018,-406.4884,119.3747,466.6835,-404.694,-480.8574,-3.1007,-48.0469,-70.915,-229.4956,-69.6999,-114.9404,372.8744,-247.5689,250.4333,252.9375,71.5672,323.3984,268.7582,16.7518,-258.5373,252.518,378.1721,-197.3271,-211.1179,444.2923,-152.2646,262.3183,159.3338,259.6788,271.9127,-26.8833,403.0915,-56.9197,-445.8869,-108.8167,417.8988,13.4232,-281.765,-405.8573,262.7831,-279.493,328.5591,-453.3941,-116.0368,435.4734,-439.0927,-332.9565,355.4955,324.9878,33.3519,-165.0182,188.1811,467.3455,185.1057,-233.8598,-17.6827,283.4271,-329.1247,-402.9721,404.7866,-358.7031,-267.4074,441.8363,320.2389,-128.0179,339.544,196.2018,-60.2688,336.0228,-440.1943,318.6882,-158.2596,277.0925,-487.4971,-338.9865,-275.716,136.8547,-253.6206,-40.2807,-357.0971,188.0344,-203.0674,449.9618,-223.2508,468.1441,302.4002,-65.0044,342.4431,205.6774,-118.636,-29.9706,183.9825,223.956,314.0691,137.0129,-8.0452,-15.131,-269.8643,-12.691,228.9777,-147.8384,-347.1117,-283.1905,459.2004,296.1321,-483.1799,414.3423,383.0187,-408.5525,-286.8169,482.5853,9.5232,-459.4968,-333.2521,109.0969,129.5107,43.4369,455.8283,-4.0423,-318.5019,339.1641,416.3581,-309.0429,84.2325,-355.8753,264.7671,43.8922,-298.6039,412.4413,19.4198,-251.279,-191.157,-478.2058,251.5709,-178.9633,479.293,188.399,380.9755,268.6575,120.3467,-322.0305,-255.4894,-377.515,56.9153,-133.9486,156.2546,-428.9581,-54.994,28.2146,158.7121,-426.7307,491.0086,-150.7205,-233.1005,244.5174,45.911,-406.1181,233.1636,175.9334,414.2805,421.7396,-322.8029,-252.2412,35.7622,318.5223,-141.5121,-375.4407,380.3081,222.1228,443.7844,367.377,-202.9594,-493.6231,-184.2242,-253.9838,463.1952,-416.3887,252.0867,-63.5317,411.0727,98.6261,330.7369,363.5685,-498.1848,-413.7246,-2.5996,-238.3547,-355.6041,-303.698,43.6266,383.1105,-72.3066,274.7491,321.9322,220.9543,-30.5578,400.0891,-181.7069,-386.4403,497.2206,-408.9611,138.485,-133.5666,-340.2569,-223.6313,270.884,-215.9399,74.3931,-244.1364,353.4219,-156.9905,488.3148,96.352,401.8525,-468.8344,129.9715,-27.1953,-168.631,187.7049,-336.5255,331.0652,204.3538,36.0182,366.8502,-468.6579,478.1409,-332.6136,-281.8499,63.7165,-458.8161,14.8894,-145.6397,267.1499,85.2025,326.3764,-419.6361,-133.9626,102.0618,443.3099,-207.9032,132.7032,234.001,-26.0754,105.6478,174.1252,-403.3511,-164.9714,-262.9344,-58.9668,357.6414,355.7508,-331.8443,153.5733,417.5712,260.7394,-150.1053,-435.6525,-364.1558,328.6183,-270.0863,107.1746,345.7998,480.8749,206.3896,-498.237,495.0835,481.9384,418.5571,-246.5213,-363.7304,311.7076,-53.1664,-297.3839,122.3105,-13.9226,-145.9754,-189.1748,460.9375,194.5417,-28.1346,-261.2177,-88.8396,-254.6407,-465.3148,-169.5377,24.3113,-116.2323,-420.3526,317.2107,-231.6227,-270.8239,387.8598,412.4251,428.1373,308.2044,275.2082,402.3663,-209.9843,-492.7269,225.1948,326.469,207.3557,-131.7677,371.9408,-139.3098,324.205,-126.6204,-335.0853,-248.2587,-344.907,307.2109,-441.3296,-318.027,414.6535,172.0537,-280.4991,331.0475,-158.0178,-285.1951,12.3632,149.9347,282.8302,-91.5624,-180.6097,496.0881,368.2567,357.6875,-194.2106,48.9213,-479.2956,-165.139,238.7811,302.7007,297.2805,208.7099,-5.5755,-85.7911,-358.1111,344.6131,415.7199,-219.1525,490.5003,-46.0096,498.2818,-91.8067,384.0104,396.1107,408.2827,-5.3919,-333.7992,-168.985,273.72,359.7125,227.7621,158.3406,-366.9722,3.7709,27.2728,71.9754,269.5792,-365.281,117.9152,-184.3682,356.9013,-142.6579,-496.7598,122.0194,89.1247,4.1914,-81.9905,465.0841,115.4727,169.6116,-199.9951,-223.3149,-447.3022,11.831,320.2368,105.1316,344.2462,8.6333,62.2285,-70.3944,-284.6694,-482.4229,-448.1569,-237.7858,222.3921,-172.1386,-312.5756,-390.0565,398.951,119.9784,-419.6537,121.3186,481.3011,-181.6662,-56.0219,424.1359,7.1461,138.8567,-307.0606,334.066,254.0897,473.7227,45.5936,133.7268,49.5334,-283.3406,179.4466,105.6191,-30.4162,271.5774,6.1156,110.4732,286.4325,13.3431,494.0139,-371.7624,283.3652,272.0558,-302.343,122.7245,-463.9261,299.9807,282.4502,-262.4911,183.4289,222.7474,-229.5973,141.6188,262.5468,278.1155,-331.0891,-393.6027,-230.1461,201.6657,-93.3604,-395.8877,-125.2013,-222.973,368.3759,234.6628,-28.6809,-151.0703,432.0315,253.1214,430.7065,-143.6963,499.84,85.1683,280.4354,196.6013,139.0476,120.8148,-398.8155,-335.5504,229.0516,403.8604,-383.9868,-79.975,-152.77,220.4036,135.0355,238.2176,-242.3085,-177.0743,381.8202,411.167,378.0153,456.5976,364.013,24.2316,-395.4659,-210.2581,138.7539,479.7398,-291.7797,-123.0491,188.9817,42.8931,-354.4479,358.853,-43.6168,-190.6656,-103.3037,47.8915,-358.5402,374.9758,493.9951,-427.2376,-119.1142,-453.2975,-326.2696,-212.8273,-142.2931,-179.795,355.77,-156.2903,331.2006,451.9252,185.2944,-96.1941,173.0447,345.2744,43.0151,381.7845,-143.4125,84.654,-208.7053,-293.141,333.6349,-80.472,-376.9817,214.6298,-43.0931,-254.7834,-421.6961,-368.844,467.5544,-418.61,-66.6824,-350.2671,348.8241,252.3495,41.8677,-128.869,90.0391,-136.7405,-136.7822,489.8074,-396.8204,63.8355,323.9557,-83.6674,451.263,152.8955,-291.7497,410.0787,-299.7468,51.34,-298.6066,-58.853,325.911,-281.9541,-15.3457,299.1325,-347.4959,388.407,343.1096,28.1816,24.3013,-111.3312,190.5583,279.9848,-479.8894,123.2182,233.8425,-466.2128,-134.7122,217.8674,432.9523,-186.799,-477.2512,-223.5514,64.274,141.5251,-161.2187,150.2791,-228.1087,81.172,451.0879,-230.3818,-304.9398,402.1081,199.1266,275.3423,-123.9548,-21.1815,-384.544,446.9626,208.9692,-337.4827,-58.1011,344.2642,230.2868,44.9176,245.9885,-284.1875,-351.6104,108.1289,459.649,191.4334,53.591,136.7139,10.5912,-15.8411,62.8305,448.5256,194.7705,-356.3214,84.4996,-133.2502,-358.6308,262.7949,219.8741,-355.3985,468.2922,243.7227,-408.3166,188.6111,-221.7264,-286.8234,-340.3046,-224.5375,332.2615,73.2788,-24.7857,-485.2204,-136.7196,-162.9693,92.6017,-99.611,-186.5203,495.5483,240.8051,409.6493,-58.1321,-154.1239,-335.9719,-82.4408,-471.3057,-43.373,301.0884,-96.6359,-236.6906,435.7313,-227.7263,-406.8904,-392.3187,169.0043,-371.0852,-271.3652,-57.4466,-196.8455,52.741,361.7395,-117.8599,190.5339,276.6457,-321.9851,425.881,-473.2662,-74.2968,221.3612,-465.4429,181.723,-78.4508,21.6152,148.8107,-166.1687,-281.6391,-462.3636,-420.5255,-161.4143,98.8383,-374.5345,-366.2851,187.1506,-405.1865,239.4847,-246.8352,33.1748,-344.1211,477.9759,-294.1354,-359.5015,-44.8454,151.7072,-22.7324,-260.3293,99.1414,-20.5536,173.3766,-422.6692,458.3853,-199.7898,-236.3929,365.2599,-66.4191,388.3472,283.0336,-268.9463,269.5704,360.9679,-322.102,-407.0705,-93.0994,338.9108,-189.1359,-216.9102,-249.0153,122.6058,-254.8318,-112.2771,-279.0506,-168.4431,392.888,394.7607,468.0544,340.1852,-293.1288,-8.2912,-419.2608,323.3382,-93.8793,-242.0672,427.7716,-441.6906,128.3229,424.4679,-71.8586,134.5411,-74.5205,18.4141,17.7277,126.9123,-137.6119,33.3783,222.9912,-279.3582,89.1226,-90.031,12.7221,98.7767,-80.2372,-485.9212,-481.6575,-325.9729,318.8005,-433.786,-296.6337,421.6515,-27.2786,-445.2456,451.8876,-482.1014,-143.1098,186.1258,-90.2432,-297.7479,-351.0026,-423.7518,-219.6096,-269.2043,33.5767,-325.4335,392.4866,-418.243,112.5852,-248.1306,451.2154,-419.2995,154.5752,483.6323,-315.962,-196.872,406.1769,-356.9868,67.5251,-255.6475,103.5181,-450.4418,386.9518,456.4057,99.4591,-166.636,275.5374,200.4925,99.7623,292.6794,-422.3998,419.4837,-466.548,-462.8519,-381.4489,472.8356,-129.9563,441.4941,-376.1232,-114.1945,233.5531,313.6963,394.9503,-278.7558,350.7515,47.9427,220.7074,-178.9789,-346.0485,-128.5665,8.9461,159.9838,-57.3637,351.9478,-65.9411,-258.1788,498.9494,-472.613,-428.5678,17.3981,-435.3682,-421.155,-54.9177,-490.2348,178.3777,-31.9618,-242.1805,362.3736,380.8179,446.4272,-23.9142,61.3588,-489.5704,363.6446,-186.1519,-351.8684,-322.2791,-226.0431,404.6996,203.9824,306.0958,234.0145,-180.4996,452.0633,257.171,-83.6197,-393.152,396.6934,32.156,-428.7645,183.7886,494.767,68.3905,278.9785,-40.4759,261.7298,236.5778,4.5577,-130.9582,433.2837,-298.1139,-107.9822,-196.8446,-121.1765,-292.5509,-246.4546,-258.6038,280.1334,-52.6511,483.2928,-185.7577,-75.3705,351.3411,179.1282,-479.3838,166.2733,-197.9043,282.6848,-50.4744,-492.7178,183.6435,-127.2379,483.646,433.0805,-228.5488,139.8314,-145.1337,-403.1749,306.2704,122.7149,479.6928,85.3866,108.095,-224.152,494.6848,-368.4504,-180.7579,61.7136,51.2045,-383.0103,-376.4816,-292.8217,-201.118,332.1516,425.2758,138.1284,-229.4302,432.9081,2.9898,-437.7631,-448.2151,129.9126,-170.2405,499.0396,-48.2137,363.8046,-423.2511,-28.0804,-267.826,-356.6288,-99.9371,-409.8465,170.4902,-269.2584,-277.4098,300.8819,-142.5889,339.0952,16.2275,-310.8646,201.0733,-495.5905,341.9279,-149.1184,-494.4928,-81.7343,209.9762,273.4892,380.3163,359.2424,-242.5,-42.1268,-303.9792,11.6018,361.5483,416.4178,10.3282,195.9796,148.8096,-60.9724,-205.5221,-145.4574,-341.5913,426.8996,-19.5843,60.6265,-133.4191,-139.8737,281.7465,461.2854,-270.8902,61.0182,-58.6791,-254.0193,-234.1206,-208.7334,39.7498,-14.337,-68.2319,-342.2756,403.6834,401.6122,-166.1637,47.3592,-325.7,274.5459,343.4873,328.3783,-370.1657,-122.8967,-231.3182,122.6609,119.2685,-223.5437,-210.8076,116.5022,340.2814,256.1852,-217.3487,-150.9598,331.1343,-453.8182,-448.0842,-95.2475,-340.9942,-416.7835,-96.7226,-328.7212,-373.4337,472.2214,-484.522,-465.1583,330.0712,73.2052,-55.1266,-352.8984,341.0742,-230.4845,321.0752,236.2116,35.1902,75.3489,-469.4042,110.2036,35.1156,454.7224,103.0685,-221.7499,-23.6898,-259.2362,-110.509,-261.0039,219.2391,-139.9404,155.7723,377.9713,434.0318,-365.1397,459.1471,-318.5774,323.4256,194.325,-311.9529,-153.9019,-346.5811,76.4069,443.2121,-199.407,495.6636,-138.5213,-145.3432,-151.7758,-365.3547,263.6507,-491.1686,-183.5585,-12.6044,318.5346,-443.8639,-179.0338,477.9093,-355.5118,-423.0035,-229.1166,-96.7782,-479.2384,192.9085,223.3407,-302.9472,297.3847,477.584,-297.5958,168.6023,-80.6912,-89.8717,87.1476,-129.7807,346.5576,-253.9729,-399.6858,-389.5785,35.1648,-180.451,-49.6084,83.9582,-185.2329,97.283,195.5249,-91.6969,199.202,-449.792,333.4825,-113.7558,443.434,394.3587,-94.9074,71.2092,-251.1774,-85.047,-46.4004,20.2595,341.1073,-91.2527,86.3775,303.1247,-336.9011,343.9894,-384.1261,154.4411,-465.2493,-63.3249,488.0231,348.6725,458.2093,322.401,220.2532,283.3734,-386.4252,-256.5262,-87.2205,96.8199,47.6908,-399.6307,214.7716,-19.9177,-458.513,-194.3218,-320.5342,-275.857,-301.6955,-84.9038,358.3475,-88.9271,499.7721,-161.7403,355.4894,313.6211,-176.1703,61.8427,107.603,-176.063,-426.5408,292.3612,58.3331,-115.8853,471.4131,-76.4815,-309.6263,361.4518,192.4763,-145.7968,256.3888,133.335,-474.0901,-366.9793,-495.223,457.2366,170.056,285.0152,89.8213,225.2251,354.1822,-298.374,-332.9164,-55.2409,306.9283,25.9392,218.0624,7.5085,-151.8768,-155.4932,6.0001,201.4506,-259.9874,485.1078,-362.8516,-230.1434,-398.2512,243.0012,32.302,-197.91,144.1195,-89.4196,-44.0399,-371.7866,227.6007,492.7526,499.3824,162.2475,279.0325,177.0781,341.0137,199.6009,108.1678,312.2319,-211.5001,-92.675,357.0513,-337.924,-348.984,-350.3677,173.3473,-193.7346,-318.5609,-2.0928,46.6287,-346.8513,36.634,-277.4949,-149.325,481.1378,370.3864,-139.6689,-332.2805,48.0292,109.8363,494.6994,373.6992,495.7442,400.4998,-26.2276,-308.7669,188.9497,257.9182,-116.6944,269.8932,197.005,123.1139,-356.2058,485.1982,-4.0119,397.8434,-204.67,-494.5133,-414.1299,142.1512,-36.5446,390.0718,6.9876,263.1216,457.5598,89.6086,-266.3804,17.3457,88.8182,236.6271,81.175,-170.2249,-5.7664,422.7852,180.3349,-135.2642,149.2285,-70.6607,-46.169,-389.3313,230.6125,388.4853,-438.3426,111.8034,300.0416,37.5604,-437.3868,-114.1336,312.7777,-99.1161,-312.9015,-147.3787,-434.0536,19.5034,141.706,-281.4504,-208.9608,281.4619,-361.0596,-464.2757,77.8205,232.5575,165.4104,424.8738,124.5555,342.038,86.7543,278.0216,311.2686,337.834,-90.0545,-210.1143,-488.4095,-80.7535,92.3731,-122.622,-288.0571,1.7285,-5.2998,100.0717,-395.0571,-477.5587,-160.5642,-119.4214,-232.233,415.7276,-204.3216,-436.7766,-103.4644,-427.0939,-31.0927,-440.2919,120.5971,-223.3623,-199.0988,304.8697,432.5731,-231.5791,-397.696,306.4134,330.1018,32.4345,-175.719,464.6091,-291.5686,300.1631,-167.4592,238.9574,104.5893,-187.2215,-294.0111,-361.9094,480.6847,-304.2133,-448.7144,67.7235,-255.9669,254.7379,464.5465,6.8909,-368.7554,337.5993,39.1928,-376.0625,433.4224,-109.1488,341.7731,377.843,446.839,-192.283,251.1592,437.6812,-478.3409 ] + - do: + cluster.health: + wait_for_events: languid - do: indices.get_mapping: index: test-too-big-still-float @@ -162,7 +167,9 @@ teardown: dims: 5 index: true similarity: cosine - + - do: + cluster.health: + wait_for_events: languid - do: indices.get_mapping: index: set-dense-vector @@ -343,7 +350,9 @@ teardown: properties: my_dense_vector_field: type: dense_vector - + - do: + cluster.health: + wait_for_events: languid - do: indices.get_mapping: index: test-mapped-index @@ -390,7 +399,9 @@ teardown: properties: my_child_dense_vector_field: type: dense_vector - + - do: + cluster.health: + wait_for_events: languid - do: indices.get_mapping: index: test-mapped-index @@ -546,7 +557,9 @@ teardown: my_float1: [ 159.1, 289.56, -128.7424, 145.9871, -164.0003, 86.4034, -89.6929, 257.9717, 131.6075, 67.9233, -144.8255, 223.8446, 77.3228, -210.1163, -139.4783, 12.6499, 15.4491, 108.3465, -189.3947, 178.2045, -187.5925, 184.5089, 77.3022, -202.7439, -13.4959, 115.9719, -139.4332, 196.7845, 104.7573, -156.7746, 166.9878, 68.3936, 159.8473, -141.4446, 21.1947, 186.5908, -209.6895, 68.6169, 44.1255, 147.4659, 56.5079, -179.7997, -85.1651, 11.4847, 124.1662, 96.2246, -178.6705, 85.5925, 205.3616, -16.4704, 172.4947, -115.2535, -58.1722, 94.4836, 34.6458, -70.1011, -58.8047, 149.9562, -37.8998, 196.9805, -169.3555, -163.9432, 188.5611, 214.8378, 29.3182, -24.8724, 152.9382, -109.4345, -123.6716, -8.2441, 64.5902, 27.8083, 40.8185, -94.3161, 58.1463, -138.7432, 24.6805, -88.7222, -11.2018, 206.6434, 201.9024, 87.3079, -3.2883, -60.2484, -109.5789, 105.5766, -116.6709, -17.7073, -71.5093, -75.2937, -176.8691, -146.4967, 53.7586, 199.5294, 55.9754, -48.7399, 82.2051, 135.2921, 22.4408, -116.4008, -33.7538, 29.7207, 6.3692, -97.5768, -12.7982, -200.9331, -62.2743, 81.0843, 136.2247, 150.2565, 139.6838, 155.2657, -25.7447, 198.5955, 18.8099, 46.9014, -60.2672, 136.4801, 171.8966, 172.5842, 13.9123, 75.8386, -64.2444, -48.1964, 135.9685, 7.4927, -40.6424, -76.8922 ] - + - do: + cluster.health: + wait_for_events: languid - do: indices.get_mapping: index: test-copyto-index From eb8b7526796ecdb57d6c50fb7ea97de2a83ded3e Mon Sep 17 00:00:00 2001 From: Joseph Crail Date: Wed, 18 Oct 2023 14:47:46 -0700 Subject: [PATCH 007/203] [Profiling] Update how frame group IDs are created to match total expected frames (#100865) * Refactor out FrameGroupID builder * Only use Unix path separator * Use hash to create frame group ID We also use file ID and function name to create the frame group ID if the source filename is empty. * Trim source filename to basename and parent * Make utility class uninstantiable * Check if string is null also * Use generated expected value instead of hard-coded --- .../xpack/profiling/FrameGroupID.java | 40 ++++++++++ .../TransportGetFlamegraphAction.java | 32 +------- .../xpack/profiling/FrameGroupIDTests.java | 73 +++++++++++++++++++ .../TransportGetFlamegraphActionTests.java | 18 ++--- 4 files changed, 123 insertions(+), 40 deletions(-) create mode 100644 x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/FrameGroupID.java create mode 100644 x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/FrameGroupIDTests.java diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/FrameGroupID.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/FrameGroupID.java new file mode 100644 index 0000000000000..32273d56d0176 --- /dev/null +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/FrameGroupID.java @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.common.Strings; + +import java.util.Objects; + +public final class FrameGroupID { + private static final char UNIX_PATH_SEPARATOR = '/'; + + private FrameGroupID() {} + + public static String getBasenameAndParent(String fullPath) { + if (Strings.isEmpty(fullPath)) { + return fullPath; + } + int lastSeparatorIdx = fullPath.lastIndexOf(UNIX_PATH_SEPARATOR); + if (lastSeparatorIdx <= 0) { + return fullPath; + } + int nextSeparatorIdx = fullPath.lastIndexOf(UNIX_PATH_SEPARATOR, lastSeparatorIdx - 1); + return nextSeparatorIdx == -1 ? fullPath : fullPath.substring(nextSeparatorIdx + 1); + } + + public static String create(String fileId, Integer addressOrLine, String exeFilename, String sourceFilename, String functionName) { + if (Strings.isEmpty(functionName)) { + return Integer.toString(Objects.hash(fileId, addressOrLine)); + } + if (Strings.isEmpty(sourceFilename)) { + return Integer.toString(Objects.hash(fileId, functionName)); + } + return Integer.toString(Objects.hash(exeFilename, functionName, getBasenameAndParent(sourceFilename))); + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java index 97e5cdf5a74fa..5e85442151d0c 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java @@ -17,11 +17,9 @@ import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.EsExecutors; -import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.tasks.Task; import org.elasticsearch.transport.TransportService; -import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -98,7 +96,7 @@ static GetFlamegraphResponse buildFlamegraph(GetStackTracesResponse response) { String executable = response.getExecutables().getOrDefault(fileId, ""); for (Frame frame : stackFrame.frames()) { - String frameGroupId = createFrameGroupId(fileId, addressOrLine, executable, frame.fileName(), frame.functionName()); + String frameGroupId = FrameGroupID.create(fileId, addressOrLine, executable, frame.fileName(), frame.functionName()); int nodeId; if (builder.isExists(frameGroupId)) { @@ -130,34 +128,6 @@ static GetFlamegraphResponse buildFlamegraph(GetStackTracesResponse response) { return builder.build(); } - @SuppressForbidden(reason = "Using pathSeparator constant to extract the filename with low overhead") - private static String getFilename(String fullPath) { - if (fullPath == null || fullPath.isEmpty()) { - return fullPath; - } - int lastSeparatorIdx = fullPath.lastIndexOf(File.pathSeparator); - return lastSeparatorIdx == -1 ? fullPath : fullPath.substring(lastSeparatorIdx + 1); - } - - private static String createFrameGroupId( - String fileId, - Integer addressOrLine, - String exeFilename, - String sourceFilename, - String functionName - ) { - StringBuilder sb = new StringBuilder(); - if (functionName.isEmpty()) { - sb.append(fileId); - sb.append(addressOrLine); - } else { - sb.append(exeFilename); - sb.append(functionName); - sb.append(getFilename(sourceFilename)); - } - return sb.toString(); - } - private static class FlamegraphBuilder { private int currentNode = 0; private int size = 0; diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/FrameGroupIDTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/FrameGroupIDTests.java new file mode 100644 index 0000000000000..50cfdd28a98fc --- /dev/null +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/FrameGroupIDTests.java @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +import org.elasticsearch.test.ESTestCase; + +import java.util.Objects; + +public class FrameGroupIDTests extends ESTestCase { + public void testEmptySourceFilename() { + String given = FrameGroupID.getBasenameAndParent(""); + assertEquals("", given); + } + + public void testNonPathSourceFilename() { + String given = FrameGroupID.getBasenameAndParent("void jdk.internal.misc.Unsafe.park(boolean, long)"); + assertEquals("void jdk.internal.misc.Unsafe.park(boolean, long)", given); + } + + public void testRootSourceFilename() { + String given = FrameGroupID.getBasenameAndParent("/"); + assertEquals("/", given); + } + + public void testRelativePathSourceFilename() { + String given = FrameGroupID.getBasenameAndParent("../src/main.c"); + assertEquals("src/main.c", given); + } + + public void testAbsolutePathSourceFilename() { + String given = FrameGroupID.getBasenameAndParent("/usr/local/go/src/runtime/lock_futex.go"); + assertEquals("runtime/lock_futex.go", given); + } + + public void testEmptyFunctionName() { + String expected = Integer.toString(Objects.hash("FEDCBA9876543210", 177863)); + String given = FrameGroupID.create("FEDCBA9876543210", 177863, "", "", ""); + assertEquals(expected, given); + } + + public void testFunctionNameAndEmptySourceFilename() { + String expected = Integer.toString(Objects.hash("FEDCBA9876543210", "void jdk.internal.misc.Unsafe.park(boolean, long)")); + String given = FrameGroupID.create("FEDCBA9876543210", 6694, "
", "", "void jdk.internal.misc.Unsafe.park(boolean, long)"); + assertEquals(expected, given); + } + + public void testFunctionNameAndSourceFilenameWithAbsolutePath() { + String expected = Integer.toString( + Objects.hash("main", "futex_wake", FrameGroupID.getBasenameAndParent("/usr/local/go/src/runtime/lock_futex.go")) + ); + String given = FrameGroupID.create("FEDCBA9876543210", 64, "main", "/usr/local/go/src/runtime/lock_futex.go", "futex_wake"); + assertEquals(expected, given); + } + + public void testFunctionNameAndSourceFilenameWithoutAbsolutePath() { + String expected = Integer.toString( + Objects.hash("
", "void jdk.internal.misc.Unsafe.park(boolean, long)", FrameGroupID.getBasenameAndParent("bootstrap.java")) + ); + String given = FrameGroupID.create( + "FEDCBA9876543210", + 29338, + "
", + "bootstrap.java", + "void jdk.internal.misc.Unsafe.park(boolean, long)" + ); + assertEquals(expected, given); + } +} diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java index d931e4e17d5b4..fb68418d224d5 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java @@ -58,15 +58,15 @@ public void testCreateFlamegraph() { assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 1), response.getCountExclusive()); assertEquals( List.of( - Map.of("fr28zxcZ2UDasxYuu6dV-w12784352", 1), - Map.of("fr28zxcZ2UDasxYuu6dV-w19334053", 2), - Map.of("fr28zxcZ2UDasxYuu6dV-w19336161", 3), - Map.of("fr28zxcZ2UDasxYuu6dV-w18795859", 4), - Map.of("fr28zxcZ2UDasxYuu6dV-w18622708", 5), - Map.of("fr28zxcZ2UDasxYuu6dV-w18619213", 6), - Map.of("fr28zxcZ2UDasxYuu6dV-w12989721", 7), - Map.of("fr28zxcZ2UDasxYuu6dV-w13658842", 8), - Map.of("fr28zxcZ2UDasxYuu6dV-w16339645", 9), + Map.of("174640828", 1), + Map.of("181190529", 2), + Map.of("181192637", 3), + Map.of("180652335", 4), + Map.of("180479184", 5), + Map.of("180475689", 6), + Map.of("174846197", 7), + Map.of("175515318", 8), + Map.of("178196121", 9), Map.of() ), response.getEdges() From c7c7eee3b5ad161ff7af3ac59a01133c06e91ae5 Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Thu, 19 Oct 2023 06:37:09 +0100 Subject: [PATCH 008/203] ESQL: add serialization support for MvExpand and EsqlProject logical plan nodes (#101071) --- .../xpack/esql/io/stream/PlanNamedTypes.java | 27 ++++++++++- .../esql/io/stream/PlanNamedTypesTests.java | 47 ++++++++++++++++++- .../xpack/ql/plan/logical/EsRelation.java | 4 +- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java index eb10d50c72cdb..93dc2fb094f54 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java @@ -112,7 +112,9 @@ import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.Grok; +import org.elasticsearch.xpack.esql.plan.logical.MvExpand; import org.elasticsearch.xpack.esql.plan.logical.TopN; +import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; import org.elasticsearch.xpack.esql.plan.physical.AggregateExec; import org.elasticsearch.xpack.esql.plan.physical.DissectExec; import org.elasticsearch.xpack.esql.plan.physical.EnrichExec; @@ -255,9 +257,11 @@ public static List namedTypeEntries() { of(LogicalPlan.class, EsRelation.class, PlanNamedTypes::writeEsRelation, PlanNamedTypes::readEsRelation), of(LogicalPlan.class, Eval.class, PlanNamedTypes::writeEval, PlanNamedTypes::readEval), of(LogicalPlan.class, Enrich.class, PlanNamedTypes::writeEnrich, PlanNamedTypes::readEnrich), + of(LogicalPlan.class, EsqlProject.class, PlanNamedTypes::writeEsqlProject, PlanNamedTypes::readEsqlProject), of(LogicalPlan.class, Filter.class, PlanNamedTypes::writeFilter, PlanNamedTypes::readFilter), of(LogicalPlan.class, Grok.class, PlanNamedTypes::writeGrok, PlanNamedTypes::readGrok), of(LogicalPlan.class, Limit.class, PlanNamedTypes::writeLimit, PlanNamedTypes::readLimit), + of(LogicalPlan.class, MvExpand.class, PlanNamedTypes::writeMvExpand, PlanNamedTypes::readMvExpand), of(LogicalPlan.class, OrderBy.class, PlanNamedTypes::writeOrderBy, PlanNamedTypes::readOrderBy), of(LogicalPlan.class, Project.class, PlanNamedTypes::writeProject, PlanNamedTypes::readProject), of(LogicalPlan.class, TopN.class, PlanNamedTypes::writeTopN, PlanNamedTypes::readTopN), @@ -683,7 +687,7 @@ static void writeDissect(PlanStreamOutput out, Dissect dissect) throws IOExcepti } static EsRelation readEsRelation(PlanStreamInput in) throws IOException { - return new EsRelation(in.readSource(), readEsIndex(in), readAttributes(in)); + return new EsRelation(in.readSource(), readEsIndex(in), readAttributes(in), in.readBoolean()); } static void writeEsRelation(PlanStreamOutput out, EsRelation relation) throws IOException { @@ -691,6 +695,7 @@ static void writeEsRelation(PlanStreamOutput out, EsRelation relation) throws IO out.writeNoSource(); writeEsIndex(out, relation.index()); writeAttributes(out, relation.output()); + out.writeBoolean(relation.frozen()); } static Eval readEval(PlanStreamInput in) throws IOException { @@ -725,6 +730,16 @@ static void writeEnrich(PlanStreamOutput out, Enrich enrich) throws IOException writeNamedExpressions(out, enrich.enrichFields()); } + static EsqlProject readEsqlProject(PlanStreamInput in) throws IOException { + return new EsqlProject(in.readSource(), in.readLogicalPlanNode(), readNamedExpressions(in)); + } + + static void writeEsqlProject(PlanStreamOutput out, EsqlProject project) throws IOException { + out.writeNoSource(); + out.writeLogicalPlanNode(project.child()); + writeNamedExpressions(out, project.projections()); + } + static Filter readFilter(PlanStreamInput in) throws IOException { return new Filter(in.readSource(), in.readLogicalPlanNode(), in.readExpression()); } @@ -764,6 +779,16 @@ static void writeLimit(PlanStreamOutput out, Limit limit) throws IOException { out.writeLogicalPlanNode(limit.child()); } + static MvExpand readMvExpand(PlanStreamInput in) throws IOException { + return new MvExpand(in.readSource(), in.readLogicalPlanNode(), in.readNamedExpression()); + } + + static void writeMvExpand(PlanStreamOutput out, MvExpand mvExpand) throws IOException { + out.writeNoSource(); + out.writeLogicalPlanNode(mvExpand.child()); + out.writeNamedExpression(mvExpand.target()); + } + static OrderBy readOrderBy(PlanStreamInput in) throws IOException { return new OrderBy( in.readSource(), diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java index 5d201ad22371c..1f92a26f58856 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java @@ -49,7 +49,9 @@ import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.Grok; +import org.elasticsearch.xpack.esql.plan.logical.MvExpand; import org.elasticsearch.xpack.esql.plan.logical.TopN; +import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; import org.elasticsearch.xpack.esql.plan.physical.AggregateExec; import org.elasticsearch.xpack.esql.plan.physical.DissectExec; import org.elasticsearch.xpack.esql.plan.physical.EnrichExec; @@ -157,12 +159,14 @@ public void testPhysicalPlanEntries() { public static final List> LOGICAL_PLAN_NODE_CLS = List.of( Aggregate.class, Dissect.class, + Enrich.class, EsRelation.class, + EsqlProject.class, Eval.class, - Enrich.class, Filter.class, Grok.class, Limit.class, + MvExpand.class, OrderBy.class, Project.class, TopN.class @@ -175,6 +179,7 @@ public void testLogicalPlanEntries() { .stream() .filter(e -> e.categoryClass().isAssignableFrom(LogicalPlan.class)) .map(PlanNameRegistry.Entry::name) + .sorted() .toList(); assertThat(actual, equalTo(expected)); } @@ -459,6 +464,38 @@ public void testDissectParserSimple() throws IOException { EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser); } + public void testEsRelation() throws IOException { + var orig = new EsRelation(Source.EMPTY, randomEsIndex(), List.of(randomFieldAttribute()), randomBoolean()); + BytesStreamOutput bso = new BytesStreamOutput(); + PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry); + PlanNamedTypes.writeEsRelation(out, orig); + var deser = PlanNamedTypes.readEsRelation(planStreamInput(bso)); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser); + } + + public void testEsqlProject() throws IOException { + var orig = new EsqlProject( + Source.EMPTY, + new EsRelation(Source.EMPTY, randomEsIndex(), List.of(randomFieldAttribute()), randomBoolean()), + List.of(randomFieldAttribute()) + ); + BytesStreamOutput bso = new BytesStreamOutput(); + PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry); + PlanNamedTypes.writeEsqlProject(out, orig); + var deser = PlanNamedTypes.readEsqlProject(planStreamInput(bso)); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser); + } + + public void testMvExpand() throws IOException { + var esRelation = new EsRelation(Source.EMPTY, randomEsIndex(), List.of(randomFieldAttribute()), randomBoolean()); + var orig = new MvExpand(Source.EMPTY, esRelation, randomFieldAttribute()); + BytesStreamOutput bso = new BytesStreamOutput(); + PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry); + PlanNamedTypes.writeMvExpand(out, orig); + var deser = PlanNamedTypes.readMvExpand(planStreamInput(bso)); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser); + } + private static void assertNamedExpression(NamedExpression origObj) { var deserObj = serializeDeserialize(origObj, PlanStreamOutput::writeExpression, PlanStreamInput::readNamedExpression); EqualsHashCodeTestUtils.checkEqualsAndHashCode(origObj, unused -> deserObj); @@ -474,6 +511,14 @@ private static void assertNamedEsField(EsField origObj) { EqualsHashCodeTestUtils.checkEqualsAndHashCode(origObj, unused -> deserObj); } + static EsIndex randomEsIndex() { + return new EsIndex( + randomAlphaOfLength(randomIntBetween(1, 25)), + Map.of(randomAlphaOfLength(randomIntBetween(1, 25)), randomKeywordEsField()), + Set.of(randomAlphaOfLength(randomIntBetween(1, 25)), randomAlphaOfLength(randomIntBetween(1, 25))) + ); + } + static UnsupportedAttribute randomUnsupportedAttribute() { return new UnsupportedAttribute( Source.EMPTY, diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java index b9fa092868592..4a31309ac8f2f 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java @@ -34,7 +34,7 @@ public EsRelation(Source source, EsIndex index, List attributes) { this(source, index, attributes, false); } - private EsRelation(Source source, EsIndex index, List attributes, boolean frozen) { + public EsRelation(Source source, EsIndex index, List attributes, boolean frozen) { super(source); this.index = index; this.attrs = attributes; @@ -43,7 +43,7 @@ private EsRelation(Source source, EsIndex index, List attributes, boo @Override protected NodeInfo info() { - return NodeInfo.create(this, EsRelation::new, index, frozen); + return NodeInfo.create(this, EsRelation::new, index, attrs, frozen); } private static List flatten(Source source, Map mapping) { From eb937267ce02818678cb8da8ebc4516359a11f53 Mon Sep 17 00:00:00 2001 From: Mary Gouseti Date: Thu, 19 Oct 2023 09:38:35 +0300 Subject: [PATCH 009/203] Health Report API should not return RED for unassigned cold/frozen shards when data is available #97606 (#100776) Quoting from the original ticket: > We discussed this yesterday and a potential solution here is to report the fully and partially mounted indices health only if the source indices are missing (or red themselves). e.g. > if `restored-logs-2023.07.11-000024` is `RED` but `logs-2023.07.11-000024` is `GREEN` the health API returns GREEN if `restored-logs-2023.07.11-000024` is `RED` AND `logs-2023.07.11-000024` is missing or `RED` itself, then the health API starts reporting on the health of the `restored-logs-2023.07.11-000024` (which would be `RED` already due to the source/original index being `RED` in the case when it still exists) Fixes: https://github.com/elastic/elasticsearch/issues/97606 --- docs/changelog/100776.yaml | 6 + ...rdsAvailabilityHealthIndicatorService.java | 101 ++++++++++- .../SearchableSnapshotsSettings.java | 1 + ...ailabilityHealthIndicatorServiceTests.java | 171 ++++++++++++++++++ .../SearchableSnapshots.java | 2 +- 5 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 docs/changelog/100776.yaml diff --git a/docs/changelog/100776.yaml b/docs/changelog/100776.yaml new file mode 100644 index 0000000000000..a0bde13f47c92 --- /dev/null +++ b/docs/changelog/100776.yaml @@ -0,0 +1,6 @@ +pr: 100776 +summary: Health Report API should not return RED for unassigned cold/frozen shards + when data is available +area: ILM+SLM +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java index 15710da073c8e..0c5f547d1cb10 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorService.java @@ -33,6 +33,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Nullable; import org.elasticsearch.health.Diagnosis; import org.elasticsearch.health.HealthIndicatorDetails; @@ -44,6 +45,7 @@ import org.elasticsearch.health.SimpleHealthIndicatorDetails; import org.elasticsearch.health.node.HealthInfo; import org.elasticsearch.indices.SystemIndices; +import org.elasticsearch.snapshots.SearchableSnapshotsSettings; import org.elasticsearch.snapshots.SnapshotShardSizeInfo; import java.util.ArrayList; @@ -132,6 +134,8 @@ public HealthIndicatorResult calculate(boolean verbose, int maxAffectedResources } } } + + status.updateSearchableSnapshotsOfAvailableIndices(); return createIndicator( status.getStatus(), status.getSymptom(), @@ -143,6 +147,7 @@ public HealthIndicatorResult calculate(boolean verbose, int maxAffectedResources // Impact IDs public static final String PRIMARY_UNASSIGNED_IMPACT_ID = "primary_unassigned"; + public static final String READ_ONLY_PRIMARY_UNASSIGNED_IMPACT_ID = "read_only_primary_unassigned"; public static final String REPLICA_UNASSIGNED_IMPACT_ID = "replica_unassigned"; public static final String RESTORE_FROM_SNAPSHOT_ACTION_GUIDE = "https://ela.st/restore-snapshot"; @@ -391,7 +396,6 @@ public HealthIndicatorResult calculate(boolean verbose, int maxAffectedResources ); private class ShardAllocationCounts { - private boolean available = true; // This will be true even if no replicas are expected, as long as none are unavailable private int unassigned = 0; private int unassigned_new = 0; private int unassigned_restarting = 0; @@ -399,14 +403,22 @@ private class ShardAllocationCounts { private int started = 0; private int relocating = 0; private final Set indicesWithUnavailableShards = new HashSet<>(); + // We keep the searchable snapshots separately as long as the original index is still available + // This is checked during the post-processing + private final SearchableSnapshotsState searchableSnapshotsState = new SearchableSnapshotsState(); private final Map> diagnosisDefinitions = new HashMap<>(); public void increment(ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) { boolean isNew = isUnassignedDueToNewInitialization(routing, state); boolean isRestarting = isUnassignedDueToTimelyRestart(routing, shutdowns); - available &= routing.active() || isRestarting || isNew; if ((routing.active() || isRestarting || isNew) == false) { - indicesWithUnavailableShards.add(routing.getIndexName()); + String indexName = routing.getIndexName(); + Settings indexSettings = state.getMetadata().index(indexName).getSettings(); + if (SearchableSnapshotsSettings.isSearchableSnapshotStore(indexSettings)) { + searchableSnapshotsState.addSearchableSnapshotWithUnavailableShard(indexName); + } else { + indicesWithUnavailableShards.add(indexName); + } } switch (routing.state()) { @@ -435,6 +447,10 @@ public void increment(ShardRouting routing, ClusterState state, NodesShutdownMet } } + public boolean areAllAvailable() { + return indicesWithUnavailableShards.isEmpty(); + } + private void addDefinition(Diagnosis.Definition diagnosisDefinition, String indexName) { diagnosisDefinitions.computeIfAbsent(diagnosisDefinition, (k) -> new HashSet<>()).add(indexName); } @@ -797,18 +813,26 @@ class ShardAllocationStatus { this.clusterMetadata = clusterMetadata; } - public void addPrimary(ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) { + void addPrimary(ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) { primaries.increment(routing, state, shutdowns, verbose); } - public void addReplica(ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) { + void addReplica(ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) { replicas.increment(routing, state, shutdowns, verbose); } + void updateSearchableSnapshotsOfAvailableIndices() { + // Searchable snapshots do not have replicas, so this post-processing is not applicable for the replicas + primaries.searchableSnapshotsState.updateSearchableSnapshotWithAvailableIndices( + clusterMetadata, + primaries.indicesWithUnavailableShards + ); + } + public HealthStatus getStatus() { - if (primaries.available == false) { + if (primaries.areAllAvailable() == false || primaries.searchableSnapshotsState.getRedSearchableSnapshots().isEmpty() == false) { return RED; - } else if (replicas.available == false) { + } else if (replicas.areAllAvailable() == false) { return YELLOW; } else { return GREEN; @@ -840,6 +864,18 @@ public String getSymptom() { } else { builder.append("all shards available."); } + if (primaries.areAllAvailable() + && primaries.searchableSnapshotsState.searchableSnapshotWithOriginalIndexAvailable.isEmpty() == false) { + if (primaries.unassigned == 1) { + builder.append( + " This is a mounted shard and the original shard is available, so there are no data availability problems." + ); + } else { + builder.append( + " These are mounted shards and the original shards are available, so there are no data availability problems." + ); + } + } return builder.toString(); } @@ -902,6 +938,25 @@ public List getImpacts() { ) ); } + Set readOnlyIndicesWithUnavailableShards = primaries.searchableSnapshotsState.getRedSearchableSnapshots(); + if (readOnlyIndicesWithUnavailableShards.isEmpty() == false) { + String impactDescription = String.format( + Locale.ROOT, + "Searching %d %s [%s] might return incomplete results.", + readOnlyIndicesWithUnavailableShards.size(), + readOnlyIndicesWithUnavailableShards.size() == 1 ? "index" : "indices", + getTruncatedIndices(readOnlyIndicesWithUnavailableShards, clusterMetadata) + ); + impacts.add( + new HealthIndicatorImpact( + NAME, + READ_ONLY_PRIMARY_UNASSIGNED_IMPACT_ID, + 1, + impactDescription, + List.of(ImpactArea.SEARCH) + ) + ); + } /* * It is possible that we're working with an intermediate cluster state, and that for an index we have no primary but a replica * that is reported as unavailable. That replica is likely being promoted to primary. The only impact that matters at this @@ -1048,4 +1103,36 @@ static List getRestoreFromSnapshotAffectedResources( return affectedResources; } } + + static class SearchableSnapshotsState { + private final Set searchableSnapshotWithUnavailableShard = new HashSet<>(); + private final Set searchableSnapshotWithOriginalIndexAvailable = new HashSet<>(); + + void addSearchableSnapshotWithUnavailableShard(String indexName) { + searchableSnapshotWithUnavailableShard.add(indexName); + } + + void addSearchableSnapshotWithOriginalIndexAvailable(String indexName) { + searchableSnapshotWithOriginalIndexAvailable.add(indexName); + } + + Set getRedSearchableSnapshots() { + return Sets.difference(searchableSnapshotWithUnavailableShard, searchableSnapshotWithOriginalIndexAvailable); + } + + // If the original index of a searchable snapshot with unavailable shards is available then we remove the searchable snapshot + // from the list of the unavailable searchable snapshots because the data is available via the original index. + void updateSearchableSnapshotWithAvailableIndices(Metadata clusterMetadata, Set indicesWithUnavailableShards) { + for (String index : searchableSnapshotWithUnavailableShard) { + assert clusterMetadata.index(index) != null : "Index metadata of index '" + index + "' should not be null"; + Settings indexSettings = clusterMetadata.index(index).getSettings(); + String originalIndex = indexSettings.get(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_INDEX_NAME_SETTING_KEY); + if (originalIndex != null + && clusterMetadata.indices().containsKey(originalIndex) != false + && indicesWithUnavailableShards.contains(originalIndex) == false) { + addSearchableSnapshotWithOriginalIndexAvailable(index); + } + } + } + } } diff --git a/server/src/main/java/org/elasticsearch/snapshots/SearchableSnapshotsSettings.java b/server/src/main/java/org/elasticsearch/snapshots/SearchableSnapshotsSettings.java index 6dd99fdd7145b..e1a8a90d1b6ea 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SearchableSnapshotsSettings.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SearchableSnapshotsSettings.java @@ -32,6 +32,7 @@ public final class SearchableSnapshotsSettings { public static final String SEARCHABLE_SNAPSHOTS_SNAPSHOT_NAME_SETTING_KEY = "index.store.snapshot.snapshot_name"; public static final String SEARCHABLE_SNAPSHOTS_SNAPSHOT_UUID_SETTING_KEY = "index.store.snapshot.snapshot_uuid"; public static final String SEARCHABLE_SNAPSHOTS_DELETE_SNAPSHOT_ON_INDEX_DELETION = "index.store.snapshot.delete_searchable_snapshot"; + public static final String SEARCHABLE_SNAPSHOT_INDEX_NAME_SETTING_KEY = "index.store.snapshot.index_name"; private SearchableSnapshotsSettings() {} diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java index 708a3125590fd..45e8fe4e525cd 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceTests.java @@ -44,12 +44,14 @@ import org.elasticsearch.health.SimpleHealthIndicatorDetails; import org.elasticsearch.health.node.HealthInfo; import org.elasticsearch.index.Index; +import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.ExecutorNames; import org.elasticsearch.indices.SystemDataStreamDescriptor; import org.elasticsearch.indices.SystemIndexDescriptorUtils; import org.elasticsearch.indices.SystemIndices; +import org.elasticsearch.snapshots.SearchableSnapshotsSettings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.mockito.stubbing.Answer; @@ -1532,6 +1534,157 @@ public void testLimitNumberOfAffectedResources() { } } + public void testShouldBeGreenWhenFrozenIndexIsUnassignedAndOriginalIsAvailable() { + String originalIndex = "logs-2023.07.11-000024"; + String restoredIndex = "restored-logs-2023.07.11-000024"; + var clusterState = createClusterStateWith( + List.of( + IndexMetadata.builder(restoredIndex) + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()) + .put(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_INDEX_NAME_SETTING_KEY, originalIndex) + .put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_STORE_TYPE) + .put(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY, randomBoolean()) + .build() + ) + .numberOfShards(1) + .numberOfReplicas(0) + .build(), + IndexMetadata.builder(originalIndex) + .settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()).build()) + .numberOfShards(1) + .numberOfReplicas(0) + .build() + ), + List.of( + index(restoredIndex, new ShardAllocation(randomNodeId(), UNAVAILABLE)), + index(originalIndex, new ShardAllocation(randomNodeId(), AVAILABLE)) + ), + List.of(), + List.of() + ); + var service = createShardsAvailabilityIndicatorService(clusterState); + + HealthIndicatorResult result = service.calculate(true, HealthInfo.EMPTY_HEALTH_INFO); + assertThat( + result, + equalTo( + createExpectedResult( + GREEN, + "This cluster has 1 unavailable primary shard. This is a mounted shard and the original " + + "shard is available, so there are no data availability problems.", + Map.of("unassigned_primaries", 1, "started_primaries", 1), + List.of(), + List.of( + new Diagnosis(ACTION_CHECK_ALLOCATION_EXPLAIN_API, List.of(new Diagnosis.Resource(INDEX, List.of(restoredIndex)))) + ) + ) + ) + ); + } + + public void testShouldBeRedWhenFrozenIndexIsUnassignedAndOriginalIsUnavailable() { + String originalIndex = "logs-2023.07.11-000024"; + String restoredIndex = "restored-logs-2023.07.11-000024"; + List indexMetadata = new ArrayList<>(2); + List routes = new ArrayList<>(2); + indexMetadata.add( + IndexMetadata.builder(restoredIndex) + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()) + .put(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_INDEX_NAME_SETTING_KEY, originalIndex) + .put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_STORE_TYPE) + .put(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY, randomBoolean()) + .build() + ) + .numberOfShards(1) + .numberOfReplicas(0) + .build() + ); + routes.add(index(restoredIndex, new ShardAllocation(randomNodeId(), UNAVAILABLE))); + // When original does not exist + { + var clusterState = createClusterStateWith(indexMetadata, routes, List.of(), List.of()); + var service = createShardsAvailabilityIndicatorService(clusterState); + + HealthIndicatorResult result = service.calculate(true, HealthInfo.EMPTY_HEALTH_INFO); + assertThat( + result, + equalTo( + createExpectedResult( + RED, + "This cluster has 1 unavailable primary shard.", + Map.of("unassigned_primaries", 1), + List.of( + new HealthIndicatorImpact( + NAME, + ShardsAvailabilityHealthIndicatorService.READ_ONLY_PRIMARY_UNASSIGNED_IMPACT_ID, + 1, + "Searching 1 index [" + restoredIndex + "] might return incomplete results.", + List.of(ImpactArea.SEARCH) + ) + ), + List.of( + new Diagnosis( + ACTION_CHECK_ALLOCATION_EXPLAIN_API, + List.of(new Diagnosis.Resource(INDEX, List.of(restoredIndex))) + ) + ) + ) + ) + ); + } + // When original index has unavavailable shards + { + indexMetadata.add( + IndexMetadata.builder(originalIndex) + .settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()).build()) + .numberOfShards(1) + .numberOfReplicas(0) + .build() + ); + routes.add(index(originalIndex, new ShardAllocation(randomNodeId(), UNAVAILABLE))); + var clusterState = createClusterStateWith(indexMetadata, routes, List.of(), List.of()); + var service = createShardsAvailabilityIndicatorService(clusterState); + + HealthIndicatorResult result = service.calculate(true, HealthInfo.EMPTY_HEALTH_INFO); + assertThat( + result, + equalTo( + createExpectedResult( + RED, + "This cluster has 2 unavailable primary shards.", + Map.of("unassigned_primaries", 2), + List.of( + new HealthIndicatorImpact( + NAME, + ShardsAvailabilityHealthIndicatorService.PRIMARY_UNASSIGNED_IMPACT_ID, + 1, + "Cannot add data to 1 index [logs-2023.07.11-000024]." + " Searches might return incomplete results.", + List.of(ImpactArea.INGEST, ImpactArea.SEARCH) + ), + new HealthIndicatorImpact( + NAME, + ShardsAvailabilityHealthIndicatorService.READ_ONLY_PRIMARY_UNASSIGNED_IMPACT_ID, + 1, + "Searching 1 index [restored-logs-2023.07.11-000024] might return incomplete results.", + List.of(ImpactArea.SEARCH) + ) + ), + List.of( + new Diagnosis( + ACTION_CHECK_ALLOCATION_EXPLAIN_API, + List.of(new Diagnosis.Resource(INDEX, List.of(originalIndex, restoredIndex))) + ) + ) + ) + ) + ); + } + } + /** * Creates the {@link SystemIndices} with one standalone system index and a system data stream */ @@ -1791,6 +1944,24 @@ private static IndexRoutingTable index(String name, ShardAllocation primaryState ); } + private static IndexRoutingTable frozenIndex(String name, ShardAllocation primaryState, String originalIndex) { + return index( + IndexMetadata.builder(name) + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()) + .put(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_INDEX_NAME_SETTING_KEY, originalIndex) + .put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_STORE_TYPE) + .put(SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_PARTIAL_SETTING_KEY, randomBoolean()) + .build() + ) + .numberOfShards(1) + .numberOfReplicas(0) + .build(), + primaryState + ); + } + private static IndexRoutingTable index(IndexMetadata indexMetadata, ShardAllocation primaryState, ShardAllocation... replicaStates) { var index = indexMetadata.getIndex(); var shardId = new ShardId(index, 0); diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java index acd9ad9e85f50..f74dba71ebb83 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java @@ -167,7 +167,7 @@ public class SearchableSnapshots extends Plugin implements IndexStorePlugin, Eng Setting.Property.NotCopyableOnResize ); public static final Setting SNAPSHOT_INDEX_NAME_SETTING = Setting.simpleString( - "index.store.snapshot.index_name", + SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_INDEX_NAME_SETTING_KEY, Setting.Property.IndexScope, Setting.Property.PrivateIndex, Setting.Property.NotCopyableOnResize From 12f2e8af648e900118e7dd5fbbb4cedeb3173bfa Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 19 Oct 2023 08:22:20 +0100 Subject: [PATCH 010/203] Introduce OperationPurpose#REPOSITORY_ANALYSIS (#101099) Allows to distinguish blob store access for repo analysis from other snapshot activities. --- .../common/blobstore/OperationPurpose.java | 1 + .../testkit/RepositoryAnalysisFailureIT.java | 29 ++++++++++++++--- .../testkit/RepositoryAnalysisSuccessIT.java | 32 +++++++++++++++---- .../blobstore/testkit/BlobAnalyzeAction.java | 13 +++++--- .../testkit/GetBlobChecksumAction.java | 4 +-- .../testkit/RegisterAnalyzeAction.java | 6 ++-- .../testkit/RepositoryAnalyzeAction.java | 12 +++---- 7 files changed, 71 insertions(+), 26 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/blobstore/OperationPurpose.java b/server/src/main/java/org/elasticsearch/common/blobstore/OperationPurpose.java index cc6c9a467c2fe..568f2968c9e61 100644 --- a/server/src/main/java/org/elasticsearch/common/blobstore/OperationPurpose.java +++ b/server/src/main/java/org/elasticsearch/common/blobstore/OperationPurpose.java @@ -16,6 +16,7 @@ */ public enum OperationPurpose { SNAPSHOT("Snapshot"), + REPOSITORY_ANALYSIS("RepositoryAnalysis"), CLUSTER_STATE("ClusterState"), INDICES("Indices"), TRANSLOG("Translog"); diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java index 251d20dbc4c4f..e5ba4c2c6930b 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java @@ -351,6 +351,10 @@ private RepositoryAnalyzeAction.Response analyseRepository(RepositoryAnalyzeActi return client().execute(RepositoryAnalyzeAction.INSTANCE, request).actionGet(30L, TimeUnit.SECONDS); } + private static void assertPurpose(OperationPurpose purpose) { + assertEquals(OperationPurpose.REPOSITORY_ANALYSIS, purpose); + } + public static class TestPlugin extends Plugin implements RepositoryPlugin { static final String DISRUPTABLE_REPO_TYPE = "disruptable"; @@ -422,7 +426,9 @@ public BlobContainer blobContainer(BlobPath path) { } @Override - public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator blobNames) {} + public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator blobNames) { + assertPurpose(purpose); + } private void deleteContainer(DisruptableBlobContainer container) { blobContainer = null; @@ -484,11 +490,13 @@ public BlobPath path() { @Override public boolean blobExists(OperationPurpose purpose, String blobName) { + assertPurpose(purpose); return blobs.containsKey(blobName); } @Override public InputStream readBlob(OperationPurpose purpose, String blobName) throws IOException { + assertPurpose(purpose); final byte[] actualContents = blobs.get(blobName); final byte[] disruptedContents = disruption.onRead(actualContents, 0L, actualContents == null ? 0L : actualContents.length); if (disruptedContents == null) { @@ -499,6 +507,7 @@ public InputStream readBlob(OperationPurpose purpose, String blobName) throws IO @Override public InputStream readBlob(OperationPurpose purpose, String blobName, long position, long length) throws IOException { + assertPurpose(purpose); final byte[] actualContents = blobs.get(blobName); final byte[] disruptedContents = disruption.onRead(actualContents, position, length); if (disruptedContents == null) { @@ -516,13 +525,15 @@ public void writeBlob( long blobSize, boolean failIfAlreadyExists ) throws IOException { + assertPurpose(purpose); writeBlobAtomic(blobName, inputStream, failIfAlreadyExists); } @Override public void writeBlob(OperationPurpose purpose, String blobName, BytesReference bytes, boolean failIfAlreadyExists) throws IOException { - writeBlob(OperationPurpose.SNAPSHOT, blobName, bytes.streamInput(), bytes.length(), failIfAlreadyExists); + assertPurpose(purpose); + writeBlob(purpose, blobName, bytes.streamInput(), bytes.length(), failIfAlreadyExists); } @Override @@ -533,18 +544,20 @@ public void writeMetadataBlob( boolean atomic, CheckedConsumer writer ) throws IOException { + assertPurpose(purpose); final BytesStreamOutput out = new BytesStreamOutput(); writer.accept(out); if (atomic) { - writeBlobAtomic(OperationPurpose.SNAPSHOT, blobName, out.bytes(), failIfAlreadyExists); + writeBlobAtomic(purpose, blobName, out.bytes(), failIfAlreadyExists); } else { - writeBlob(OperationPurpose.SNAPSHOT, blobName, out.bytes(), failIfAlreadyExists); + writeBlob(purpose, blobName, out.bytes(), failIfAlreadyExists); } } @Override public void writeBlobAtomic(OperationPurpose purpose, String blobName, BytesReference bytes, boolean failIfAlreadyExists) throws IOException { + assertPurpose(purpose); final StreamInput inputStream; try { inputStream = bytes.streamInput(); @@ -577,6 +590,7 @@ private void writeBlobAtomic(String blobName, InputStream inputStream, boolean f @Override public DeleteResult delete(OperationPurpose purpose) throws IOException { + assertPurpose(purpose); disruption.onDelete(); deleteContainer.accept(this); final DeleteResult deleteResult = new DeleteResult(blobs.size(), blobs.values().stream().mapToLong(b -> b.length).sum()); @@ -586,11 +600,13 @@ public DeleteResult delete(OperationPurpose purpose) throws IOException { @Override public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator blobNames) { + assertPurpose(purpose); blobNames.forEachRemaining(blobs.keySet()::remove); } @Override public Map listBlobs(OperationPurpose purpose) throws IOException { + assertPurpose(purpose); return disruption.onList( blobs.entrySet() .stream() @@ -600,12 +616,14 @@ public Map listBlobs(OperationPurpose purpose) throws IOEx @Override public Map children(OperationPurpose purpose) { + assertPurpose(purpose); return Map.of(); } @Override public Map listBlobsByPrefix(OperationPurpose purpose, String blobNamePrefix) throws IOException { - final Map blobMetadataByName = listBlobs(OperationPurpose.SNAPSHOT); + assertPurpose(purpose); + final Map blobMetadataByName = listBlobs(purpose); blobMetadataByName.keySet().removeIf(s -> s.startsWith(blobNamePrefix) == false); return blobMetadataByName; } @@ -618,6 +636,7 @@ public void compareAndExchangeRegister( BytesReference updated, ActionListener listener ) { + assertPurpose(purpose); final var register = registers.computeIfAbsent(key, ignored -> new BytesRegister()); listener.onResponse(OptionalBytesReference.of(disruption.onCompareAndExchange(register, expected, updated))); } diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java index 45b1bdc756789..80c42fc9630e1 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java @@ -164,6 +164,10 @@ private static BlobPath buildBlobPath(Settings settings) { } } + private static void assertPurpose(OperationPurpose purpose) { + assertEquals(OperationPurpose.REPOSITORY_ANALYSIS, purpose); + } + static class AssertingRepository extends BlobStoreRepository { private final AtomicReference blobStoreRef = new AtomicReference<>(); @@ -239,7 +243,9 @@ private void deleteContainer(AssertingBlobContainer container) { } @Override - public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator blobNames) {} + public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator blobNames) { + assertPurpose(purpose); + } @Override public void close() {} @@ -299,11 +305,13 @@ public BlobPath path() { @Override public boolean blobExists(OperationPurpose purpose, String blobName) { + assertPurpose(purpose); return blobs.containsKey(blobName); } @Override public InputStream readBlob(OperationPurpose purpose, String blobName) throws IOException { + assertPurpose(purpose); final byte[] contents = blobs.get(blobName); if (contents == null) { throw new FileNotFoundException(blobName + " not found"); @@ -313,6 +321,7 @@ public InputStream readBlob(OperationPurpose purpose, String blobName) throws IO @Override public InputStream readBlob(OperationPurpose purpose, String blobName, long position, long length) throws IOException { + assertPurpose(purpose); final byte[] contents = blobs.get(blobName); if (contents == null) { throw new FileNotFoundException(blobName + " not found"); @@ -329,6 +338,7 @@ public void writeBlob( long blobSize, boolean failIfAlreadyExists ) throws IOException { + assertPurpose(purpose); assertTrue("must only write blob [" + blobName + "] non-atomically if it doesn't already exist", failIfAlreadyExists); assertNull("blob [" + blobName + "] must not exist", blobs.get(blobName)); @@ -339,7 +349,8 @@ public void writeBlob( @Override public void writeBlob(OperationPurpose purpose, String blobName, BytesReference bytes, boolean failIfAlreadyExists) throws IOException { - writeBlob(OperationPurpose.SNAPSHOT, blobName, bytes.streamInput(), bytes.length(), failIfAlreadyExists); + assertPurpose(purpose); + writeBlob(purpose, blobName, bytes.streamInput(), bytes.length(), failIfAlreadyExists); } @Override @@ -350,18 +361,20 @@ public void writeMetadataBlob( boolean atomic, CheckedConsumer writer ) throws IOException { + assertPurpose(purpose); final BytesStreamOutput out = new BytesStreamOutput(); writer.accept(out); if (atomic) { - writeBlobAtomic(OperationPurpose.SNAPSHOT, blobName, out.bytes(), failIfAlreadyExists); + writeBlobAtomic(purpose, blobName, out.bytes(), failIfAlreadyExists); } else { - writeBlob(OperationPurpose.SNAPSHOT, blobName, out.bytes(), failIfAlreadyExists); + writeBlob(purpose, blobName, out.bytes(), failIfAlreadyExists); } } @Override public void writeBlobAtomic(OperationPurpose purpose, String blobName, BytesReference bytes, boolean failIfAlreadyExists) throws IOException { + assertPurpose(purpose); writeBlobAtomic(blobName, bytes.streamInput(), bytes.length(), failIfAlreadyExists); } @@ -391,6 +404,7 @@ private void writeBlobAtomic(String blobName, InputStream inputStream, long blob @Override public DeleteResult delete(OperationPurpose purpose) { + assertPurpose(purpose); deleteContainer.accept(this); final DeleteResult deleteResult = new DeleteResult(blobs.size(), blobs.values().stream().mapToLong(b -> b.length).sum()); blobs.clear(); @@ -399,11 +413,13 @@ public DeleteResult delete(OperationPurpose purpose) { @Override public void deleteBlobsIgnoringIfNotExists(OperationPurpose purpose, Iterator blobNames) { + assertPurpose(purpose); blobNames.forEachRemaining(blobs.keySet()::remove); } @Override public Map listBlobs(OperationPurpose purpose) { + assertPurpose(purpose); return blobs.entrySet() .stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> new BlobMetadata(e.getKey(), e.getValue().length))); @@ -411,18 +427,21 @@ public Map listBlobs(OperationPurpose purpose) { @Override public Map children(OperationPurpose purpose) { + assertPurpose(purpose); return Map.of(); } @Override public Map listBlobsByPrefix(OperationPurpose purpose, String blobNamePrefix) { - final Map blobMetadataByName = listBlobs(OperationPurpose.SNAPSHOT); + assertPurpose(purpose); + final Map blobMetadataByName = listBlobs(purpose); blobMetadataByName.keySet().removeIf(s -> s.startsWith(blobNamePrefix) == false); return blobMetadataByName; } @Override public void getRegister(OperationPurpose purpose, String key, ActionListener listener) { + assertPurpose(purpose); if (firstRegisterRead.compareAndSet(true, false) && randomBoolean() && randomBoolean()) { // only fail the first read, we must not fail the final check listener.onResponse(OptionalBytesReference.EMPTY); @@ -430,7 +449,7 @@ public void getRegister(OperationPurpose purpose, String key, ActionListener new BytesRegister()).get())); } else { final var bogus = randomFrom(BytesArray.EMPTY, new BytesArray(new byte[] { randomByte() })); - compareAndExchangeRegister(OperationPurpose.SNAPSHOT, key, bogus, bogus, listener); + compareAndExchangeRegister(purpose, key, bogus, bogus, listener); } } @@ -442,6 +461,7 @@ public void compareAndExchangeRegister( BytesReference updated, ActionListener listener ) { + assertPurpose(purpose); firstRegisterRead.set(false); if (updated.length() > 1 && randomBoolean() && randomBoolean()) { // updated.length() > 1 so we don't fail the final check because we know there can be no concurrent operations at that point diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java index 72adf752737fc..afd1ac49c9e1b 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java @@ -348,18 +348,23 @@ public StreamInput streamInput() throws IOException { }; if (atomic) { try { - blobContainer.writeBlobAtomic(OperationPurpose.SNAPSHOT, request.blobName, bytesReference, failIfExists); + blobContainer.writeBlobAtomic( + OperationPurpose.REPOSITORY_ANALYSIS, + request.blobName, + bytesReference, + failIfExists + ); } catch (BlobWriteAbortedException e) { assert request.getAbortWrite() : "write unexpectedly aborted"; } } else { - blobContainer.writeBlob(OperationPurpose.SNAPSHOT, request.blobName, bytesReference, failIfExists); + blobContainer.writeBlob(OperationPurpose.REPOSITORY_ANALYSIS, request.blobName, bytesReference, failIfExists); } } else { cancellableThreads.execute(() -> { try { blobContainer.writeBlob( - OperationPurpose.SNAPSHOT, + OperationPurpose.REPOSITORY_ANALYSIS, request.blobName, repository.maybeRateLimitSnapshots( new RandomBlobContentStream(content, request.getTargetLength()), @@ -478,7 +483,7 @@ private void cleanUpAndReturnFailure(Exception exception) { logger.trace(() -> "analysis failed [" + request.getDescription() + "] cleaning up", exception); } try { - blobContainer.deleteBlobsIgnoringIfNotExists(OperationPurpose.SNAPSHOT, Iterators.single(request.blobName)); + blobContainer.deleteBlobsIgnoringIfNotExists(OperationPurpose.REPOSITORY_ANALYSIS, Iterators.single(request.blobName)); } catch (IOException ioException) { exception.addSuppressed(ioException); logger.warn( diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java index c27271e28130b..74530824acb06 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java @@ -98,10 +98,10 @@ protected void doExecute(Task task, Request request, ActionListener li final InputStream rawInputStream; try { if (request.isWholeBlob()) { - rawInputStream = blobContainer.readBlob(OperationPurpose.SNAPSHOT, request.getBlobName()); + rawInputStream = blobContainer.readBlob(OperationPurpose.REPOSITORY_ANALYSIS, request.getBlobName()); } else { rawInputStream = blobContainer.readBlob( - OperationPurpose.SNAPSHOT, + OperationPurpose.REPOSITORY_ANALYSIS, request.getBlobName(), request.getRangeStart(), request.getRangeLength() diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java index 641d18c4204b8..cbe598675e0e2 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java @@ -121,7 +121,7 @@ class Execution extends ActionRunnable { protected void doRun() { if (((CancellableTask) task).notifyIfCancelled(listener) == false) { blobContainer.compareAndExchangeRegister( - OperationPurpose.SNAPSHOT, + OperationPurpose.REPOSITORY_ANALYSIS, registerName, bytesFromLong(currentValue), bytesFromLong(currentValue + 1L), @@ -169,10 +169,10 @@ public void onFailure(Exception e) { }; if (request.getInitialRead() > request.getRequestCount()) { - blobContainer.getRegister(OperationPurpose.SNAPSHOT, registerName, initialValueListener); + blobContainer.getRegister(OperationPurpose.REPOSITORY_ANALYSIS, registerName, initialValueListener); } else { blobContainer.compareAndExchangeRegister( - OperationPurpose.SNAPSHOT, + OperationPurpose.REPOSITORY_ANALYSIS, registerName, bytesFromLong(request.getInitialRead()), bytesFromLong( diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java index d8e3c82433704..79de8bd7b0248 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java @@ -630,16 +630,16 @@ public void onFailure(Exception exp) { } }, ref), listener -> { switch (random.nextInt(3)) { - case 0 -> getBlobContainer().getRegister(OperationPurpose.SNAPSHOT, registerName, listener); + case 0 -> getBlobContainer().getRegister(OperationPurpose.REPOSITORY_ANALYSIS, registerName, listener); case 1 -> getBlobContainer().compareAndExchangeRegister( - OperationPurpose.SNAPSHOT, + OperationPurpose.REPOSITORY_ANALYSIS, registerName, RegisterAnalyzeAction.bytesFromLong(expectedFinalRegisterValue), new BytesArray(new byte[] { (byte) 0xff }), listener ); case 2 -> getBlobContainer().compareAndSetRegister( - OperationPurpose.SNAPSHOT, + OperationPurpose.REPOSITORY_ANALYSIS, registerName, RegisterAnalyzeAction.bytesFromLong(expectedFinalRegisterValue), new BytesArray(new byte[] { (byte) 0xff }), @@ -689,7 +689,7 @@ private void ensureConsistentListing() { try { final BlobContainer blobContainer = getBlobContainer(); final Set missingBlobs = new HashSet<>(expectedBlobs); - final Map blobsMap = blobContainer.listBlobs(OperationPurpose.SNAPSHOT); + final Map blobsMap = blobContainer.listBlobs(OperationPurpose.REPOSITORY_ANALYSIS); missingBlobs.removeAll(blobsMap.keySet()); if (missingBlobs.isEmpty()) { @@ -712,11 +712,11 @@ private void ensureConsistentListing() { private void deleteContainer() { try { final BlobContainer blobContainer = getBlobContainer(); - blobContainer.delete(OperationPurpose.SNAPSHOT); + blobContainer.delete(OperationPurpose.REPOSITORY_ANALYSIS); if (failure.get() != null) { return; } - final Map blobsMap = blobContainer.listBlobs(OperationPurpose.SNAPSHOT); + final Map blobsMap = blobContainer.listBlobs(OperationPurpose.REPOSITORY_ANALYSIS); if (blobsMap.isEmpty() == false) { final RepositoryVerificationException repositoryVerificationException = new RepositoryVerificationException( request.repositoryName, From aa862640e5af74815a0d1d9b9ae3734426ffb459 Mon Sep 17 00:00:00 2001 From: Iraklis Psaroudakis Date: Thu, 19 Oct 2023 10:52:44 +0300 Subject: [PATCH 011/203] Remove translog from bwc testRecovery (#101068) When the test was trying to test recovering translog ops, since we flush on close/shutdown, it failed because it never recovered any translog ops. The code for translog recovery is irrelevant due to that and this PR proposes to remove it. Alternatively, we could simulate killing nodes forcibly before upgrading, but (a) that seems out of the ordinary for upgrades, and (b) in trying that, it did not consistently pass the test because sometimes the flush on close still happened. Fixes #52031 --- .../upgrades/FullClusterRestartIT.java | 83 +------------------ 1 file changed, 2 insertions(+), 81 deletions(-) diff --git a/qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/FullClusterRestartIT.java b/qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/FullClusterRestartIT.java index 75c3d8d77dd72..6af9bc9b11723 100644 --- a/qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/FullClusterRestartIT.java +++ b/qa/full-cluster-restart/src/javaRestTest/java/org/elasticsearch/upgrades/FullClusterRestartIT.java @@ -882,19 +882,12 @@ public void testEmptyShard() throws IOException { } /** - * Tests recovery of an index with or without a translog and the - * statistics we gather about that. + * Tests recovery of an index. */ - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/52031") public void testRecovery() throws Exception { int count; - boolean shouldHaveTranslog; if (isRunningAgainstOldCluster()) { count = between(200, 300); - /* We've had bugs in the past where we couldn't restore - * an index without a translog so we randomize whether - * or not we have one. */ - shouldHaveTranslog = randomBoolean(); Settings.Builder settings = Settings.builder(); if (minimumNodeVersion().before(Version.V_8_0_0) && randomBoolean()) { settings.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), randomBoolean()); @@ -911,21 +904,8 @@ public void testRecovery() throws Exception { flushRequest.addParameter("force", "true"); flushRequest.addParameter("wait_if_ongoing", "true"); assertOK(client().performRequest(flushRequest)); - - if (shouldHaveTranslog) { - // Update a few documents so we are sure to have a translog - indexRandomDocuments( - count / 10, - false, // flushing here would invalidate the whole thing - false, - true, - i -> jsonBuilder().startObject().field("field", "value").endObject() - ); - } - saveInfoDocument(index + "_should_have_translog", Boolean.toString(shouldHaveTranslog)); } else { count = countOfIndexedRandomDocuments(); - shouldHaveTranslog = Booleans.parseBoolean(loadInfoDocument(index + "_should_have_translog")); } // Count the documents in the index to make sure we have as many as we put there @@ -936,72 +916,13 @@ public void testRecovery() throws Exception { assertTotalHits(count, countResponse); if (false == isRunningAgainstOldCluster()) { - boolean restoredFromTranslog = false; boolean foundPrimary = false; Request recoveryRequest = new Request("GET", "/_cat/recovery/" + index); recoveryRequest.addParameter("h", "index,shard,type,stage,translog_ops_recovered"); recoveryRequest.addParameter("s", "index,shard,type"); String recoveryResponse = toStr(client().performRequest(recoveryRequest)); - for (String line : recoveryResponse.split("\n")) { - // Find the primaries - foundPrimary = true; - if (false == line.contains("done") && line.contains("existing_store")) { - continue; - } - /* Mark if we see a primary that looked like it restored from the translog. - * Not all primaries will look like this all the time because we modify - * random documents when we want there to be a translog and they might - * not be spread around all the shards. */ - Matcher m = Pattern.compile("(\\d+)$").matcher(line); - assertTrue(line, m.find()); - int translogOps = Integer.parseInt(m.group(1)); - if (translogOps > 0) { - restoredFromTranslog = true; - } - } + foundPrimary = recoveryResponse.split("\n").length > 0; assertTrue("expected to find a primary but didn't\n" + recoveryResponse, foundPrimary); - assertEquals("mismatch while checking for translog recovery\n" + recoveryResponse, shouldHaveTranslog, restoredFromTranslog); - - var luceneVersion = IndexVersion.current().luceneVersion(); - String currentLuceneVersion = luceneVersion.toString(); - int currentLuceneVersionMajor = luceneVersion.major; - String bwcLuceneVersion = getOldClusterIndexVersion().luceneVersion().toString(); - if (shouldHaveTranslog && false == currentLuceneVersion.equals(bwcLuceneVersion)) { - int numCurrentVersion = 0; - int numBwcVersion = 0; - Request segmentsRequest = new Request("GET", "/_cat/segments/" + index); - segmentsRequest.addParameter("h", "prirep,shard,index,version"); - segmentsRequest.addParameter("s", "prirep,shard,index"); - String segmentsResponse = toStr(client().performRequest(segmentsRequest)); - for (String line : segmentsResponse.split("\n")) { - if (false == line.startsWith("p")) { - continue; - } - Matcher m = Pattern.compile("((\\d+)\\.\\d+\\.\\d+)$").matcher(line); - assertTrue(line, m.find()); - String version = m.group(1); - int major = Integer.parseInt(m.group(2)); - if (currentLuceneVersion.equals(version)) { - numCurrentVersion++; - } else if (bwcLuceneVersion.equals(version)) { - numBwcVersion++; - } else if (major == currentLuceneVersionMajor - 1) { - // we can read one lucene version back. The upgrade path might have created old segment versions. - // that's ok, we just ignore them - continue; - } else { - fail( - "expected lucene version to be one of [" + currentLuceneVersion + "," + bwcLuceneVersion + "] but was " + line - ); - } - } - assertNotEquals( - "expected at least 1 current segment after translog recovery. segments:\n" + segmentsResponse, - 0, - numCurrentVersion - ); - assertNotEquals("expected at least 1 old segment. segments:\n" + segmentsResponse, 0, numBwcVersion); - } } } From f06f5824208dc8e82ff66fb67872dbd1e8c96739 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Thu, 19 Oct 2023 10:26:53 +0200 Subject: [PATCH 012/203] Fix VectorTileRestIT#testCentroidGridTypeOnPolygon (#101047) --- .../org/elasticsearch/common/geo/GeoUtils.java | 14 ++++++++++++++ .../bucket/geogrid/GeoTileUtilsTests.java | 15 ++++----------- .../xpack/vectortile/VectorTileRestIT.java | 1 - .../xpack/vectortile/rest/GridType.java | 7 +++++-- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java b/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java index 39d5d90cc6c04..ceff7bf41c587 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java @@ -581,5 +581,19 @@ public static double quantizeLat(double lat) { return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat)); } + /** + * Transforms the provided longitude to the previous longitude in lucene quantize space. + */ + public static double quantizeLonDown(double lon) { + return GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon) - 1); + } + + /** + * Transforms the provided latitude to the next latitude in lucene quantize space. + */ + public static double quantizeLatUp(double lat) { + return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat) + 1); + } + private GeoUtils() {} } diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java index e6de1e3aa5db8..7114ce46d3b79 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java @@ -256,12 +256,12 @@ public void testEncodingLuceneLonConsistency() { Matchers.anyOf(equalTo(x + 1), equalTo(tiles - 1)) ); // next encoded value down belongs to the tile - assertThat(GeoTileUtils.getXTile(quantizeLonDown(rectangle.getMaxX()), tiles), equalTo(x)); + assertThat(GeoTileUtils.getXTile(GeoUtils.quantizeLonDown(rectangle.getMaxX()), tiles), equalTo(x)); // min longitude belongs to the tile assertThat(GeoTileUtils.getXTile(GeoUtils.quantizeLon(rectangle.getMinX()), tiles), equalTo(x)); if (x != 0) { // next encoded value down belongs to the previous tile - assertThat(GeoTileUtils.getXTile(quantizeLonDown(rectangle.getMinX()), tiles), equalTo(x - 1)); + assertThat(GeoTileUtils.getXTile(GeoUtils.quantizeLonDown(rectangle.getMinX()), tiles), equalTo(x - 1)); } } } @@ -276,7 +276,7 @@ public void testEncodingLuceneLatConsistency() { assertThat(GeoTileUtils.getYTile(GeoUtils.quantizeLat(rectangle.getMaxLat()), tiles), equalTo(y)); if (y != 0) { // next encoded value up belongs to the previous tile - assertThat(GeoTileUtils.getYTile(quantizeLatUp(rectangle.getMaxLat()), tiles), equalTo(y - 1)); + assertThat(GeoTileUtils.getYTile(GeoUtils.quantizeLatUp(rectangle.getMaxLat()), tiles), equalTo(y - 1)); } // min latitude belongs to the next tile except the last one assertThat( @@ -284,15 +284,8 @@ public void testEncodingLuceneLatConsistency() { Matchers.anyOf(equalTo(y + 1), equalTo(tiles - 1)) ); // next encoded value up belongs to the tile - assertThat(GeoTileUtils.getYTile(quantizeLatUp(rectangle.getMinLat()), tiles), equalTo(y)); + assertThat(GeoTileUtils.getYTile(GeoUtils.quantizeLatUp(rectangle.getMinLat()), tiles), equalTo(y)); } } - private static double quantizeLonDown(double lon) { - return GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon) - 1); - } - - private static double quantizeLatUp(double lat) { - return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat) + 1); - } } diff --git a/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java b/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java index 1cb77ec45199f..016bfaabec0ff 100644 --- a/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java +++ b/x-pack/plugin/vector-tile/src/javaRestTest/java/org/elasticsearch/xpack/vectortile/VectorTileRestIT.java @@ -579,7 +579,6 @@ public void testInvalidAggName() { assertThat(ex.getMessage(), Matchers.containsString("Invalid aggregation name [_mvt_name]")); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101038") public void testCentroidGridTypeOnPolygon() throws Exception { final Request mvtRequest = new Request(getHttpMethod(), INDEX_POLYGON + "/_mvt/location/" + (z + 2) + "/" + 4 * x + "/" + 4 * y); mvtRequest.setJsonEntity("{\"size\" : 0, \"grid_type\": \"centroid\", \"grid_precision\": 2}"); diff --git a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/GridType.java b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/GridType.java index aa5d5a75fe4c0..517e68a2b7b08 100644 --- a/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/GridType.java +++ b/x-pack/plugin/vector-tile/src/main/java/org/elasticsearch/xpack/vectortile/rest/GridType.java @@ -16,6 +16,9 @@ import java.io.IOException; import java.util.Locale; +import static org.elasticsearch.common.geo.GeoUtils.quantizeLatUp; +import static org.elasticsearch.common.geo.GeoUtils.quantizeLonDown; + /** * Enum containing the basic geometry types for serializing {@link InternalGeoGridBucket} */ @@ -42,8 +45,8 @@ public byte[] toFeature(GridAggregation gridAggregation, InternalGeoGridBucket b throws IOException { final Rectangle r = gridAggregation.toRectangle(key); final InternalGeoCentroid centroid = bucket.getAggregations().get(RestVectorTileAction.CENTROID_AGG_NAME); - final double featureLon = Math.min(Math.max(centroid.centroid().getX(), r.getMinLon()), r.getMaxLon()); - final double featureLat = Math.min(Math.max(centroid.centroid().getY(), r.getMinLat()), r.getMaxLat()); + final double featureLon = Math.min(Math.max(centroid.centroid().getX(), r.getMinLon()), quantizeLonDown(r.getMaxLon())); + final double featureLat = Math.min(Math.max(centroid.centroid().getY(), quantizeLatUp(r.getMinLat())), r.getMaxLat()); return featureFactory.point(featureLon, featureLat); } }; From c9835b83125c86d7a29ad49ca01f5745e85b9adc Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 19 Oct 2023 19:54:56 +1100 Subject: [PATCH 013/203] Ensure the correct threadContext for RemoteClusterNodesAction (#101050) RemoteClusterNodesAction fetches NodesInfo with system context. It must restore the original caller's context when respond back. This PR ensures that. --- docs/changelog/101050.yaml | 5 +++++ .../cluster/remote/RemoteClusterNodesAction.java | 9 +++++++++ .../remote/RemoteClusterNodesActionTests.java | 13 +++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/101050.yaml diff --git a/docs/changelog/101050.yaml b/docs/changelog/101050.yaml new file mode 100644 index 0000000000000..1a68466e6e728 --- /dev/null +++ b/docs/changelog/101050.yaml @@ -0,0 +1,5 @@ +pr: 101050 +summary: Ensure the correct `threadContext` for `RemoteClusterNodesAction` +area: Network +type: bug +issues: [] diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java index a329d07fe5f63..b1394d261e790 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesAction.java @@ -17,6 +17,7 @@ import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest; import org.elasticsearch.action.admin.cluster.node.info.TransportNodesInfoAction; import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.ContextPreservingActionListener; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.nodes.BaseNodeResponse; import org.elasticsearch.client.internal.Client; @@ -100,6 +101,14 @@ public TransportAction(TransportService transportService, ActionFilters actionFi @Override protected void doExecute(Task task, Request request, ActionListener listener) { final ThreadContext threadContext = client.threadPool().getThreadContext(); + executeWithSystemContext( + request, + threadContext, + ContextPreservingActionListener.wrapPreservingContext(listener, threadContext) + ); + } + + private void executeWithSystemContext(Request request, ThreadContext threadContext, ActionListener listener) { try (var ignore = threadContext.stashContext()) { threadContext.markAsSystemContext(); if (request.remoteClusterServer) { diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java index dc2578b835de2..b593c947fa725 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/remote/RemoteClusterNodesActionTests.java @@ -46,6 +46,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -114,6 +115,7 @@ protected void Request request, ActionListener listener ) { + assertThat(threadContext.isSystemContext(), is(true)); assertSame(TransportNodesInfoAction.TYPE, action); assertThat( asInstanceOf(NodesInfoRequest.class, request).requestedMetrics(), @@ -128,7 +130,10 @@ public void close() {} ); final PlainActionFuture future = new PlainActionFuture<>(); - action.doExecute(mock(Task.class), RemoteClusterNodesAction.Request.REMOTE_CLUSTER_SERVER_NODES, future); + action.doExecute(mock(Task.class), RemoteClusterNodesAction.Request.REMOTE_CLUSTER_SERVER_NODES, ActionListener.wrap(response -> { + assertThat(threadContext.isSystemContext(), is(false)); + future.onResponse(response); + }, future::onFailure)); final List actualNodes = future.actionGet().getNodes(); assertThat(Set.copyOf(actualNodes), equalTo(expectedRemoteServerNodes)); @@ -191,6 +196,7 @@ protected void Request request, ActionListener listener ) { + assertThat(threadContext.isSystemContext(), is(true)); assertSame(TransportNodesInfoAction.TYPE, action); assertThat(asInstanceOf(NodesInfoRequest.class, request).requestedMetrics(), empty()); listener.onResponse((Response) nodesInfoResponse); @@ -202,7 +208,10 @@ public void close() {} ); final PlainActionFuture future = new PlainActionFuture<>(); - action.doExecute(mock(Task.class), RemoteClusterNodesAction.Request.ALL_NODES, future); + action.doExecute(mock(Task.class), RemoteClusterNodesAction.Request.ALL_NODES, ActionListener.wrap(response -> { + assertThat(threadContext.isSystemContext(), is(false)); + future.onResponse(response); + }, future::onFailure)); final List actualNodes = future.actionGet().getNodes(); assertThat(Set.copyOf(actualNodes), equalTo(expectedRemoteNodes)); From 1ddd9879f148f95cf05c731ac9e3f1df0dd54a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Witek?= Date: Thu, 19 Oct 2023 11:12:29 +0200 Subject: [PATCH 014/203] Add ParentTaskAssigningClient.getParentTask accessor method (#101103) --- .../client/internal/ParentTaskAssigningClient.java | 4 ++++ .../client/internal/ParentTaskAssigningClientTests.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/client/internal/ParentTaskAssigningClient.java b/server/src/main/java/org/elasticsearch/client/internal/ParentTaskAssigningClient.java index f82a5a60600c7..967e5c72efdd0 100644 --- a/server/src/main/java/org/elasticsearch/client/internal/ParentTaskAssigningClient.java +++ b/server/src/main/java/org/elasticsearch/client/internal/ParentTaskAssigningClient.java @@ -38,6 +38,10 @@ public ParentTaskAssigningClient(Client in, DiscoveryNode localNode, Task parent this(in, new TaskId(localNode.getId(), parentTask.getId())); } + public TaskId getParentTask() { + return parentTask; + } + /** * Fetch the wrapped client. Use this to make calls that don't set {@link ActionRequest#setParentTask(TaskId)}. */ diff --git a/server/src/test/java/org/elasticsearch/client/internal/ParentTaskAssigningClientTests.java b/server/src/test/java/org/elasticsearch/client/internal/ParentTaskAssigningClientTests.java index e92dd5f11f106..f140e624cc674 100644 --- a/server/src/test/java/org/elasticsearch/client/internal/ParentTaskAssigningClientTests.java +++ b/server/src/test/java/org/elasticsearch/client/internal/ParentTaskAssigningClientTests.java @@ -36,6 +36,8 @@ protected void } }; try (ParentTaskAssigningClient client = new ParentTaskAssigningClient(mock, parentTaskId[0])) { + assertEquals(parentTaskId[0], client.getParentTask()); + // All of these should have the parentTaskId set client.bulk(new BulkRequest()); client.search(new SearchRequest()); From d931fdc62fc6210729e75e305629fd0bbf46fc7f Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Thu, 19 Oct 2023 11:15:08 +0200 Subject: [PATCH 015/203] Mute GeoTileGridAggregatorTests (#101109) relates https://github.com/elastic/elasticsearch/issues/101077 --- .../aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java index 6a34eb104fd19..e92a268c49efe 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.search.aggregations.bucket.geogrid; import org.apache.lucene.geo.GeoEncodingUtils; +import org.apache.lucene.tests.util.LuceneTestCase; import org.elasticsearch.common.geo.GeoBoundingBox; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoUtils; @@ -16,6 +17,7 @@ import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Rectangle; +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101077") public class GeoTileGridAggregatorTests extends GeoGridAggregatorTestCase { @Override From 7f5b68711d16d88121d8b8ebe5149d5c8436f641 Mon Sep 17 00:00:00 2001 From: Abdon Pijpelink Date: Thu, 19 Oct 2023 11:34:21 +0200 Subject: [PATCH 016/203] [DOCS] Inconsistent ulimit (#100948) * [DOCS] Inconsistent ulimit * Another one --- docs/reference/setup/sysconfig/configuring.asciidoc | 2 +- docs/reference/setup/sysconfig/file-descriptors.asciidoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/setup/sysconfig/configuring.asciidoc b/docs/reference/setup/sysconfig/configuring.asciidoc index 4ee7017510152..912a25f22619a 100644 --- a/docs/reference/setup/sysconfig/configuring.asciidoc +++ b/docs/reference/setup/sysconfig/configuring.asciidoc @@ -20,7 +20,7 @@ require that system limits are specified in a On Linux systems, `ulimit` can be used to change resource limits on a temporary basis. Limits usually need to be set as `root` before switching to the user that will run Elasticsearch. For example, to set the number of -open file handles (`ulimit -n`) to 65,536, you can do the following: +open file handles (`ulimit -n`) to 65,535, you can do the following: [source,sh] -------------------------------- diff --git a/docs/reference/setup/sysconfig/file-descriptors.asciidoc b/docs/reference/setup/sysconfig/file-descriptors.asciidoc index 905a29d846c38..8eed9ef98305b 100644 --- a/docs/reference/setup/sysconfig/file-descriptors.asciidoc +++ b/docs/reference/setup/sysconfig/file-descriptors.asciidoc @@ -10,7 +10,7 @@ limited only by available resources. Elasticsearch uses a lot of file descriptors or file handles. Running out of file descriptors can be disastrous and will most probably lead to data loss. Make sure to increase the limit on the number of open files descriptors for -the user running Elasticsearch to 65,536 or higher. +the user running Elasticsearch to 65,535 or higher. For the `.zip` and `.tar.gz` packages, set <> as root before starting Elasticsearch, or set `nofile` to `65535` in From 81d1d0c1c2f6c88dd6c053cee52ca92450a6426a Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:59:01 +0100 Subject: [PATCH 017/203] ESQL: Fix intermittently failing PowTest.testEvaluate {TestCase=pow(integer, double)} (#100970) --- .../esql/expression/function/AbstractFunctionTestCase.java | 2 ++ .../esql/expression/function/scalar/math/PowTests.java | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 3a6479215f479..a139c21473f47 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -220,7 +220,9 @@ private void testEvaluate(boolean readFloating) { } } assertThat(result, not(equalTo(Double.NaN))); + assert testCase.getMatcher().matches(Double.POSITIVE_INFINITY) == false; assertThat(result, not(equalTo(Double.POSITIVE_INFINITY))); + assert testCase.getMatcher().matches(Double.NEGATIVE_INFINITY) == false; assertThat(result, not(equalTo(Double.NEGATIVE_INFINITY))); assertThat(result, testCase.getMatcher()); if (testCase.getExpectedWarnings() != null) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowTests.java index a8744206e91f9..58f56e54c7245 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowTests.java @@ -185,8 +185,8 @@ public static Iterable parameters() { ) ), new TestCaseSupplier("pow(integer, double)", () -> { - // Negative numbers to a non-integer power are NaN - int base = randomIntBetween(0, 1000); + // Positive numbers to a non-integer power + int base = randomIntBetween(1, 1000); double exp = randomDoubleBetween(-10.0, 10.0, true); double expected = Math.pow(base, exp); TestCaseSupplier.TestCase testCase = new TestCaseSupplier.TestCase( @@ -336,7 +336,7 @@ public static Iterable parameters() { }), new TestCaseSupplier("pow(long, double)", () -> { // Negative numbers to non-integer power are NaN - long base = randomLongBetween(0, 1000); + long base = randomLongBetween(1, 1000); double exp = randomDoubleBetween(-10.0, 10.0, true); double expected = Math.pow(base, exp); TestCaseSupplier.TestCase testCase = new TestCaseSupplier.TestCase( From 7e94d4a1ee0145714a20a13f67d2fe524e2c32ea Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 19 Oct 2023 11:05:31 +0100 Subject: [PATCH 018/203] Change NodeConstructor PluginsService constructor to a normal method (#101108) --- .../org/elasticsearch/node/NodeConstruction.java | 16 ++++++++-------- .../elasticsearch/node/NodeServiceProvider.java | 5 +++-- .../elasticsearch/plugins/PluginsService.java | 11 ----------- .../java/org/elasticsearch/node/MockNode.java | 4 ++-- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index 179cf8670210d..2f51d6f87077d 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -327,7 +327,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi throws IOException { // Pass the node settings to the DeprecationLogger class so that it can have the deprecation.skip_deprecated_settings setting: DeprecationLogger.initialize(initialEnvironment.settings()); - Settings tmpSettings = Settings.builder() + Settings environmentSettings = Settings.builder() .put(initialEnvironment.settings()) .put(Client.CLIENT_TYPE_SETTING_S.getKey(), "node") .build(); @@ -356,7 +356,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi Build.current().qualifiedVersion() ); } - if (Environment.PATH_SHARED_DATA_SETTING.exists(tmpSettings)) { + if (Environment.PATH_SHARED_DATA_SETTING.exists(environmentSettings)) { // NOTE: this must be done with an explicit check here because the deprecation property on a path setting will // cause ES to fail to start since logging is not yet initialized on first read of the setting deprecationLogger.warn( @@ -375,7 +375,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi + "multiple disks. This feature will be removed in a future release." ); } - if (Environment.dataPathUsesList(tmpSettings)) { + if (Environment.dataPathUsesList(environmentSettings)) { // already checked for multiple values above, so if this is a list it is a single valued list deprecationLogger.warn( DeprecationCategory.SETTINGS, @@ -399,8 +399,8 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi (e, apmConfig) -> logger.error("failed to delete temporary APM config file [{}], reason: [{}]", apmConfig, e.getMessage()) ); - this.pluginsService = serviceProvider.pluginsServiceCtor(initialEnvironment).apply(tmpSettings); - final Settings settings = Node.mergePluginSettings(pluginsService.pluginMap(), tmpSettings); + pluginsService = serviceProvider.newPluginService(initialEnvironment, environmentSettings); + final Settings settings = Node.mergePluginSettings(pluginsService.pluginMap(), environmentSettings); /* * Create the environment based on the finalized view of the settings. This is to ensure that components get the same setting @@ -461,12 +461,12 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi // creating `NodeEnvironment` breaks the ability to rollback to 7.x on an 8.0 upgrade (`upgradeLegacyNodeFolders`) so do this // after settings validation. - nodeEnvironment = new NodeEnvironment(tmpSettings, environment); + nodeEnvironment = new NodeEnvironment(environmentSettings, environment); logger.info( "node name [{}], node ID [{}], cluster name [{}], roles {}", - Node.NODE_NAME_SETTING.get(tmpSettings), + Node.NODE_NAME_SETTING.get(environmentSettings), nodeEnvironment.nodeId(), - ClusterName.CLUSTER_NAME_SETTING.get(tmpSettings).value(), + ClusterName.CLUSTER_NAME_SETTING.get(environmentSettings).value(), DiscoveryNode.getRolesFromSettings(settings) .stream() .map(DiscoveryNodeRole::roleName) diff --git a/server/src/main/java/org/elasticsearch/node/NodeServiceProvider.java b/server/src/main/java/org/elasticsearch/node/NodeServiceProvider.java index e11d6016ad5cd..ab90ca42bca98 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeServiceProvider.java +++ b/server/src/main/java/org/elasticsearch/node/NodeServiceProvider.java @@ -49,8 +49,9 @@ */ class NodeServiceProvider { - Function pluginsServiceCtor(Environment initialEnvironment) { - return PluginsService.getPluginsServiceCtor(initialEnvironment); + PluginsService newPluginService(Environment environment, Settings settings) { + // this creates a PluginsService with an empty list of classpath plugins + return new PluginsService(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile()); } ScriptService newScriptService( diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java index e55e5d96aa532..600ba59199817 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -25,7 +25,6 @@ import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.Tuple; -import org.elasticsearch.env.Environment; import org.elasticsearch.jdk.JarHell; import org.elasticsearch.jdk.ModuleQualifiedExportsService; import org.elasticsearch.node.ReportingService; @@ -715,16 +714,6 @@ public final List filterPlugins(Class type) { return plugins().stream().filter(x -> type.isAssignableFrom(x.instance().getClass())).map(p -> ((T) p.instance())).toList(); } - /** - * Get a function that will take a {@link Settings} object and return a {@link PluginsService}. - * This function passes in an empty list of classpath plugins. - * @param environment The environment for the plugins service. - * @return A function for creating a plugins service. - */ - public static Function getPluginsServiceCtor(Environment environment) { - return settings -> new PluginsService(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile()); - } - static final LayerAndLoader createPluginModuleLayer( PluginBundle bundle, ClassLoader parentLoader, diff --git a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java index 3dd8c74ef4a38..d2a2f865ffd7a 100644 --- a/test/framework/src/main/java/org/elasticsearch/node/MockNode.java +++ b/test/framework/src/main/java/org/elasticsearch/node/MockNode.java @@ -277,8 +277,8 @@ private MockNode( ) { super(NodeConstruction.prepareConstruction(environment, new MockServiceProvider() { @Override - Function pluginsServiceCtor(Environment initialEnvironment) { - return settings -> new MockPluginsService(settings, initialEnvironment, classpathPlugins); + PluginsService newPluginService(Environment environment, Settings settings) { + return new MockPluginsService(settings, environment, classpathPlugins); } }, forbidPrivateIndexSettings)); From d8f1627e2705f731e9f094c57eca56be113d4a61 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 19 Oct 2023 12:10:58 +0200 Subject: [PATCH 019/203] Remove vagrant support for OS packaging tests (#100987) - This hasnt been maintained in a while and the vagrant gradle plugin also broke compatibiliy for gradle --configuration-cache. Also this removes a lot maintenance burden. - Rework DistroTestPlugin and simplify task dependencies - Rename :qa:os to :qa:packaging - Update testing doc recommending buildkite tools for debugging packaging tests --- TESTING.asciidoc | 126 +---------- .../internal/test/DistroTestPlugin.java | 203 +----------------- .../internal/test/GradleDistroTestTask.java | 106 --------- .../internal/vagrant/VagrantBasePlugin.java | 149 ------------- .../internal/vagrant/VagrantExtension.java | 82 ------- .../internal/vagrant/VagrantMachine.java | 202 ----------------- .../vagrant/VagrantProgressLogger.java | 56 ----- .../internal/vagrant/VagrantShellTask.java | 117 ---------- qa/os/centos-7/build.gradle | 1 - qa/os/debian-9/build.gradle | 1 - qa/os/fedora-28/build.gradle | 1 - qa/os/fedora-29/build.gradle | 1 - qa/os/oel-7/build.gradle | 0 qa/os/sles-12/build.gradle | 0 qa/os/ubuntu-1804/build.gradle | 1 - qa/os/windows-2012r2/build.gradle | 13 -- qa/os/windows-2016/build.gradle | 13 -- qa/{os => packaging}/README.md | 0 qa/{os => packaging}/build.gradle | 10 - ...rchiveGenerateInitialCredentialsTests.java | 0 .../packaging/test/ArchiveTests.java | 0 .../packaging/test/CertGenCliTests.java | 0 .../packaging/test/ConfigurationTests.java | 0 .../packaging/test/CronEvalCliTests.java | 0 .../packaging/test/DebMetadataTests.java | 0 .../packaging/test/DebPreservationTests.java | 0 .../packaging/test/DockerTests.java | 0 .../test/EnrollNodeToClusterTests.java | 0 .../test/EnrollmentProcessTests.java | 0 .../test/HttpClientThreadsFilter.java | 0 .../test/KeystoreManagementTests.java | 0 .../packaging/test/PackageTests.java | 0 .../packaging/test/PackageUpgradeTests.java | 0 ...ackagesSecurityAutoConfigurationTests.java | 0 .../packaging/test/PackagingTestCase.java | 0 .../packaging/test/PasswordToolsTests.java | 0 .../packaging/test/PluginCliTests.java | 0 .../packaging/test/RpmMetadataTests.java | 0 .../packaging/test/RpmPreservationTests.java | 0 .../packaging/test/SqlCliTests.java | 0 .../test/TemporaryDirectoryConfigTests.java | 0 .../packaging/test/WindowsServiceTests.java | 0 .../packaging/util/Archives.java | 0 .../elasticsearch/packaging/util/Cleanup.java | 0 .../packaging/util/Distribution.java | 0 .../packaging/util/FileExistenceMatchers.java | 0 .../packaging/util/FileMatcher.java | 0 .../packaging/util/FileUtils.java | 0 .../packaging/util/Installation.java | 0 .../packaging/util/Packages.java | 0 .../packaging/util/Platforms.java | 0 .../packaging/util/ProcessInfo.java | 0 .../packaging/util/ServerUtils.java | 0 .../elasticsearch/packaging/util/Shell.java | 0 .../packaging/util/docker/Docker.java | 0 .../util/docker/DockerFileAttributes.java | 0 .../util/docker/DockerFileMatcher.java | 0 .../packaging/util/docker/DockerRun.java | 0 .../packaging/util/docker/DockerShell.java | 0 .../packaging/util/docker/MockServer.java | 0 .../src/test/resources/log4j2-test.properties | 0 .../org/elasticsearch/packaging/test/http.crt | 0 .../org/elasticsearch/packaging/test/http.key | 0 .../elasticsearch/packaging/test/http_ca.crt | 0 .../elasticsearch/packaging/test/http_ca.key | 0 .../packaging/test/transport.crt | 0 .../packaging/test/transport.key | 0 .../packaging/test/transport_ca.crt | 0 68 files changed, 6 insertions(+), 1076 deletions(-) delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/GradleDistroTestTask.java delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantBasePlugin.java delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantExtension.java delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantMachine.java delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantProgressLogger.java delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantShellTask.java delete mode 100644 qa/os/centos-7/build.gradle delete mode 100644 qa/os/debian-9/build.gradle delete mode 100644 qa/os/fedora-28/build.gradle delete mode 100644 qa/os/fedora-29/build.gradle delete mode 100644 qa/os/oel-7/build.gradle delete mode 100644 qa/os/sles-12/build.gradle delete mode 100644 qa/os/ubuntu-1804/build.gradle delete mode 100644 qa/os/windows-2012r2/build.gradle delete mode 100644 qa/os/windows-2016/build.gradle rename qa/{os => packaging}/README.md (100%) rename qa/{os => packaging}/build.gradle (90%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/ArchiveGenerateInitialCredentialsTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/ArchiveTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/CertGenCliTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/ConfigurationTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/CronEvalCliTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/DebMetadataTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/DebPreservationTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/DockerTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/EnrollmentProcessTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/HttpClientThreadsFilter.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/PackageTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/PackageUpgradeTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/PackagesSecurityAutoConfigurationTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/PasswordToolsTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/PluginCliTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/RpmMetadataTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/RpmPreservationTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/SqlCliTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/TemporaryDirectoryConfigTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Archives.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Cleanup.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Distribution.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/FileExistenceMatchers.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/FileMatcher.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/FileUtils.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Installation.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Packages.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Platforms.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/ServerUtils.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/Shell.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileAttributes.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileMatcher.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/docker/DockerRun.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/docker/DockerShell.java (100%) rename qa/{os => packaging}/src/test/java/org/elasticsearch/packaging/util/docker/MockServer.java (100%) rename qa/{os => packaging}/src/test/resources/log4j2-test.properties (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/http.crt (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/http.key (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/http_ca.crt (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/http_ca.key (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/transport.crt (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/transport.key (100%) rename qa/{os => packaging}/src/test/resources/org/elasticsearch/packaging/test/transport_ca.crt (100%) diff --git a/TESTING.asciidoc b/TESTING.asciidoc index d33121a15dcf7..dbd11cce0256c 100644 --- a/TESTING.asciidoc +++ b/TESTING.asciidoc @@ -487,136 +487,16 @@ If in doubt about which command to use, simply run :check == Testing packaging -The packaging tests use Vagrant virtual machines or cloud instances to verify +The packaging tests are run on different build vm cloud instances to verify that installing and running Elasticsearch distributions works correctly on supported operating systems. These tests should really only be run on ephemeral systems because they're destructive; that is, these tests install and remove packages and freely modify system settings, so you will probably regret it if you execute them on your development machine. -When you run a packaging test, Gradle will set up the target VM and mount your -repository directory in the VM. Once this is done, a Gradle task will issue a -Vagrant command to run a *nested* Gradle task on the VM. This nested Gradle -runs the actual "destructive" test classes. - -. Install Virtual Box and Vagrant. -+ -. (Optional) Install https://github.com/fgrehm/vagrant-cachier[vagrant-cachier] to squeeze -a bit more performance out of the process: -+ --------------------------------------- -vagrant plugin install vagrant-cachier --------------------------------------- -+ -. You can run all of the OS packaging tests with `./gradlew packagingTest`. -This task includes our legacy `bats` tests. To run only the OS tests that are -written in Java, run `.gradlew distroTest`, will cause Gradle to build the tar, -zip, and deb packages and all the plugins. It will then run the tests on every -available system. This will take a very long time. -+ -Fortunately, the various systems under test have their own Gradle tasks under -`qa/os`. To find the systems tested, do a listing of the `qa/os` directory. -To find out what packaging combinations can be tested on a system, run -the `tasks` task. For example: -+ ----------------------------------- -./gradlew :qa:os:ubuntu-1804:tasks ----------------------------------- -+ -If you want a quick test of the tarball and RPM packagings for Centos 7, you -would run: -+ -------------------------------------------------------------------------------------------------- -./gradlew :qa:os:centos-7:distroTest.default-rpm :qa:os:centos-7:distroTest.default-linux-archive -------------------------------------------------------------------------------------------------- - -Note that if you interrupt Gradle in the middle of running these tasks, any boxes started -will remain running and you'll have to stop them manually with `./gradlew --stop` or -`vagrant halt`. - -All the regular vagrant commands should just work so you can get a shell in a -VM running trusty by running -`vagrant up ubuntu-1804 --provider virtualbox && vagrant ssh ubuntu-1804`. - -=== Testing packaging on Windows - -The packaging tests also support Windows Server 2012R2 and Windows Server 2016. -Unfortunately we're not able to provide boxes for them in open source use -because of licensing issues. Any Virtualbox image that has WinRM and Powershell -enabled for remote users should work. - -Specify the image IDs of the Windows boxes to gradle with the following project -properties. They can be set in `~/.gradle/gradle.properties` like - ------------------------------------- -vagrant.windows-2012r2.id=my-image-id -vagrant.windows-2016.id=another-image-id ------------------------------------- - -or passed on the command line like `-Pvagrant.windows-2012r2.id=my-image-id` -`-Pvagrant.windows-2016=another-image-id` - -These properties are required for Windows support in all gradle tasks that -handle packaging tests. Either or both may be specified. - -If you're running vagrant commands outside of gradle, specify the Windows boxes -with the environment variables - -* `VAGRANT_WINDOWS_2012R2_BOX` -* `VAGRANT_WINDOWS_2016_BOX` - -=== Testing VMs are disposable - -It's important to think of VMs like cattle. If they become lame you just shoot -them and let vagrant reprovision them. Say you've hosed your precise VM: +=== Reproducing packaging tests ----------------------------------------------------- -vagrant ssh ubuntu-1604 -c 'sudo rm -rf /bin'; echo oops ----------------------------------------------------- - -All you've got to do to get another one is - ----------------------------------------------- -vagrant destroy -f ubuntu-1604 && vagrant up ubuntu-1604 --provider virtualbox ----------------------------------------------- - -The whole process takes a minute and a half on a modern laptop, two and a half -without vagrant-cachier. - -It's possible that some downloads will fail and it'll be impossible to restart -them. This is a bug in vagrant. See the instructions here for how to work -around it: -https://github.com/mitchellh/vagrant/issues/4479 - -Some vagrant commands will work on all VMs at once: - ------------------- -vagrant halt -vagrant destroy -f ------------------- - -`vagrant up` would normally start all the VMs but we've prevented that because -that'd consume a ton of ram. - -=== Iterating on packaging tests - -Because our packaging tests are capable of testing many combinations of OS -(e.g., Windows, Linux, etc.), package type (e.g., zip file, RPM, etc.), -Elasticsearch distribution type (e.g., default or OSS), and so forth, it's -faster to develop against smaller subsets of the tests. For example, to run -tests for the default archive distribution on Fedora 28: - ------------------------------------------------------------ -./gradlew :qa:os:fedora-28:distroTest.default-linux-archive ------------------------------------------------------------ - -These test tasks can use the `--tests`, `--info`, and `--debug` parameters just like -non-OS tests can. For example: - ------------------------------------------------------------ -./gradlew :qa:os:fedora-28:distroTest.default-linux-archive \ - --tests "com.elasticsearch.packaging.test.ArchiveTests" ------------------------------------------------------------ +To reproduce or debug packaging tests failures we recommend using using our provided https://github.com/elastic/elasticsearch-infra/blob/master/buildkite-tools/README.md[*buildkite tools*] == Testing backwards compatibility diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/DistroTestPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/DistroTestPlugin.java index ada263853c07d..bcbe1740630ce 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/DistroTestPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/DistroTestPlugin.java @@ -16,16 +16,10 @@ import org.elasticsearch.gradle.Version; import org.elasticsearch.gradle.VersionProperties; import org.elasticsearch.gradle.internal.InternalDistributionDownloadPlugin; -import org.elasticsearch.gradle.internal.Jdk; import org.elasticsearch.gradle.internal.JdkDownloadPlugin; -import org.elasticsearch.gradle.internal.conventions.GUtils; -import org.elasticsearch.gradle.internal.conventions.util.Util; import org.elasticsearch.gradle.internal.docker.DockerSupportPlugin; import org.elasticsearch.gradle.internal.docker.DockerSupportService; import org.elasticsearch.gradle.internal.info.BuildParams; -import org.elasticsearch.gradle.internal.vagrant.VagrantBasePlugin; -import org.elasticsearch.gradle.internal.vagrant.VagrantExtension; -import org.elasticsearch.gradle.internal.vagrant.VagrantMachine; import org.elasticsearch.gradle.test.SystemPropertyCommandLineArgumentProvider; import org.elasticsearch.gradle.util.GradleUtils; import org.gradle.api.Action; @@ -36,27 +30,19 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.artifacts.type.ArtifactTypeDefinition; -import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.specs.Specs; -import org.gradle.api.tasks.Copy; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.testing.Test; -import org.gradle.initialization.layout.BuildLayout; -import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.function.Supplier; -import java.util.stream.Stream; - -import javax.inject.Inject; import static org.elasticsearch.gradle.distribution.ElasticsearchDistributionTypes.ARCHIVE; import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.ALL_INTERNAL; @@ -67,32 +53,18 @@ import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_IRONBANK; import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_UBI; import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.RPM; -import static org.elasticsearch.gradle.internal.vagrant.VagrantMachine.convertLinuxPath; -import static org.elasticsearch.gradle.internal.vagrant.VagrantMachine.convertWindowsPath; /** * This class defines gradle tasks for testing our various distribution artifacts. */ public class DistroTestPlugin implements Plugin { - private static final String SYSTEM_JDK_VERSION = "17.0.2+8"; - private static final String SYSTEM_JDK_VENDOR = "adoptium"; - private static final String GRADLE_JDK_VERSION = "16.0.2+7"; - private static final String GRADLE_JDK_VENDOR = "adoptium"; // all distributions used by distro tests. this is temporary until tests are per distribution private static final String EXAMPLE_PLUGIN_CONFIGURATION = "examplePlugin"; - private static final String IN_VM_SYSPROP = "tests.inVM"; private static final String DISTRIBUTION_SYSPROP = "tests.distribution"; private static final String BWC_DISTRIBUTION_SYSPROP = "tests.bwc-distribution"; private static final String EXAMPLE_PLUGIN_SYSPROP = "tests.example-plugin"; - private final File rootDir; - - @Inject - public DistroTestPlugin(BuildLayout buildLayout) { - this.rootDir = buildLayout.getRootDirectory(); - } - @Override public void apply(Project project) { project.getRootProject().getPluginManager().apply(DockerSupportPlugin.class); @@ -118,16 +90,9 @@ public void apply(Project project) { List> windowsTestTasks = new ArrayList<>(); Map>> linuxTestTasks = new HashMap<>(); - Map>> upgradeTestTasks = new HashMap<>(); - Map> depsTasks = new HashMap<>(); for (ElasticsearchDistribution distribution : testDistributions) { String taskname = destructiveDistroTestTaskName(distribution); - TaskProvider depsTask = project.getTasks().register(taskname + "#deps"); - // explicitly depend on the archive not on the implicit extracted distribution - depsTask.configure(t -> t.dependsOn(distribution.getArchiveDependencies())); - depsTask.configure(t -> t.dependsOn(examplePlugin.getDependencies())); - depsTasks.put(taskname, depsTask); TaskProvider destructiveTask = configureTestTask(project, taskname, distribution, t -> { t.onlyIf( "Docker is not available", @@ -136,7 +101,7 @@ public void apply(Project project) { addDistributionSysprop(t, DISTRIBUTION_SYSPROP, distribution::getFilepath); addDistributionSysprop(t, EXAMPLE_PLUGIN_SYSPROP, () -> examplePlugin.getSingleFile().toString()); t.exclude("**/PackageUpgradeTests.class"); - }, depsTask); + }, distribution.getArchiveDependencies(), examplePlugin.getDependencies()); if (distribution.getPlatform() == Platform.WINDOWS) { windowsTestTasks.add(destructiveTask); @@ -164,82 +129,15 @@ public void apply(Project project) { } String upgradeTaskname = destructiveDistroUpgradeTestTaskName(distribution, version.toString()); - TaskProvider upgradeDepsTask = project.getTasks().register(upgradeTaskname + "#deps"); - upgradeDepsTask.configure(t -> t.dependsOn(distribution, bwcDistro)); - depsTasks.put(upgradeTaskname, upgradeDepsTask); TaskProvider upgradeTest = configureTestTask(project, upgradeTaskname, distribution, t -> { addDistributionSysprop(t, DISTRIBUTION_SYSPROP, distribution::getFilepath); addDistributionSysprop(t, BWC_DISTRIBUTION_SYSPROP, bwcDistro::getFilepath); t.include("**/PackageUpgradeTests.class"); - }, upgradeDepsTask); + }, distribution, bwcDistro); versionTasks.get(version.toString()).configure(t -> t.dependsOn(upgradeTest)); - upgradeTestTasks.computeIfAbsent(version.toString(), k -> new ArrayList<>()).add(upgradeTest); } } } - - // setup jdks used by no-jdk tests, and by gradle executing - TaskProvider linuxGradleJdk = createJdk(project, "gradle", GRADLE_JDK_VENDOR, GRADLE_JDK_VERSION, "linux", "x64"); - TaskProvider linuxSystemJdk = createJdk(project, "system", SYSTEM_JDK_VENDOR, SYSTEM_JDK_VERSION, "linux", "x64"); - TaskProvider windowsGradleJdk = createJdk(project, "gradle", GRADLE_JDK_VENDOR, GRADLE_JDK_VERSION, "windows", "x64"); - TaskProvider windowsSystemJdk = createJdk(project, "system", SYSTEM_JDK_VENDOR, SYSTEM_JDK_VERSION, "windows", "x64"); - - project.subprojects(vmProject -> { - vmProject.getPluginManager().apply(VagrantBasePlugin.class); - TaskProvider gradleJdk = isWindows(vmProject) ? windowsGradleJdk : linuxGradleJdk; - TaskProvider systemJdk = isWindows(vmProject) ? windowsSystemJdk : linuxSystemJdk; - configureVM(vmProject, rootDir, gradleJdk, systemJdk); - List vmDependencies = Arrays.asList( - gradleJdk, - systemJdk, - project.getConfigurations().getByName("testRuntimeClasspath") - ); - - Map> vmLifecyleTasks = lifecycleTasks(vmProject, "distroTest"); - Map> vmVersionTasks = versionTasks(vmProject, "distroUpgradeTest"); - TaskProvider distroTest = vmProject.getTasks().register("distroTest"); - - // windows boxes get windows distributions, and linux boxes get linux distributions - if (isWindows(vmProject)) { - configureVMWrapperTasks(vmProject, windowsTestTasks, depsTasks, wrapperTask -> { - vmLifecyleTasks.get(ARCHIVE).configure(t -> t.dependsOn(wrapperTask)); - }, vmDependencies); - } else { - for (var entry : linuxTestTasks.entrySet()) { - ElasticsearchDistributionType type = entry.getKey(); - TaskProvider vmLifecycleTask = vmLifecyleTasks.get(type); - configureVMWrapperTasks(vmProject, entry.getValue(), depsTasks, wrapperTask -> { - vmLifecycleTask.configure(t -> t.dependsOn(wrapperTask)); - - // Only VM sub-projects that are specifically opted-in to testing Docker should - // have the Docker task added as a dependency. Although we control whether Docker - // is installed in the VM via `Vagrantfile` and we could auto-detect its presence - // in the VM, the test tasks e.g. `destructiveDistroTest.default-docker` are defined - // on the host during Gradle's configuration phase and not in the VM, so - // auto-detection doesn't work. - // - // The shouldTestDocker property could be null, hence we use Boolean.TRUE.equals() - boolean shouldExecute = (type.isDocker()) || Boolean.TRUE.equals(vmProject.findProperty("shouldTestDocker")); - - if (shouldExecute) { - distroTest.configure(t -> t.dependsOn(wrapperTask)); - } - }, vmDependencies); - } - - for (var entry : upgradeTestTasks.entrySet()) { - String version = entry.getKey(); - TaskProvider vmVersionTask = vmVersionTasks.get(version); - configureVMWrapperTasks( - vmProject, - entry.getValue(), - depsTasks, - wrapperTask -> { vmVersionTask.configure(t -> t.dependsOn(wrapperTask)); }, - vmDependencies - ); - } - } - }); } private static Map> lifecycleTasks(Project project, String taskPrefix) { @@ -252,7 +150,6 @@ private static Map> lifecycleTask lifecyleTasks.put(ARCHIVE, project.getTasks().register(taskPrefix + ".archives")); lifecyleTasks.put(DEB, project.getTasks().register(taskPrefix + ".packages")); lifecyleTasks.put(RPM, lifecyleTasks.get(DEB)); - return lifecyleTasks; } @@ -266,67 +163,6 @@ private static Map> versionTasks(Project project, String return versionTasks; } - private static TaskProvider createJdk( - Project project, - String purpose, - String vendor, - String version, - String platform, - String architecture - ) { - Jdk jdk = JdkDownloadPlugin.getContainer(project).create(platform + "-" + purpose); - jdk.setVendor(vendor); - jdk.setVersion(version); - jdk.setPlatform(platform); - jdk.setArchitecture(architecture); - - String taskname = "copy" + GUtils.capitalize(platform) + GUtils.capitalize(purpose) + "Jdk"; - TaskProvider copyTask = project.getTasks().register(taskname, Copy.class); - copyTask.configure(t -> { - t.from(jdk); - t.into(new File(project.getBuildDir(), "jdks/" + platform + "-" + architecture + "-" + vendor + "-" + version)); - }); - return copyTask; - } - - private static void configureVM( - Project project, - File rootDir, - TaskProvider gradleJdkProvider, - TaskProvider systemJdkProvider - ) { - String box = project.getName(); - - // setup VM used by these tests - VagrantExtension vagrant = project.getExtensions().getByType(VagrantExtension.class); - vagrant.setBox(box); - - vagrant.vmEnv("SYSTEM_JAVA_HOME", convertPath(rootDir, vagrant, systemJdkProvider, "", "")); - // set java home for gradle to use. package tests will overwrite/remove this for each test case - vagrant.vmEnv("JAVA_HOME", convertPath(rootDir, vagrant, gradleJdkProvider, "", "")); - if (System.getenv("JENKINS_URL") != null) { - Stream.of("JOB_NAME", "JENKINS_URL", "BUILD_NUMBER", "BUILD_URL").forEach(name -> vagrant.vmEnv(name, System.getenv(name))); - } - vagrant.setIsWindowsVM(isWindows(project)); - } - - private static Object convertPath( - File rootDirectory, - VagrantExtension vagrant, - TaskProvider jdkProvider, - String additionaLinux, - String additionalWindows - ) { - return Util.toStringable(() -> { - String hostPath = jdkProvider.get().getDestinationDir().toString(); - if (vagrant.isWindowsVM()) { - return convertWindowsPath(rootDirectory, hostPath) + additionalWindows; - } else { - return convertLinuxPath(rootDirectory, hostPath) + additionaLinux; - } - }); - } - private static Configuration configureExamplePlugin(Project project) { Configuration examplePlugin = project.getConfigurations().create(EXAMPLE_PLUGIN_CONFIGURATION); examplePlugin.getAttributes().attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.ZIP_TYPE); @@ -335,32 +171,6 @@ private static Configuration configureExamplePlugin(Project project) { return examplePlugin; } - private static void configureVMWrapperTasks( - Project project, - List> destructiveTasks, - Map> depsTasks, - Action> configure, - Object... additionalDeps - ) { - for (TaskProvider destructiveTask : destructiveTasks) { - String destructiveTaskName = destructiveTask.getName(); - String taskname = destructiveTaskName.substring("destructive".length()); - taskname = taskname.substring(0, 1).toLowerCase(Locale.ROOT) + taskname.substring(1); - TaskProvider vmTask = project.getTasks().register(taskname, GradleDistroTestTask.class, t -> { - t.setGroup(JavaBasePlugin.VERIFICATION_GROUP); - t.setDescription("Runs " + destructiveTaskName.split("\\.", 2)[1] + " tests within vagrant"); - t.setTaskName(destructiveTaskName); - t.extraArg("-D'" + IN_VM_SYSPROP + "'"); - t.dependsOn(depsTasks.get(destructiveTaskName)); - t.dependsOn(additionalDeps); - t.setLogLevel(project.getGradle().getStartParameter().getLogLevel().toString()); - t.setExtension(project.getExtensions().findByType(VagrantExtension.class)); - t.setService(project.getExtensions().getByType(VagrantMachine.class)); - }); - configure.execute(vmTask); - } - } - private static TaskProvider configureTestTask( Project project, String taskname, @@ -377,9 +187,7 @@ private static TaskProvider configureTestTask( t.setClasspath(testSourceSet.getRuntimeClasspath()); t.setTestClassesDirs(testSourceSet.getOutput().getClassesDirs()); t.setWorkingDir(project.getProjectDir()); - if (System.getProperty(IN_VM_SYSPROP) == null) { - t.dependsOn(deps); - } + t.dependsOn(deps); configure.execute(t); }); } @@ -438,11 +246,6 @@ private static ElasticsearchDistribution createDistro( return distro; } - // return true if the project is for a windows VM, false otherwise - private static boolean isWindows(Project project) { - return project.getName().contains("windows"); - } - private static String distroId(ElasticsearchDistributionType type, Platform platform, boolean bundledJdk, Architecture architecture) { return "default-" + (type == ARCHIVE ? platform + "-" : "") diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/GradleDistroTestTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/GradleDistroTestTask.java deleted file mode 100644 index bf81e6316e635..0000000000000 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/GradleDistroTestTask.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.gradle.internal.test; - -import org.elasticsearch.gradle.internal.vagrant.VagrantShellTask; -import org.gradle.api.file.ProjectLayout; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.options.Option; -import org.gradle.initialization.layout.BuildLayout; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.inject.Inject; - -import static org.elasticsearch.gradle.internal.vagrant.VagrantMachine.convertLinuxPath; -import static org.elasticsearch.gradle.internal.vagrant.VagrantMachine.convertWindowsPath; - -/** - * Run a gradle task of the current build, within the configured vagrant VM. - */ -public class GradleDistroTestTask extends VagrantShellTask { - - private String taskName; - private String testClass; - - private List extraArgs = new ArrayList<>(); - - private final ProjectLayout projectLayout; - private final BuildLayout buildLayout; - - private String logLevel; - - @Inject - public GradleDistroTestTask(BuildLayout buildLayout, ProjectLayout projectLayout) { - super(buildLayout); - this.buildLayout = buildLayout; - this.projectLayout = projectLayout; - } - - public void setTaskName(String taskName) { - this.taskName = taskName; - } - - @Input - public String getTaskName() { - return taskName; - } - - @Option(option = "tests", description = "Sets test class or method name to be included, '*' is supported.") - public void setTestClass(String testClass) { - this.testClass = testClass; - } - - @Input - public List getExtraArgs() { - return extraArgs; - } - - public void extraArg(String arg) { - this.extraArgs.add(arg); - } - - public void setLogLevel(String logLevel) { - this.logLevel = logLevel; - } - - @Override - protected List getWindowsScript() { - return getScript(true); - } - - @Override - protected List getLinuxScript() { - return getScript(false); - } - - private List getScript(boolean isWindows) { - String cacheDir = projectLayout.getBuildDirectory().dir("gradle-cache").get().getAsFile().getAbsolutePath(); - StringBuilder line = new StringBuilder(); - line.append(isWindows ? "& .\\gradlew " : "./gradlew "); - line.append(taskName); - line.append(" --project-cache-dir "); - line.append( - isWindows - ? convertWindowsPath(buildLayout.getRootDirectory(), cacheDir) - : convertLinuxPath(buildLayout.getRootDirectory(), cacheDir) - ); - line.append(" -S"); - line.append(" --parallel"); - line.append(" -D'org.gradle.logging.level'=" + logLevel); - if (testClass != null) { - line.append(" --tests="); - line.append(testClass); - } - extraArgs.stream().map(s -> " " + s).forEach(line::append); - return Collections.singletonList(line.toString()); - } -} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantBasePlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantBasePlugin.java deleted file mode 100644 index b7b4d20d51f56..0000000000000 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantBasePlugin.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.gradle.internal.vagrant; - -import org.elasticsearch.gradle.ReaperPlugin; -import org.elasticsearch.gradle.internal.InternalReaperPlugin; -import org.elasticsearch.gradle.util.GradleUtils; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.execution.TaskActionListener; -import org.gradle.api.execution.TaskExecutionListener; -import org.gradle.api.tasks.TaskState; - -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Locale; -import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class VagrantBasePlugin implements Plugin { - - @Override - public void apply(Project project) { - project.getRootProject().getPluginManager().apply(VagrantSetupCheckerPlugin.class); - project.getRootProject().getPluginManager().apply(VagrantManagerPlugin.class); - project.getRootProject().getPluginManager().apply(InternalReaperPlugin.class); - - var reaperServiceProvider = GradleUtils.getBuildService(project.getGradle().getSharedServices(), ReaperPlugin.REAPER_SERVICE_NAME); - var extension = project.getExtensions().create("vagrant", VagrantExtension.class, project); - var service = project.getExtensions().create("vagrantService", VagrantMachine.class, extension, reaperServiceProvider); - - project.getGradle() - .getTaskGraph() - .whenReady( - graph -> service.refs = graph.getAllTasks() - .stream() - .filter(t -> t instanceof VagrantShellTask) - .filter(t -> t.getProject() == project) - .count() - ); - } - - /** - * Check vagrant and virtualbox versions, if any vagrant test tasks will be run. - */ - static class VagrantSetupCheckerPlugin implements Plugin { - - private static final Pattern VAGRANT_VERSION = Pattern.compile("Vagrant (\\d+\\.\\d+\\.\\d+)"); - private static final Pattern VIRTUAL_BOX_VERSION = Pattern.compile("(\\d+\\.\\d+)"); - - @Override - public void apply(Project project) { - if (project != project.getRootProject()) { - throw new IllegalArgumentException("VagrantSetupCheckerPlugin can only be applied to the root project of a build"); - } - - project.getGradle().getTaskGraph().whenReady(graph -> { - boolean needsVagrant = graph.getAllTasks().stream().anyMatch(t -> t instanceof VagrantShellTask); - if (needsVagrant) { - checkVersion(project, "vagrant", VAGRANT_VERSION, 1, 8, 6); - checkVersion(project, "vboxmanage", VIRTUAL_BOX_VERSION, 5, 1); - } - }); - } - - void checkVersion(Project project, String tool, Pattern versionRegex, int... minVersion) { - ByteArrayOutputStream pipe = new ByteArrayOutputStream(); - project.exec(spec -> { - spec.setCommandLine(tool, "--version"); - spec.setStandardOutput(pipe); - }); - String output = pipe.toString(StandardCharsets.UTF_8).trim(); - Matcher matcher = versionRegex.matcher(output); - if (matcher.find() == false) { - throw new IllegalStateException( - tool + " version output [" + output + "] did not match regex [" + versionRegex.pattern() + "]" - ); - } - - String version = matcher.group(1); - List versionParts = Stream.of(version.split("\\.")).map(Integer::parseInt).toList(); - for (int i = 0; i < minVersion.length; ++i) { - int found = versionParts.get(i); - if (found > minVersion[i]) { - break; // most significant version is good - } else if (found < minVersion[i]) { - final String exceptionMessage = String.format( - Locale.ROOT, - "Unsupported version of %s. Found [%s], expected [%s+]", - tool, - version, - Stream.of(minVersion).map(String::valueOf).collect(Collectors.joining(".")) - ); - - throw new IllegalStateException(exceptionMessage); - } // else equal, so check next element - } - } - } - - /** - * Adds global hooks to manage destroying, starting and updating VMs. - */ - static class VagrantManagerPlugin implements Plugin, TaskActionListener, TaskExecutionListener { - - @Override - public void apply(Project project) { - if (project != project.getRootProject()) { - throw new IllegalArgumentException("VagrantManagerPlugin can only be applied to the root project of a build"); - } - project.getGradle().addListener(this); - } - - private void callIfVagrantTask(Task task, Consumer method) { - if (task instanceof VagrantShellTask) { - VagrantMachine service = task.getProject().getExtensions().getByType(VagrantMachine.class); - method.accept(service); - } - } - - @Override - public void beforeExecute(Task task) { /* nothing to do */} - - @Override - public void afterActions(Task task) { /* nothing to do */ } - - @Override - public void beforeActions(Task task) { - callIfVagrantTask(task, VagrantMachine::maybeStartVM); - } - - @Override - public void afterExecute(Task task, TaskState state) { - callIfVagrantTask(task, service -> service.maybeStopVM(state.getFailure() != null)); - } - } - -} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantExtension.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantExtension.java deleted file mode 100644 index 67799e6a6e751..0000000000000 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantExtension.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.gradle.internal.vagrant; - -import org.gradle.api.Project; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.MapProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; - -import java.io.File; -import java.util.Map; - -public class VagrantExtension { - - private final Property box; - private final MapProperty hostEnv; - private final MapProperty vmEnv; - private final RegularFileProperty vagrantfile; - private boolean isWindowsVM; - - public VagrantExtension(Project project) { - this.box = project.getObjects().property(String.class); - this.hostEnv = project.getObjects().mapProperty(String.class, Object.class); - this.vmEnv = project.getObjects().mapProperty(String.class, Object.class); - this.vagrantfile = project.getObjects().fileProperty(); - this.vagrantfile.convention(project.getRootProject().getLayout().getProjectDirectory().file("Vagrantfile")); - this.isWindowsVM = false; - } - - @Input - public String getBox() { - return box.get(); - } - - public void setBox(String box) { - // TODO: should verify this against the Vagrantfile, but would need to do so in afterEvaluate once vagrantfile is unmodifiable - this.box.set(box); - } - - @Input - public Map getHostEnv() { - return hostEnv.get(); - } - - public void hostEnv(String name, Object value) { - hostEnv.put(name, value); - } - - @Input - public Map getVmEnv() { - return vmEnv.get(); - } - - public void vmEnv(String name, Object value) { - vmEnv.put(name, value); - } - - @Input - public boolean isWindowsVM() { - return isWindowsVM; - } - - public void setIsWindowsVM(boolean isWindowsVM) { - this.isWindowsVM = isWindowsVM; - } - - @Input - public File getVagrantfile() { - return this.vagrantfile.get().getAsFile(); - } - - public void setVagrantfile(File file) { - vagrantfile.set(file); - } -} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantMachine.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantMachine.java deleted file mode 100644 index 6cf614fd7eb3c..0000000000000 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantMachine.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.gradle.internal.vagrant; - -import org.apache.commons.io.output.TeeOutputStream; -import org.elasticsearch.gradle.LoggedExec; -import org.elasticsearch.gradle.ReaperService; -import org.elasticsearch.gradle.internal.LoggingOutputStream; -import org.elasticsearch.gradle.internal.conventions.util.Util; -import org.gradle.api.Action; -import org.gradle.api.provider.Provider; -import org.gradle.internal.logging.progress.ProgressLogger; -import org.gradle.internal.logging.progress.ProgressLoggerFactory; -import org.gradle.process.ExecOperations; - -import java.io.File; -import java.io.OutputStream; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Objects; -import java.util.function.UnaryOperator; - -import javax.inject.Inject; - -/** - * An helper to manage a vagrant box. - * - * This is created alongside a {@link VagrantExtension} for a project to manage starting and - * stopping a single vagrant box. - */ -public class VagrantMachine { - - private final VagrantExtension extension; - private final Provider reaperServiceProvider; - private ReaperService reaper; - // pkg private so plugin can set this after construction - long refs; - private boolean isVMStarted = false; - - public VagrantMachine(VagrantExtension extension, Provider reaperServiceProvider) { - this.extension = extension; - this.reaperServiceProvider = reaperServiceProvider; - } - - @Inject - protected ProgressLoggerFactory getProgressLoggerFactory() { - throw new UnsupportedOperationException(); - } - - @Inject - protected ExecOperations getExecOperations() { - throw new UnsupportedOperationException(); - } - - public void execute(Action action) { - VagrantExecSpec vagrantSpec = new VagrantExecSpec(); - action.execute(vagrantSpec); - - Objects.requireNonNull(vagrantSpec.command); - - LoggedExec.exec(getExecOperations(), execSpec -> { - execSpec.setExecutable("vagrant"); - File vagrantfile = extension.getVagrantfile(); - execSpec.setEnvironment(System.getenv()); // pass through env - execSpec.environment("VAGRANT_CWD", vagrantfile.getParentFile().toString()); - execSpec.environment("VAGRANT_VAGRANTFILE", vagrantfile.getName()); - extension.getHostEnv().forEach(execSpec::environment); - - execSpec.args(vagrantSpec.command); - if (vagrantSpec.subcommand != null) { - execSpec.args(vagrantSpec.subcommand); - } - execSpec.args(extension.getBox()); - if (vagrantSpec.args != null) { - execSpec.args(Arrays.asList(vagrantSpec.args)); - } - - UnaryOperator progressHandler = vagrantSpec.progressHandler; - if (progressHandler == null) { - progressHandler = new VagrantProgressLogger("==> " + extension.getBox() + ": "); - } - OutputStream output = execSpec.getStandardOutput(); - // output from vagrant needs to be manually curated because --machine-readable isn't actually "readable" - OutputStream progressStream = new ProgressOutputStream(vagrantSpec.command, progressHandler); - execSpec.setStandardOutput(new TeeOutputStream(output, progressStream)); - }); - } - - // start the configuration VM if it hasn't been started yet - void maybeStartVM() { - if (isVMStarted) { - return; - } - execute(spec -> { - spec.setCommand("box"); - spec.setSubcommand("update"); - }); - - // Destroying before every execution can be annoying while iterating on tests locally. Therefore, we provide a flag that defaults - // to true that can be used to control whether or not to destroy any test boxes before test execution. - boolean destroyVM = Util.getBooleanProperty("vagrant.destroy", true); - if (destroyVM) { - execute(spec -> { - spec.setCommand("destroy"); - spec.setArgs("--force"); - }); - } - - // register box to be shutdown if gradle dies - reaper = reaperServiceProvider.get(); - reaper.registerCommand(extension.getBox(), "vagrant", "halt", "-f", extension.getBox()); - - // We lock the provider to virtualbox because the Vagrantfile specifies lots of boxes that only work - // properly in virtualbox. Virtualbox is vagrant's default but its possible to change that default and folks do. - execute(spec -> { - spec.setCommand("up"); - spec.setArgs("--provision", "--provider", "virtualbox"); - }); - isVMStarted = true; - } - - // stops the VM if refs are down to 0, or force was called - void maybeStopVM(boolean force) { - assert refs >= 1; - this.refs--; - if ((refs == 0 || force) && isVMStarted) { - execute(spec -> spec.setCommand("halt")); - reaper.unregister(extension.getBox()); - } - } - - public static String convertLinuxPath(File rootDir, String path) { - return "/elasticsearch/" + rootDir.toPath().relativize(Paths.get(path)); - } - - public static String convertWindowsPath(File rootDir, String path) { - return "C:\\elasticsearch\\" + rootDir.toPath().relativize(Paths.get(path)).toString().replace('/', '\\'); - } - - public static class VagrantExecSpec { - private String command; - private String subcommand; - private String[] args; - private UnaryOperator progressHandler; - - private VagrantExecSpec() {} - - public void setCommand(String command) { - this.command = command; - } - - public void setSubcommand(String subcommand) { - this.subcommand = subcommand; - } - - public void setArgs(String... args) { - this.args = args; - } - - /** - * A function to translate output from the vagrant command execution to the progress line. - * - * The function takes the current line of output from vagrant, and returns a new - * progress line, or {@code null} if there is no update. - */ - public void setProgressHandler(UnaryOperator progressHandler) { - this.progressHandler = progressHandler; - } - } - - private class ProgressOutputStream extends LoggingOutputStream { - - private ProgressLogger progressLogger; - private UnaryOperator progressHandler; - - ProgressOutputStream(String command, UnaryOperator progressHandler) { - this.progressHandler = progressHandler; - this.progressLogger = getProgressLoggerFactory().newOperation("vagrant"); - progressLogger.start(extension.getBox() + "> " + command, "hello"); - } - - @Override - protected void logLine(String line) { - String progress = progressHandler.apply(line); - if (progress != null) { - progressLogger.progress(progress); - } - } - - @Override - public void close() { - progressLogger.completed(); - } - } - -} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantProgressLogger.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantProgressLogger.java deleted file mode 100644 index a204bbc668db8..0000000000000 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantProgressLogger.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.gradle.internal.vagrant; - -import java.util.function.UnaryOperator; - -public class VagrantProgressLogger implements UnaryOperator { - - private static final String HEADING_PREFIX = "==> "; - - private final String squashedPrefix; - private String lastLine = ""; - private String heading = ""; - private boolean inProgressReport = false; - - public VagrantProgressLogger(String squashedPrefix) { - this.squashedPrefix = squashedPrefix; - } - - @Override - public String apply(String line) { - if (line.startsWith("\r\u001b")) { - /* We don't want to try to be a full terminal emulator but we want to - keep the escape sequences from leaking and catch _some_ of the - meaning. */ - line = line.substring(2); - if ("[K".equals(line)) { - inProgressReport = true; - } - return null; - } - if (line.startsWith(squashedPrefix)) { - line = line.substring(squashedPrefix.length()); - inProgressReport = false; - lastLine = line; - if (line.startsWith(HEADING_PREFIX)) { - line = line.substring(HEADING_PREFIX.length()); - heading = line + " > "; - } else { - line = heading + line; - } - } else if (inProgressReport) { - inProgressReport = false; - line = lastLine + line; - } else { - return null; - } - return line; - } -} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantShellTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantShellTask.java deleted file mode 100644 index 8c2bd05a95938..0000000000000 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/vagrant/VagrantShellTask.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.gradle.internal.vagrant; - -import org.gradle.api.DefaultTask; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.TaskAction; -import org.gradle.initialization.layout.BuildLayout; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.UnaryOperator; - -import static org.elasticsearch.gradle.internal.vagrant.VagrantMachine.convertLinuxPath; -import static org.elasticsearch.gradle.internal.vagrant.VagrantMachine.convertWindowsPath; - -/** - * A shell script to run within a vagrant VM. - * - * The script is run as root within the VM. - */ -public abstract class VagrantShellTask extends DefaultTask { - - private VagrantExtension extension; - private VagrantMachine service; - - private UnaryOperator progressHandler = UnaryOperator.identity(); - private BuildLayout buildLayout; - - public VagrantShellTask(BuildLayout buildLayout) { - this.buildLayout = buildLayout; - } - - @Input - protected abstract List getWindowsScript(); - - @Input - protected abstract List getLinuxScript(); - - @Input - public UnaryOperator getProgressHandler() { - return progressHandler; - } - - public void setProgressHandler(UnaryOperator progressHandler) { - this.progressHandler = progressHandler; - } - - public void setExtension(VagrantExtension extension) { - this.extension = extension; - } - - public void setService(VagrantMachine service) { - this.service = service; - } - - @TaskAction - public void runScript() { - if (extension.isWindowsVM()) { - service.execute(spec -> { - spec.setCommand("winrm"); - List script = new ArrayList<>(); - script.add("try {"); - script.add("cd " + convertWindowsPath(buildLayout.getRootDirectory(), buildLayout.getRootDirectory().toString())); - extension.getVmEnv().forEach((k, v) -> script.add("$Env:" + k + " = \"" + v + "\"")); - script.addAll(getWindowsScript().stream().map(s -> " " + s).toList()); - script.addAll( - Arrays.asList( - " exit $LASTEXITCODE", - "} catch {", - // catch if we have a failure to even run the script at all above, equivalent to set -e, sort of - " echo $_.Exception.Message", - " exit 1", - "}" - ) - ); - spec.setArgs("--elevated", "--command", String.join("\n", script)); - spec.setProgressHandler(progressHandler); - }); - } else { - try { - service.execute(spec -> { - spec.setCommand("ssh"); - - List script = new ArrayList<>(); - script.add("sudo bash -c '"); // start inline bash script - script.add("pwd"); - script.add("cd " + convertLinuxPath(buildLayout.getRootDirectory(), buildLayout.getRootDirectory().toString())); - extension.getVmEnv().forEach((k, v) -> script.add("export " + k + "=" + v)); - script.addAll(getLinuxScript()); - script.add("'"); // end inline bash script - spec.setArgs("--command", String.join("\n", script)); - spec.setProgressHandler(progressHandler); - }); - } catch (Exception e) { - /*getLogger().error("Failed command, dumping dmesg", e); - service.execute(spec -> { - spec.setCommand("ssh"); - spec.setArgs("--command", "dmesg"); - spec.setProgressHandler(line -> { - getLogger().error(line); - return null; - }); - });*/ - throw e; - } - } - } - -} diff --git a/qa/os/centos-7/build.gradle b/qa/os/centos-7/build.gradle deleted file mode 100644 index 814b04d4aec5f..0000000000000 --- a/qa/os/centos-7/build.gradle +++ /dev/null @@ -1 +0,0 @@ -project.ext.shouldTestDocker = true diff --git a/qa/os/debian-9/build.gradle b/qa/os/debian-9/build.gradle deleted file mode 100644 index 814b04d4aec5f..0000000000000 --- a/qa/os/debian-9/build.gradle +++ /dev/null @@ -1 +0,0 @@ -project.ext.shouldTestDocker = true diff --git a/qa/os/fedora-28/build.gradle b/qa/os/fedora-28/build.gradle deleted file mode 100644 index 814b04d4aec5f..0000000000000 --- a/qa/os/fedora-28/build.gradle +++ /dev/null @@ -1 +0,0 @@ -project.ext.shouldTestDocker = true diff --git a/qa/os/fedora-29/build.gradle b/qa/os/fedora-29/build.gradle deleted file mode 100644 index 814b04d4aec5f..0000000000000 --- a/qa/os/fedora-29/build.gradle +++ /dev/null @@ -1 +0,0 @@ -project.ext.shouldTestDocker = true diff --git a/qa/os/oel-7/build.gradle b/qa/os/oel-7/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/qa/os/sles-12/build.gradle b/qa/os/sles-12/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/qa/os/ubuntu-1804/build.gradle b/qa/os/ubuntu-1804/build.gradle deleted file mode 100644 index 814b04d4aec5f..0000000000000 --- a/qa/os/ubuntu-1804/build.gradle +++ /dev/null @@ -1 +0,0 @@ -project.ext.shouldTestDocker = true diff --git a/qa/os/windows-2012r2/build.gradle b/qa/os/windows-2012r2/build.gradle deleted file mode 100644 index 63b149712bcfd..0000000000000 --- a/qa/os/windows-2012r2/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -import org.elasticsearch.gradle.internal.test.GradleDistroTestTask - -String boxId = project.properties.get('vagrant.windows-2012r2.id') -if (boxId != null) { - vagrant { - hostEnv 'VAGRANT_WINDOWS_2012R2_BOX', boxId - } -} else { - // box id was not supplied, so disable the distro tests - tasks.withType(GradleDistroTestTask).configureEach { - onlyIf("Project property vagrant.windows-2012r2.id set") { false } - } -} diff --git a/qa/os/windows-2016/build.gradle b/qa/os/windows-2016/build.gradle deleted file mode 100644 index c54cc97c68bfe..0000000000000 --- a/qa/os/windows-2016/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -import org.elasticsearch.gradle.internal.test.GradleDistroTestTask - -String boxId = project.properties.get('vagrant.windows-2016.id') -if (boxId != null) { - vagrant { - hostEnv 'VAGRANT_WINDOWS_2016_BOX', boxId - } -} else { - // box id was not supplied, so disable the distro tests - tasks.withType(GradleDistroTestTask).configureEach { - enabled = false - } -} diff --git a/qa/os/README.md b/qa/packaging/README.md similarity index 100% rename from qa/os/README.md rename to qa/packaging/README.md diff --git a/qa/os/build.gradle b/qa/packaging/build.gradle similarity index 90% rename from qa/os/build.gradle rename to qa/packaging/build.gradle index b12c53e63e10c..758dfe6661766 100644 --- a/qa/os/build.gradle +++ b/qa/packaging/build.gradle @@ -36,13 +36,3 @@ tasks.named("test").configure { enabled = false } tasks.register('destructivePackagingTest') { dependsOn 'destructiveDistroTest' } - -subprojects { Project platformProject -> - tasks.register('packagingTest') { - dependsOn 'distroTest' - } - - vagrant { - hostEnv 'VAGRANT_PROJECT_DIR', platformProject.projectDir.absolutePath - } -} diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/ArchiveGenerateInitialCredentialsTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/ArchiveGenerateInitialCredentialsTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/ArchiveGenerateInitialCredentialsTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/ArchiveGenerateInitialCredentialsTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/ArchiveTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/ArchiveTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/ArchiveTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/ArchiveTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/CertGenCliTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/CertGenCliTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/CertGenCliTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/CertGenCliTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/ConfigurationTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/ConfigurationTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/ConfigurationTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/ConfigurationTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/CronEvalCliTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/CronEvalCliTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/CronEvalCliTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/CronEvalCliTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/DebMetadataTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/DebMetadataTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/DebMetadataTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/DebMetadataTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/DebPreservationTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/DebPreservationTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/DebPreservationTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/DebPreservationTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/DockerTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/DockerTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollmentProcessTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/EnrollmentProcessTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollmentProcessTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/EnrollmentProcessTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/HttpClientThreadsFilter.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/HttpClientThreadsFilter.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/HttpClientThreadsFilter.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/HttpClientThreadsFilter.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackageTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackageTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageUpgradeTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackageUpgradeTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/PackageUpgradeTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackageUpgradeTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagesSecurityAutoConfigurationTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackagesSecurityAutoConfigurationTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/PackagesSecurityAutoConfigurationTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackagesSecurityAutoConfigurationTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PasswordToolsTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/PasswordToolsTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/PasswordToolsTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/PasswordToolsTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PluginCliTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/PluginCliTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/PluginCliTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/PluginCliTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/RpmMetadataTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/RpmMetadataTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/RpmMetadataTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/RpmMetadataTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/RpmPreservationTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/RpmPreservationTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/RpmPreservationTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/RpmPreservationTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/SqlCliTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/SqlCliTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/SqlCliTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/SqlCliTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/TemporaryDirectoryConfigTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/TemporaryDirectoryConfigTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/TemporaryDirectoryConfigTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/TemporaryDirectoryConfigTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Archives.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Archives.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Archives.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Archives.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Cleanup.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Cleanup.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Cleanup.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Cleanup.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Distribution.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Distribution.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Distribution.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Distribution.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/FileExistenceMatchers.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/FileExistenceMatchers.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/FileExistenceMatchers.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/FileExistenceMatchers.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/FileMatcher.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/FileMatcher.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/FileMatcher.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/FileMatcher.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/FileUtils.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/FileUtils.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/FileUtils.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/FileUtils.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Installation.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Installation.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Installation.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Installation.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Packages.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Packages.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Platforms.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Platforms.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Platforms.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Platforms.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/ServerUtils.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/ServerUtils.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/ServerUtils.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/ServerUtils.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/Shell.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/Shell.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileAttributes.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileAttributes.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileAttributes.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileAttributes.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileMatcher.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileMatcher.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileMatcher.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerFileMatcher.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerRun.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerRun.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerRun.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerRun.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerShell.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerShell.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/docker/DockerShell.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/DockerShell.java diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/docker/MockServer.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/MockServer.java similarity index 100% rename from qa/os/src/test/java/org/elasticsearch/packaging/util/docker/MockServer.java rename to qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/MockServer.java diff --git a/qa/os/src/test/resources/log4j2-test.properties b/qa/packaging/src/test/resources/log4j2-test.properties similarity index 100% rename from qa/os/src/test/resources/log4j2-test.properties rename to qa/packaging/src/test/resources/log4j2-test.properties diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/http.crt b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http.crt similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/http.crt rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http.crt diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/http.key b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http.key similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/http.key rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http.key diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/http_ca.crt b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http_ca.crt similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/http_ca.crt rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http_ca.crt diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/http_ca.key b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http_ca.key similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/http_ca.key rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/http_ca.key diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/transport.crt b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/transport.crt similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/transport.crt rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/transport.crt diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/transport.key b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/transport.key similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/transport.key rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/transport.key diff --git a/qa/os/src/test/resources/org/elasticsearch/packaging/test/transport_ca.crt b/qa/packaging/src/test/resources/org/elasticsearch/packaging/test/transport_ca.crt similarity index 100% rename from qa/os/src/test/resources/org/elasticsearch/packaging/test/transport_ca.crt rename to qa/packaging/src/test/resources/org/elasticsearch/packaging/test/transport_ca.crt From 9273c7d5a6d11c146b95776e32d95d87dcefcaae Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 19 Oct 2023 12:14:59 +0200 Subject: [PATCH 020/203] Make BWC build logic configuration cache compliant (#101070) --- .../gradle/internal/InternalBwcGitPlugin.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalBwcGitPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalBwcGitPlugin.java index 768cac92a3dbd..d51770ffd30ed 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalBwcGitPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalBwcGitPlugin.java @@ -82,6 +82,8 @@ public void apply(Project project) { }); TaskProvider addRemoteTaskProvider = tasks.register("addRemote", addRemote -> { + String rootProjectName = project.getRootProject().getName(); + addRemote.dependsOn(findRemoteTaskProvider); addRemote.onlyIf("remote exists", task -> ((boolean) extraProperties.get("remoteExists")) == false); addRemote.doLast(new Action() { @@ -99,7 +101,7 @@ public void execute(Task task) { : null ) ) - .getOrElse("https://github.com/" + remoteRepo + "/" + project.getRootProject().getName()); + .getOrElse("https://github.com/" + remoteRepo + "/" + rootProjectName); spec.commandLine("git", "remote", "add", remoteRepo, remoteRepoUrl); }); } @@ -127,6 +129,7 @@ public void execute(Task task) { String projectPath = project.getPath(); TaskProvider checkoutBwcBranchTaskProvider = tasks.register("checkoutBwcBranch", checkoutBwcBranch -> { checkoutBwcBranch.dependsOn(fetchLatestTaskProvider); + ExtraPropertiesExtension taskExtensionsProperties = checkoutBwcBranch.getExtensions().getExtraProperties(); checkoutBwcBranch.doLast(new Action() { @Override public void execute(Task task) { @@ -136,9 +139,7 @@ public void execute(Task task) { .orElse(providerFactory.systemProperty("tests.bwc.refspec." + bwcBranch)) .orElse( providerFactory.provider( - () -> task.getExtensions().getExtraProperties().has("refspec") - ? task.getExtensions().getExtraProperties().get("refspec").toString() - : null + () -> taskExtensionsProperties.has("refspec") ? taskExtensionsProperties.get("refspec").toString() : null ) ) .getOrElse(remote.get() + "/" + bwcBranch); From e9b11e4ea2cf658d6e0887a00a9cc2e6e072786b Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 19 Oct 2023 12:51:49 +0100 Subject: [PATCH 021/203] Initial set of minor improvements for NodeConstruction class (#101107) * Add a standard method for getting a single object from plugins * Refactor a registration inside an object constructor * Use more streams --- .../metadata/TemplateUpgradeService.java | 5 - .../elasticsearch/node/NodeConstruction.java | 225 ++++++++---------- .../elasticsearch/plugins/PluginsService.java | 2 +- .../metadata/TemplateUpgradeServiceTests.java | 21 +- .../org/elasticsearch/node/NodeTests.java | 3 +- 5 files changed, 107 insertions(+), 149 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java index f02e389157540..93ef161c255f8 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/TemplateUpgradeService.java @@ -19,7 +19,6 @@ import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentHelper; @@ -64,7 +63,6 @@ public class TemplateUpgradeService implements ClusterStateListener { private Map lastTemplateMetadata; - @SuppressWarnings("this-escape") public TemplateUpgradeService( Client client, ClusterService clusterService, @@ -81,9 +79,6 @@ public TemplateUpgradeService( } return upgradedTemplates; }; - if (DiscoveryNode.isMasterNode(clusterService.getSettings())) { - clusterService.addListener(this); - } } @Override diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index 2f51d6f87077d..952130ecf6357 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -77,7 +77,6 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.core.IOUtils; -import org.elasticsearch.core.Strings; import org.elasticsearch.discovery.DiscoveryModule; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; @@ -197,6 +196,7 @@ import java.io.Closeable; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -204,6 +204,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -213,8 +214,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.stream.Collectors.toList; -import static org.elasticsearch.common.util.CollectionUtils.concatLists; import static org.elasticsearch.core.Types.forciblyCast; /** @@ -323,6 +322,25 @@ NamedXContentRegistry namedXContentRegistry() { return xContentRegistry; } + private Optional getSinglePlugin(Class pluginClass) { + return getSinglePlugin(pluginsService.filterPlugins(pluginClass).stream(), pluginClass); + } + + private Optional getSinglePlugin(Stream plugins, Class pluginClass) { + var it = plugins.iterator(); + if (it.hasNext() == false) { + return Optional.empty(); + } + T plugin = it.next(); + if (it.hasNext()) { + List allPlugins = new ArrayList<>(); + allPlugins.add(plugin); + it.forEachRemaining(allPlugins::add); + throw new IllegalStateException("A single " + pluginClass.getName() + " was expected but got :" + allPlugins); + } + return Optional.of(plugin); + } + private void construct(Environment initialEnvironment, NodeServiceProvider serviceProvider, boolean forbidPrivateIndexSettings) throws IOException { // Pass the node settings to the DeprecationLogger class so that it can have the deprecation.skip_deprecated_settings setting: @@ -406,8 +424,8 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi * Create the environment based on the finalized view of the settings. This is to ensure that components get the same setting * values, no matter they ask for them from. */ - this.environment = new Environment(settings, initialEnvironment.configFile()); - Environment.assertEquivalent(initialEnvironment, this.environment); + environment = new Environment(settings, initialEnvironment.configFile()); + Environment.assertEquivalent(initialEnvironment, environment); final List> executorBuilders = pluginsService.flatMap(p -> p.getExecutorBuilders(settings)).toList(); @@ -424,7 +442,9 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi Task.HEADERS_TO_COPY.stream() ).collect(Collectors.toSet()); - final TelemetryProvider telemetryProvider = getTelemetryProvider(pluginsService, settings); + final TelemetryProvider telemetryProvider = getSinglePlugin(TelemetryPlugin.class).map(p -> p.getTelemetryProvider(settings)) + .orElse(TelemetryProvider.NOOP); + final Tracer tracer = telemetryProvider.getTracer(); final TaskManager taskManager = new TaskManager(settings, threadPool, taskHeaders, tracer); @@ -446,7 +466,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi threadPool::absoluteTimeInMillis ); AnalysisModule analysisModule = new AnalysisModule( - this.environment, + environment, pluginsService.filterPlugins(AnalysisPlugin.class), pluginsService.getStablePluginRegistry() ); @@ -477,7 +497,11 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi ScriptModule.registerClusterSettingsListeners(scriptService, settingsModule.getClusterSettings()); final NetworkService networkService = new NetworkService( - getCustomNameResolvers(pluginsService.filterPlugins(DiscoveryPlugin.class)) + pluginsService.filterPlugins(DiscoveryPlugin.class) + .stream() + .map(d -> d.getCustomNameResolver(environment.settings())) + .filter(Objects::nonNull) + .toList() ); List clusterPlugins = pluginsService.filterPlugins(ClusterPlugin.class); @@ -503,12 +527,12 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi final IngestService ingestService = new IngestService( clusterService, threadPool, - this.environment, + environment, scriptService, analysisModule.getAnalysisRegistry(), pluginsService.filterPlugins(IngestPlugin.class), client, - IngestService.createGrokThreadWatchdog(this.environment, threadPool), + IngestService.createGrokThreadWatchdog(environment, threadPool), documentParsingObserverSupplier ); final SetOnce repositoriesServiceReference = new SetOnce<>(); @@ -523,16 +547,17 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi SearchModule searchModule = new SearchModule(settings, pluginsService.filterPlugins(SearchPlugin.class)); IndexSearcher.setMaxClauseCount(SearchUtils.calculateMaxClauseValue(threadPool)); - List namedWriteables = Stream.of( - NetworkModule.getNamedWriteables().stream(), - IndicesModule.getNamedWriteables().stream(), - searchModule.getNamedWriteables().stream(), - pluginsService.flatMap(Plugin::getNamedWriteables), - ClusterModule.getNamedWriteables().stream(), - SystemIndexMigrationExecutor.getNamedWriteables().stream(), - inferenceServiceRegistry.getNamedWriteables().stream() - ).flatMap(Function.identity()).toList(); - final NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(namedWriteables); + final NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry( + Stream.of( + NetworkModule.getNamedWriteables().stream(), + IndicesModule.getNamedWriteables().stream(), + searchModule.getNamedWriteables().stream(), + pluginsService.flatMap(Plugin::getNamedWriteables), + ClusterModule.getNamedWriteables().stream(), + SystemIndexMigrationExecutor.getNamedWriteables().stream(), + inferenceServiceRegistry.getNamedWriteables().stream() + ).flatMap(Function.identity()).toList() + ); NamedXContentRegistry xContentRegistry = new NamedXContentRegistry( Stream.of( NetworkModule.getNamedXContents().stream(), @@ -542,7 +567,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi ClusterModule.getNamedXWriteables().stream(), SystemIndexMigrationExecutor.getNamedXContentParsers().stream(), HealthNodeTaskExecutor.getNamedXContentParsers().stream() - ).flatMap(Function.identity()).collect(toList()) + ).flatMap(Function.identity()).toList() ); final List features = pluginsService.filterPlugins(SystemIndexPlugin.class).stream().map(plugin -> { SystemIndices.validateFeatureName(plugin.getFeatureName(), plugin.getClass().getCanonicalName()); @@ -615,7 +640,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi // collect engine factory providers from plugins final Collection enginePlugins = pluginsService.filterPlugins(EnginePlugin.class); final Collection>> engineFactoryProviders = enginePlugins.stream() - .map(plugin -> (Function>) plugin::getEngineFactory) + .>>map(plugin -> plugin::getEngineFactory) .toList(); final Map indexStoreFactories = pluginsService.filterPlugins(IndexStorePlugin.class) @@ -754,26 +779,13 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi reservedStateHandlers.add(new ReservedComposableIndexTemplateAction(templateService, settingsModule.getIndexScopedSettings())); // add all reserved state handlers from plugins - List pluginHandlers = pluginsService.loadServiceProviders( - ReservedClusterStateHandlerProvider.class - ); - pluginHandlers.forEach(h -> reservedStateHandlers.addAll(h.handlers())); + pluginsService.loadServiceProviders(ReservedClusterStateHandlerProvider.class) + .forEach(h -> reservedStateHandlers.addAll(h.handlers())); - List terminationHandlers = pluginsService.loadServiceProviders(TerminationHandlerProvider.class) + var terminationHandlers = pluginsService.loadServiceProviders(TerminationHandlerProvider.class) .stream() - .map(prov -> prov.handler()) - .toList(); - if (terminationHandlers.size() == 1) { - this.terminationHandler = terminationHandlers.get(0); - } else if (terminationHandlers.size() > 1) { - throw new IllegalStateException( - Strings.format( - "expected at most one termination handler, but found %s: [%s]", - terminationHandlers.size(), - terminationHandlers.stream().map(it -> it.getClass().getCanonicalName()) - ) - ); - } + .map(TerminationHandlerProvider::handler); + terminationHandler = getSinglePlugin(terminationHandlers, TerminationHandler.class).orElse(null); ActionModule actionModule = new ActionModule( settings, @@ -824,8 +836,8 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi ); if (DiscoveryNode.isMasterNode(settings)) { clusterService.addListener(new SystemIndexMetadataUpgradeService(systemIndices, clusterService)); + clusterService.addListener(new TemplateUpgradeService(client, clusterService, threadPool, indexTemplateMetadataUpgraders)); } - new TemplateUpgradeService(client, clusterService, threadPool, indexTemplateMetadataUpgraders); final Transport transport = networkModule.getTransportSupplier().get(); final TransportService transportService = serviceProvider.newTransportService( pluginsService, @@ -850,7 +862,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi final RecoverySettings recoverySettings = new RecoverySettings(settings, settingsModule.getClusterSettings()); RepositoriesModule repositoriesModule = new RepositoriesModule( - this.environment, + environment, pluginsService.filterPlugins(RepositoryPlugin.class), transportService, clusterService, @@ -978,8 +990,8 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi settings, clusterService.getClusterSettings() ); - final List> builtinTaskExecutors = List.of(systemIndexMigrationExecutor, healthNodeTaskExecutor); - final List> pluginTaskExecutors = pluginsService.filterPlugins(PersistentTaskPlugin.class) + final Stream> builtinTaskExecutors = Stream.of(systemIndexMigrationExecutor, healthNodeTaskExecutor); + final Stream> pluginTaskExecutors = pluginsService.filterPlugins(PersistentTaskPlugin.class) .stream() .map( p -> p.getPersistentTasksExecutor( @@ -990,10 +1002,9 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi clusterModule.getIndexNameExpressionResolver() ) ) - .flatMap(List::stream) - .collect(toList()); + .flatMap(List::stream); final PersistentTasksExecutorRegistry registry = new PersistentTasksExecutorRegistry( - concatLists(pluginTaskExecutors, builtinTaskExecutors) + Stream.concat(pluginTaskExecutors, builtinTaskExecutors).toList() ); final PersistentTasksClusterService persistentTasksClusterService = new PersistentTasksClusterService( settings, @@ -1003,8 +1014,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi ); resourcesToClose.add(persistentTasksClusterService); - final List shutdownAwarePlugins = pluginsService.filterPlugins(ShutdownAwarePlugin.class); - final PluginShutdownService pluginShutdownService = new PluginShutdownService(shutdownAwarePlugins); + PluginShutdownService pluginShutdownService = new PluginShutdownService(pluginsService.filterPlugins(ShutdownAwarePlugin.class)); clusterService.addListener(pluginShutdownService); final RecoveryPlannerService recoveryPlannerService = getRecoveryPlannerService(threadPool, clusterService, repositoryService); @@ -1040,7 +1050,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi b.bind(PluginsService.class).toInstance(pluginsService); b.bind(Client.class).toInstance(client); b.bind(NodeClient.class).toInstance(client); - b.bind(Environment.class).toInstance(this.environment); + b.bind(Environment.class).toInstance(environment); b.bind(ThreadPool.class).toInstance(threadPool); b.bind(NodeEnvironment.class).toInstance(nodeEnvironment); b.bind(ResourceWatcherService.class).toInstance(resourceWatcherService); @@ -1194,13 +1204,8 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi } private Supplier getDocumentParsingObserverSupplier() { - List plugins = pluginsService.filterPlugins(DocumentParsingObserverPlugin.class); - if (plugins.size() == 1) { - return plugins.get(0).getDocumentParsingObserverSupplier(); - } else if (plugins.size() == 0) { - return () -> DocumentParsingObserver.EMPTY_INSTANCE; - } - throw new IllegalStateException("too many DocumentParsingObserverPlugin instances"); + return getSinglePlugin(DocumentParsingObserverPlugin.class).map(DocumentParsingObserverPlugin::getDocumentParsingObserverSupplier) + .orElse(() -> DocumentParsingObserver.EMPTY_INSTANCE); } /** @@ -1214,13 +1219,11 @@ private static CircuitBreakerService createCircuitBreakerService( ClusterSettings clusterSettings ) { String type = Node.BREAKER_TYPE_KEY.get(settings); - if (type.equals("hierarchy")) { - return new HierarchyCircuitBreakerService(settings, breakerSettings, clusterSettings); - } else if (type.equals("none")) { - return new NoneCircuitBreakerService(); - } else { - throw new IllegalArgumentException("Unknown circuit breaker type [" + type + "]"); - } + return switch (type) { + case "hierarchy" -> new HierarchyCircuitBreakerService(settings, breakerSettings, clusterSettings); + case "none" -> new NoneCircuitBreakerService(); + default -> throw new IllegalArgumentException("Unknown circuit breaker type [" + type + "]"); + }; } /** @@ -1234,22 +1237,12 @@ private static ReloadablePlugin wrapPlugins(List reloadablePlu try { plugin.reload(settings); } catch (IOException e) { - throw new RuntimeException(e); + throw new UncheckedIOException(e); } } }; } - private static TelemetryProvider getTelemetryProvider(PluginsService pluginsService, Settings settings) { - final List telemetryPlugins = pluginsService.filterPlugins(TelemetryPlugin.class); - - if (telemetryPlugins.size() > 1) { - throw new IllegalStateException("A single TelemetryPlugin was expected but got: " + telemetryPlugins); - } - - return telemetryPlugins.isEmpty() ? TelemetryProvider.NOOP : telemetryPlugins.get(0).getTelemetryProvider(settings); - } - private HealthService createHealthService( ClusterService clusterService, ClusterModule clusterModule, @@ -1257,7 +1250,7 @@ private HealthService createHealthService( ThreadPool threadPool, SystemIndices systemIndices ) { - var serverHealthIndicatorServices = List.of( + var serverHealthIndicatorServices = Stream.of( new StableMasterHealthIndicatorService(coordinationDiagnosticsService, clusterService), new RepositoryIntegrityHealthIndicatorService(clusterService), new ShardsAvailabilityHealthIndicatorService(clusterService, clusterModule.getAllocationService(), systemIndices), @@ -1266,9 +1259,8 @@ private HealthService createHealthService( ); var pluginHealthIndicatorServices = pluginsService.filterPlugins(HealthPlugin.class) .stream() - .flatMap(plugin -> plugin.getHealthIndicatorServices().stream()) - .toList(); - return new HealthService(concatLists(serverHealthIndicatorServices, pluginHealthIndicatorServices), threadPool); + .flatMap(plugin -> plugin.getHealthIndicatorServices().stream()); + return new HealthService(Stream.concat(serverHealthIndicatorServices, pluginHealthIndicatorServices).toList(), threadPool); } private static HealthPeriodicLogger createHealthPeriodicLogger( @@ -1285,39 +1277,23 @@ private RecoveryPlannerService getRecoveryPlannerService( ClusterService clusterService, RepositoriesService repositoryService ) { - final List recoveryPlannerServices = pluginsService.filterPlugins(RecoveryPlannerPlugin.class) + var recoveryPlannerServices = pluginsService.filterPlugins(RecoveryPlannerPlugin.class) .stream() .map( plugin -> plugin.createRecoveryPlannerService( new ShardSnapshotsService(client, repositoryService, threadPool, clusterService) ) ) - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - if (recoveryPlannerServices.isEmpty()) { - return new PeerOnlyRecoveryPlannerService(); - } else if (recoveryPlannerServices.size() > 1) { - throw new IllegalStateException("Expected a single RecoveryPlannerService but got: " + recoveryPlannerServices.size()); - } - return recoveryPlannerServices.get(0); + .flatMap(Optional::stream); + return getSinglePlugin(recoveryPlannerServices, RecoveryPlannerService.class).orElseGet(PeerOnlyRecoveryPlannerService::new); } private WriteLoadForecaster getWriteLoadForecaster(ThreadPool threadPool, Settings settings, ClusterSettings clusterSettings) { - final List clusterPlugins = pluginsService.filterPlugins(ClusterPlugin.class); - final List writeLoadForecasters = clusterPlugins.stream() - .flatMap(clusterPlugin -> clusterPlugin.createWriteLoadForecasters(threadPool, settings, clusterSettings).stream()) - .toList(); - - if (writeLoadForecasters.isEmpty()) { - return WriteLoadForecaster.DEFAULT; - } - - if (writeLoadForecasters.size() > 1) { - throw new IllegalStateException("A single WriteLoadForecaster was expected but got: " + writeLoadForecasters); - } + var writeLoadForecasters = pluginsService.filterPlugins(ClusterPlugin.class) + .stream() + .flatMap(clusterPlugin -> clusterPlugin.createWriteLoadForecasters(threadPool, settings, clusterSettings).stream()); - return writeLoadForecasters.get(0); + return getSinglePlugin(writeLoadForecasters, WriteLoadForecaster.class).orElse(WriteLoadForecaster.DEFAULT); } private PersistedClusterStateService newPersistedClusterStateService( @@ -1326,38 +1302,23 @@ private PersistedClusterStateService newPersistedClusterStateService( ThreadPool threadPool, CompatibilityVersions compatibilityVersions ) { - final List persistedClusterStateServiceFactories = pluginsService - .filterPlugins(ClusterCoordinationPlugin.class) + var persistedClusterStateServiceFactories = pluginsService.filterPlugins(ClusterCoordinationPlugin.class) .stream() .map(ClusterCoordinationPlugin::getPersistedClusterStateServiceFactory) - .flatMap(Optional::stream) - .toList(); - - if (persistedClusterStateServiceFactories.size() > 1) { - throw new IllegalStateException("multiple persisted-state-service factories found: " + persistedClusterStateServiceFactories); - } - - if (persistedClusterStateServiceFactories.size() == 1) { - return persistedClusterStateServiceFactories.get(0) - .newPersistedClusterStateService(nodeEnvironment, xContentRegistry, clusterSettings, threadPool, compatibilityVersions); - } - - return new PersistedClusterStateService(nodeEnvironment, xContentRegistry, clusterSettings, threadPool::relativeTimeInMillis); - } + .flatMap(Optional::stream); - /** - * Get Custom Name Resolvers list based on a Discovery Plugins list - * - * @param discoveryPlugins Discovery plugins list - */ - private List getCustomNameResolvers(List discoveryPlugins) { - List customNameResolvers = new ArrayList<>(); - for (DiscoveryPlugin discoveryPlugin : discoveryPlugins) { - NetworkService.CustomNameResolver customNameResolver = discoveryPlugin.getCustomNameResolver(environment.settings()); - if (customNameResolver != null) { - customNameResolvers.add(customNameResolver); - } - } - return customNameResolvers; + return getSinglePlugin(persistedClusterStateServiceFactories, ClusterCoordinationPlugin.PersistedClusterStateServiceFactory.class) + .map( + f -> f.newPersistedClusterStateService( + nodeEnvironment, + xContentRegistry, + clusterSettings, + threadPool, + compatibilityVersions + ) + ) + .orElseGet( + () -> new PersistedClusterStateService(nodeEnvironment, xContentRegistry, clusterSettings, threadPool::relativeTimeInMillis) + ); } } diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java index 600ba59199817..3b838db463a4f 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -361,7 +361,7 @@ public T loadSingletonServiceProvider(Class service, Supplier fallback var services = loadServiceProviders(service); if (services.size() > 1) { throw new IllegalStateException(String.format(Locale.ROOT, "More than one extension found for %s", service.getSimpleName())); - } else if (services.size() == 0) { + } else if (services.isEmpty()) { return fallback.get(); } return services.get(0); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java index 0e1f7caa3f432..780b0dd138563 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java @@ -36,7 +36,6 @@ import org.junit.Before; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -49,6 +48,9 @@ import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; import static org.elasticsearch.test.ClusterServiceUtils.setState; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.anEmptyMap; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -90,7 +92,7 @@ public void testCalculateChangesAddChangeAndDelete() { IndexTemplateMetadata.builder("changed_test_template").patterns(randomIndexPatterns()).build() ); - final TemplateUpgradeService service = new TemplateUpgradeService(null, clusterService, threadPool, Arrays.asList(templates -> { + final TemplateUpgradeService service = new TemplateUpgradeService(null, clusterService, threadPool, List.of(templates -> { if (shouldAdd) { assertNull( templates.put( @@ -126,23 +128,22 @@ public void testCalculateChangesAddChangeAndDelete() { if (shouldAdd) { assertThat(changes.v1().get("added_test_template"), notNullValue()); if (shouldChange) { - assertThat(changes.v1().keySet(), hasSize(2)); + assertThat(changes.v1(), aMapWithSize(2)); assertThat(changes.v1().get("changed_test_template"), notNullValue()); } else { - assertThat(changes.v1().keySet(), hasSize(1)); + assertThat(changes.v1(), aMapWithSize(1)); } } else { if (shouldChange) { assertThat(changes.v1().get("changed_test_template"), notNullValue()); - assertThat(changes.v1().keySet(), hasSize(1)); + assertThat(changes.v1(), aMapWithSize(1)); } else { - assertThat(changes.v1().keySet(), empty()); + assertThat(changes.v1(), anEmptyMap()); } } if (shouldRemove) { - assertThat(changes.v2(), hasSize(1)); - assertThat(changes.v2().contains("removed_test_template"), equalTo(true)); + assertThat(changes.v2(), contains("removed_test_template")); } else { assertThat(changes.v2(), empty()); } @@ -280,7 +281,7 @@ public void testClusterStateUpdate() throws InterruptedException { return null; }).when(mockIndicesAdminClient).deleteTemplate(any(DeleteIndexTemplateRequest.class), any(ActionListener.class)); - new TemplateUpgradeService(mockClient, clusterService, threadPool, Arrays.asList(templates -> { + TemplateUpgradeService service = new TemplateUpgradeService(mockClient, clusterService, threadPool, List.of(templates -> { assertNull( templates.put( "added_test_template", @@ -329,6 +330,8 @@ public void clusterChanged(ClusterChangedEvent event) { } }; + clusterService.addListener(service); + ClusterState prevState = ClusterState.EMPTY_STATE; ClusterState state = ClusterState.builder(prevState) .nodes( diff --git a/server/src/test/java/org/elasticsearch/node/NodeTests.java b/server/src/test/java/org/elasticsearch/node/NodeTests.java index 24bbca6ddf512..9a9ffeea84610 100644 --- a/server/src/test/java/org/elasticsearch/node/NodeTests.java +++ b/server/src/test/java/org/elasticsearch/node/NodeTests.java @@ -673,7 +673,7 @@ public void testPluggablePersistedClusterStateServiceValidation() throws IOExcep List.of(TestClusterCoordinationPlugin1.class, TestClusterCoordinationPlugin2.class, getTestTransportPlugin()) ) ).getMessage(), - containsString("multiple persisted-state-service factories found") + containsString("A single " + ClusterCoordinationPlugin.PersistedClusterStateServiceFactory.class.getName() + " was expected") ); try (Node node = new MockNode(baseSettings().build(), List.of(TestClusterCoordinationPlugin1.class, getTestTransportPlugin()))) { @@ -686,5 +686,4 @@ public void testPluggablePersistedClusterStateServiceValidation() throws IOExcep } } } - } From 5ae576fafc79a23710e25ee11caca6430a2c4871 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 19 Oct 2023 13:52:20 +0200 Subject: [PATCH 022/203] Make TestClusterPlugin configuration cache compatible (#101069) --- .../testclusters/TestClustersPlugin.java | 105 ++++++++++++------ 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersPlugin.java b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersPlugin.java index 586a9485ab834..72a462c3cd8c9 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersPlugin.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersPlugin.java @@ -16,8 +16,6 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; -import org.gradle.api.execution.TaskActionListener; -import org.gradle.api.execution.TaskExecutionListener; import org.gradle.api.file.ArchiveOperations; import org.gradle.api.file.FileSystemOperations; import org.gradle.api.internal.file.FileOperations; @@ -26,11 +24,19 @@ import org.gradle.api.logging.Logging; import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; -import org.gradle.api.tasks.TaskState; +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; +import org.gradle.build.event.BuildEventsListenerRegistry; import org.gradle.internal.jvm.Jvm; import org.gradle.process.ExecOperations; +import org.gradle.tooling.events.FinishEvent; +import org.gradle.tooling.events.OperationCompletionListener; +import org.gradle.tooling.events.task.TaskFailureResult; +import org.gradle.tooling.events.task.TaskFinishEvent; import java.io.File; +import java.util.HashMap; +import java.util.Map; import java.util.function.Function; import javax.inject.Inject; @@ -153,17 +159,26 @@ private void createListClustersTask(Project project, NamedDomainObjectContainer< } - static class TestClustersHookPlugin implements Plugin { - @Override + static abstract class TestClustersHookPlugin implements Plugin { + @Inject + public abstract BuildEventsListenerRegistry getEventsListenerRegistry(); + + @Inject + public TestClustersHookPlugin() {} + public void apply(Project project) { if (project != project.getRootProject()) { throw new IllegalStateException(this.getClass().getName() + " can only be applied to the root project."); } - Provider registryProvider = GradleUtils.getBuildService( project.getGradle().getSharedServices(), REGISTRY_SERVICE_NAME ); + + Provider testClusterTasksService = project.getGradle() + .getSharedServices() + .registerIfAbsent("testClusterTasksService", TaskEventsService.class, spec -> {}); + TestClustersRegistry registry = registryProvider.get(); // When we know what tasks will run, we claim the clusters of those task to differentiate between clusters @@ -173,10 +188,10 @@ public void apply(Project project) { configureClaimClustersHook(project.getGradle(), registry); // Before each task, we determine if a cluster needs to be started for that task. - configureStartClustersHook(project.getGradle(), registry); + configureStartClustersHook(project.getGradle(), registry, testClusterTasksService); // After each task we determine if there are clusters that are no longer needed. - configureStopClustersHook(project.getGradle(), registry); + getEventsListenerRegistry().onTaskCompletion(testClusterTasksService); } private static void configureClaimClustersHook(Gradle gradle, TestClustersRegistry registry) { @@ -192,39 +207,57 @@ private static void configureClaimClustersHook(Gradle gradle, TestClustersRegist }); } - private static void configureStartClustersHook(Gradle gradle, TestClustersRegistry registry) { - gradle.addListener(new TaskActionListener() { - @Override - public void beforeActions(Task task) { - if (task instanceof TestClustersAware == false) { - return; - } - // we only start the cluster before the actions, so we'll not start it if the task is up-to-date - TestClustersAware awareTask = (TestClustersAware) task; - awareTask.beforeStart(); - awareTask.getClusters().forEach(registry::maybeStartCluster); - } - - @Override - public void afterActions(Task task) {} + private void configureStartClustersHook( + Gradle gradle, + TestClustersRegistry registry, + Provider testClusterTasksService + ) { + testClusterTasksService.get().registry(registry); + gradle.getTaskGraph().whenReady(taskExecutionGraph -> { + taskExecutionGraph.getAllTasks() + .stream() + .filter(task -> task instanceof TestClustersAware) + .map(task -> (TestClustersAware) task) + .forEach(awareTask -> { + testClusterTasksService.get().register(awareTask.getPath(), awareTask); + awareTask.doFirst(task -> { + awareTask.beforeStart(); + awareTask.getClusters().forEach(registry::maybeStartCluster); + }); + }); }); } + } + + static public abstract class TaskEventsService implements BuildService, OperationCompletionListener { + + Map tasksMap = new HashMap<>(); + private TestClustersRegistry registryProvider; + + public void register(String path, TestClustersAware task) { + tasksMap.put(path, task); + } - private static void configureStopClustersHook(Gradle gradle, TestClustersRegistry registry) { - gradle.addListener(new TaskExecutionListener() { - @Override - public void afterExecute(Task task, TaskState state) { - if (task instanceof TestClustersAware == false) { - return; + public void registry(TestClustersRegistry registry) { + registryProvider = registry; + } + + @Override + public void onFinish(FinishEvent finishEvent) { + if (finishEvent instanceof TaskFinishEvent taskFinishEvent) { + // Handle task finish event... + String taskPath = taskFinishEvent.getDescriptor().getTaskPath(); + if (tasksMap.containsKey(taskPath)) { + TestClustersAware task = tasksMap.get(taskPath); + // unclaim the cluster if the task has been executed and the cluster has been claimed in the doFirst block. + if (task.getDidWork()) { + task.getClusters() + .forEach( + cluster -> registryProvider.stopCluster(cluster, taskFinishEvent.getResult() instanceof TaskFailureResult) + ); } - // always unclaim the cluster, even if _this_ task is up-to-date, as others might not have been - // and caused the cluster to start. - ((TestClustersAware) task).getClusters().forEach(cluster -> registry.stopCluster(cluster, state.getFailure() != null)); } - - @Override - public void beforeExecute(Task task) {} - }); + } } } } From aee4f5e087a27bacb1e5ce8eb69fbc5272afabef Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:00:25 +0300 Subject: [PATCH 023/203] Skip tsdb delete test for unsupported versions (#101124) Yet another test affected by the fix for showing the synthetic source, #98808. This can trigger an assert in older versions as the mapping they produce (without synthetic source) doesn't match the one they may get from the master, if the latter is in version 8.10+. Fixes #101121 --- .../resources/rest-api-spec/test/delete/70_tsdb.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/delete/70_tsdb.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/delete/70_tsdb.yml index 4730415a3162c..130f3690bb298 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/delete/70_tsdb.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/delete/70_tsdb.yml @@ -1,3 +1,9 @@ +--- +setup: + - skip: + version: "8.7.00 - 8.9.99" + reason: "Synthetic source shows up in the mapping in 8.10 and on, may trigger assert failures in mixed cluster tests" + --- "basic tsdb delete": - skip: From dc47b2c143c6220ead2d380fc764941413a18811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Witek?= Date: Thu, 19 Oct 2023 14:01:20 +0200 Subject: [PATCH 024/203] [Transform] Make `GetCheckpointAction` and `GetCheckpointNodeAction` cancellable (#100808) --- docs/changelog/100808.yaml | 5 ++ .../transform/action/GetCheckpointAction.java | 9 ++ .../action/GetCheckpointNodeAction.java | 15 ++++ .../GetCheckpointActionRequestTests.java | 43 +++++++--- .../GetCheckpointNodeActionRequestTests.java | 51 ++++++++--- .../TransformCheckpointServiceNodeTests.java | 4 +- .../TransformGetCheckpointTests.java | 2 +- .../action/TransportGetCheckpointAction.java | 57 +++++++----- .../TransportGetCheckpointNodeAction.java | 16 +++- .../action/TransportGetTransformAction.java | 9 +- .../TransportGetTransformStatsAction.java | 86 +++++++++++-------- .../TransportPreviewTransformAction.java | 8 +- .../checkpoint/DefaultCheckpointProvider.java | 18 ++-- .../TimeBasedCheckpointProvider.java | 4 +- .../TransformCheckpointService.java | 6 +- .../transforms/ClientTransformIndexer.java | 6 +- .../transforms/TransformIndexer.java | 4 - .../TransformPersistentTasksExecutor.java | 7 +- .../transform/transforms/TransformTask.java | 11 +-- .../DefaultCheckpointProviderTests.java | 12 ++- .../TimeBasedCheckpointProviderTests.java | 8 +- .../ClientTransformIndexerTests.java | 21 ++--- .../TransformIndexerFailureHandlingTests.java | 3 +- ...IndexerFailureOnStatePersistenceTests.java | 10 ++- .../transforms/TransformTaskTests.java | 3 - 25 files changed, 270 insertions(+), 148 deletions(-) create mode 100644 docs/changelog/100808.yaml diff --git a/docs/changelog/100808.yaml b/docs/changelog/100808.yaml new file mode 100644 index 0000000000000..1abbfdcebf74e --- /dev/null +++ b/docs/changelog/100808.yaml @@ -0,0 +1,5 @@ +pr: 100808 +summary: Make tasks that calculate checkpoints cancellable +area: Transform +type: bug +issues: [] diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java index 1829e82758d3f..1b2378fd63ea3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java @@ -16,6 +16,8 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.tasks.CancellableTask; +import org.elasticsearch.tasks.TaskId; import java.io.IOException; import java.util.Arrays; @@ -24,6 +26,8 @@ import java.util.Map.Entry; import java.util.Objects; +import static org.elasticsearch.core.Strings.format; + /** * Transform internal API (no REST layer) to retrieve index checkpoints. */ @@ -105,6 +109,11 @@ public IndicesRequest indices(String... indices) { public boolean allowsRemoteIndices() { return false; } + + @Override + public CancellableTask createTask(long id, String type, String action, TaskId parentTaskId, Map headers) { + return new CancellableTask(id, type, action, format("get_checkpoint[%d]", indices.length), parentTaskId, headers); + } } public static class Response extends ActionResponse { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java index ebc34211040c9..afef902d007f9 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java @@ -17,6 +17,8 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.tasks.CancellableTask; +import org.elasticsearch.tasks.TaskId; import java.io.IOException; import java.util.Arrays; @@ -25,6 +27,8 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.core.Strings.format; + public class GetCheckpointNodeAction extends ActionType { public static final GetCheckpointNodeAction INSTANCE = new GetCheckpointNodeAction(); @@ -146,5 +150,16 @@ public IndicesOptions indicesOptions() { return originalIndices.indicesOptions(); } + @Override + public CancellableTask createTask(long id, String type, String action, TaskId parentTaskId, Map headers) { + return new CancellableTask( + id, + type, + action, + format("get_checkpoint_node[%d;%d]", indices() != null ? indices().length : 0, shards != null ? shards.size() : 0), + parentTaskId, + headers + ); + } } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java index 0bda09772da64..1eccab78c8e13 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java @@ -10,6 +10,8 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.tasks.CancellableTask; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.core.transform.action.GetCheckpointAction.Request; @@ -17,21 +19,17 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.stream.Stream; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; public class GetCheckpointActionRequestTests extends AbstractWireSerializingTestCase { @Override protected Request createTestInstance() { - return new Request( - randomBoolean() ? null : generateRandomStringArray(10, 10, false, false), - IndicesOptions.fromParameters( - randomFrom(IndicesOptions.WildcardStates.values()).name().toLowerCase(Locale.ROOT), - Boolean.toString(randomBoolean()), - Boolean.toString(randomBoolean()), - Boolean.toString(randomBoolean()), - SearchRequest.DEFAULT_INDICES_OPTIONS - ) - ); + return randomRequest(randomBoolean() ? 10 : null); } @Override @@ -63,4 +61,29 @@ protected Request mutateInstance(Request instance) { return new Request(indices.toArray(new String[0]), indicesOptions); } + + public void testCreateTask() { + Request request = randomRequest(17); + CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); + assertThat(task.getDescription(), is(equalTo("get_checkpoint[17]"))); + } + + public void testCreateTaskWithNullIndices() { + Request request = new Request(null, null); + CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); + assertThat(task.getDescription(), is(equalTo("get_checkpoint[0]"))); + } + + private static Request randomRequest(Integer numIndices) { + return new Request( + numIndices != null ? Stream.generate(() -> randomAlphaOfLength(10)).limit(numIndices).toArray(String[]::new) : null, + IndicesOptions.fromParameters( + randomFrom(IndicesOptions.WildcardStates.values()).name().toLowerCase(Locale.ROOT), + Boolean.toString(randomBoolean()), + Boolean.toString(randomBoolean()), + Boolean.toString(randomBoolean()), + SearchRequest.DEFAULT_INDICES_OPTIONS + ) + ); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java index ac5d1e3859445..aa914122f786f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java @@ -11,12 +11,18 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.tasks.CancellableTask; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction.Request; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + public class GetCheckpointNodeActionRequestTests extends AbstractWireSerializingTestCase { @Override @@ -26,15 +32,7 @@ protected Reader instanceReader() { @Override protected Request createTestInstance() { - Set shards = new HashSet<>(); - OriginalIndices originalIndices = randomOriginalIndices(randomIntBetween(0, 20)); - int numberOfRandomShardIds = randomInt(10); - - for (int i = 0; i < numberOfRandomShardIds; ++i) { - shards.add(new ShardId(randomAlphaOfLength(4) + i, randomAlphaOfLength(4), randomInt(5))); - } - - return new Request(shards, originalIndices); + return new Request(randomShards(randomInt(10)), randomOriginalIndices(randomIntBetween(0, 20))); } @Override @@ -62,7 +60,39 @@ protected Request mutateInstance(Request instance) { } } - private OriginalIndices randomOriginalIndices(int numIndices) { + public void testCreateTask() { + Request request = new Request(randomShards(7), randomOriginalIndices(19)); + CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); + assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[19;7]"))); + } + + public void testCreateTaskWithNullShardsAndIndices() { + Request request = new Request(null, OriginalIndices.NONE); + CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); + assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[0;0]"))); + } + + public void testCreateTaskWithNullShards() { + Request request = new Request(null, randomOriginalIndices(13)); + CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); + assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[13;0]"))); + } + + public void testCreateTaskWithNullIndices() { + Request request = new Request(randomShards(11), OriginalIndices.NONE); + CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); + assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[0;11]"))); + } + + private static Set randomShards(int numShards) { + Set shards = new HashSet<>(); + for (int i = 0; i < numShards; ++i) { + shards.add(new ShardId(randomAlphaOfLength(4) + i, randomAlphaOfLength(4), randomInt(5))); + } + return shards; + } + + private static OriginalIndices randomOriginalIndices(int numIndices) { String[] randomIndices = new String[numIndices]; for (int i = 0; i < numIndices; i++) { randomIndices[i] = randomAlphaOfLengthBetween(5, 10); @@ -70,5 +100,4 @@ private OriginalIndices randomOriginalIndices(int numIndices) { IndicesOptions indicesOptions = randomBoolean() ? IndicesOptions.strictExpand() : IndicesOptions.lenientExpandOpen(); return new OriginalIndices(randomIndices, indicesOptions); } - } diff --git a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java index 49adb7c194b22..2c01d9d7381e3 100644 --- a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java +++ b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.admin.indices.stats.ShardStats; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.cluster.routing.RecoverySource; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; @@ -43,6 +44,7 @@ import org.elasticsearch.index.warmer.WarmerStats; import org.elasticsearch.indices.TestIndexNameExpressionResolver; import org.elasticsearch.search.suggest.completion.CompletionStats; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.tasks.TaskManager; import org.elasticsearch.test.client.NoOpClient; import org.elasticsearch.transport.ActionNotFoundTransportException; @@ -405,7 +407,7 @@ private static void getCheckpoint( (l, infoBuilder) -> l.onResponse(infoBuilder.build()) ); transformCheckpointService.getCheckpointingInfo( - mockClientForCheckpointing, + new ParentTaskAssigningClient(mockClientForCheckpointing, new TaskId("dummy-node:123456")), transformId, lastCheckpointNumber, nextCheckpointPosition, diff --git a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java index 521a1deafe797..bde1777bdb47d 100644 --- a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java +++ b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java @@ -239,7 +239,7 @@ protected void doExecute( ActionListener listener ) { ++calls; - getGlobalCheckpoints(mockIndicesService, request.getShards(), listener); + getGlobalCheckpoints(mockIndicesService, task, request.getShards(), listener); } public int getCalls() { diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java index 8167305ef0cee..05349d86f012e 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.transport.ActionNotFoundTransportException; import org.elasticsearch.transport.TransportRequestOptions; @@ -38,6 +39,7 @@ import org.elasticsearch.xpack.core.transform.action.GetCheckpointAction.Response; import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -146,31 +148,23 @@ protected AsyncGetCheckpointsFromNodesAction( public void start() { GroupedActionListener groupedListener = new GroupedActionListener<>( nodesAndShards.size(), - ActionListener.wrap(responses -> { - // the final list should be ordered by key - Map checkpointsByIndexReduced = new TreeMap<>(); - - // merge the node responses - for (GetCheckpointNodeAction.Response response : responses) { - response.getCheckpoints().forEach((index, checkpoint) -> { - if (checkpointsByIndexReduced.containsKey(index)) { - long[] shardCheckpoints = checkpointsByIndexReduced.get(index); - for (int i = 0; i < checkpoint.length; ++i) { - shardCheckpoints[i] = Math.max(shardCheckpoints[i], checkpoint[i]); - } - } else { - checkpointsByIndexReduced.put(index, checkpoint); - } - }); - } - - listener.onResponse(new Response(checkpointsByIndexReduced)); - }, listener::onFailure) + ActionListener.wrap(responses -> listener.onResponse(mergeNodeResponses(responses)), listener::onFailure) ); for (Entry> oneNodeAndItsShards : nodesAndShards.entrySet()) { + if (task instanceof CancellableTask) { + // There is no point continuing this work if the task has been cancelled. + if (((CancellableTask) task).notifyIfCancelled(listener)) { + return; + } + } if (localNodeId.equals(oneNodeAndItsShards.getKey())) { - TransportGetCheckpointNodeAction.getGlobalCheckpoints(indicesService, oneNodeAndItsShards.getValue(), groupedListener); + TransportGetCheckpointNodeAction.getGlobalCheckpoints( + indicesService, + task, + oneNodeAndItsShards.getValue(), + groupedListener + ); continue; } @@ -207,5 +201,26 @@ public void start() { ); } } + + private static Response mergeNodeResponses(Collection responses) { + // the final list should be ordered by key + Map checkpointsByIndexReduced = new TreeMap<>(); + + // merge the node responses + for (GetCheckpointNodeAction.Response response : responses) { + response.getCheckpoints().forEach((index, checkpoint) -> { + if (checkpointsByIndexReduced.containsKey(index)) { + long[] shardCheckpoints = checkpointsByIndexReduced.get(index); + for (int i = 0; i < checkpoint.length; ++i) { + shardCheckpoints[i] = Math.max(shardCheckpoints[i], checkpoint[i]); + } + } else { + checkpointsByIndexReduced.put(index, checkpoint); + } + }); + } + + return new Response(checkpointsByIndexReduced); + } } } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java index 798191577cdb2..b0d15d27c0c86 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java @@ -16,6 +16,7 @@ import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction; @@ -43,12 +44,23 @@ public TransportGetCheckpointNodeAction( @Override protected void doExecute(Task task, Request request, ActionListener listener) { - getGlobalCheckpoints(indicesService, request.getShards(), listener); + getGlobalCheckpoints(indicesService, task, request.getShards(), listener); } - protected static void getGlobalCheckpoints(IndicesService indicesService, Set shards, ActionListener listener) { + protected static void getGlobalCheckpoints( + IndicesService indicesService, + Task task, + Set shards, + ActionListener listener + ) { Map checkpointsByIndexOfThisNode = new HashMap<>(); for (ShardId shardId : shards) { + if (task instanceof CancellableTask) { + // There is no point continuing this work if the task has been cancelled. + if (((CancellableTask) task).notifyIfCancelled(listener)) { + return; + } + } final IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); final IndexShard indexShard = indexService.getShard(shardId.id()); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformAction.java index a7dd4a3011495..880067facab75 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformAction.java @@ -69,15 +69,16 @@ public TransportGetTransformAction( @Override protected void doExecute(Task task, Request request, ActionListener listener) { - final ClusterState state = clusterService.state(); - TransformNodes.warnIfNoTransformNodes(state); + final TaskId parentTaskId = new TaskId(clusterService.localNode().getId(), task.getId()); + final ClusterState clusterState = clusterService.state(); + TransformNodes.warnIfNoTransformNodes(clusterState); // Step 2: Search for all the transform tasks (matching the request) that *do not* have corresponding transform config. ActionListener> searchTransformConfigsListener = ActionListener.wrap(r -> { Set transformConfigIds = r.results().stream().map(TransformConfig::getId).collect(toSet()); Collection> transformTasks = TransformTask.findTransformTasks( request.getId(), - state + clusterState ); List errors = transformTasks.stream() .map(PersistentTasksCustomMetadata.PersistentTask::getId) @@ -88,7 +89,7 @@ protected void doExecute(Task task, Request request, ActionListener li }, listener::onFailure); // Step 1: Search for all the transform configs matching the request. - searchResources(request, new TaskId(clusterService.localNode().getId(), task.getId()), searchTransformConfigsListener); + searchResources(request, parentTaskId, searchTransformConfigsListener); } @Override diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java index 98343d5593dfa..1dffb11975aa1 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java @@ -115,45 +115,55 @@ protected Response newResponse( } @Override - protected void taskOperation(CancellableTask actionTask, Request request, TransformTask task, ActionListener listener) { + protected void taskOperation( + CancellableTask actionTask, + Request request, + TransformTask transformTask, + ActionListener listener + ) { // Little extra insurance, make sure we only return transforms that aren't cancelled - ClusterState state = clusterService.state(); - String nodeId = state.nodes().getLocalNode().getId(); + ClusterState clusterState = clusterService.state(); + String nodeId = clusterState.nodes().getLocalNode().getId(); + final TaskId parentTaskId = new TaskId(nodeId, actionTask.getId()); - if (task.isCancelled() == false) { - task.getCheckpointingInfo( - transformCheckpointService, - ActionListener.wrap( - checkpointingInfo -> listener.onResponse(new Response(Collections.singletonList(deriveStats(task, checkpointingInfo)))), - e -> { - logger.warn("Failed to retrieve checkpointing info for transform [" + task.getTransformId() + "]", e); - listener.onResponse( - new Response( - Collections.singletonList(deriveStats(task, null)), - 1L, - Collections.emptyList(), - Collections.singletonList(new FailedNodeException(nodeId, "Failed to retrieve checkpointing info", e)) - ) - ); - } - ), - // at this point the transport already spend some time budget in `doExecute`, it is hard to tell what is left: - // recording the time spend would be complex and crosses machine boundaries, that's why we use a heuristic here - TimeValue.timeValueMillis( - (long) ((request.getTimeout() != null - ? request.getTimeout().millis() - : AcknowledgedRequest.DEFAULT_ACK_TIMEOUT.millis()) * CHECKPOINT_INFO_TIMEOUT_SHARE) - ) - ); - } else { + if (actionTask.notifyIfCancelled(listener)) { + return; + } + if (transformTask.isCancelled()) { listener.onResponse(new Response(Collections.emptyList())); + return; } + transformTask.getCheckpointingInfo( + transformCheckpointService, + new ParentTaskAssigningClient(client, parentTaskId), + ActionListener.wrap( + checkpointingInfo -> listener.onResponse( + new Response(Collections.singletonList(deriveStats(transformTask, checkpointingInfo))) + ), + e -> { + logger.warn("Failed to retrieve checkpointing info for transform [" + transformTask.getTransformId() + "]", e); + listener.onResponse( + new Response( + Collections.singletonList(deriveStats(transformTask, null)), + 1L, + Collections.emptyList(), + Collections.singletonList(new FailedNodeException(nodeId, "Failed to retrieve checkpointing info", e)) + ) + ); + } + ), + // at this point the transport already spend some time budget in `doExecute`, it is hard to tell what is left: + // recording the time spend would be complex and crosses machine boundaries, that's why we use a heuristic here + TimeValue.timeValueMillis( + (long) ((request.getTimeout() != null ? request.getTimeout().millis() : AcknowledgedRequest.DEFAULT_ACK_TIMEOUT.millis()) + * CHECKPOINT_INFO_TIMEOUT_SHARE) + ) + ); } @Override protected void doExecute(Task task, Request request, ActionListener finalListener) { - TaskId parentTaskId = new TaskId(clusterService.localNode().getId(), task.getId()); - + final TaskId parentTaskId = new TaskId(clusterService.localNode().getId(), task.getId()); final ClusterState clusterState = clusterService.state(); TransformNodes.warnIfNoTransformNodes(clusterState); @@ -231,16 +241,16 @@ protected void doExecute(Task task, Request request, ActionListener fi private static void setNodeAttributes( TransformStats transformStats, PersistentTasksCustomMetadata persistentTasksCustomMetadata, - ClusterState state + ClusterState clusterState ) { var pTask = persistentTasksCustomMetadata.getTask(transformStats.getId()); if (pTask != null) { - transformStats.setNode(NodeAttributes.fromDiscoveryNode(state.nodes().get(pTask.getExecutorNode()))); + transformStats.setNode(NodeAttributes.fromDiscoveryNode(clusterState.nodes().get(pTask.getExecutorNode()))); } } - static TransformStats deriveStats(TransformTask task, @Nullable TransformCheckpointingInfo checkpointingInfo) { - TransformState transformState = task.getState(); + static TransformStats deriveStats(TransformTask transformTask, @Nullable TransformCheckpointingInfo checkpointingInfo) { + TransformState transformState = transformTask.getState(); TransformStats.State derivedState = TransformStats.State.fromComponents( transformState.getTaskState(), transformState.getIndexerState() @@ -253,13 +263,13 @@ static TransformStats deriveStats(TransformTask task, @Nullable TransformCheckpo reason = Strings.isNullOrEmpty(reason) ? "transform is set to stop at the next checkpoint" : reason; } return new TransformStats( - task.getTransformId(), + transformTask.getTransformId(), derivedState, reason, null, - task.getStats(), + transformTask.getStats(), checkpointingInfo == null ? TransformCheckpointingInfo.EMPTY : checkpointingInfo, - TransformHealthChecker.checkTransform(task, transformState.getAuthState()) + TransformHealthChecker.checkTransform(transformTask, transformState.getAuthState()) ); } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPreviewTransformAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPreviewTransformAction.java index 37dff75a76f6b..8e0a935ffaa53 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPreviewTransformAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportPreviewTransformAction.java @@ -210,7 +210,7 @@ private void getPreview( SyncConfig syncConfig, ActionListener listener ) { - Client parentTaskAssigningClient = new ParentTaskAssigningClient(client, parentTaskId); + Client parentTaskClient = new ParentTaskAssigningClient(client, parentTaskId); final SetOnce> mappings = new SetOnce<>(); @@ -279,7 +279,7 @@ private void getPreview( builder.endObject(); var pipelineRequest = new SimulatePipelineRequest(BytesReference.bytes(builder), XContentType.JSON); pipelineRequest.setId(pipeline); - parentTaskAssigningClient.execute(SimulatePipelineAction.INSTANCE, pipelineRequest, pipelineResponseActionListener); + parentTaskClient.execute(SimulatePipelineAction.INSTANCE, pipelineRequest, pipelineResponseActionListener); } } }, listener::onFailure); @@ -287,7 +287,7 @@ private void getPreview( ActionListener> deduceMappingsListener = ActionListener.wrap(deducedMappings -> { mappings.set(deducedMappings); function.preview( - parentTaskAssigningClient, + parentTaskClient, timeout, filteredHeaders, source, @@ -297,6 +297,6 @@ private void getPreview( ); }, listener::onFailure); - function.deduceMappings(parentTaskAssigningClient, filteredHeaders, source, deduceMappingsListener); + function.deduceMappings(parentTaskClient, filteredHeaders, source, deduceMappingsListener); } } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java index 86ef29b2af370..f8293787b8943 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java @@ -18,7 +18,7 @@ import org.elasticsearch.action.admin.indices.stats.ShardStats; import org.elasticsearch.action.support.GroupedActionListener; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Strings; @@ -58,7 +58,7 @@ class DefaultCheckpointProvider implements CheckpointProvider { private static final Logger logger = LogManager.getLogger(DefaultCheckpointProvider.class); protected final Clock clock; - protected final Client client; + protected final ParentTaskAssigningClient client; protected final RemoteClusterResolver remoteClusterResolver; protected final TransformConfigManager transformConfigManager; protected final TransformAuditor transformAuditor; @@ -69,7 +69,7 @@ class DefaultCheckpointProvider implements CheckpointProvider { DefaultCheckpointProvider( final Clock clock, - final Client client, + final ParentTaskAssigningClient client, final RemoteClusterResolver remoteClusterResolver, final TransformConfigManager transformConfigManager, final TransformAuditor transformAuditor, @@ -133,7 +133,10 @@ protected void getIndexCheckpoints(ActionListener> listener) } for (Map.Entry> remoteIndex : resolvedIndexes.getRemoteIndicesPerClusterAlias().entrySet()) { - Client remoteClient = client.getRemoteClusterClient(remoteIndex.getKey(), EsExecutors.DIRECT_EXECUTOR_SERVICE); + ParentTaskAssigningClient remoteClient = new ParentTaskAssigningClient( + client.getRemoteClusterClient(remoteIndex.getKey(), EsExecutors.DIRECT_EXECUTOR_SERVICE), + client.getParentTask() + ); getCheckpointsFromOneCluster( remoteClient, transformConfig.getHeaders(), @@ -148,7 +151,7 @@ protected void getIndexCheckpoints(ActionListener> listener) } private void getCheckpointsFromOneCluster( - Client client, + ParentTaskAssigningClient client, Map headers, String[] indices, String cluster, @@ -184,14 +187,13 @@ private void getCheckpointsFromOneCluster( } private static void getCheckpointsFromOneClusterV2( - Client client, + ParentTaskAssigningClient client, Map headers, String[] indices, String cluster, ActionListener> listener ) { GetCheckpointAction.Request getCheckpointRequest = new GetCheckpointAction.Request(indices, IndicesOptions.LENIENT_EXPAND_OPEN); - ActionListener checkpointListener; if (RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY.equals(cluster)) { checkpointListener = ActionListener.wrap( @@ -230,7 +232,7 @@ private static void getCheckpointsFromOneClusterV2( * BWC fallback for nodes/cluster older than 8.2 */ private static void getCheckpointsFromOneClusterBWC( - Client client, + ParentTaskAssigningClient client, Map headers, String[] indices, String cluster, diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java index a30f3269ca96f..28daeab50c640 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java @@ -13,7 +13,7 @@ import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -45,7 +45,7 @@ class TimeBasedCheckpointProvider extends DefaultCheckpointProvider { TimeBasedCheckpointProvider( final Clock clock, - final Client client, + final ParentTaskAssigningClient client, final RemoteClusterResolver remoteClusterResolver, final TransformConfigManager transformConfigManager, final TransformAuditor transformAuditor, diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java index 4527b6039bcad..ac5e9b75c364a 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java @@ -10,7 +10,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.xpack.core.transform.transforms.TimeSyncConfig; @@ -53,7 +53,7 @@ public TransformCheckpointService( this.remoteClusterResolver = new RemoteClusterResolver(settings, clusterService.getClusterSettings()); } - public CheckpointProvider getCheckpointProvider(final Client client, final TransformConfig transformConfig) { + public CheckpointProvider getCheckpointProvider(final ParentTaskAssigningClient client, final TransformConfig transformConfig) { if (transformConfig.getSyncConfig() instanceof TimeSyncConfig) { return new TimeBasedCheckpointProvider( clock, @@ -85,7 +85,7 @@ public CheckpointProvider getCheckpointProvider(final Client client, final Trans * @param listener listener to retrieve the result */ public void getCheckpointingInfo( - final Client client, + final ParentTaskAssigningClient client, final String transformId, final long lastCheckpointNumber, final TransformIndexerPosition nextCheckpointPosition, diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java index 00fa7f200a3c3..45bef4650640a 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexer.java @@ -27,7 +27,7 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.master.AcknowledgedRequest; -import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; @@ -75,7 +75,7 @@ class ClientTransformIndexer extends TransformIndexer { private static final TimeValue PIT_KEEP_ALIVE = TimeValue.timeValueSeconds(30); private static final Logger logger = LogManager.getLogger(ClientTransformIndexer.class); - private final Client client; + private final ParentTaskAssigningClient client; private final AtomicBoolean oldStatsCleanedUp = new AtomicBoolean(false); private final AtomicReference seqNoPrimaryTermAndIndexHolder; @@ -89,7 +89,7 @@ class ClientTransformIndexer extends TransformIndexer { CheckpointProvider checkpointProvider, AtomicReference initialState, TransformIndexerPosition initialPosition, - Client client, + ParentTaskAssigningClient client, TransformIndexerStats initialStats, TransformConfig transformConfig, TransformProgress transformProgress, diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java index 9294aef87526d..cf6c50ec60faf 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformIndexer.java @@ -218,10 +218,6 @@ public TransformCheckpoint getNextCheckpoint() { return nextCheckpoint; } - public CheckpointProvider getCheckpointProvider() { - return checkpointProvider; - } - /** * Request a checkpoint */ diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutor.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutor.java index 0d2ce26363298..6a8a8c8548491 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutor.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformPersistentTasksExecutor.java @@ -16,6 +16,7 @@ import org.elasticsearch.action.LatchedActionListener; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -181,6 +182,7 @@ protected void nodeOperation(AllocatedPersistentTask task, @Nullable TransformTa final String transformId = params.getId(); final TransformTask buildTask = (TransformTask) task; + final ParentTaskAssigningClient parentTaskClient = new ParentTaskAssigningClient(client, buildTask.getParentTaskId()); // NOTE: TransformPersistentTasksExecutor#createTask pulls in the stored task state from the ClusterState when the object // is created. TransformTask#ctor takes into account setting the task as failed if that is passed in with the // persisted state. @@ -189,7 +191,7 @@ protected void nodeOperation(AllocatedPersistentTask task, @Nullable TransformTa // // We want the rest of the state to be populated in the task when it is loaded on the node so that users can force start it again // later if they want. - final ClientTransformIndexerBuilder indexerBuilder = new ClientTransformIndexerBuilder().setClient(buildTask.getParentTaskClient()) + final ClientTransformIndexerBuilder indexerBuilder = new ClientTransformIndexerBuilder().setClient(parentTaskClient) .setTransformServices(transformServices); final SetOnce stateHolder = new SetOnce<>(); @@ -346,7 +348,7 @@ protected void nodeOperation(AllocatedPersistentTask task, @Nullable TransformTa // <1> Check the latest internal index (IMPORTANT: according to _this_ node, which might be newer than master) is installed TransformInternalIndex.createLatestVersionedIndexIfRequired( clusterService, - buildTask.getParentTaskClient(), + parentTaskClient, transformInternalIndexAdditionalSettings, templateCheckListener ); @@ -420,7 +422,6 @@ protected AllocatedPersistentTask createTask( type, action, parentTaskId, - client, persistentTask.getParams(), (TransformState) persistentTask.getState(), transformServices.getScheduler(), diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java index c2affd3881e27..803f68d928a98 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java @@ -15,7 +15,6 @@ import org.elasticsearch.ElasticsearchTimeoutException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ListenerTimeouts; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.Strings; @@ -63,7 +62,6 @@ public class TransformTask extends AllocatedPersistentTask implements TransformS private static final Logger logger = LogManager.getLogger(TransformTask.class); private static final IndexerState[] RUNNING_STATES = new IndexerState[] { IndexerState.STARTED, IndexerState.INDEXING }; - private final ParentTaskAssigningClient parentTaskClient; private final TransformTaskParams transform; private final TransformScheduler transformScheduler; private final ThreadPool threadPool; @@ -79,7 +77,6 @@ public TransformTask( String type, String action, TaskId parentTask, - Client client, TransformTaskParams transform, TransformState state, TransformScheduler transformScheduler, @@ -88,7 +85,6 @@ public TransformTask( Map headers ) { super(id, type, action, TransformField.PERSISTENT_TASK_DESCRIPTION_PREFIX + transform.getId(), parentTask, headers); - this.parentTaskClient = new ParentTaskAssigningClient(client, parentTask); this.transform = transform; this.transformScheduler = transformScheduler; this.threadPool = threadPool; @@ -125,10 +121,6 @@ public TransformTask( } } - public ParentTaskAssigningClient getParentTaskClient() { - return parentTaskClient; - } - public String getTransformId() { return transform.getId(); } @@ -183,6 +175,7 @@ public TransformIndexerStats getStats() { public void getCheckpointingInfo( TransformCheckpointService transformsCheckpointService, + ParentTaskAssigningClient parentTaskClient, ActionListener listener, TimeValue timeout ) { @@ -217,7 +210,7 @@ public void getCheckpointingInfo( ); return; } - transformIndexer.getCheckpointProvider() + transformsCheckpointService.getCheckpointProvider(parentTaskClient, transformIndexer.getConfig()) .getCheckpointingInfo( transformIndexer.getLastCheckpoint(), transformIndexer.getNextCheckpoint(), diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProviderTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProviderTests.java index 5d05d89aacdce..2457e2719c0ee 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProviderTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProviderTests.java @@ -19,11 +19,13 @@ import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.support.DefaultShardOperationFailedException; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.set.Sets; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.MockLogAppender; import org.elasticsearch.test.MockLogAppender.LoggingExpectation; @@ -68,6 +70,7 @@ public class DefaultCheckpointProviderTests extends ESTestCase { private Clock clock; private Client client; + private ParentTaskAssigningClient parentTaskClient; private Client remoteClient1; private Client remoteClient2; private Client remoteClient3; @@ -81,6 +84,7 @@ public void setUpMocks() { when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY)); client = mock(Client.class); when(client.threadPool()).thenReturn(threadPool); + parentTaskClient = new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")); remoteClient1 = mock(Client.class); when(remoteClient1.threadPool()).thenReturn(threadPool); remoteClient2 = mock(Client.class); @@ -253,7 +257,7 @@ public void testHandlingShardFailures() throws Exception { DefaultCheckpointProvider provider = new DefaultCheckpointProvider( clock, - client, + parentTaskClient, remoteClusterResolver, transformConfigManager, transformAuditor, @@ -319,7 +323,7 @@ public void testCreateNextCheckpointWithRemoteClient() throws InterruptedExcepti DefaultCheckpointProvider provider = new DefaultCheckpointProvider( clock, - client, + parentTaskClient, remoteClusterResolver, transformConfigManager, transformAuditor, @@ -370,7 +374,7 @@ public void testCreateNextCheckpointWithRemoteClients() throws InterruptedExcept DefaultCheckpointProvider provider = new DefaultCheckpointProvider( clock, - client, + parentTaskClient, remoteClusterResolver, transformConfigManager, transformAuditor, @@ -397,7 +401,7 @@ public void testCreateNextCheckpointWithRemoteClients() throws InterruptedExcept private DefaultCheckpointProvider newCheckpointProvider(TransformConfig transformConfig) { return new DefaultCheckpointProvider( clock, - client, + parentTaskClient, new RemoteClusterResolver(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), transformConfigManager, transformAuditor, diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProviderTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProviderTests.java index 05c1a372dc471..5f1c0e6bb7f76 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProviderTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProviderTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.action.search.SearchResponseSections; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; @@ -26,6 +27,7 @@ import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.transform.TransformConfigVersion; @@ -71,6 +73,7 @@ public class TimeBasedCheckpointProviderTests extends ESTestCase { private Clock clock; private Client client; + private ParentTaskAssigningClient parentTaskClient; private IndexBasedTransformConfigManager transformConfigManager; private MockTransformAuditor transformAuditor; @@ -78,10 +81,11 @@ public class TimeBasedCheckpointProviderTests extends ESTestCase { public void setUpMocks() { clock = mock(Clock.class); when(clock.millis()).thenReturn(123456789L); - client = mock(Client.class); ThreadPool threadPool = mock(ThreadPool.class); when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY)); + client = mock(Client.class); when(client.threadPool()).thenReturn(threadPool); + parentTaskClient = new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")); transformConfigManager = mock(IndexBasedTransformConfigManager.class); transformAuditor = MockTransformAuditor.createMockAuditor(); } @@ -278,7 +282,7 @@ private void testCreateNextCheckpoint( private TimeBasedCheckpointProvider newCheckpointProvider(TransformConfig transformConfig) { return new TimeBasedCheckpointProvider( clock, - client, + parentTaskClient, new RemoteClusterResolver(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), transformConfigManager, transformAuditor, diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java index 09b46c72c2ead..a850c7beef7dd 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/ClientTransformIndexerTests.java @@ -21,7 +21,7 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.action.support.ActionTestUtils; -import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexNotFoundException; @@ -34,6 +34,7 @@ import org.elasticsearch.search.internal.ShardSearchContextId; import org.elasticsearch.search.profile.SearchProfileResults; import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.client.NoOpClient; import org.elasticsearch.threadpool.ThreadPool; @@ -138,7 +139,7 @@ public void testPitInjection() throws InterruptedException { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, @@ -231,7 +232,7 @@ public void testPitInjectionIfPitNotSupported() throws InterruptedException { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, @@ -307,7 +308,7 @@ public void testDisablePit() throws InterruptedException { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, @@ -370,7 +371,7 @@ public void testDisablePitWhenThereIsRemoteIndexInSource() throws InterruptedExc mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, @@ -413,7 +414,7 @@ public void testDisablePitWhenThereIsRemoteIndexInSource() throws InterruptedExc public void testHandlePitIndexNotFound() throws InterruptedException { // simulate a deleted index due to ILM try (PitMockClient client = new PitMockClient(getTestName(), true)) { - ClientTransformIndexer indexer = createTestIndexer(client); + ClientTransformIndexer indexer = createTestIndexer(new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456"))); SearchRequest searchRequest = new SearchRequest("deleted-index"); searchRequest.source().pointInTimeBuilder(new PointInTimeBuilder("the_pit_id")); Tuple namedSearchRequest = new Tuple<>("test-handle-pit-index-not-found", searchRequest); @@ -425,7 +426,7 @@ public void testHandlePitIndexNotFound() throws InterruptedException { // simulate a deleted index that is essential, search must fail (after a retry without pit) try (PitMockClient client = new PitMockClient(getTestName(), true)) { - ClientTransformIndexer indexer = createTestIndexer(client); + ClientTransformIndexer indexer = createTestIndexer(new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456"))); SearchRequest searchRequest = new SearchRequest("essential-deleted-index"); searchRequest.source().pointInTimeBuilder(new PointInTimeBuilder("the_pit_id")); Tuple namedSearchRequest = new Tuple<>("test-handle-pit-index-not-found", searchRequest); @@ -444,7 +445,7 @@ private static class MockClientTransformIndexer extends ClientTransformIndexer { CheckpointProvider checkpointProvider, AtomicReference initialState, TransformIndexerPosition initialPosition, - Client client, + ParentTaskAssigningClient client, TransformIndexerStats initialStats, TransformConfig transformConfig, TransformProgress transformProgress, @@ -582,7 +583,7 @@ private ClientTransformIndexer createTestIndexer() { return createTestIndexer(null); } - private ClientTransformIndexer createTestIndexer(Client client) { + private ClientTransformIndexer createTestIndexer(ParentTaskAssigningClient client) { ThreadPool threadPool = mock(ThreadPool.class); when(threadPool.executor("generic")).thenReturn(mock(ExecutorService.class)); @@ -597,7 +598,7 @@ private ClientTransformIndexer createTestIndexer(Client client) { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client == null ? mock(Client.class) : client, + client == null ? mock(ParentTaskAssigningClient.class) : client, mock(TransformIndexerStats.class), TransformConfigTests.randomTransformConfig(), null, diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureHandlingTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureHandlingTests.java index 424514b99f683..a460dca4a1b41 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureHandlingTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureHandlingTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.breaker.CircuitBreaker.Durability; import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.settings.Settings; @@ -135,7 +136,7 @@ static class MockedTransformIndexer extends ClientTransformIndexer { checkpointProvider, initialState, initialPosition, - mock(Client.class), + mock(ParentTaskAssigningClient.class), jobStats, transformConfig, /* TransformProgress */ null, diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureOnStatePersistenceTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureOnStatePersistenceTests.java index afa10afbdf638..aeb94cd2c2f66 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureOnStatePersistenceTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformIndexerFailureOnStatePersistenceTests.java @@ -11,10 +11,12 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.LatchedActionListener; import org.elasticsearch.client.internal.Client; +import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.client.NoOpClient; import org.elasticsearch.threadpool.ThreadPool; @@ -63,7 +65,7 @@ private static class MockClientTransformIndexer extends ClientTransformIndexer { CheckpointProvider checkpointProvider, AtomicReference initialState, TransformIndexerPosition initialPosition, - Client client, + ParentTaskAssigningClient client, TransformIndexerStats initialStats, TransformConfig transformConfig, TransformProgress transformProgress, @@ -220,7 +222,7 @@ public void fail(String failureMessage, ActionListener listener) { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, @@ -302,7 +304,7 @@ public void fail(String failureMessage, ActionListener listener) { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, @@ -432,7 +434,7 @@ public void fail(String failureMessage, ActionListener listener) { mock(CheckpointProvider.class), new AtomicReference<>(IndexerState.STOPPED), null, - client, + new ParentTaskAssigningClient(client, new TaskId("dummy-node:123456")), mock(TransformIndexerStats.class), config, null, diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java index 2ee2ed051a7d2..4d5807913636d 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/TransformTaskTests.java @@ -129,7 +129,6 @@ public void testStopOnFailedTaskWithStoppedIndexer() { "some_type", "some_action", TaskId.EMPTY_TASK_ID, - client, createTransformTaskParams(transformConfig.getId()), transformState, new TransformScheduler(clock, threadPool, Settings.EMPTY), @@ -208,7 +207,6 @@ public void testStopOnFailedTaskWithoutIndexer() { "some_type", "some_action", TaskId.EMPTY_TASK_ID, - client, createTransformTaskParams(transformConfig.getId()), transformState, new TransformScheduler(Clock.systemUTC(), threadPool, Settings.EMPTY), @@ -428,7 +426,6 @@ public void testApplyNewAuthState() { "some_type", "some_action", TaskId.EMPTY_TASK_ID, - client, createTransformTaskParams(transformConfig.getId()), transformState, new TransformScheduler(Clock.systemUTC(), threadPool, Settings.EMPTY), From 7a5bc9e9fb6c53ceb90a5b6e78b07f10bb1e6598 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Thu, 19 Oct 2023 14:09:24 +0200 Subject: [PATCH 025/203] Create gradle service to start mock apm server (#100839) when running gradlew run it should be possible to quickly setup a fake apm server that will be simply logging received apm agent requests. This commits adds a gradle build service that will be run before the RunTask and will start a simple http server based on https://github.com/elastic/apm-agent-java-plugin-example/blob/main/plugin/src/test/java/co/elastic/apm/mock/MockApmServer.java --- TESTING.asciidoc | 2 + .../gradle/testclusters/MockApmServer.java | 129 ++++++++++++++++++ .../gradle/testclusters/RunTask.java | 31 ++++- 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 build-tools/src/main/java/org/elasticsearch/gradle/testclusters/MockApmServer.java diff --git a/TESTING.asciidoc b/TESTING.asciidoc index dbd11cce0256c..0393cf92776fa 100644 --- a/TESTING.asciidoc +++ b/TESTING.asciidoc @@ -108,6 +108,8 @@ password: `elastic-password`. - In order to set an Elasticsearch setting, provide a setting with the following prefix: `-Dtests.es.` - In order to pass a JVM setting, e.g. to disable assertions: `-Dtests.jvm.argline="-da"` - In order to use HTTPS: ./gradlew run --https +- In order to start a mock logging APM server on port 9999 and configure ES cluster to connect to it, +use `./gradlew run --with-apm-server` ==== Customizing the test cluster for ./gradlew run diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/MockApmServer.java b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/MockApmServer.java new file mode 100644 index 0000000000000..7ec74ee19d1bb --- /dev/null +++ b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/MockApmServer.java @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.testclusters; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * This is a server which just accepts lines of JSON code and if the JSON + * is valid and the root node is "transaction", then adds that JSON object + * to a transaction list which is accessible externally to the class. + * + * The Elastic agent sends lines of JSON code, and so this mock server + * can be used as a basic APM server for testing. + * + * The HTTP server used is the JDK embedded com.sun.net.httpserver + */ +public class MockApmServer { + private static final Logger logger = Logging.getLogger(MockApmServer.class); + private int port; + + public MockApmServer(int port) { + this.port = port; + } + + /** + * Simple main that starts a mock APM server and prints the port it is + * running on. This is not needed + * for testing, it is just a convenient template for trying things out + * if you want play around. + */ + public static void main(String[] args) throws IOException, InterruptedException { + MockApmServer server = new MockApmServer(9999); + server.start(); + } + + private static volatile HttpServer instance; + + /** + * Start the Mock APM server. Just returns empty JSON structures for every incoming message + * @return - the port the Mock APM server started on + * @throws IOException + */ + public synchronized int start() throws IOException { + if (instance != null) { + throw new IOException("MockApmServer: Ooops, you can't start this instance more than once"); + } + InetSocketAddress addr = new InetSocketAddress("0.0.0.0", port); + HttpServer server = HttpServer.create(addr, 10); + server.createContext("/exit", new ExitHandler()); + server.createContext("/", new RootHandler()); + + server.start(); + instance = server; + logger.lifecycle("MockApmServer started on port " + server.getAddress().getPort()); + return server.getAddress().getPort(); + } + + public int getPort() { + return port; + } + + /** + * Stop the server gracefully if possible + */ + public synchronized void stop() { + logger.lifecycle("stopping apm server"); + instance.stop(1); + instance = null; + } + + class RootHandler implements HttpHandler { + public void handle(HttpExchange t) { + try { + InputStream body = t.getRequestBody(); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[8 * 1024]; + int lengthRead; + while ((lengthRead = body.read(buffer)) > 0) { + bytes.write(buffer, 0, lengthRead); + } + logger.lifecycle(("MockApmServer reading JSON objects: " + bytes.toString())); + + String response = "{}"; + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + static class ExitHandler implements HttpHandler { + private static final int STOP_TIME = 3; + + public void handle(HttpExchange t) { + try { + InputStream body = t.getRequestBody(); + String response = "{}"; + t.sendResponseHeaders(200, response.length()); + OutputStream os = t.getResponseBody(); + os.write(response.getBytes()); + os.close(); + instance.stop(STOP_TIME); + instance = null; + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java index d0b2581895db7..953c0447ec71b 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/RunTask.java @@ -31,7 +31,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -public class RunTask extends DefaultTestClustersTask { +public abstract class RunTask extends DefaultTestClustersTask { public static final String CUSTOM_SETTINGS_PREFIX = "tests.es."; private static final Logger logger = Logging.getLogger(RunTask.class); @@ -40,6 +40,7 @@ public class RunTask extends DefaultTestClustersTask { private static final String transportCertificate = "private-cert2.p12"; private Boolean debug = false; + private Boolean apmServerEnabled = false; private Boolean preserveData = false; @@ -54,6 +55,7 @@ public class RunTask extends DefaultTestClustersTask { private final Path tlsBasePath = Path.of( new File(getProject().getRootDir(), "build-tools-internal/src/main/resources/run.ssl").toURI() ); + private MockApmServer mockServer; @Option(option = "debug-jvm", description = "Enable debugging configuration, to allow attaching a debugger to elasticsearch.") public void setDebug(boolean enabled) { @@ -65,6 +67,16 @@ public Boolean getDebug() { return debug; } + @Input + public Boolean getApmServerEnabled() { + return apmServerEnabled; + } + + @Option(option = "with-apm-server", description = "Run simple logging http server to accept apm requests") + public void setApmServerEnabled(Boolean apmServerEnabled) { + this.apmServerEnabled = apmServerEnabled; + } + @Option(option = "data-dir", description = "Override the base data directory used by the testcluster") public void setDataDir(String dataDirStr) { dataDir = Paths.get(dataDirStr).toAbsolutePath(); @@ -172,6 +184,19 @@ public void beforeStart() { node.setting("xpack.security.transport.ssl.keystore.path", "transport.keystore"); node.setting("xpack.security.transport.ssl.certificate_authorities", "transport.ca"); } + + if (apmServerEnabled) { + mockServer = new MockApmServer(9999); + try { + mockServer.start(); + node.setting("telemetry.metrics.enabled", "true"); + node.setting("tracing.apm.agent.server_url", "http://127.0.0.1:" + mockServer.getPort()); + } catch (IOException e) { + logger.warn("Unable to start APM server", e); + } + + } + } } if (debug) { @@ -242,6 +267,10 @@ public void runAndWait() throws IOException { if (thrown != null) { logger.debug("exception occurred during close of stdout file readers", thrown); } + + if (apmServerEnabled && mockServer != null) { + mockServer.stop(); + } } } From b803f0fcddeba53450432ef7f89d57527238a403 Mon Sep 17 00:00:00 2001 From: Carlos Delgado <6339205+carlosdelest@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:13:14 +0200 Subject: [PATCH 026/203] Fix examples in synonym graph token filter docs (#101101) --- .../tokenfilters/synonym-graph-tokenfilter.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/analysis/tokenfilters/synonym-graph-tokenfilter.asciidoc b/docs/reference/analysis/tokenfilters/synonym-graph-tokenfilter.asciidoc index 30be28614d122..ce3d0a367dc4e 100644 --- a/docs/reference/analysis/tokenfilters/synonym-graph-tokenfilter.asciidoc +++ b/docs/reference/analysis/tokenfilters/synonym-graph-tokenfilter.asciidoc @@ -38,7 +38,7 @@ Use `synonyms_set` configuration option to provide a synonym set created via Syn ---- "filter": { "synonyms_filter": { - "type": "synonym", + "type": "synonym_graph", "synonyms_set": "my-synonym-set", "updateable": true } @@ -51,7 +51,7 @@ Use `synonyms_path` to provide a synonym file : ---- "filter": { "synonyms_filter": { - "type": "synonym", + "type": "synonym_graph", "synonyms_path": "analysis/synonym-set.txt" } } @@ -66,7 +66,7 @@ Use `synonyms` to define inline synonyms: ---- "filter": { "synonyms_filter": { - "type": "synonym", + "type": "synonym_graph", "synonyms": ["pc => personal computer", "computer, pc, laptop"] } } From 7c2a727247b5169fd0261c7091d3b2b417e10786 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 19 Oct 2023 14:17:51 +0200 Subject: [PATCH 027/203] Restore original fillInStackTrace for TimeExceededException (#101048) While filling in the stacktrace has a cost that we could avoid if the exception never gets re-thrown, we know of cases where it is not caught properly which we want to track down, and that becomes very difficult without the stacktrace. --- .../search/internal/ContextIndexSearcher.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java b/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java index eec1d70d17423..1733029af9024 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java +++ b/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java @@ -499,12 +499,7 @@ public void throwTimeExceededException() { } private static class TimeExceededException extends RuntimeException { - - @Override - public Throwable fillInStackTrace() { - // never re-thrown so we can save the expensive stacktrace - return this; - } + // This exception should never be re-thrown, but we fill in the stacktrace to be able to trace where it does not get properly caught } /** From b4698b3ecbf6f566729c1d1defa5a8df641d5cda Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Thu, 19 Oct 2023 08:44:24 -0400 Subject: [PATCH 028/203] [ML] Removing exception rethrow in (#100925) * Removing rethrow * Updating suppression and asserting not runnable future --------- Co-authored-by: Elastic Machine --- .../job/process/AbstractProcessWorkerExecutorService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java index dee608e69f5bb..d5734dc5b0e95 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java @@ -10,7 +10,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.util.concurrent.AbstractRunnable; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.SuppressForbidden; @@ -21,6 +20,7 @@ import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -50,7 +50,7 @@ public abstract class AbstractProcessWorkerExecutorService e * for execution when the queue is full a 429 error is thrown. * @param queueSupplier BlockingQueue constructor */ - @SuppressForbidden(reason = "properly rethrowing errors, see EsExecutors.rethrowErrors") + @SuppressForbidden(reason = "wraps a queue and handles errors appropriately") public AbstractProcessWorkerExecutorService( ThreadContext contextHolder, String processName, @@ -107,12 +107,14 @@ public void start() { while (running.get()) { Runnable runnable = queue.poll(500, TimeUnit.MILLISECONDS); if (runnable != null) { + assert runnable instanceof RunnableFuture == false + : "Rethrowing errors for RunnableFuture instances is not supported"; + try { runnable.run(); } catch (Exception e) { logger.error(() -> "error handling process [" + processName + "] operation", e); } - EsExecutors.rethrowErrors(ThreadContext.unwrap(runnable)); } else if (shouldShutdownAfterCompletingWork.get()) { running.set(false); } From 1266718e1cdef07a8c7cd1a463707825fabf7277 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Thu, 19 Oct 2023 14:46:57 +0200 Subject: [PATCH 029/203] Add NotExistAssertion to yml test framework (#101111) currently to assert that a field does not exist, tests are using is_false. This has a drawback because is less readable and also would succeed when value is present and has a default value (0, false, etc) This commit addes a NotExistAssertion to make sure that a field does not exist in a response --- .../rest/yaml/section/ExecutableSection.java | 3 +- .../rest/yaml/section/NotExistsAssertion.java | 48 +++++++++++++++++++ .../rest/yaml/section/AssertionTests.java | 20 ++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/NotExistsAssertion.java diff --git a/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java b/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java index 81255fe10d933..64832d47cc7b3 100644 --- a/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java +++ b/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/ExecutableSection.java @@ -37,7 +37,8 @@ public interface ExecutableSection { new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("contains"), ContainsAssertion::parse), new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("length"), LengthAssertion::parse), new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("close_to"), CloseToAssertion::parse), - new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("exists"), ExistsAssertion::parse) + new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("exists"), ExistsAssertion::parse), + new NamedXContentRegistry.Entry(ExecutableSection.class, new ParseField("not_exists"), NotExistsAssertion::parse) ); /** diff --git a/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/NotExistsAssertion.java b/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/NotExistsAssertion.java new file mode 100644 index 0000000000000..f012bee73763b --- /dev/null +++ b/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/NotExistsAssertion.java @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.test.rest.yaml.section; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.xcontent.XContentLocation; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Represents an exists assert section: + *

+ * - not_exists: get.fields.bar + */ +public class NotExistsAssertion extends Assertion { + + private static final Logger logger = LogManager.getLogger(NotExistsAssertion.class); + + public static NotExistsAssertion parse(XContentParser parser) throws IOException { + return new NotExistsAssertion(parser.getTokenLocation(), ParserUtils.parseField(parser)); + } + + public NotExistsAssertion(XContentLocation location, String field) { + super(location, field, false /* not used */); + } + + @Override + protected void doAssert(Object actualValue, Object expectedValue) { + logger.trace("assert that field [{}] does not exists with any value", getField()); + String errorMessage = errorMessage(); + assertThat(errorMessage, actualValue, nullValue()); + } + + private String errorMessage() { + return "field [" + getField() + "] exists, but should not."; + } +} diff --git a/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java b/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java index 1ac0bff285b96..953b5f261485d 100644 --- a/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java +++ b/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/AssertionTests.java @@ -185,4 +185,24 @@ public void testExists() throws IOException { AssertionError e = expectThrows(AssertionError.class, () -> existsAssertion.doAssert(null, existsAssertion.getExpectedValue())); assertThat(e.getMessage(), containsString("field [get.fields._timestamp] does not exist")); } + + public void testDoesNotExist() throws IOException { + parser = createParser(YamlXContent.yamlXContent, "get.fields._timestamp"); + + NotExistsAssertion existnotExistsAssertion = NotExistsAssertion.parse(parser); + + assertThat(existnotExistsAssertion, notNullValue()); + assertThat(existnotExistsAssertion.getField(), equalTo("get.fields._timestamp")); + + existnotExistsAssertion.doAssert(null, existnotExistsAssertion.getExpectedValue()); + + AssertionError e = expectThrows( + AssertionError.class, + () -> existnotExistsAssertion.doAssert( + randomFrom(1, "", "non-empty", List.of(), Map.of(), 0, false), + existnotExistsAssertion.getExpectedValue() + ) + ); + assertThat(e.getMessage(), containsString("field [get.fields._timestamp] exists, but should not")); + } } From a62572f38287af6db78eb2f29ccf37bf64caf599 Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Thu, 19 Oct 2023 15:37:22 +0200 Subject: [PATCH 030/203] Include totals in flamegraph response (#101126) With this commit we include a few summary metrics in the flamegraph response: * total number of samples * total self CPU * total CPU These properties simplify rendering differential flamegraphs. --- docs/changelog/101126.yaml | 5 +++ .../profiling/GetFlameGraphActionIT.java | 21 ++++++++++++ .../profiling/GetFlamegraphResponse.java | 32 ++++++++++++++++++- .../profiling/GetStackTracesResponse.java | 17 ++++++++-- .../TransportGetFlamegraphAction.java | 20 ++++++++++-- .../TransportGetStackTracesAction.java | 16 +++++++++- .../GetStackTracesResponseTests.java | 5 +-- .../RestGetStackTracesActionTests.java | 6 ++-- .../TransportGetFlamegraphActionTests.java | 12 +++++-- 9 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 docs/changelog/101126.yaml create mode 100644 x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java diff --git a/docs/changelog/101126.yaml b/docs/changelog/101126.yaml new file mode 100644 index 0000000000000..7a0f45891b171 --- /dev/null +++ b/docs/changelog/101126.yaml @@ -0,0 +1,5 @@ +pr: 101126 +summary: Include totals in flamegraph response +area: Application +type: enhancement +issues: [] diff --git a/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java b/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java new file mode 100644 index 0000000000000..f0df6c69ba09e --- /dev/null +++ b/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.profiling; + +public class GetFlameGraphActionIT extends ProfilingTestCase { + public void testGetStackTracesUnfiltered() throws Exception { + GetStackTracesRequest request = new GetStackTracesRequest(1, null); + GetFlamegraphResponse response = client().execute(GetFlamegraphAction.INSTANCE, request).get(); + // only spot-check top level properties - detailed tests are done in unit tests + assertEquals(4, response.getSize()); + assertEquals(1.0d, response.getSamplingRate(), 0.001d); + assertEquals(3, response.getSelfCPU()); + assertEquals(4, response.getTotalCPU()); + assertEquals(1, response.getTotalSamples()); + } +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java index c006b52c5ed27..d357971e68b1f 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetFlamegraphResponse.java @@ -23,6 +23,9 @@ public class GetFlamegraphResponse extends ActionResponse implements ChunkedToXContentObject { private final int size; private final double samplingRate; + private final int selfCPU; + private final int totalCPU; + private final long totalSamples; private final List> edges; private final List fileIds; private final List frameTypes; @@ -51,6 +54,9 @@ public GetFlamegraphResponse(StreamInput in) throws IOException { this.sourceLines = in.readCollectionAsList(StreamInput::readInt); this.countInclusive = in.readCollectionAsList(StreamInput::readInt); this.countExclusive = in.readCollectionAsList(StreamInput::readInt); + this.selfCPU = in.readInt(); + this.totalCPU = in.readInt(); + this.totalSamples = in.readLong(); } public GetFlamegraphResponse( @@ -67,7 +73,10 @@ public GetFlamegraphResponse( List sourceFileNames, List sourceLines, List countInclusive, - List countExclusive + List countExclusive, + int selfCPU, + int totalCPU, + long totalSamples ) { this.size = size; this.samplingRate = samplingRate; @@ -83,6 +92,9 @@ public GetFlamegraphResponse( this.sourceLines = sourceLines; this.countInclusive = countInclusive; this.countExclusive = countExclusive; + this.selfCPU = selfCPU; + this.totalCPU = totalCPU; + this.totalSamples = totalSamples; } @Override @@ -101,6 +113,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeCollection(this.sourceLines, StreamOutput::writeInt); out.writeCollection(this.countInclusive, StreamOutput::writeInt); out.writeCollection(this.countExclusive, StreamOutput::writeInt); + out.writeInt(this.selfCPU); + out.writeInt(this.totalCPU); + out.writeLong(this.totalSamples); } public int getSize() { @@ -159,6 +174,18 @@ public List getSourceLines() { return sourceLines; } + public int getSelfCPU() { + return selfCPU; + } + + public int getTotalCPU() { + return totalCPU; + } + + public long getTotalSamples() { + return totalSamples; + } + @Override public Iterator toXContentChunked(ToXContent.Params params) { return Iterators.concat( @@ -187,6 +214,9 @@ public Iterator toXContentChunked(ToXContent.Params params ChunkedToXContentHelper.array("CountExclusive", Iterators.map(countExclusive.iterator(), e -> (b, p) -> b.value(e))), Iterators.single((b, p) -> b.field("Size", size)), Iterators.single((b, p) -> b.field("SamplingRate", samplingRate)), + Iterators.single((b, p) -> b.field("SelfCPU", selfCPU)), + Iterators.single((b, p) -> b.field("TotalCPU", totalCPU)), + Iterators.single((b, p) -> b.field("TotalSamples", totalSamples)), ChunkedToXContentHelper.endObject() ); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java index c25733bf8587c..39dd7cd611e64 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesResponse.java @@ -33,6 +33,7 @@ public class GetStackTracesResponse extends ActionResponse implements ChunkedToX private final Map stackTraceEvents; private final int totalFrames; private final double samplingRate; + private final long totalSamples; public GetStackTracesResponse(StreamInput in) throws IOException { this.stackTraces = in.readBoolean() @@ -59,6 +60,7 @@ public GetStackTracesResponse(StreamInput in) throws IOException { this.stackTraceEvents = in.readBoolean() ? in.readMap(StreamInput::readInt) : null; this.totalFrames = in.readInt(); this.samplingRate = in.readDouble(); + this.totalSamples = in.readLong(); } public GetStackTracesResponse( @@ -67,7 +69,8 @@ public GetStackTracesResponse( Map executables, Map stackTraceEvents, int totalFrames, - double samplingRate + double samplingRate, + long totalSamples ) { this.stackTraces = stackTraces; this.stackFrames = stackFrames; @@ -75,6 +78,7 @@ public GetStackTracesResponse( this.stackTraceEvents = stackTraceEvents; this.totalFrames = totalFrames; this.samplingRate = samplingRate; + this.totalSamples = totalSamples; } @Override @@ -115,6 +119,7 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeInt(totalFrames); out.writeDouble(samplingRate); + out.writeLong(totalSamples); } public Map getStackTraces() { @@ -141,6 +146,10 @@ public double getSamplingRate() { return samplingRate; } + public long getTotalSamples() { + return totalSamples; + } + @Override public Iterator toXContentChunked(ToXContent.Params params) { return Iterators.concat( @@ -151,7 +160,11 @@ public Iterator toXContentChunked(ToXContent.Params params optional("stack_trace_events", stackTraceEvents, ChunkedToXContentHelper::map), Iterators.single((b, p) -> b.field("total_frames", totalFrames)), Iterators.single((b, p) -> b.field("sampling_rate", samplingRate)), - // start and end are intentionally not written to the XContent representation because we only need them on the transport layer + // the following fields are intentionally not written to the XContent representation (only needed on the transport layer): + // + // * start + // * end + // * totalSamples ChunkedToXContentHelper.endObject() ); } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java index 5e85442151d0c..f26a6b1fb3a84 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphAction.java @@ -72,7 +72,11 @@ public void onFailure(Exception e) { } static GetFlamegraphResponse buildFlamegraph(GetStackTracesResponse response) { - FlamegraphBuilder builder = new FlamegraphBuilder(response.getTotalFrames(), response.getSamplingRate()); + FlamegraphBuilder builder = new FlamegraphBuilder( + response.getTotalSamples(), + response.getTotalFrames(), + response.getSamplingRate() + ); if (response.getTotalFrames() == 0) { return builder.build(); } @@ -131,6 +135,9 @@ static GetFlamegraphResponse buildFlamegraph(GetStackTracesResponse response) { private static class FlamegraphBuilder { private int currentNode = 0; private int size = 0; + private int selfCPU; + private int totalCPU; + private final long totalSamples; // Map: FrameGroupId -> NodeId private final List> edges; private final List fileIds; @@ -146,7 +153,7 @@ private static class FlamegraphBuilder { private final List countExclusive; private final double samplingRate; - FlamegraphBuilder(int frames, double samplingRate) { + FlamegraphBuilder(long totalSamples, int frames, double samplingRate) { // as the number of frames does not account for inline frames we slightly overprovision. int capacity = (int) (frames * 1.1d); this.edges = new ArrayList<>(capacity); @@ -161,6 +168,7 @@ private static class FlamegraphBuilder { this.sourceLines = new ArrayList<>(capacity); this.countInclusive = new ArrayList<>(capacity); this.countExclusive = new ArrayList<>(capacity); + this.totalSamples = totalSamples; // always insert root node int nodeId = this.addNode("", 0, false, "", 0, "", 0, "", 0, 0, null); this.setCurrentNode(nodeId); @@ -193,6 +201,7 @@ public int addNode( this.sourceFileNames.add(sourceFileName); this.sourceLines.add(sourceLine); this.countInclusive.add(samples); + this.totalCPU += samples; this.countExclusive.add(0); if (frameGroupId != null) { this.edges.get(currentNode).put(frameGroupId, node); @@ -216,11 +225,13 @@ public int getNodeId(String frameGroupId) { public void addSamplesInclusive(int nodeId, int sampleCount) { Integer priorSampleCount = this.countInclusive.get(nodeId); this.countInclusive.set(nodeId, priorSampleCount + sampleCount); + this.totalCPU += sampleCount; } public void addSamplesExclusive(int nodeId, int sampleCount) { Integer priorSampleCount = this.countExclusive.get(nodeId); this.countExclusive.set(nodeId, priorSampleCount + sampleCount); + this.selfCPU += sampleCount; } public GetFlamegraphResponse build() { @@ -238,7 +249,10 @@ public GetFlamegraphResponse build() { sourceFileNames, sourceLines, countInclusive, - countExclusive + countExclusive, + selfCPU, + totalCPU, + totalSamples ); } } diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java index 113b600f7702b..2a489fed7c2cd 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java @@ -206,6 +206,7 @@ private void searchEventGroupByStackTrace( stackTraceEvents.put(bucket.getKeyAsString(), finalCount); } } + responseBuilder.setTotalSamples(totalFinalCount); log.debug( "Found [{}] stacktrace events, resampled with sample rate [{}] to [{}] events ([{}] unique stack traces).", totalCount, @@ -500,6 +501,7 @@ private static class GetStackTracesResponseBuilder { private Map executables; private Map stackTraceEvents; private double samplingRate; + private long totalSamples; public void setStackTraces(Map stackTraces) { this.stackTraces = stackTraces; @@ -545,8 +547,20 @@ public void setSampleRate(double rate) { this.samplingRate = rate; } + public void setTotalSamples(long totalSamples) { + this.totalSamples = totalSamples; + } + public GetStackTracesResponse build() { - return new GetStackTracesResponse(stackTraces, stackFrames, executables, stackTraceEvents, totalFrames, samplingRate); + return new GetStackTracesResponse( + stackTraces, + stackFrames, + executables, + stackTraceEvents, + totalFrames, + samplingRate, + totalSamples + ); } } } diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/GetStackTracesResponseTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/GetStackTracesResponseTests.java index c468d88cec4a7..566c4d24fc088 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/GetStackTracesResponseTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/GetStackTracesResponseTests.java @@ -42,9 +42,10 @@ protected GetStackTracesResponse createTestInstance() { ) ); Map executables = randomNullable(Map.of("QCCDqjSg3bMK1C4YRK6Tiw", "libc.so.6")); - Map stackTraceEvents = randomNullable(Map.of(randomAlphaOfLength(12), randomIntBetween(1, 200))); + int totalSamples = randomIntBetween(1, 200); + Map stackTraceEvents = randomNullable(Map.of(randomAlphaOfLength(12), totalSamples)); - return new GetStackTracesResponse(stackTraces, stackFrames, executables, stackTraceEvents, totalFrames, 1.0); + return new GetStackTracesResponse(stackTraces, stackFrames, executables, stackTraceEvents, totalFrames, 1.0, totalSamples); } @Override diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/RestGetStackTracesActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/RestGetStackTracesActionTests.java index 59eb6889f02a1..4b6aef4f544f9 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/RestGetStackTracesActionTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/RestGetStackTracesActionTests.java @@ -48,7 +48,8 @@ public void testPrepareEmptyRequest() { Collections.emptyMap(), Collections.emptyMap(), 0, - 1.0 + 1.0, + 0 ); }); RestRequest profilingRequest = new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.POST) @@ -73,7 +74,8 @@ public void testPrepareParameterizedRequest() { Collections.emptyMap(), Collections.emptyMap(), 0, - 0.0 + 0.0, + 0 ); }); RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.POST) diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java index fb68418d224d5..7b3a572c918de 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/TransportGetFlamegraphActionTests.java @@ -48,7 +48,8 @@ public void testCreateFlamegraph() { Map.of("fr28zxcZ2UDasxYuu6dV-w", "containerd"), Map.of("2buqP1GpF-TXYmL4USW8gA", 1), 9, - 1.0d + 1.0d, + 1 ); GetFlamegraphResponse response = TransportGetFlamegraphAction.buildFlamegraph(stacktraces); assertNotNull(response); @@ -111,10 +112,14 @@ public void testCreateFlamegraph() { assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0), response.getFunctionOffsets()); assertEquals(List.of("", "", "", "", "", "", "", "", "", ""), response.getSourceFileNames()); assertEquals(List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0), response.getSourceLines()); + assertEquals(1, response.getSelfCPU()); + assertEquals(10, response.getTotalCPU()); + assertEquals(1L, response.getTotalSamples()); + } public void testCreateEmptyFlamegraphWithRootNode() { - GetStackTracesResponse stacktraces = new GetStackTracesResponse(Map.of(), Map.of(), Map.of(), Map.of(), 0, 1.0d); + GetStackTracesResponse stacktraces = new GetStackTracesResponse(Map.of(), Map.of(), Map.of(), Map.of(), 0, 1.0d, 0); GetFlamegraphResponse response = TransportGetFlamegraphAction.buildFlamegraph(stacktraces); assertNotNull(response); assertEquals(1, response.getSize()); @@ -131,5 +136,8 @@ public void testCreateEmptyFlamegraphWithRootNode() { assertEquals(List.of(0), response.getFunctionOffsets()); assertEquals(List.of(""), response.getSourceFileNames()); assertEquals(List.of(0), response.getSourceLines()); + assertEquals(0, response.getSelfCPU()); + assertEquals(0, response.getTotalCPU()); + assertEquals(0L, response.getTotalSamples()); } } From 2e9ef62f2ba26474fdc9cfc2b8f0c66d6398623f Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Thu, 19 Oct 2023 07:18:08 -0700 Subject: [PATCH 031/203] Verify driver thread context in responding thread (#101097) The test failed when the Driver completed too quickly, causing the verification process to occur in the test thread rather than the responding thread. This change ensures that the responding thread performs the verification by moving the path inside the listener. Closes #101095 --- .../compute/operator/DriverTests.java | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverTests.java index 8640f2c32133b..38ba64f78523e 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/DriverTests.java @@ -9,8 +9,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRunnable; -import org.elasticsearch.action.support.PlainActionFuture; -import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; @@ -34,12 +32,14 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.equalTo; public class DriverTests extends ESTestCase { - public void testThreadContext() { + public void testThreadContext() throws Exception { DriverContext driverContext = driverContext(); ThreadPool threadPool = threadPool(); try { @@ -58,27 +58,28 @@ public Page getOutput() { outPages.add(page); }), () -> {}); ThreadContext threadContext = threadPool.getThreadContext(); - SubscribableListener future = new SubscribableListener<>(); + CountDownLatch latch = new CountDownLatch(1); try (ThreadContext.StoredContext ignored = threadContext.stashContext()) { threadContext.putHeader("user", "user1"); - Driver.start(threadContext, threadPool.executor("esql"), driver, between(1, 1000), future); + Driver.start(threadContext, threadPool.executor("esql"), driver, between(1, 1000), ActionListener.running(() -> { + try { + assertRunningWithRegularUser(threadPool); + assertThat(outPages, equalTo(inPages)); + Map> actualResponseHeaders = new HashMap<>(); + for (Map.Entry> e : threadPool.getThreadContext().getResponseHeaders().entrySet()) { + actualResponseHeaders.put(e.getKey(), Sets.newHashSet(e.getValue())); + } + Map> expectedResponseHeaders = new HashMap<>(warning1.warnings); + for (Map.Entry> e : warning2.warnings.entrySet()) { + expectedResponseHeaders.merge(e.getKey(), e.getValue(), Sets::union); + } + assertThat(actualResponseHeaders, equalTo(expectedResponseHeaders)); + } finally { + latch.countDown(); + } + })); } - future.addListener(ActionListener.running(() -> { - assertRunningWithRegularUser(threadPool); - assertThat(outPages, equalTo(inPages)); - Map> actualResponseHeaders = new HashMap<>(); - for (Map.Entry> e : threadPool.getThreadContext().getResponseHeaders().entrySet()) { - actualResponseHeaders.put(e.getKey(), Sets.newHashSet(e.getValue())); - } - Map> expectedResponseHeaders = new HashMap<>(warning1.warnings); - for (Map.Entry> e : warning2.warnings.entrySet()) { - expectedResponseHeaders.merge(e.getKey(), e.getValue(), Sets::union); - } - assertThat(actualResponseHeaders, equalTo(expectedResponseHeaders)); - })); - PlainActionFuture completion = new PlainActionFuture<>(); - future.addListener(completion); - completion.actionGet(TimeValue.timeValueSeconds(30)); + assertTrue(latch.await(30, TimeUnit.SECONDS)); } finally { terminate(threadPool); } From cfa11d2df398c663f71441e0c8acc2b53c214fdf Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Thu, 19 Oct 2023 07:24:52 -0700 Subject: [PATCH 032/203] [DOCS] Adds enrich policies and data retention for Index Management (#100922) * [DOCS] Adds enrich policies and data retention for Index Management * Update docs/reference/indices/index-mgmt.asciidoc Co-authored-by: Abdon Pijpelink * Update docs/reference/indices/index-mgmt.asciidoc Co-authored-by: Abdon Pijpelink * Update docs/reference/indices/index-mgmt.asciidoc Co-authored-by: Abdon Pijpelink * Update docs/reference/indices/index-mgmt.asciidoc Co-authored-by: Abdon Pijpelink * Update docs/reference/indices/index-mgmt.asciidoc Co-authored-by: Abdon Pijpelink * [DOCS] Incorporates review comments --------- Co-authored-by: Abdon Pijpelink --- .../index-mgmt/management-data-stream.png | Bin 0 -> 751333 bytes .../index-mgmt/management-enrich-policies.png | Bin 0 -> 170474 bytes docs/reference/indices/index-mgmt.asciidoc | 111 ++++++++++-------- 3 files changed, 60 insertions(+), 51 deletions(-) create mode 100644 docs/reference/images/index-mgmt/management-data-stream.png create mode 100644 docs/reference/images/index-mgmt/management-enrich-policies.png diff --git a/docs/reference/images/index-mgmt/management-data-stream.png b/docs/reference/images/index-mgmt/management-data-stream.png new file mode 100644 index 0000000000000000000000000000000000000000..01534fdec2a2334848867d767acf9cb6845bde5e GIT binary patch literal 751333 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¯v1^*d49M$)p@`;-dvv4oAqATlW955z+_ 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%0A!=M zPX90hJcQ+0;!SuqE+Hv?>NUt=5ZqB;6nRD=WcWb+;x>#qaJj|fl#vX=uOZabpQkhQ zaCZ=VfH8g~U`9Za+_O)Ikd179ULvNx5nHZ|S|=K%FW#+2^uCX0wN+)(ch%=> z-!b-jUHqOiLv048eJkKxR9&`S)iN)Pkuc_K`dR$BG}U4PY<1g^;i3ye9tIB}LSs;) z!7jlJmed$^kH+DayX@9JHgc)OPMl%_lhu$4faeyD;<#HBH}?p=tKYw#LQFEyTi~*{ zmGqUK>Pf~AKFoRqxaLh-1bk3}WbJXtP0-k{888>_b!4-#8`Y+w;}$<#x~7#{qiz( zs9=4=Vm>a4tjh0$by8?D&UbM!F=M%EOA6q1 z6<3U%*Xmv0rH}o~r%n_e(-FbYG>nN2;c|+0^ZuP4h9Y`3mxZFGD7shwhI=5;rHWvV zLwCuddR3*m@jp4iZ+me;8Yt3DovjojE7taU)U!Uly23&yg_cGPYv!5IE22NLHc(st z`41z^fHy(g#|FGUH{SbhyHGLetoCMdPLFe{d5)nol=fs7%{N5X^C(m66KuUw>^`%# zftja*uB|wiK7zO1G|lS8#f50-1ZbPwB855sN;P7C3*bJA>#gB#QePrGwU6nK2)JSD z7@O?)ZYHjX-p9<1Gt}7vsrEOFd%@=j#+gMK<#s5`I_2b8Let~Pw5=`*0j@o~i1fxf z4NHWW-k4L7>F`|&I3I)_W*h3VZD1SxwTQ!Vu|Whv54@p~`35+adhlGKw> zh}P-HBeop5sFU)$ejJ1wEhQ6c{W}|EzAc8;S2yl`hVw=QpeHU?OZTzO=E(*hKL(&n zUhGRdnyPYl-)S=sU;EiniUk*sEB7`LqwZpO>E;7WIWVmMXE#ql;H1z{f*>3*uN9i$ z)%ae0pVN+8d!QaSQf}r7mB|VU*j}!=Lz|f_P7m*{mKpgaTSBFLP|zQkqSA|wDHal< zPpwv6kVL$=O6toi4+-kFLmI&KU!4 z{T^k7=Yofg$Fa7OqDb!kB86W?+7ZNvHcaHB6S=QZQ?!7Qy#n`L?QaO3k^?l8o6mu+ z1qeM|_jbN`q~+v%7k!v=bF_m)pOUQoxV*>_#8UfI4%*Gh1G)gZ9YJbd{Rg#P-K z12k*AZ&ot(JZwx?%S|&&Gw~4F{OU4g7p)6I3tkT&m)P*hxk>~ZDmPJIwz6qPTSUnU zMc<5f4+`LlQ~2|IFg4vhKJg%T8WM?4+3_KRq|1_#dDvp4K>&Qh#ouD$cDbQ{#o5zh z@TO;&Zu7eQvFAUuUT^nM2djh zcWZ_3`gJBfZV$7(`~7Kn3Yr+jFiK-;6umJGD5HLw)tvQ+i<-aB=ZUrEPM3}~wdQ1~y=|;ig)7g-LMUSb)|Bx5iW(C#7}dyh zI$kG}CtLpSHES(VaP%wmU!+J7nYQj4K9ij7i!Rv)y4LJgcGwU{S+@0e>9^{fRi(9f zXs5p})-MqaR;4D%jUlbpuMI=y+9>+&h;AO{Sj5MM zc_}k*@UKgcaQA(c3FA%f#!$w`=Aix0q`Uti*kNt#iytZ-CixH}-n4;a=EL%FSX5Xp z!XrVEhCHS%FobC3iOeTXj#O>rW@s#oZtFX%_*LmDo^tx`he9ET8{~v0LNnxA7drzN zhU+%+Gjj}&zI))s+5TgY08B$?1@Sex*iu?85{V-*?pV`ZGtQQmad!-3r$SuQ_fL^Fik6iIiNKfyzOt5oHbAA!M01^bL!8COg4Nt?N z?m++5o;SH#*I0jsmWooAEGOLYBv9e;9SL0_10Fy11BmvBM&s*YiL<5$4ikc_9x{&g z-pFc9=l2k&F;P|=^N=h%Lq0}VAE(TDnLC3K{^^iL`ug1fxsNP`KT}&GPF}ZmxSkfs zYlNkO&!TYP1(e=lqTc~O+{-84TgK(E~j~42mgy|s@)~~koS?Xc&MfB89 ziV!2%$0KZ3=LHN@3sV4OEe6i3y| z!aG}@0j19+rGM$DoskAz8M`0ghh`PkI$_?KsCj#8$v`p0AxcTn=%63%Xc)w9Lg4*}rOGq#!_YuMYjVmwuAo0`btQxZgd6|1#9Gr>dEFe=de?!!%m(^c4;w6s z7~oD^m|rp+Ptg29xljbFpTbmH;MPq}PhTHbz4`p}m}xi2!1@~uaVi6AaAgi!Sg|kf z+kbkv!SN*a$!u`vp7Wu!gTW$vcos6aVtEz2jA^!+61T;^;_|y5_XE6|)!G@*lxhCc zNs24(d1}qoKG<3X2Z85wzkU)_;c5jy)M9P`n5nm3EIvEGz84FBB+#p;M}LlGmN!m) zr|;*0(*B>=^)PGi$KR`MhaGh{?k8C4FCyny>RHg|=iYneD_>&~f%V2ITdv*EunjX?+RVs5D4u!t=r=3JaA8XeX-KTDJ{UG|L1bSmrtUbSGwzh18H9J*GB z8K#Poco@&!VyQ2~HO}mC^{~CwX)n)O!G-YH+Yv({T#nn_FIS&8wKXS^7Utg??sK#~ zY-dMF_mzqvCG&?(cKq6if;#tuI>}BBbWRm z-3yg_twFQ=?KkO3LLUupIQf_PFMmH-uzjST;<#l#fF)t?!&qLEhJCyGQf59$8YOKv z6Dm`X<+@ZAE@nkoE$Z}=k&&&f)f)pfAf=BKZRLR~G#yt2T3-#0dX}nhn5)RLHZ-9Q3Q!_1aa1yF;sl2AD@_W0S>t^-Fk3+{zv0Q7;jMk7ni8=4M#U(j7Bt2* z3+=ppmyn*&FYWuwo&3YI9QJ1Nd5s;1){#%}MDQQH&x~Q3+Xu|A-|S;hk@fGS)z%EJ z-~}OSY_v2rkJwQ2$zs^ry1%FzO1|e-Jfnr?eu%xlb;)D+!`+}AU}r~y1Up+l62MIV zKA7@l5ZDsT1xasPc?ySNN%ytbr7l!gQ;qh$YEaKlf2$73dmVodBx)swmHB6cf`FkefWnK$nRM^XWqGfMAB0nUH zluLY}t1t+~>4C6tTA@v6$8GUA0&8E)sShTZF zbW?|Ud9s_zz|>u-RXIszt8Z2B=#g!X`gEmg?fSfwhoq=7a+sZtkS1hnANQB zzV<92y6IJKi#H(MqWX{L3k_l# zl0F$A&CB*%l6{d^B5xUmU=6@9;_5$f@O5hzA8M9*O89|pl=sTY^f_L6-`%F{?JL61 zj-9pte7MUOBc7FD=Z%E#828UsPFkXoNX42I%TtLG->5aYo9y1$nmLgTi!a>%bkY>o zL&BR#E8kYh0LC+CN_a2FQHn@H#6c;37Ju$yw+V(4OD-#Po!Fkh=X2C)gK{VCMzdN! zB05g!60>dSJ{9j+SD4%%GNfL$8WA*SfByH|vG0=V>?i5OzL%(qc1z!1!5i$%6D++W@6UhfoI?8o*TM-mg?*%T^1o8de$Z8q3#Z46xQ{+CWQ+`TMT z=H2^fKa08CJNEnV`epB&+U4Gr$)*o#fu||~Ud6hycl)%b>B|uvlR?TZ-9M{6B{h@C zvoO}5CW;lEL^uy)ayg86JG*4+1$yri^o|E-o@Pt84)JzqKBjiS#W!Q9Ff(cTx_i`# zYWwKGeQvb#yJL$qV@wtrrT%Ws)l|;YOyzk6Yk1v9ODI`VgezA$@SOcR^m5`E<;~#X z?uSsv-;I;V+{@^{%-yOo;-zK8)%EY0hl6kUkEJnKNvrb*3l<-ex|rwq$|`d&XIAISBdx-VfctY{i02LRQGzwW6qhiL3au1_r%x(US!EJ2`&Q|Jgv=m09JdB z9^C)T@q&*08r+^wi9+gYx%)G!cS`Du^Van*@T!5>#()3X#fi=V9AjFc?Myk*CQQGn zw~Xg@oaO)NWA6|_CFHX)nNGWmLAGS;r_>~5qcWf$=X6>+Y#CFFO&1tX!0SSQWPFE0 z@(GFR<3vh5*Z8Poy*UyBoC>1odf5}XR-$W6w2bKt0Ul5uwahny?>E06O;6uPq`0;^ z5cOdFH7NTy_P%ZiDqE|o?9^jh`6T|FrM=9X3!)~|(%3kUqGAzBeR4i`6>%Z$cAXfB zVhyXqJ;o$1RVQ_}M+QF35dmi0nvW+vT>K}8%?biajQE>_ud3a^ug+7Lx+FsQ`bukX znI9}3E|(W?m;>8~AF;4~LmWSa)$1I^J6?s{_VNR7o8Yv61Wa}|PigP$E1`&n#=f-{Y6a9_}Ni#m{#3Wcj0zu7$_ z&+Strx-{J9pS=nHQG+X}3Nr#_pNTrNh@t^LyS`I~Fr3iK_&jH}FJlU!tz<E-s{l9RSFJ@{6{q_vgKV(_x4=ri4h_+n}3$YI$&O zK7Y(HZlU_jKqqp9#;9?zqKLb4oW|O>mYHPK%GJI!6>GK@WpNX|9j%Ch|3!mhaI% zfgMG4-nw+b3^w1t&Z*r?M%`Bh`iZSy@UaTj8@p);2J?C?S9CW|nR7F`pz4?2n#VNt zgFhuV#I!j(uI%qK&T1CFzI0cpBPZvEz89k);C*^cCU;vFa59#13VU%PFB&u7SXkK~ zcchsv5q2llkm*`~&81n}csI~mxf>?GHfgc(>xZ79)X%Dc&FO>Ey|{F1J)gN5kmJvU zQMDbTEuIPm7pi&P4MOh4#yTP~!?vkc+#9~YYN9@E#>sTOyuzKr_umk_2(9ypd(O2s z07S0x7B|7^cFebAg$`HIWub03s@~T5{?l76g=*Z(tIqI9w0HrRMQh+(t1~h3k#!O3 zG_i4z>WCNzVkhlthb4f+Mc=1B|IAN~L29Sgx%3(pxunFb5_4mRRD)9QBy&mhd@|H?gysgUP()?M-_c`s2 zs?mx&ee?n4pRAjtJa#UK&+uh6bGG$)78Vn78sY^+1~1GTuAl9(j}y;1e)Rks8dm16 zel_eWyP1xkAIdcxF{N2*VQD8*6nJg75YvT|zxB@pTJGZg zAuBK8Q5ga!0RVPBN7dyYtkMZ@eT(iB)Ks@PK;Gv8ygd7{uf>O2m=p?sXoza*Ql>k4STP;*?>>h zYxTAV5UJf&R%{K1B*XVMhOTVsqGAbRbx9-L*Ef0JOm1edw$48-9r1z#hKeCMFK(Ol zho-CQMPpgObBjDIQ9q^Ml9w)+H3mL;*5Z%JHH4K_d;HyhxtSw@L-1~mxhrsK=oS*2 z@`vd&Ee(tUyWPBv&YV<$i8;27lvFkk%qh2?J>zQ7Y56^owHAc#9$I(edzc<(-+WVS zNp=mDOOn%6!z<$#xICrxD4HNB^aA)T7lW1F)pq?LIZMR4F2g|47B+Mibcfi%c3LpLsa zm)_W+%l{gYxKNYHaJCx;O#OdY0NameX^(^n1@9fA;cF3qd0tMI`jkW??)_SrXPvWo zSQPu}b89yZz=+QQ;A}asg{o6NckJPJMRxF?iZ7NVle0>_M=+}uo5G!_Ri||~VJ`3k z6E43q&f|0?{}WT68J3`1n3Kznxc+HoriwwN#pg~Rx*hf=J;2A>vWDEuO!*5f$AP`~ zoV<3}_k8$qqXWht?#(n;LeC9Q^N8_C9no(H{At0h?{t!sH3C3+gw955a0}BWQq19J zPh0~GPAt6oQ3&%$#%cx(yvj+L-lEodFI9+7W&t5Z4GO*A6tgQ^R2hSrw}h&p4VC>x`hKTV z?~YO3A^z|sl9e{>x?=@%G|_lpd+(a|UX;&1C!)SaI%ofVM@BVMND`!+dA5^?nBGXx zk^;3Hsr5cWEpzyj_3%=`_0ajalJLO;TiP3z3!akuQV=@EDgy&>pa>soqN9Pk5iUJG zWy|2;ETPNzgs)sEC>*;XJnibH1S7b}_`tRD_EEOfqfD{RLM-Qx+oT7?1^@T}sLm^5 zylW9LF+u3G-|SZr9EqJo+Gu)vtr!^{a@m)-t2BM3Qj~X`E)r`T?Hv!8+t3V+v~A8=>Arcek0mPU zP-*{!HNJ_xr?PLTTBI{f*-QhFAp7pupjxZ#SHe_z{b{S(_07xJhp3i|;F|0PDo+!M zt?%O!-#BC5kSo6i3Rr%3arR(DBb)+){ZBMo;Au|u&Y?gHdh#!vzl4-uzgLp5+fkPM zc5q|hwcj!-kJWMcV?T4Zz~=&8R^d2Rgjy;pHxLiZk$k}@(lfDuO7&U#dhfeIyrepn z0?LF|01T(Kmr@MuT}IPnRw?xCU3bQFr77zt*G!y$_Xko=N}B|uFO_~7nW{46-fVF@35-gu?L{vcRG#g8`WdiV5dIJGcTLc z{d^g4G@ocLo#Ce;MgG?7nS6-BY%PFD@@JyYv8hu_e-t^7^}nwjy8fOs!Fd>A%(OdU;%5AL%ZN+;29%~k6bL;g^?qTgCw6$;H>^M(UFsMl;?$!)qDtFGLw+|S$=QK?-m?)waUwym(w&f?i}N9TO= zCx4jMQOEAKFp}^IyBHKfyf%B;d7gi{y73`OoR;~M@YxDDWbC{o#bmlI5`2rs!poeJ z0=I?T{BtP@PNDVIrb-(p|BYI_6NO6e#n#Z)QPof( zsf_RYqe31No7}EWM&OeEUb%Cc88IsX1&ULZ?# z!_rr!p9~a|)h_$^uWwdkZpcD1_jz6Z5yyq`}Y1oF7!J)49+s z7p+Dg?V1R77Bopjqf?IBThAP_mB5})gK(a$b)<_dEtu78U1lkvO>p7EUtYO~bWD2$ zh@u1+0|0PZxddL59upd|G5o_m^|pbFBj>J7J5n4t<6!T1AoBhh6TFSMZ?Qb&d;dS_ zw?c>ABCj9jhl-|(X?V=nYK_#MGiM_!JI}o{qXW+7o}`3)x9hu-KDaWLO7Fm{BH%_@ zmf0+AYX9GBD>KGHw{pt4pH;E{Yb`sb+cLzL6zP!DoIrGViKVsCKQZ%7LCF~7TQdo4 z4t(lb+wxT71G|TELehh)O8f4P$06+&^rmr*Mp?{(Tx;#iVS^+Qjq9uSm;Z;bw~lH$ z@c(~DH>gO%08vrtkZx2AQcyr(ln6-oV2qGdT0pv$P`XElut_6Gj!|QD3Vw}SWGklv1vsO0-g zHSA(Fq)UUwcM}QA6m>CAnMNo#xKc3liThbfZtuj_`4;RG2_by=j%2xs+eF)=9E8z6 zT5hogB6C5?=LbfkOxqHo=7!Bq2H1N^y+v46!Ges6Ze-{^;vE@sxucPAF+v>2_N zem1}_cj4V}Y68H6tyFw&C*qz<1pWqao5e+Rb5hVNb0

czuOu@T&B;p+i@?3Y4Gb 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

265Q?awi?xc7l5 zfdeEGdFu(ob0$BiAt2?yP6<}>u9JdA`*)HipU~*^&s;M`g|vX1K%gAxoqC$-K^HE7%vkq!gB=gNx&nzgoOlccZC98UPLC#aa z5NI;0Wyj*dBw@QYm_GR(H9N~nj0_zd!(JzbRI6La#qF;@>*ov1%w`zjJH2(3Cs9g% z@81p6IQK?gKb{I){zo~cCt7qW4o=RPv0)cY@H zLN>`VcNH>djD2_(>-z=Py~n@d_xs0V_Two%3tR%NQJBE9Q5kDW8`DRz0T@1b5IlT5 zRbCpuDsLWq3^`21ABU!uvlhu|SS!Rol%bs74ctQaiMPNGfyOt5x9E=^*W5)#%Bo-&SLmBt*E}tuEQ?8?)sfPZ?eP9#aoSCG+$|X{KFYIw8^qM+$=Wd{8-$}{ zS`!aGc~%G+>Tw@4M}?eOZVubhVzL=zk5lSL)3%FzaB1c`>_O)ypQCDK&ae|vROS7!mnj#|qocqEsI<@HQ?_k2 z9$)1xiqo(_HLY^aX3TT2N1A+AChm95?W%`Ui})&tl!lhCUH~_d{^Yqbc;%4u{cnW} z%9ER5>t|;150CbT289C=@&=8(f^@WgR2nGhn{-?BWrC-VTe27udIHiH650%K5^|M1rDsmlOHUjBIVf_&wJ|`O&5r@82vD53OVhb27(2Jqq4M%GGNJ243wi zf5fYxS&8X>SxKg#*-!N4S441^gAAF{*;eHn=aon4oa!7`vWQKo*hTMs=Tk`Fr^_~U zwD>&N{09Q-5H*HqE9a;b6^#c}0@5D+Z7PjavNc`0T(HQs$WlS8XC$4T z#f%i#VjoPgz%#U#pi!DT2?#~IP7#TIF>3@K=`O(ep^?7xoy}5)r4(J){XMc+3eLZY^NGD4#BfU|wq+o)>o3~egxwkpmHb|G*LvMD zJ44g7<#>Z!Om!ZP0esQ!(XLhv#5Wz$N=$Ej7k!;oWSKy@5gKvq*Vm>Yq|=f-c!0vC z-?U89wEY~sLlZJ@FlI2xruH~d^lh#9n@`DUzUE~2&i>Zwna$3G8U_evc2Kk$-0kW8 zSZH`EBxJXnEJIBeSValE{PyKV3ftt=AW*}89rzqsIiPY2d$puhW^^?n;@eKn*$Fvv z489xOiviSOW`igHdc@MC>3-kIr)wK@wdw2~rtNP^D+=8G?)alm;I(oD_HEY+G_$H9 zQ0wh=olO^mb{;V~FLd$em6@YAMfcrqi+ncyGUF2P;jQsE|M>)#wRuFh_?>09(A4BN z@xsd5iWTPDHeuU=CdOt@2u7JrOkN7cK6%AoB+c{ zj|9~nm`(dcWWs$LI)m@)RpS~xcTo#G-Ic$8YCMRO^^H63!7d{czNgl)E<&q zjC?!}(xQAxhst2q&^^Fi>(Mr#t<`x#MN{%_TgoxqNQhrfGt2g~lq=ne@{~Ntjoczm zB)3krJ^5~+KDooA)yXe_k<5HWJ{7061AK}(EOt6jX{l!Jvz4v&;k2&Qk zK&objQ`de8GWpY%`NN*r7;WeD+mTK7_1)b0`Ov0!#v&_v-fy*jMNUZ7Ux!S=wPa!Y zbG&s60wts+pKp! zw~%9Z{d|J7;y=UE$yz9bO@{UA^|nMGUy0o7b^m5ERjPi1@L}R(L6`8^mGEpi;__rDMKwO=+K*qkyBZ^7{3`VUFHb zJUTy{SzrFzF~8uAB($X}!Q(=AbAGa+x;_;$XB~oB?MtF8gUdk6>_17y?Kb`{?+IN# z=04`7qi|x$i;0SyQ7ocX4M9JPi~%U)L|?>>r(Ag}LRH{Qj->enx|{jpF11 zI>zYlHo+yH=LhWa*l~B4=8G=CceJ@mZs?O}_8>VqV4mEMw<}eaoZsn7?9x<;ML~3! z!&HArT2gtJNmX(kdxO8g{yJBq;oSkv$td=?TSlf|y|2gwiPL{KR*VZ_9M$ zPv#;%gCVuo~4^9@V^f2 z30vgWoqbpd7dxn9&|P?TU#GX3LY2dLE=A&G1V8dEU4g`I1$_hRRoz{C!HB{Fwt+=b zdhb9@_ruOQOw44HS|qhPMY8mh3X6e+VQttoPb#XQLT<_hA3tE2H*22}#LZ=Ma-YVsHhE@6T< zvjW%R^7n^8D?)Ftc*OUilCVSQ_OMm7tI zqK?w@-9TuKBqfw~+2ifoKOuI}n&-S(G1Xat9C2Yagt8aH9O6de!0LvRpQ(SBWC7cH z>8In}vi^tBC%!nvz}U4MUf!1^*J?dWX(N^!lNf`dc^StUJ7*5HxL;t2d&``wf{qpb zo7*+x?1N7q>usm5@&<279u=Q8+pLjYWY-+aMH|A)NeBH_Gb7HuXzSCw$%Wi7)I$uN zRS|R{e?$Tn(DNh?VyQh5t%i;aCC6u=$I4^&dpzr(qRO znTW%e%m4V{_CqG9Ov6s-4#S&c?w5sKmq8!) zbm{%Umv{AXfA=()S@Rm`8$JsSeN@S3@pBEumfCJ=k6$>_rQ#vm8M(Hg*eUCGw(X5Z zQ=oKthQV84F1W*oKgB$p`UwI<(HVfN(aezAX@F{jpm3LW7bT3|FJ$2G@QcjS4EkCr zDRoptq{cWk*IQFl^L<>I@z;wzH56(djkX@@yO=&kIPQ~Fc2`N}em%t4O=-yTG<;m- zUTBOuT6QHw{PShw{$Rt~E9aJGn%Fw(`sD-8283ukmY}rL*>v~#Va-$L#}kKDj;~*T zbA0);q~Z|=-w~l5Y#-n|{0@A44ZSlsXekYzZomYQWWdM^Qz^plP)Lr1n59a>*orma zr?g7zq-3?v83LxAGdenxqub;BgPwrLm70S2Sa|jW2S{G?nkLskR^0E?9vz}T5Tz9V zAAQ09-#G+~-Db+|oSycfR6f7?^Q!TM#M{2H(8ZV*hM@P^c)ndi2nAb&vCry6^ZoD& z4qb`JMFF~w`}g;ZO-vjBaW?P_@;aJZ&rwg+zSFV^_87J1NVZZ)wG(>CWaz$gzo5Ju zrnxz5cI#UC7R!0MrFaM^)PQ0jU@>HoD`L~#kqtsq#saW$ohETnzDDh`o1D(qs5?if<`jekhUJOpBe zB$nCZXqvB4Q@pGQqi$!Yzpx4Uy!h;@G=Uy z$5X_-aYhXuzuIFMf29{7On1M)*hPE2Rtux5;#)d8X?3=_IJ`u8=1Vr-H5Jx-Oy~}D z72c|a9YlnNMu@4Gy-MSs;#?07)Ga9dy>qQ!NdV>hR>D10;)@q*gPJV590>r(V5bo5~6RurQZvE86Qc{>Z?O6@Et8nxYiDj;N zYI%Y+nXLUL^1bf9P0`@Y0*Hk=ZFu}Vc4u!;7+l=-JQ3bp}dq`$;?I%UJOFaMEw|IP0F7Ug}FOZo3P~v z>Nbn(U9I=>#=2EHyf7IeJKSu3M!P@p)Axgx<6%69pC5vv%)7qpr+G*T1xU+m0Z;Gex2_O*SIkX8>b+l ze6G-YS`>5L)Wa{dpGCbM@y@H1lRK#f!sUr~md_U#%WO_Fe$bwpwC2ESe4%x!eXM@f zMt>dCW?CkAlwYigO?YbW^h(_ZC<&{lh=02XIQgf;O4)ZE#;TPM-$^bky$$fwb^F8| z&s(6gOj+{Ib6nKo?c(Tl)7&SWukNqd%71h^-Wh6X>D{$Zx+*l?4<41XU|n(g@I_2S zWKxTd$J2jdR`AZsmjh{JS*YBVWqB7*7cW_(^$Yx1@+`m0$K6cW6vdFCogx*s3y%Z~ zNJ9vBpECh=GnD)(#?xHGPE(?%kE!TZ{jZCPR=CDFkoz4_|8angrrx_z(t8C6NUzGe zs|M>D%e*+xgb5~rmA{%@eO4(5- zq&BZ`eX+?mtO7^lB%lhuJ~le~VJbjBi#`ZhHApE#(y#lhK1i*8`)~mwW1e_X;u=Fc zC(aJ5&a*4GNu*eLuLFv|b?5vwt`cjl}S6K>DU&W=_uS3QtX^@~jW%N`u{TOT5Pqd4uYo#VZroNI3<4 zm0kkmxG3?<0<$>0>prwL}99W^DjG6Kc9wTr$mQ;$}U)kmL?}8*sBG? zA7m824dRjFX0d}e(+&$&nr+)vAX}pQ(7YF2y z71vbZ_i+%@#Gp^5x=Q`RZ+LwD$t0*x-858Vj+#ckWerTM5zSPkPVXpJl|tZ;{QM$q zerd6xILofXzYlyBxm`4lJQ5il>Rab zlL%vakpg*5nUIr+%QTJf%q*Y(4)X)Le zEe*u?xoG#rvu@6a0d5W%XXazxJXxFC%!r7foMaznSG{%m&d`tq9k(vzI2882sVlDF z#4&j;nRbQFi{ricwyuytj|<9yL{^zAOKBe&>=$?)V9M}MDf&W*y6XhDQeopKop1^S zxjan0J4jn=-5k#I^r?=Ug4a&GQuDk=NtH*0l0{e-TUSXtLgj&K)TQJ@R_iYyna(#~ zlM?@WJY6hSQqD)&rwfg9S=wdYI7SP>_7SoC) zsmTkK8u}M1T7TzO8hDAIJQ%hpJ)*Z4mEm6fCY)=3{)Ez>!Mw7A&~n(_lZrmd(NAQK zkEu{OVrY(yx%&-tI$BD@tk!Lji~qj7+l;~)MZq6#^|yv0biOMJ?nAe@-90dRIHsvp z1;bMF%H1z#YdwrGOtabIIitTaOc6S;S?bUp*Yye5S_)Y0UmlxSKr{Rw#?Jex>F90K zAtChMJ5i(~NRR;1f+&bo1?fnnBfWPBK}4km=^aFhN=LdtKnMY;(tGb+dVllXncdl) zd3Wah3o?@*&gY!xx$gVg7X62D%;VYFaU!Y~Cwikg5Q_*?DrmJuo`<9Mji-z?Yko;C zp++HB92}WS{RIA9&(&}Dw>W0S01ek=T*J{}4teCr4196lt&RVu*F!2uzcWQ7>L+)^ zURaMAO8^oJ3{iKvMrVW$odtP&qvK#~A1AM9*tflS3?)csDJ%{tswL38?kd@JCnF99 z_kV19^vk=PT2m0}Sq353#9qisXg#>m0bH^WUtaiY@|O?d!kar;l+p*Z)&Z8X*X-A8 zMuLD#pIN_%tmNo`j!`~-?aYTC;wu_AO%omBV*ryCDxZHm0xgTz9Kgiceku3QmiBJX z&RLpKE7Ht`dV_wcX8q7lOr1kcvxMFJ+ckGw;&N&ozaF>rSO0{0k`;d;0TXuyC7H*Y zZm#?gjSKlLab7K$^U9M_{>P6WQUOSpNBOqGVq&$m&E>0%KQD`a+dNSD&qW>Ua#fz= zWIL_-xwYYV%TW&NJ)knodiloTtv|MEt&i=i-^sR&lm1R^?VVh)X>v17U0F>Bx_8cA zo_4`wAE>8u#2?`V8kKGY*?*wPgGdl3VL{w=Qgi-8qebQ7#V;0kp5`fKx7Frd*Kq|^ zH#hf{MpCfImj8+B{8<~pxR$WSeop(-YoPPG<(eP3FgN~iGRtuqIX$jR2K$8Kd_p~_ z9+Fdd6?-gj!3&dQ8_Cdzg++hPm>U`r<{a{WF#_JeyRjGQ-E8zO7_1U)ltDoxQ=Mxii9r$>!Mjt@(N81x$vzdlSw~vAyc&tGfWaj&``K8u(_8Y5b#b z<*S{|sSM$~63NVPn9D(G)`6rWrBcWeNpa)#;YCQwR?XYr;4#af^ZUn=R!SyJTYej{ zKGSZEBlHss%Ja1kU>3q+<_*P~J%uZW9JMv=&Xt0ofvk#|Ex>?6^P`h+@5eE?(3*Dc z$9+dm&9P-|M2*H>B1MPA6Es*mM_zB%Ih1&q|K2SHj?;4I=xixAwdp8bA?5NAO93Ol zD;CI)O`2NaDNZVTt^b~zq0Hek#h1SAafJ!4O_|bjdKmR(_t_^dalRMY*c({%P?V(O z@WSc(BloEquk_8bc8W)_k`bqKA^qE|>CW|vOL9)()T!V6@IjEhg@v*Ar0$4|kg|a=l=jU^dC9;1{Yz3pDhpBHFrLXY1yBhDb%vY;N&xU6_ zJ<0_$1HO_I+GYR;Y@@UNmt;Nqai)qnnl2ez#FM;<4L7k{n~XRAqxHs^K2V$+M+|YAHqc95*1#RB6fCdE4r_EL<}SqS*82XNV{xvkQDv|D9^y;MLJAi25_$; z>3-}6c-1akZuX{JJhDq9rK;%9xd{AF%@rAL%%o#KEU9k~alO&94M7?_gSh^il+x49T_tBGt8eV& z)i~4`het?orv)_fQrLi~Y~x<}0<&)%i1m39C~}mroJ1e+IzBW0vCejX$W2v%Hl~_d zi{|ScJB(!9{ZMiyC(`bH4LCK9;JgC&=dN66@}6+m*>7q^N4Abd=o-)(oge<_4ruxL zn>g-?fAUO=q|GnP=ZPyrX`b=Z06F>|KRhkmr#J*@O-DIgTHdr=@7?4oZd{E~Vy5HI zIwjFl5z@|t_5EbaH|^ElLOfTFU;gM3jr{u?4y-?%d9E}qoOH;xiK%gwar;?wL)EU? z_e)ktD%wlTYR7wp!rV+EU?M-g>K%H?Uer%-A=fxH*z@*%fr8JbXS!_2x8KL#ZU-VNp)bL`1r60d$Y%;Dfy|NaUv-Dga_XA% z0;5n#=`#~T)qO`m-QN|p?LKm70`_jXK=oQ*x{ zwV++?!~POsu@|fVc>PByYJK0J^9_>6C>dgC9b0MX3u%&_scardH0j7%ueg*~8{913@k@E#dL&23)Rc}kpa!{9e=v)m>_bi{YfQI^c3MW?BylQzNgXBhbNexzo5}8i+dFyVqz>}lC_d% zLb&?F^Q|sCKs6SlB>7=dR!ZtmNr?a>AHv>Dd!O8f-X!Up=hNwfL6k=v0LEB|M;N9LhG;E8qP!AIlo8R;Aza`Ey6Z?c?NlMk@+lz?PEp zkgTUm>Ar`rbhUcK)M2tbP<|F_2!4=S0hDIHoew4UuAgFjM+D)KJ|7&ckHUY}{z-iP zsU}EAill);%1mlU^Y0b19ND9*uwQ?7bHT7CJPbX&I$EwFnSJ0akj-0q8cGIDWJ;8? ztycD+A)`a9U-q4nFOP5D1(G4Sp@FnVyy-j`w&wP)Ij;k`u*Zq0NjV(lvZRbme)0E+ z`%`P7VZ@ZKxx1$EwFc8)b54jDvCdd+>>Ty03Yr@F~se({b8 zl>*-Z)lDMn=vrHvuc}?7ZCo;)0i zP^2MkX7Zw*?_5dZz4}iTzAfCjIpPYo!F&U*Vx&ZKBn6Tg@Ia6)~ zSqmte%yk`K=wEJ7RA$zQ4SW_KgAx54noCL>v<7SfBy!vh{>}A7&l<< z*X_+~r%x}45Q*ttX3*3n+r{j)?(7b&0oA5pv~?E6Nq)ZYDm+p4q2fSYnG9f`C>6?NbfUtt@wn;bhWvw(gzX0n*!to4LxP{{r#-^izKe~!N=>bR0NTQf%;Ag!>JZ-v zz}syRZw?Ic0Qroq?){)dP51Hjcz!K5>Gmn^tHXn+z%>Qyg%XA%u*j#KR&4p3_3=SV zeW}fWA23tE@^mpO+V+qy=zBwh4b`(r+v~)7$XZqEJI72D-XW8b&QNtf(9^K2=$^!J zHh_KQ+&_&#+p*Hi8N3rG!80H;y1Ex>UOD2@d|uEC?()?Lnhw8r#Znet@TL@5zn#*nAl%A=VA>H zO3#zP@fh+O+6E=T*3xVpx8?0CW8<1dMpo}R-iM{x=6HI0vjlb;w#%^;U@Kr3c>?_R9RQFMh5$b3;DbUl^Zi);YZFBU_HXe!$UCkV?hI%nZLt8Tc~z=39ckrvo@_5; z7yR>(d^F8a8Q8FzQ$k=cfvnYEy5o{sw^L&x*=JtecFZ~?y2;fexltA?$Rh>udHuQ+ z+xu{&c4D)esOWX*CZ16K?htO0cVd*U5Z?0JnC!H)w6w-Qp0u_q5C2Gx4mx8=ti1md ztl*IhGo9zp+dAVr11W{1iE}R9{O~k`-qq#zj0SpsG+PY7rQ^}LFfV@CyeswS?)q zH7-di_Q7bmI30I>ZL;GuYPH&kWu8SkTRggF0GH9M*0M%F0nPg7t@*hU5nBhV?7Xp@ z+BiM)b}av`fLI%hj#YS6uvPKRMr90FL7B)doPEVYXI@7xZL&O9`id* z4SF1-D(TW11Ccmls{fMRV^V%+o!`APLi+mS>MEPdH&FlY)4ahCU2`svEJY};R3*2mYC za1$pcPbghzwk5gtl`}>5ufD0zR1MYq^!K{w{V=idMSh^+s!d3i^7_DBsv zSkL&(GBg#^7IM`YGX7X1vPI-5faD&@Kxr-53ROo)AV>(2P7c>%%9WS* zFP~}xlSH;FbGDhE8*TF=x`2YMVnAgPJt;f4_;9m6;>{vGC62` zhR{21Gzea}Qm$4uzSU-66J$wP<(nuS*?|iMkBlXhtl)KYRr7$cC;5jP*`9#*a77+ zoa~q?VhlPMAR^?ggOMK+su+Dx(I1Y#_?twi;j6$@EXX0kR<*A^@NR7_4cDQVp84=J z`@ALncJF-c>i12$q#Jh-z8fa=N*%r%#+_9WHC>VkBvSyni+Sdw>eTgCZI@#*>1jx~ zSAN2GEejhcmv{GeU3D#$o!x@p3+oiDrAsjg>b{A4y}~AJ=iPoxDKzzZ4xRXowY7K1 zO;nyow=9SK=>9vbTR}E=tf5m&4ZjssA!BKDsBT=UakoB?sk$_G z+VtVhcJg5s^{IOeP72Vg#506~9!Cu$>Isr24-P8j;wcw(pzgw)GeJp84P`C^7dP8qaFjr9gow2A}Cu;H2L3G*gYHT6cz+GVmR-(2%anEBOpDCahc%}v)jo#! zq{4UUK=pW`*;~s6lG;NRm(xh@Pw=rrtp_tlMVdM?V)hBk>^M zyEewJW;w-{wIb4re@IutEE($Vb3!G&^{VQ1_OuncPNM~b(FmR9MKL8ewuJ++%)E6~ z-GsNZt%ti7P7$rAR=9pOZP$9C72ZhGj!%!RZTcC0C46x-(W0@4t!(unXVX_vO5E-N zSNgGblVsT_y+v3I$?M%8fa{5#aXx3^;tR#YS0JAFSM<*)Nn%7S&PWLFxilNi7h?U9 zP}EN=Rm&d20R`WEbo7#yI0pxZ`vmX+P=%ZTw2-)hS|sflquIRK+|0^Co@5`aSO%wO>6%Ohk6soe(!)+cw*ND+BIBBN}Yi1^V2 z%*bdFr!%;k+DAT<9C3`-RXc{-Yg8Yw_Vc#3<+?l+0eF2@N4OB$9%@}@{_(2I?jb-G zka9TUyiVKSN%SOe8X(7OGX3EQI$pOwtQ#s4r?@tBf&mSO^WeIhXJ&-zqJot&2knuR z04l3zq?2OUO^uU3A{&9R5SKt1BPEbK2Vi>Z>=TQ2^O8e9SZ ziBrEZT~P$Q>v@6n8X$)8m%oUR8OZdRvA<4~Vg?lEf&ik(++Q2>#egT!9rs+`f6QD# zdK2#8_h0C(a6}hG(%?ygCvCC(!VkSEFf5pl^(-fhO;)96w$`C*bB0e~&z4tKC$0+J z1M*ux$VnYT$oEZLT1vzho(kO~*19Of^)vl&G6uv?z}WM0<5bbPqkjmx|gW zlPpFZufV=pU`8A*T~2q)!d+RuC&x$uED_?R(NXrGNw9Rq_sw4=s)$>VS!T6J z7of*sw77b$Kny^T9Va3%?$xKtz0MuHtC?Bhh1-n0Ao+g#iu$5`Efnvv0Wkd2_!&N8 z;!QILuB)aB|KD8z0XR02exiX$v3JctZ$4hWw4jxMaZl>W#caTm`KU2D`k|$O5u8<* z`&w(D%upEKS6p)SB6xrt*c0ZJ!uscP^la+Q4~4+LWmqthE3T#U{Xg}8r_x2rN^byY zOKm$3EN@^s(|Lg7Hli0Lm99bZJnG4pRwC)+vfc(5`(e?NTl9~$SK5vQUTYIWl1T)g zhx(36>>hrx{UA|(od#*7wzgdtb|m>4?ARuxXNCr|Q`I^*sW1HqR7(^C)`#-2nn0n@ zUR9@*upO2u!D0!5X}^iTu;Q_D?{ICe-EEn%k_uKuaiMJSx;=f9NI@ZE3w9|(cICPK z_zm5LcQHmFi3)<^xq0p{b8i2X2c@tJ@%-9mA&sq)=aXkeXN_m_R!v}dA!VV}?}i4P z#gt93&_BYo%%m8{I_t1=-*u8lQFHLi3MxZgU58VPcrtI$W!sdPp?aG_^xG=l8;g0L zW5a;21qI(IlhZ?b>Z10rl0oK6T(_J-VAAV64msI~5N9!R^N4~2S zDf6=JE7QjVx2d;s_nIY$-G3+^aMBSxD5%`9|9C-i+-YuI+9v-<_JFsVH9#%S35%hQ zeNwH$d%aw*qWUxV$H8ZXr$kOL8wh_^Ty$*YL)ACI?{}LI{PAOPo9C@63`H$V$#+$+ zY_BxPcEVz#IW08PWvGLxPTN~T{#|N+FH1`8{q0iFAuwus@l&}4L6P4tu@`U~Fi>JPFzxSzPB6qYOP-`Nml2MmI(A}K?TM+{~T`&$r)zN6F= z6e_Y$cxVIinB2*X|E6Bmi$Ob9VfL@zQ3z6R`(qI9+s>Aj`!-~-#g`G{P7kt@xWlx& zeV=Aq>Jo|r@h6JdHPsv_=y5Q<0|0T>esy^m3;zvF-#@o~*FyQQa z#9bJT`LY@fkwSekQH^ z1_|NlrxtzSKE#V5^JD}s0Wr$OG`r(vPq;&A?;;<0p~yTPO96U=?LqjS4l~d( zqZ4(8|CQra1(BZ1r?xq~mF`vL&&@*Jio{$2N=#Htt)#wpKnUWjL1J5y7<`|mNEJ>C zjf9BtegVJPY-^JbWkWF!XYLu>@PQH15jn)I?$j3aPw=6T28c&6A5w*8F+%Yw7d(F^ zb87!GjaT{iG`NBsu$1Er{*xEi9G7?djod(30ORTJaJ` zp13y0A>JBsnGQMSN4XNz9}8_n=~*v}>JZt$ox$PGfck6RdN9p?>LCHntT1Z&x*I1i z^ChaZ-d@<;o^k5u zSPqN~R|}#C3v!ZB@8ZcyI|TMT7SVv$Yr8WQvrcn0qa8wq5AundM&;HOzHKsOOfC2~ zOqyxiHvZ{)M%&YbDJc_hH-ROQ&~%Mm^D+rhd@O~+1k-aFMs>d%aQvf%Opi(NUT1sL z>$;Ia@XQoJXp56_X?2si(GSL}&EX&8ONr+KZ@L-9Oa#y-Mum~&VBJEp*V@(UJlub5 zk5Xa#shC9D02}?WtdVQm>HVTz{<``CeZ*t0AK-J`hexIJ@)x8%^0Mq!v*7P}?H#m) zUe0PtrHvj#{Dm>|XY6OU{-Z?zL|cUtgR3)X@P=%)DeA zPGxkJSSqcdZn&r+@Y#5qENecu`=}u?)Y}wLw^>>GYy1_-s+O9Ts~VXj!B+tMNKlsk z1X=8l6V&go(@QNeC>_8?FW$9Y>yt*L$YcHfEB zR=2Ss#e(APhFjiNeDAfbEurlXtFK+7UwIpj9g-TRT<+6j;>yM_S|m8G#HMd!=heVH zK1Kn}a*=t5d|_*0ai#@fBs6O@L3L845Pc{-!XtZeO+Lvv0lt-HTw-P4g%ORY4*$bWP2n{~GazujK_90TuY$v3M-b?p0aCXeG_^OMU&HyKd z)tu}DMNuf^$l}~H5qKxG9EXXzC4NBo>_Z6QlcR&q?38}g9MBOJ1-PwooN#3C2BI&) zRUXQ}mHlV+!sP$_F))H*GG)D4>KP=apHN&bN?^mqN1~#&&)z~Nw8G}RE<%4 z6j7uJAI(1oHZ(T&)G#pKmxRPi6#?1-NjZ&HJyXJHkidN{?+?+RkoKm`A+!U?o+D)H zLFEXB5?~0z*Nhck-f`JgZ^=D7S>N1*nRWN|K_?bZ<9O+P*(Lz1!{kw|=D{x@QakW( zhGuVQ`24B1v8UzH3^^nKCFs`Q)uR@S3f8d35>5w;CpZ9rCyDNal6PuzNae(cv&!d) zfpCBTcWRg?NBm8E_%;0ocwV8Op$v9M!e^ozYTaXQkKDAl_<7;`X?`YwVb(Y-B=c>M z#QstkB^tM>8+08I&^>k=&{HzdI(KtY%jdD&7uX**otT&iQ#|~P@g_W38lt83Xxl@= zK|%AjSMBrnwbv85^}0lEBa&|*Blrt+U62RSNT;o}5?L08eklUoFniM*rkr!@{53-jbj=%zp&Rj2UdQjPqyL7` zl*o*YU#ro-c!4QV6dmIU1qC+N#R^VL$@p1U&;%S)2BZNFZYE$X7A=9S}?)zJY?0%4iN~^^{6(t}j!TQs5XOg%39CJZ5p9z?sfA#@5!^-+h~0W~%$WI2rK)#**X-X` zX$)UF3fOz?)t4s82-6yPZ56gM8%1y1pCQ>8$+a)CzSzepYfb*#Ylfqd-|63%Spj&E z9Y4f*^Otw8ac4SZ5y$Wv9UoQT^$gIo?tLXqX+YQ!^V}~rliCal5qrk*KwR8@X<^Ex#nRz!)PZA-`ilTJm(A<- zyt%lWy>hp#ikm~YGZe!U`?!R3ux#MPg@W~4AzzP3bBBZM;_G31N9*9x@$s+K=|tx& zAfkGnAs4|ZMQEowk@4y!GtxlIW3o?(VJGbqCij_q^iK3#zQl-=`+G zE@JF~`{$5a#n={)vpvgGP1S9G|Gbn*0dc{>QBkAp_T36`w{V2jw&0D3KPr2#Q#DX0 z5O%>5l&a}f9P~MweLdmOP}iD*m6NmL@^F<6Y6Lo7G||oGJEuYRnx^}`wy^lTK?aw& zj=K3n)LMk~-7Y-c+8<oaZL6j?TvH;3U?;$l0;bBoOcVO5iIRtM; zmwX9v6(<1aK+{8@ci?_bpwF2m03V z|JkcJXNA?%;m_7cr>Dt&HBM7CX??Hs013&dn$Yo!`8Q(_6ViLpw{24@Lc+Di?uk=h z^L`5ZbHn??YMA=ipZiyDum+$qEe>hH@|7oJ7c4ZVs+Ea15Qo#SH2Ot69!NNIbG>Ko z-f}S#lHw9URqouZ;}N)uIg33rk20t#Fz^@t0zbj>Iq8kn5$JGv?ip<{M+vVNx2_u3 zDi&Qn-{4V$x!3XUWu20JknLV2cS=Ha5r}bdOIp zNgV;dh}~Foi|eFErKu9Oo{+!Y-C@_wQJ&Z_zWk~R>@@ux8B0sSG_=OF+S72kTi-Qf zR^Qk0%%$Ks@K_Usij|%AvtHiaa`w2M0oD)27Z;mUY2e57o#g<##Kg`uH(?!^TJPWEQC(Q7hzTU+v;5NxNc-=0MfxiRk%| zA1HfEMfK)g z`;K<<(mro?mr;j=#3orqS*3qb%!nP+Q%v!X(mk(E1$)mm(YK&}y;x}EDZUrpKCJCx z1=YtaB;lVl@sqZCtlabbcdTgRHL=)6_0n&Y6A&G+v-zPiUU=$k(aJzCrI=#TZ1*VK z#wWj7Id$Oj)t*w9BbFB{G*tt;vusn)pb<)B|BY2tl2lA`eq4_PFI3ht1J(m&(d5-%sv5gp(dAcr$G69+%j zwL~)tn?K-W{p!alUoII8LV{pSnAM=@HBl8!^Ct9 zx1tSzO(_8R)pvxGhFCyMxt!cmfVCM()a6Tp>roNL@B;{dWz*c_^A(+3)eWF}y05z% zt@_n06n|2#V_sV;%CbAY<3aTL;mBAJxs@ZIdD9F`Bt|7p4y6=tfcNLs_YddDPQFxU z{AoQu(?U)Yu7p1GW$(sMN-;96v`2*S#aPAjaj1ZKd2#*Pa-rAM@jgH9D-lP3lia(u z)2_SZa%Q@^vOe;*11f56AFUM{b<4#GjubbJH>~(}wzi~9l2haCTlYp7f6N2{pVg2K z7v2mPY|0_K35Gy$RSZf5{3q_Z5OPX(7>7*O%8(Ys%%_}(SZ#0VC&^9{DjpOncKBRp zg^C?FFa<9XSYMt%q+DBPx@m|RdWDC120!e^TN4BWSdjFkYD`+opd)~z5}$IQ!4b17 zYTFf%?noDS8x%VIR(aC7lgj!p!;=PMH2C-qIcM;_n|nKd=jMDktytK|0yS>lH!twV z-;bsE7)VUR!0MW5_wa41*G4kyXWnMso08D!&sCu*s3)VvW|lNGH_`y|xnuWc$Tge` zUUJs=^Ukr2#$C$?JH0j>Ii6H_E3Se0HyD5j0CJ@pXYi$mqx`@gWH6hYh$H)XGx!t2 z&U`W}1E#K!d`0}UOZQ+$@M`L^Y&`MjPtWsCCGMr0g@YA33{l)2cKQ8B@1j3GN%*L< z17smwU$NeB>NKS{x!iBeApM4#O4U>UbV-TI z*xcNt{JAPOuM<=4U(;!A;=MclX27u_kc*vI&*x%R7Fd!>4{RY|7WcM(8%6@#UWnkT zlz}2S#t4gfc%=HDjXJJ2_I&gqJ+it2v3$Kh0SEpf{@U?t3i|8qHDKb7gXbDk?^cER z-sTO)$`KE6ohkGa*R@YkvgooZv!3n~AV>Vc-zM=&;rvw%H{^^EJd9jSzu*9>D z3Ge`qRaC+xAy?xyC$OQ@zCO`_MzRTgBe90dtVYe|sFI_t7eT~x-=06}Dj>NscVzHt zI-L!0-0Ak5h3|@7%7^5Yx~Pv1Gs|k3*}2TVd!=9V7wa5ckh!5at~t8EHeAMX8LBLU zmSk5c_X+GJZzu;2!0|a3yoawIvtxG-bZw$gHK01QTs$ zec!F(N(=kZ0hPKTbxCOQpS*0}N+9&L!CTWde5}MmxO^w!I*tCpYOeV&!BC3ZanNM) zISIQBtL=X6#PILx4HvaEms}tA;jxn2w;ItRv4%m&Z(-5Fp;{Tqd(9>jH*>*ToK6}P zR}%o|=!W}jVO=S){hew1Y5>b)T7V^N>ZK}ni`y=+K|7u2m-e$cCDAap-#Amvxwg@q zM%Gle%X`Y3ouMHI7Y=1pQ$&swLxz~^u@V!9lTR80XM8s7E4mM|vi@SZR^LTYGK;+) zWs6_tO}XsiZ`sQlkTxy7X~#lA^&@ghcHy$kwXv7)dMokt?|);yTQuymhUADmnRJeZ{0J*j+tMyMFkTSj z3!S!9|0WwGKmIPGv}UA$%_O`3bJ+qWe+W)RE>i=0i&^(_{hlJ8B>z5M) z-XUlH)0t*+Wv!IdVUF6y{2{b665-R0C9V$@1Be?wTeMyZReA3kq4^Y4I0{s_$eO{|1RAEmr3 z$1OAF|vg6*w9v3x=-PmS_rtk6aKfg!!D=Wc6U^v~ub>F3X>|*}K8tVR- z;x$hQ7p{a4hQ8DRxYr8toDNecB|WV5=Vh(GWn0b)@#|ou04fB3pV*NtqNo{r1U{e= zy8QPQ%1B;hlvddD985OK@QgU#v<1rHb48z`#adBAdJ2hy0^Y_?wC} zhl`EwA#M5>jrMP-I)1ZljoLqdGTa*p-RG);nj&e7#q-yGIBol=YSlJ+Bu~!hlsz|Z z`~&uVrLyY9*d6oJz8WQ`^yM^2uSS3a=I_pnJ+gc=$8t3E) z9K5g&<>Jz1a?7XAPGx)9!lIu^nAoXFviw?L-=J*nzM$t*uzmM3hZPNZX(^)crn3Z} zB2ydyfAR%d^YOE64s%PVYYfrrV@HSufAi>){nZfmg%$u_Btu?6+$-s*)bx*rSd&Gi zn?27h3@VEU5=(SWJgW_gkpb{sw$SO2)IT^*o)5C#WN6v3#HU_aQ1@g8`XhZCnMs}e z>VRoUAI&=Q>K$PHmf7`)ezKhW@?VS2nV4S2-BOe8#9XOA`L<-sy55Fdj)4~4nT>yU z`%Im^M($si>ZjHRJXP%1h?ZTc@Jh8;6P1wc0;Caw-OEXJnWQC#(Bcbj{y%u2C4Cz(2SM>d+QhkhvNd(Xwg~nQIFIpU5=qJ9hUgx4gD+=ug zTReL0{wg~=)wOD93H6asMflG#kytv+t`zW4o|q0(aQC7RTG{DVqQskHmFyR znFd)lW<^AqqaZq@oJrp_;Ci3QZdKlg*LUfEQFs5@^%#wCF34ftHreOA+-DvrTfLT$ zUSPkO69d;viYgQ#_%L>Ou0qMF^Wp4guR8o}6<`J3q}!gs$HF3?5acBrzn`%`R-I=D zwp*>#6dRDyNjM*5lC=_N*r3DTau z7=ma}QkT|5yrjyZ3)a35JnHw-QWDOU+C89+`V|Irb9n18?SM)$Uo7GFPzclbIMML% zm>4qp;YuKZHiDWIOLdcziyU`e7}0ZTR>HYWgO4+!XK>S`uHv29K%f zxP=G{-%zAp;sVuX(DGhHS}X>ukFX@o+K9AbC#(5(gZ5jH2*F>_z-1E@>8=TNUt3;& z1dRkb12_p}FF{3T^R3EBCDiCkW6L}x148WH3+${}<8CbsZUfh~%HR=}GFsEhR2n+UZB_*(8URx#ag#$Mf0ThI&2dW@^q3R)?M?8m8 z1W)$OLeIMbKkgCDzbhkx0}R75(0ehQR^O@TCE=jSbOnMBMD4&2BpA3p0zh{tgC{at z(Ff6wbXch4?%!D5qZ4O!VJ`RRqX}`9so%!3yoG>`LwR>^AMFEpkG*^&;<~9wc ze*f+1};(%^bBhY;qvAE{T2WK|nl_#bZ4_==@~<26$u7?J_gbLp5fULK*#V@VLs{9x^#)sOgUMcHITH?Y=hy zgmV|Xt|sg5f?zJ?#(W0B_y~0-z+to#atHU7R!zWQadVI;=$(yaJHCF%Q3S3%?n$0N zN%E1CsOMwieeM?_erl<$sgd4Z-l@^XcV`HM3aJs1&P=Y)uL(Yk<67nR(c2oaj?74^ z2R!NM^|HjN8Ze|5sYC6~GsdO#92XY-_?PD5iL7&SET6CHp%Kg&@L;H=%=DZ08ez3t z&@9`++B=^auR@rL{QBL&5770oDL(6MP6+F~Fmbw#uLscbd+*f4*LM}Eurk|!2%;_T zPi71k`Q6_M71I+8*uUNjnzI6Bs7#qYKwn0+YDV$*{K+0Avc;OQC+f{jOdy=eaM97h zMlcb5cZ(|e2S(wOG=|LR^zG&zpRNnVdx-AJmqK|0&=ppv-_Z!(GV^u9hW1~{el7hL zUH$2`j^0+QPgL(#+OJbhWMl{J%E&UWM|opU>x>>q9nae?Yuh_I{>AP@){t(uchURL z#@AH5JJ=XhZLv(R!-sWXJ%5tLNOgV{dBxBZSxQPurfC5&*N2L?RZd@ROb@*ZH*d>` z8!&8GvET#pA&(2d4Q)D&9r-qIIsp)EV)_0?HQ=v{(JkQ9y>wBGs3n~jsMv}F&~{y{n6iAA201O3O3BY7vZaIxDjrJ^RDQHn%j)xulYM2~ zKy{>32EhMK;`-C!*tcw3YjA4*#1+<<#H%)%h%?h!ZIEsX*Is{=EXnAwyth%p`&S#g z;K4>bWmQ`we7&x!eXFx`nvzrNIR#l!dJ#xbS@C%%u{o@)qwb@bVWW3+z-EMYs<{^r zuJ=9cLVkXGDK>uRGB|WvNyXW-vY`Qc_1mG_-E>Z;>^$K}?}YF{T5sz*n}cj975VO{ z4@#udax_WSXZJz6(tGzD6X=eGcHhPXzAs6$c1p&Y zEQ#kR8rg>3?Dd2gl_P5rgyn+4W~iI!U;>+@q|)*&!1t;_GR|cZY$TljeBw4MBxHsf zzk*P#N(Q$q&5c7Qm+%Xf$q3j-=&T(NV2Q_xHgHT~yFtlZJ1xwps-%BtZ}5K3>q+-) zEUM}9r2C|jD+LiO!p>Me6_vXlJ+=_`Z)d^~b6T#x&g_yoFLwomC4Gq~PW)?YD9n2h z?sOLC)ag=(e=B+JNS-|gv7yu2B+H|x zy1FZc-L@n5S^J9XQA|}PTlf8V(&lPX`opu4l?q>IhN^d!p}GGmEl)R?CYY+9P`aPvqZ?tp zQ@vMJz*R-3rf82Mk(JAS75H&?qmG#zG3gDZgr9LECdSB8d&a9rs0 z-cUwH0vvv0)f{ljJ(F$S&*DHtoVsV-DAy~<5;;(9@{^V%B2FfScsEr!Fa9SF{&6^- z`T4V>tK=d4C{lbL7YA4_5I2+vf!}C6WUH4wW&O z48|!LIXNo@;&dMs$=Uy6?5)F^eBAiojnOSgODLtJfDENY1!pJJp-#Pz1*R_APC-&U;{eIuC_nY9v-$&$Ry);Dp zoZ^~dH&-#}>a#3eFC+b$7?DW?0}g-S-;T&|NFBHt_C*t|;O z6O4ZxLiU?%rD1V_n2GT)B3$3fpKk2wL;C*DM~eL;YLC2@Y!0SaH|tf0d@i?b%rS!1 z-S_U3QgUPOMYrHiGF|@KAX}3f5nNeE+TU z!`Ip7uDs5F#NH<1;aQ<4ak6W$(Xk9X$fF)zW4H;#_3WAdJsJHB?OzGWj(4^sx;LrJ zrTXA z68E^MfQbCT8YO8~(uNLEX*o_2?Y4O&(1tZ)<2R+a@NsAqvK zmRE4q25HgaGId`oXbKArSXB}gG&Dkb1^|}>1U->Zuteb=dC)ATNY zG*4|Ee%$Xgc-HKU2H|+RIkUR)7rCag?J$doSksy}{dvDzK(;(uf&c zWlS^#a~KsD>e))<{>Pu7S*e85VJIBDiR0O!N;KjOH}29liy(k!;=n zzD0j2_wE{-DxO8Rbx-szBMl$B{O&&qWuOx$De{|-s@QRhMc1h!o4j7|zBgOoQIM#7 zvWV|nQTR(RmD*c9;KR_p&NYcbwbRf>AU8>8vJ*G0cwCpCZwMCLN|iKJcMbVPj8S+9f79Fg>kCmmgQt$F z(k(V=&zyx=)6me6=CPK#QSixeNQCLquDdLcDeM7YLsnW~pUq3l*>2{T=X9JE6ZHep zquKrjf!tY76_if?GVJJ*@vM-St`(Ot_lpxq_~^T1ESDu!pA&N*ee|!Zgz1>bwLqz$ zM9|98Y_zbPCbFYX5IAQ-3zcXG(gI;7dW8q#_d8&?k&<6abyG2~+I2((nzZKz7+9te zu)gcu?+oGAKnL;dhHG{5znNQ{M{LT8`b)K*(&|d3(Z8`QpT7`?`5PbuK^sDe;q*L% zi!Q(tPno;xdX zUl@;@cLXzD5e*1^bi>c+r=>5Hm-qV)xtfG7KGSZx9V;Et3A$ZKp73)$#J*jo-B})I$uV>f;A~Vv9O#H+1DbVJ*BzmgXYT~miS=GbO<`CE z91%V|Ff}C0_~@a~uDa#GF~g^kGadhX&%oo0!7OYPoy0ZC{5D+*EXY4B8d{G2r1D*s zVv5RFXo31?6YWZW?Q(uv#hVXT4ATf?hWt4)j4-Ow{=lv-*!8CMa);({-$jad<@B1_ z}!%t=6{Sm6Jn~&EGrZ11+Om_Q)T*o;& zU0qVncGE3?UIbuGP8#2Cvb(9<%G_QCX3_3T`T8eNpaz6SXPG-i+vT|68_v<_&1UcH zV^;rF22}_HOh(ACx@y=L?6v^yl;V>Mj4l5SEp@f7a-;M`wZCWq(5s(nj73 zuZ*$-Bp^iADArINCk&MV9d%2RqP)Z}^s8T?4*Y5uZJg=X^h2=SYg{f?f9Fb}GUV|{ zo|yJ-PN|^odk!AGrr#=k54*-Ci3#dYm0WVgQ0^`lk{&=+JE+`t@7ZGi=lt4h_KU@j8ym!Hs zMLK2aHnv0Mh?MzXfaQxXHQJGFqpb2tW| zO03_-{0?Vrh#&Ed5Wz_Yq@t9U0GNkm=xG2GDZeOJ2!NWVC+_~n;5{*X{zpsd89Icq zK^lN7>YLbq8uxBNz6uw2F3FlpnHdRvq!UvEgoYgjA0`nW6+Kew5Uu`HwU03`AoQV3 zq|1S)--}7;gKlMzxe{Tw(Wf1DciA!ksf;>gseEVZy_y~DI@M7ml&W|0mcp#N)DDWO zeyld%uGGxS?4%zZAbGO>nt~)gx-ispvkxZVJ&^_aIwQ>t$qD|W${`ry)Z1~O zKL+w za&*}h8JcoIH)5Nudkb@=fNsfwpi%50m?XajQDXMMejo7*DPW3KUA2PVg+_)h41Nwc z`cHau;~rTH3EW@jL0MnQ2?3_tK{&H$tkK6)M*b~~O1Rs_@j;}-^{G?lq6Hb6tUq$S zAv$J6x8jfXOp3WfxtCq4d%*5iF)_E|b^Je8$-|K6nNR)P&KsZlhKXT8&z44C9>^N#8Y2Ah25m%B(o&YunpH|-0jko-JbR$d z>P_oQKA+UkmFDEx8kTs-YNGV>U>I`TlG!XHnnR9y#h_F{B( zP&8gSI1Ak`Eq^fUmG?z`;yO2oXYD~~+rVX;t#8Q1w@w<8ye)#ggt|{Cm*`RHtp33( zvLvS4a4jbGr0;$OB2iwEH3fK@`@A3PO%*J3+oTKe$Jz(C_v5mR$#*S84@eknmy_nl zHp-p%IBRv#`TU?+O1?#T*EbI8aKdX-lvjP%rTvQQ2g5eW2Vq<1qKUi31AZlyKI?_j z4l}Ww-8or2a7>#W5jN6wP=;GU38g)p*jsYEz_`f0z}z_ODYssiRw~@^o-FtW^B8|7 zB@vJ(zYA(`x4179TWh;)pC~Ju5$u>a-0cnRi#3FTdZoxgg{I>IA%4$@LU<4YjcU$g z>~rs}eYfh^j&Aqs&fmi?c;IIo_X2+hAp+f6=sOTfIJGk3YUnT3mEn`t4(SV7p5~}tmW6R zJrbC9@n+Nd4DTtf4ZCwec(9H)&tVbl>-to69TV=gfF<9_ zmi}?cJSuZ@1<=|^y#Hnz1A#Lw3Zx)nmO&KsCe=`fQtsw2BIQfK2+J>E%_#<6c`qw%_l2=4xU&dcorT_SYQudv!({o}p1@$QC-!k)Az_1yXfd8<3e7^Jw~XYaD-p3J&vL9Q=|+1}!dzP}3l{=jZ2H zR;=!{p`O6gP8E!c$wWx%&~9ed%xiXioD+6~!h2`-WXe6@#UgD{7^ozzz5NmRopnmR znU+@v1-MLX2@!aV%sdgk*-AM-nlerwSjC6RKdpOF`;J7R@KPg+bG zw4q|LKiHQ0;zWtJjoY~=_@U9XXdq=eDK_L^Ahs5u)&-!elW7q;4=L#gSeqfKoV}eR zC^|;&3zSkF_w4a9j%S%`mgJZi#r6L!+htpqT@`_q&8|=12Hf!0hAjGrNOhff0w0q8 zVcw1!aQvP9Dllen*5=TAS*Xczx7o`WYjkz{S5!>fJqwE}?3`Ks7T4X!htb!Tkf*u! z#qx4(PcpRvY|+F4s0lz%%*F0Yd%EL~w!vIsk2I8P;@#F6vOS;1$g*gHuNNq52gN!syr2cBEI zmEGIg+jjk+TDIvw_4M`XZ4b#T2&=>!vvnS3VZCA(Far3kB;Ca$t~6z5wWK>>O(-Db zz$NL+d1OOaZH(}4-4jqh-EBvI$C2y|I*R-Ay=B~j|6YWO z4@h*w&BU^ z(Ox0=-Z`arCTt^k|AYUABiOC`Yl83ZuE~Ya?i;X2P!`plN}p%obHHIeNEK^R1zvZk z?F<8_MQG11FF;NF7V*5r*9AG+VY6v{zh}lyv=%D!5Oq0{$le2r;C+gdjydN`?Adu% zD2Am}zdYNu_)r>cN<|n)Tt14ov*SM}JyP4|m>o8KAH3UfdF<0LV|FrOb{n!UtcRV{ zpQ1T?FxI{&Z~T(1d_2y49ML@Gaey_dyqSwGGqv_tYG1aP)NDYJ(HpOryuxl77rvXhqu6fr3yEt(##!7D+gRx3mZOWu)Im1*JodVlToBBj&@@G^ zQC+SKeDOw(vIi9 z;J&(@f~l^JEc)51cWrQ|XZQPcJI;(x2MhJGj-b?*4R%iNWYt$@!ZC236M}fsRVy^=ajLZ+``<4Djj7 z%yeg;ouiKl;gR%p%oQ`6+%ggv8mQkwvCc69jCMH^nOi>pe^>zTjy|OP$s(-I`nsZJ zI|8MU-4h}BbBSjA>_!&ivwzv>EWZ)KzA`dgWLj=!Wk+Ijl`)KVZ9TY9#HN8ft?^H3 zuc6?kDLK@S8}8lI5Vh^NB2_0`ZVO+hSLqPV=Lm~aT@6EOK1#T zDfHYW0^5FEKyZdy_i0ZJzahdlTkO7c--e2wu(4%|MmqOsi!zV4GIH$WFMjeum2=#7 z`7LX{on~P#-^H+WgLlqWR+PHG?^I6b_uh}7IPr9z}>)9<&T^!x*KhTjpIauD29o&sYD zsv0E!$AbXA5c>X{aLuSr;+r#dG1Um*s(lSY34Q|(vcBQAQlMRFFbLRudr(V;666sN z(vohViez0Ok9tY2b)n5_PFX*_ldMSQ0kOabM%>zGrhHLzpx`@F^CLVsDSZQnt3;$c zcYErZ(%vO8D^6v?_I2X4D!2`Z63>03)4*6MpXg-7)WiB+yt+(Cjh+rFQ>6Ue`;oCK z7`@j}Q~hx_dE=;jws0vmeA!#d?9a%DyB7X|dQ4NBC=4XA>tFLVuE_b#$5gn>&B&f* zBboIqnca;`0wxr>(*8NYS)o1$3M7=i&>YjcXxNP}|G;c;qTVqS{I};iieb9gBk6H` z_^Sj3jiUsCsKYdBVq{rI0B-m2%#c_B)v~=#P{`|c0HPd6)7cF??L}Z=OQ1a=8X#!z z8-ip7H|tYK(kZ&&-_eSM2I}e@H+}p;t1G+g%<7p&R`Q>c`(?WHU8+AhEwO0@L*73~ zPAh9fBj)D)@#xRyHFm(XUX9?w0&K64l<2^OTtsx3=Ej|2c6rA4`>IcvANst++BqC< z%dbFk&+dfMD@rlqrRD?Uc z;<2CDWF;uCs(;4i&)6e|GR&CoRBBMRz5y^OtJdfG? zz(km{+J|h)@_>7M_+sgs!^-{-Y9g?r%;P2@^QY<~H?Z1%ltx@X&LXbH@#dgL*f_J1 z)HXPHy8*@r*vkOC5Yg9-xz?jMlk@geZG7ch?-G3aZL_*Igy(nR?x=F(cg6E#1>}Ri zKMW-%Y-hgqom*1;YE7aX-C6<{(;1?P%idPyub4k-miqqjQj%s!(!^| z-xVAI$BvzF>HTy`0x#FMT1)-{zD37jRi3+qWww&e0Z0r!$Ex6yEJ#Z|5Bbl zo31euVWPR`l<@u=MfcX;wZB zvgZ6}xvxsCLXDVwRBv*}RrubQ7Pq(5wjSYP0rUPtQ#qQw!XeWgZr4hqk9|)68U-RU zPf442AKwSy2WnvRxki3i!&vSo?A18c@Fvmzz||Glwzx+UhLiWJ!X%wfwuGo@72R;H z5Lz(Vc>}ytrYX{2cS*k}A_Xg2ySH1y<5{uc(OR@^WL|VM18wgZz`~ z<>#eSPp&~!J6eZav{&yt?+$1xbJhtT6h|)Y1y+_QQdg!L1ReCG4K9ax86B)T3giS! z8XO8QCkblz>9d=b#r6o0x6fE}PG7LGmEEtT6Zli5^HPoF(ee3cu*AT8&%xZDF`qUu zAY!J;-R>mn87Mm7ZiN2iQfD?(sa4~!@ZqM_72T&lYnC4f#Iy<$CDIl84ZLvhp|B=r zaEVF8im>r+x%*37GCft8vEYVtr8&E0L`BXcWQccZ!e+u}pHgSCURL2^fMa!O`G)`H zae7@3`>KAU>l`P9{k??E&p!nv61mTPxEL5!f@umjnE8H*7ItJixxtmuz@GT!o6YfF zAzar@aNWJeZt44Ki8ojfs_T}x1(Ft?Zl;^P#sU9jeRj!4LNDnmlJdbBXKa_$->Wo0 z1)zQWA;c9teR8JruJ@giqq`eJ{A%~-xX-fa2icD9)*KmTA_dC-{7hI`#7*@z<=N^t7 zufn@umShGZzY|*s-BsC9I={TQc91KZT?A@cT@}97%XBCbTJu3!^j+XuhGQ9YBiu9s zL`1NZ4)ADM!z+UK!-9)h=0-`WbL1VIQluDx@6{xp{jmgp{uM&SEeI@8+TWHJYY4m#zZ}5a^cYD}VD@=yrIkmX&5+nR2h7?t z$J2F+z;3>WV*H)`jh9ZIaD5}Drl-XyK>=-YY(dK-a*>Uo^teMR`kL`;N%GgSzkNup zR^_JIl!($ekFOmk)gp|&rGwG&q|TXmUv@Xqcw#Kw?%X4}8Bgzkyk z`t^>2Kh-Fza<@u3Q&uW>p$~z5pTHaTIxcnJf z-KuQxUNoiREK8RCFSIPM-B{8=qi*gkbEK~%S0Oi4l1u3!pOpgEfq8hO<7#*B?`R0= zz*4gjL)%4Z8Hn*WgBVy2?Igkw7CaJY+S>BnRh77x=F@-vIzH=)60p`~9JJK|a_ea+_UahAgbb+^wK;DbLK!~i8_m&TM8rL@YO|-1^A^OP0aZm zcSzAZ2W3&V z>#h;{b_~%9?FT1k%5IELKJ6P!8hbC*&O8o)Tngl6@FZ9>8iAN1G&RUtopv8Z?B>jFGIF)PeP+6|Q`)U?iMYqN^eE^; z)cQQk>FE4E5dhst?B$}u-^90+#4ZT4pxP{tp2g33h*_22!^cd@9cfZ9aF@4lXY{}A zi`_-;E>uqb&u-ZhRl9bxD!=C{Fx?t5;o(%}+p%t6c4tS_S^&HR*(fmEtRJo#_&SVh zC&+m5sQYZR(uv&cGu)VA)trJ>ANC!lO1QHHW9M_|dc7>_+@v2CeFzXn)lBx%DLIBw zbbqn>8dkaO$+jzD$uEZ#&Z>$Dapr=5b2td$ShmBn_!!; z;HA{K9YS@SU=L||2z8&fMUIBeh)ab1tRKJ&GN>xfiauTPucTVAy7L4$DQ*E_Ondw> zaJ_G-U#;V_uF@=mr%gF1C_%vC=oTNoClUkHLRT?1vx*Tp3_e6 zx$n>VlEhvm5X?BHk3LrP2jaQW$qKBJl>PH9FD=XG{C>0^|6Ztpo-DxZ_i(Vs>-)eN z+JvK0QP4NcqZXZQPKYQh428$3Fh4%k@pU`NJzT4C`9Qc9;31*H8)^nPF@+fp#n<_F zE&_pCera|gzW4&_)4`N&61J)mggBeg3XgeZwnD{lhO#o>{=W1F3#(cQ7Iw>udu}HZ zleKnlNS1RYC>VslwO?P=1gu`vcg1#kRwwnPqGZ(5MBl~ed2PVg|K8I~6K~N^!TaRgF9Yw2yH)Ac*SM(S z_#F)5n1QHuqj>+HO|4o%pArME1V7i@;!^;EcN9&g+*C|Vpf6wis8<;L9e6ikG)E$g z;#7i$h!J@h{Ep+vy_AM8r+O&^iaI!-t(A;vrj&p~^XD3RlTF4yUMKh;7YCKR|0`2< zJ#S#;7x-)jrNA;33#jx@PB_OS^NphsMsi#LUH>AdgH$6>@ajlwthX^!WXVDhcv0zV zi~gw5N-X^&7L@RhtybcX9c&RbUa_(~dC2;`xIb{>Co#V)z~CwFE!$IEKTrSFaaq>j z_E1Brl`1Y#HX$?kzeE7=tEypj-^1^PSMOQLxS>CNr}aoWgB-hQshm%+NSRwj@G zR7PkYE{G;HLN&?8&p3^{g09U}amHnKpsdI^mKDA42&`d6I=z+!m=9kYVjlGBAIPQ> z-ASP3Tl7E>0s{WJoYe1eF>%535Zd)7K{`ACWd9p;u;zK0GTmb zrgx4x8YOFaZs9H)hS!a*bHZ`0*OKfwId&vFufgXt+>Y5m{C$N9W zP^t{i$YOW$qt~)xE%+zF`x0~FEWHv0X=apN?TaXWw zo|IS(UDv;Yi@B&>{$y)6KxEMux0i`jZ`UFKefj}Qsnj+lJhVSdSqvvR}o81`LMV*$m0m?L?bp35q^3|*4_a>CqPe-Hl-ER8|HKLz`-%PIQ}i*) zEcrV2B~)J71-%X+XYA{`G+I4}u532zr6>pXnJIbiysv-4r=$9M4#y$$ooka}`KWoD z)fh59_~YmZn+8As(k>NiEN-to<8wr{%&OWu<<%FP$-rI&1)+GA1M1Z7_wv3R4j9NV2)wW*DvzZ?U0U{>6^zT<=D3=;;Ma-Si`q^LScjR{($IW0)fg1xVXEi1pjkoHgLadGl)3@gut?OAtU|B`ToE=I_u*uCiA}bOjGW=&k zGvmf013A(wM%k%@un^|0I^}%%TevO{3WE6rQ^NWd(oH$|M;+~`ibv{4xM#Y2S;T%T z`n-}E)fF(kJFAOiscKt*iUGfJIaavTAy2hKHe&{IS~%ga!!?&N+0~AtR-0)hETJ9p zfwJP@unM5S!P@`{_R)7a^4Q_M!Xn9*mKLWT!@Q;gij`CE(1v@Bqcv&0hK30Qk9z&l z9@ocp`WcTUlM!3xJl;S@XS!uR+P1*9T6$ zT)NrOKMLF?axG1RjqNB$f2b%?WE}c~g~l|O!}PUGpX;@hy0-6MMmbEZ=^9hGz)0GY zB7z#cW$xeTp7zRJ0rYEI2({zmGLMBFF`O-Y(l@%oE`HU(d?Y^5&bq4DS*`bL>AL-Q z+1ZonQQj8P73DNKbz<-FF6*+gU1(c_!|vICv=z&04*tue{ZxmI-u$uIvd-@UFriM& znEaXd)yMbx+sa*6ZuuPWBVJBgNxzjN&V8M@W>^~f$;ny%^@!R^Z{O`bzl?qn|0vk3 z4Be{{^8DO3ix;pr6^GL-@J86lL`TRXUv$t`i>w{|M~_@ynigA>4tzJ%9k8ev_yI#S z$4$(9PM*Gh1ETcPhM)SWZ<|$aqte77R_Lla)Xd71j5{{b;-+EtMx1eeD}#<(lETT7 zbw{p8$dpJ7 z%SvqU?KnD3VCa-k`|bIqqf~M27r1F#_p>H<-AP|J3KCL+7@~ZBlzeB^*@>aZ|EguA z$kfW+!7xF=?u3{=ICFWD&#Po47rZ}kY`CMq?_NwN+S}lziQKw4;gX0gD%v_54C}+{ zM#P*wM}fB|=I1R7M7_U?#o%|5&U z|8{Lhu6P9&l;cR{a%RPeMSu*!rk3UAz{!@0CHn-3_-MVRwev6Uj5O;`iNEoAfBxhd z`HJ$beoTP7^M%0nR0c0sS^!J%@4ebv;{7ASC9wHMYsB$`ml#N4=iFLwui*q?R&rtA z;&-2|>5Qv8)s-#cEX1VfjKyzhk=Hr`Q9Ce+aro66Q-9z|W^hBWzJ|?u?oxPXN}#S& z#dTk}y7#n+z%+{LQuY?*f|Jj`PLHTR%ql(q&b0-3Em5|O-r=?M7sNYg42#yw(tPpa zk^IloI>>n}ud99!zdHw6_dSjcKE&YGo1-c%jpV$MCAJz;S>BHh<*k*=?)e;2dPuK3 z67+<;KRtE>Kg5;O#B)7Cu3uP-z2BkxZK1_ghfcR$p3(Gp1IFI@qv&DMZik6@YcblP zu-Uj5$u>OGEw%Q1?Z>gvTaK$baV|UhJNJEPZiuJTMR4Jp5S3VN;9H(uuP zhs2V~GyNYA7MF!W;>7#sg=jC68OeO$6@ou6ap(|CDZe?~BB>P0YAC3Bvm|-_{c@c5 z>Xq)IMd@=^M@s@1_NQBywKD9#tnc(>V zIaXWotp>hE`-~nttjkkZ8~F3Oa4VYctVFhoBD6Q@*P2gQ2SWmEMl+1RKy zQTZyZpN*G1R;<*~EPxKMMOgENMXC`J6|qge&Vb#D)lVX$Ihx^X#X6<;PF@zQ-z{Mb ze1UTPw_4zqIN0_bA-XXCn#UjJYkqOM_U7cJNqL#GR)x)DkKST|67}aQ6h@tw(+4uZ z`%|fo2x~5z1B>G)RpKf>HOFn$9noC5_Pv%-gO=K?K>%14)~~L$Ccs)17r5H_d)^_q z6CN^-4{7sDc4iYo)(KQO z$_>*zk6$sw@|0jK2V>_;6q|Oh`98`(ssd)yU$_AXW@rakzT9eXp>}J;NmEL%h2x_2T2mn8`+#F`tg0@ zZ$F8B0=2<_&mF~25(b!=X#C0Qyzw^4bipg&0Y#w`zB>Ul^g}9a$jeku96gM9eeomn zNxZ+8NaoG)6F#5PjvR%QcR5;K#|fK5;||xyuN=l5_=koEp8aXZ@bAvGEy&+lFSldkSVlCS6)9}Se4C_C8$@FAtW1u!E|$L$4~)hi{(AqcUMx6FP1 zyHar?gZTr*%0qitudzK0pBCC|Cabk%6ZW0}Q%|1w~veH)sGm_*%_s@C*vuy+?dLRWhnLj`PcM3lC2Y){JA`+wlr^i=T+`YSwSE zMDS&Xy4z+KS85zew(n6FhdAlr-+!gubr9Ks{Mf+NoRm33va3%|(6w$_A5TU3Dd;rA zPr86Rgp|!B0-555dF(1d;gnZgzpY80Wy&*R zL{4r3*i|rU{ zlu-fEHRuE0UpQ8+L{sFpJYeY&7Dmn13fW`z!q*ETfEeviG=TSxr4b5JKeL?_O@7(j zXn)R-WaGndt=f9meW!v%)kM?wn((}r^*)GF*<&i28chN{cM8JJgx(3d7W%UHIG~*j zS6)r$X4_%7258p;FBB^d>I>{)UxjPF#hZcA6#-O z`w2jLsDE>&zgl7>%nAuz>9$^yRb89z59a~s@(ld<^G!XN?{gEwd$rL`BB47W-)b_j z?i7TouxzQc(jS(yyNLD+24w7==OAm|{<`R-QMr%3n8Jb|YRx`;&@4i%{5o@-1~00W zjOfVxzq*~^X(U_C5EZ6*bb;S3>HeYtE}ogLQ8ELJ!!{HApVSmfJ2u&UN!U|`y|Kv% z3MRH3J(ZEn3|$(TLUfQ67=AlcTAt$P)tX$paamwX-1bP0zq##3VPx5KtH)&@6TmZ3 z*wQfy4q3Yuw8waprLhU|pt95IF*l$$c;^gl_2q2C=hQkfMSYD0C4|0dKTN4gmLyM< zZ9i^1pJ7I#MPy4=H!CPTvAz&^kHTJ4t%(|RUBq+b9W@v8)Q`c|167~Rm?uJWD}b$8 zRp3exQDd@u=GXU*?tT=A*@_oZp{6i5Z?dD9c|bg;_OAE}`D8kO;HwNXi$+P28GHK^dB>4x? zdOtkA{if~BNX@C6J=rJeVB^VTT8&sva{^|LVcOsb7ls6N`rC0D7(2+hLUZjbY+4Li zf@~Rx8_g9p_pl$<(QB7r{Sd>R^S)T#vW5eS5S|gAfsDAtEaTu)^P0zqX?8w}Y!D1m zRekI8@a7NV++m5DD|OA1Ctd-Z8r$Y*dH17BZNy?q>E7Cc=N_(QPeOai^i1#+y=SS> zaaFpEC0uE!IO_roPSL?vXqLJL++q+^sSWD2ZW_2ZB9}?jxwOk&#>AUxrU=81%9dyq z^#YabHH3eD)}_+@arM}StEsyfXPq@BP>qyctE0yl!;)yXTdtRtz35aHLb?6&aLCEJ znT;EDdu1v}iZPG^b|gwi)Yk)Z8ejF0!;298W^eU3uLx`ka!T;77Lv@rnh zLWHumq=mnFuMxVgve{c;trka;y^t)+49^pp1B0`bv{RErruF2W2Ai%OO{r{faS74e zI$PMS^xr~ST8sHCcsZUd`^#Ln2ErG{*2xRESr9?i^VMpIkVB$bu>xk<;_cG5rY+fK zv73rqzow=(Ys`@8QxD+7Bd5D8S^68m$H#{Ha`B%$H&Ycml+icDI^7t5!rbP9ZYvEE z%C$%$M7(pL_4QslE*X=l)Mw+& zc6}b^M~Bg>pWOP#UE9@$$;{ZoEc-F@pC+)4q(2KZE)wZmD%~B#0z4H1jyqIJu^rAg z^Mk0AaQE*jXB^HyF4L90Yd%aZAKx;66Ks$7mzN~T*%j&#S+>wdMAT*zmStkdSW;tT zxpKM_dk>4g^CM8i!M)-f)FJGJ^UrP{kr$HItnmsS;(VoBW4(?vFz1fOcc1KfJW7hi zQpG1pPm7oT!#sbO@V2JzqRi9mlxI!GFpQJh1f~-AU-7cQb2N_u3=7f)X8ZdYJ~5 z%fQ+PokxF)zVHS$T_nYD$xZow`SjcDwm)dKM*-UUOl*BbU9$B|t)69x?nrfeY-b;2 z?U$$ahX~)G($`1}I;{=X9Ih=0vTjjQX?f;0!+LbYt8#1*r~sPxZ1Y~G3;tbLoa`!b z>b)Zm`B|Bx=q`Qrsd?+SunOu&W&PZ+xVjmeLSLURD7wkP4vb5z&=oq_rJb=~WE8FG zf+p!k_NFJ{ACV zcg_50@b~!tr(f%TvPx&4E(tGJ{9Nc(fku|mzM=H>>-f2KflmUtVo*qNa`uNLqeLfG ztEUu3=}PeL2jE^KFbnh z5M*p_0a=mpA)B_6X^nlK-|G?e4YL9GR-^46&G7Vh%iP5#m^`XXOLsajk09kMBNZDTxz1&A)S8|Vh9E66AH+DL|*&vI1Y>0GivUDicW3pdgHf&{?ijgRgA=GNA|KWZ-{%SRPcyvL6p4vzt41jvU7P~6 z_zIFj3MdLxDAGPGlc$(76Vh;0K(oGRF=q9+`w)D#800WoRBq>iTgB`fu-iO~&6xoe z>@};koZQ%9HsFF^h8_f?VD-t43&0mx`p4z!*Kb&^)*HRT*15Pn@m?_?>nqaXG?XKa z=A4wn>&g}eX)YDH2C*h~xj zE|5^xuhEB4J6JFas-=)tasuY`YxDnN>^;NT`~$ZCkfNw+t5(gnN^6!@?A6w45v^Ub zR4LJ@6+2PC7DcV95vx^u@10P4h7y~o5qpymBLCdS^M796$MNJ-v7r^Yb~Y zXtwJFB%$cKyyBVfU#C=tR#X*Uh;pnpziX@Z=-w_U1$qBP(>5fPnOm@tw7SM3vil?^ zY-~7&GGA(4-F~U{_URM42Hr@uo+_oRVaNQ4Y*s#6}Tb(;Yz!mBN-@ROy)fV*VAlkfqrl1M@}DfG@1@N2rRtA zKoa=z-)i|}@q<(1gC!C+`jR##O`<7=x6GZ46f^p_|3oy}>2BLm#K@-cNk)P+DCo85 zA27G&+3%~zq`Dyi^mE$yH5(erQgeK286J$74g8BWvo-lC#psJIs1tfXvPS?STu>aX zbbS|3S__nb1^Ua~-O&Ytb-y34El|fWSqBb`KPzf9-1u?6eU4DE)$uN!Nt(4G^DP`E zn%U^fR&|S--+fT_NtVkb$2NHEftF#5k);`(FsGUP$pI7l(b~(O&1LWIP6QF>EN|b! za-3?hcMFBUR+`R#`$QQ$ff)d#-34v8ZpF8WCYM;&p-fAGDhSSw%T-Px@s$=5>Q9^e zT-X1~i8c#f1^hZFseATrk?!H|=cTPx`VDx3`jrIXf`@aEYFgC(pgp|^Zu?xHe3)~= zJDcS%-ul!d&}i|#8>dJ(gJemM{<|kMxYhPnyRaK2CzQw+eG~bPDflog@?|hp<+*SvYIE{LwNy8 zV3lJpuMtaA(zscNEsX4K!cq8jccu=leDm6(wi@Z98@fU;gH5Cny?RvW&0fMvZ?mr~ zg*tf^IQz}&5y5?OxC1ez$2CjLMASkMY7$nTBFjpH#b7tJGyC zE?u44s(|6L0=2i6>*SVF`2CbV`RVi~KUSR-IpkZ4vG(LE zPf_?%-8>Ly{OTR-gspM$5U;!Nb8b3eb^lA~I!%S@SwN-)%n|hj z1CS}>2Gdk#^ybnK7hPNbfl$>{5N`a3L-)6L8E341hhw-7;oX9zR8)r%gWMsMOd=IHvT3^Y9@kMG}ps>uIS_77!qGNM+=g7dvgrWx=UTzGz1hou@f>03IbE5wWB7}3=}K}?R4=_)V&VKXaJVksRdj=<$QA{*G9JsT}+$*9cx?w%#Dr;Y)`ziYtAhzw9OgUvu z*0s(*t{fm9A^Ri~)zzibD~(QvA!n(ZN;3hIN5dkGotzg&plbN463)%*zBEXFR2ZOk zYzynsAi%Jo2p05l9j@89QKA9nk+Z#Fq=XW z3km4re-`?6O7JaNgnos&W@5EFs&^)?v2m%cQFKA{+s&j>i>g|Zz`?Hu1g2I3#22A3 zDWxdN;h+?lx4jy$b827GQz8&0vMIZmGhu7%U*Y0Y#`L8Yxk=Krfy7n3FVR(AQ|hi9 zQD+tgXBe-zo?CM?yl;l%OIC0N;^vR0-ULQ`PAR zk#8F1Jo2YdYEk%NzHMk1Jg{Z4{*_8MEe-VezZ{P{*?Y9kMhLrV5s)ol@1olJ<=Ik$ z8v~lv-b=+*7ZlQQ$@t8F#;tC=K9Z$|&>`|zaR&0G$xDwKvP=)hvF=oh)Z|`B$&z#VMcP8soc!8x|s^qF9 z7@NGpyxu4+`*HEN{V46+?=sM7K4id@3m(0zh-)5^mJVex2RIU`M#aprI+ih?6;{On z2YdR&S)pGr1Lw)dJ{h;`XU6zVQzWcP+}ih3V@etTM;EX`lS zG8%&#;Uv{NVdj~%(VUsx3`*oCOobWV+CI?7WTqu0tDvVPhj4ndQ|hs+LNrC7!Mn|x z*%)fwC#A_eY1~Ho+-=?HQ0~I-UFmj2hac!yGUsU1-SwoUsg8l6ed$G&j{=r>g-O2P zek~zSq5WUca?8+!#DM*KB`v`ZJnw+g+-f6udj|7B4h3WSUicWn4LO|4IA*Tjs&n<- zV1~)DV^3@w_S=V;5Bt_{-|*2Msi^V{bnr!TNE8NFn##tc%hvr2!v{?}etQg`Kkslc z5}UmGfz#7ftm%rL-0E?__8AbGp&NL|$8GhzB|r}8cLe)?4p)RiV(6&hiqZ79mLoKo zub5`}hfZ~mxQ1ZGhmWrgZrp$K25S#$o&HoS^oUF zD3-`oy0?7Ks7Cu$dliC3i2k*oMN}yAkwK^{}LEIkC<(<4LH&>*+EzU zUpZFU#=j#U!cK^%%rv!kq3nNB54{wEB(ZPEsubkB>`yDSbFkgoj*N#n$3YlK(O_|N zVpm)WU8}m_74M?`>LYn=Kear8mm3slNAfc~5?;DZf(fnuYj?^nbISH?@xT}dKX7_Z z$Rlr*CfMuluyu9Sb(EEbBe~z^3URWd+ zPC)5i*|zwB0UJO^V#r-@y}yIOaekDG0{VGNlc!|53sq#FnrK3|YKPX^GXTEhV|%{@ zv(pbvGoQqlPf0&U6fpNJd}Lsjb=!u12P=aspXKVX(!K}F5kl(so(Ck|CcogGQY%vt zP|31_W79mgKUp|VT#P!Np%NUAyXIE56L*vJEq#pb8g@TtJMOa-98^1hNO}0-Z7V%C z)z}H6?~(r`NM`kGPw;v`0PxkW!7DeILg1Pm_pns?lcC@s8WWXQD?N`idF~(|`Q<*! zGm&IyvAzGOG4F@isx8OfhbKG#0yz8r)`5PQ%}<)wZYvuS_~c(3XYeWuZVP<>>e=o9 z)dFl|ezbSfdhtaLD!u-zrh8S(%-`y=-K`U~mA3vrMU8#wnO264dHSb-<>1{GlY)a| z8^KeD_XakMX%|h~U8jyCpHdtrTlpQ=vep)RnH?z=%#O#oz?IKAAy?<;Ui({JYA8dy z_`hXX=C}T$4YQ0rGq)or*%;WLoD%IbX+84LA+u%!DK_{aS`T8`YAVZ2e?Js*e-pGT zrv0~0WN6v@Z`>f_2-l;G%Wv}|yB9_XJV)~ZUhuxiS#rpGf#c76j?T#I--7%wmxsTb zdng?|u-Ce)f8z``*(l3Iw7hr_Q6xG|by*wg%YQmGrdgIU?x1@QbQk*DR-VEWxCE5*D7D&l6Z9$jASaaEf%c%k3$}F&&i#hgKOLd|Z^7LTqg{WY5%c7vP0dqDvcW$~%NmVKrV24ne z1zQR8UuRuI#(bLHRzalM#~V$V-2L*AWxxAJt0sa~I?6~*zVpS-rV>e~8Gox#jyCPy zcw0THedt2SB%iV`Kq=^YAgcQ}t|5u;ou#`YT|Bh@;>$Et?>6Fc4_RyBZiKrVR{|!6 zU$#*^qFMbq_5M@V^yZ6#+y_3^6@i*1pK4)Jb~R2USFO_}wTq?ZE)r3!xmKbap?U*+ z`fXC*3agA9lwYLE%BQ9$6O@~~tTn083TA90aa#PHX`69W(W0XBYuv@tjFIEsyia+Y zVwXcj5D!|#*lRWNIH@Yyw0)p2(WqGmGh=Qr+blLLX6-f1YD7Jy zvtjR!uGv|)X7_cy$T?gsO8^ArHf;gqyUu%NFBkWKPGJl|&Ti4cvOXGc6imD|S|O$# zO;c~OENAu`rmjKW!L1ea#UNzaTd6&upSL{|N3ylCXM1d_@c(vr6?lW(3LhK*@t%V! zseR*+-IORfsKP_j))&(v2Bd4tsOH+vWE$1?yF?U`d_avTl4|r-mKbvBir;JOC~^)a zZzuZ553p;z*wRxPZ?(hMv-AYH3O425A$Cm)A}t53*JTtcMu0#VOOyn}v6tVaiKPxA zsQ@~x^{nADvl?m6Vfs(Wv2S@pB#P4w-F$MHK#$N|<;8-XN@sb@b;j2p$tkk8$V?Ef%rCq-oN!A) zOBu|Z-Xru^seS*NJ)f!()^j8FKB4~IKiMfg#inU53`~+OOu`aaljk9hV-~H8`;wu6 zPOe|PINL2_Z#a8ksN-wHL4@}ozAz`A$Kh_blDQsB z+jb?L%C@Y+Ad`DA%!C+djkH->mi1F#jZNemct9=pLo1upfrk2stZQmrHH(1iui4D# zpiG0nMfVbuB2zQCTa|HilLtI2$5Z`0GoV}rGXAe!`&avv9lPCp^NXmTQYL%0T;w!4 zxXtHGm? z=X$Us9X2zk;PZ7byzvj}Xgnwo%#*g&I687sOxc${4^`&fibhBNjIM$ASW;h!B08fj z946M$cN@|I3(;6LE8)x+G$B#m{`%nNhA%8{n48}$6y#-;qOzx3CW%dI0Wdnt)j_0f zH`=#iJu}`LW456@ptyw}-0l9sn=hT`H_v(SgXLQNzEk_Bud|r9>4aY60gmkH-|sUB z0TG*>C`f-fL{OUEze8)8a;$nj7<2IM4%GKof&mY4AQ9qh6*#jvnKTtrg*0FK+>wUA zWuKsr1WlaIwnqBDm&BjeWNumY&yKwt9Y@b5^>xi}j8QXb2V(+(I?w#(v`Y@{J;HfZ zC|D=Aoj(p8O=KyE*7G0Z>PzOL?dsFyv^oxD4HxJ0`l&JvdQg2QW9e|fq4T_}!l>C1 z(g?u$ZJk7HJe^D=)UW(N_Djg&Ub&gT%wXKt1AfjKd<=iSQM$hNO>eh>m@ygORB3~) z`*71&HtUQPe$;;8fmbBO?#u_DEka70q)|Q}jx+Iw^9DLG%_?oL+fYZ$$i+j*SnQ;F zg*mlo>9RH4mA)lT<`f8LZf4mU8X!W7IWVbpqe+~V^tZJhi<||LD4rIp89Xx`-Q1R{ zn@3ZbPW+V)-GtXKn@dZypuGj(QmWg0WiXGxEsuAuF^4CeykENMj(UK!>fm0~g7W;U znuCV>Dn|Y$sq1NF>_{0b%JF88f)MpCv-qAa!`&zg*J}cwi9s2vfmwHhM-=}5`WA)u zh8;2(J5phWelD7h^Zwrn_H*GICzFWT|3(aoH|_WCB|swht;Reh{;vx__mWAK#D$Vj zCZL_;!M%Ekffw%2ODzOVKmLK&O`Jn1+i0wrOPwIBMdza2{G7ZCr`U$fiVjlQoT4v_ ziX8u}NB@Y%l6tP|Ry_5-KJml_dE%T<7{4SDyTC18{#uVNTIal|uU=l)4kPr2Sfswg zAP`vo$12`&dg0cz;lCmwbD=|tA(t@13C!uUnTM3y`x}v=qGR}}>5ii3iX8SemItlg z)fyguaT3e^c*0cAz@4zq5Jvxqhq_S4r@hjVU3T;lo^FZ7-|YmY>=J!+IqEZvW&&*w zDn32bbsu~7_QLX)i3dGzZe`lWE7h4`)VA67drU1Dz8YM+sh@OlX?9#lH<;U^J+hrc z!~%Qwnh(%J?eGtBCBjxxVur|Tpt*w><@Bdd8xjK_He{Tpv2FR^ZuN=D;B@&@2a87q zk@CKJq33g;!GuA~&iew>2EnqC^bDr&|89}!w0vB3Q%fv~Nh`Z<60YOB_X7xgmjk^8 z6Tf4yRkm=&8~NgyW;L%4YRoXrJ56R3K+I}?goED-xxvU$eRs(*uuusX)a||UZH6O} zKWJf2rLPp2M=L2H##6^j&5KuLYk7RWAJPP3GWBR1(+qB`yyce7g~v;czS4Ahweu(| z16jv+Uu>e#ph~VcIy7YRs&HJe`0B`CsGa&wNVX9P*qoj%oGHdDaiK2{gcc*wIi7`=g(c_6H_s>sy-3dQmSyN)yz=p)D`7+5sVv!# zy7@UnPzzeZWH=@LdZbQoIk_}<-PrcwzdF5jHK|?bP-g+`&?&8eas20 z1vQ}7plnpVyZ`*Q{-J)dnHl5Fg|DJRd@JJDp0pEiicir_-fS=N}3OBx1e+Ri!Q| zvi77qA(g6%^!U{tCJr+EAR0vnS%73AdL{=!Vc;L3+JDE7z%uV>Y7PtM*7~Q^sRsLQ zfwxu9)ok+i5>{$;CV_V3cg&UPK{0W#rnkeaR|U%8dqTnbC zy?@hGW}@a*W^$+%JR$pdCD>n!@uM`*T|2GHkGhQ;7Mobl+7m>jQjDjp{ey7Ct!0LA zVV-MDt@czRTDDi>YC^rR0Dz&V0Jb}cJE|!RDKw$7wkc-Kz`JCg{Pj5P;2T-n0!CyX zSo}HFjnV&#jtq=d^3XZl+@BAiX#h?a=rw#?>SE%^zja?1Y<4TQsYz7|(HPN0>%njt zC^u~DmJI|Qs_eA_WeX%tFKNdO<_OA_Kgm(vXWxr509-oZyH@RFxH5j&ARMkgpzOWz zy~pc0Il~+ytZ|Jo*~AvW-PWTjRm;}&bqVm>uC86XlJT~C=&Eox&HCMsP|JcwnoY1K z0dR)@9a;0s%Ko+8Wik!xnjDf&oqo}E?<{u^xpAzLB>h`d#hp+DKf*FO$L3woeyDK| zPxjnStzjRdZ=~Ukk|5DEKJ+meHR(05?ELPQ=C@rFDcO|*6zia`b#{QbDS2vbr4Bm1 zO*zX@^sekG?XLWI`O1i#h`h6N*Z3zo!EPR^)S;oI8dfmmb8%60FRLuAD}A)yE;M?T z>6tKfMbPU_&P7xLOsfb$qCf}MOeKrIOu=DYymY2Y*a9X5ZF%C9WazRnh^ z751oj;)@G0(~A};0lOV3%0XG7K}9CMC`;>bgsQK>`D$^>bkj+Uyo|jb&b2_N_$U{0 zgP&SxqMHXnF8A;y6F3Wsi$rCVdNaNaoY3r6%ebw+du!Vbw>Jv5b8gLWbpV#?dTV>W zBm$0o`9b)SNpYX|$WfX z^JGHjphW*D5&F)C>DPhv!O7BO2ZmvJ9N?C(l8{;B4;g1d_)hQ6lN@}eAtejb#&1Bx z(%So|*5VmN1~4Whl1yR^Y|wUnw6 zGL)Ixi!7}Fgmc0SeOaBQSb0SVkO0dzDDlPvf9lpqMO@@IB~aAn0G)(!74--d$?X{N1s{bQkVq3(eqBv_Rf#SKsN_-_IMDe>a%Qtnn>h{&*f;dLzMHRj~zaoYhJy~n{tBNX&gyXy)cp0W--(*yaOrm=8~ZpTX_ zfBTn7=j<9sQeAvoGm|q*-iNoF?g#TB>-`K=p+k!97JBq_i@)$8eRfF7Z@0^lHQoHY+%pswa4VtnHah68%! z-62w=i^-awE*sxjH9in(Y#wdDrkcP2VHvyu z($w3N=~|NaglB~R9I3mL`D{B(y9g}k%9GdO1^KZ*08^MbS%RE@8XZx>KqarP2`KyO z2<}jo>h(FMngSeWy|vo|$vFPu(}va5K^FjoxVgd9L++INp|sPSJ+#|1cm5=B?|E=z z9xf$es)KQvqdSF%H)inPnN2UAhQL0irz+~1X^{CEvy6*o^c0`?7dB9(tqC3EJGB8^ z{Ly4~4XpFttR>BY2r86wW^dkgG19#0G6%9U>}aPaE?bsgR;yWK*%|+++u`Yy1=92M zi9?*4w8)!7z^1+8#a~`<^Z4A{q%x|<(|k#U;p0%RGL?AU(Z+AnyX_}_)q|E*A<*Gg8 z^92VQ54J9C5$yJ_GI-Vf9q(Oglef<0^A+Z#cohnNk6WDWC%d^uZ|^wlD&+`FIReJc zJZniWw*0Szu}wUc3ZUPAuVMYy>dDxj22HrGKqSiUY6MGO2l`S&-zbNQ#I%xeRXLaV zo2^5)ocD~6nNK-P$jJZbb*BCl(zyX*o&omq6T`lywcd-YxXL`YllS}!=Cs-8#ZF_) zXD4EH{?_;M?^oik)#PuDfhI-`n9<93bw$?KQqnjkRI)c4lQeBA4=g6C^2J{2nsWGr z%$BYb;}fnP4D(NTxMY5IkL-Q>FQZhq^kRp7uqp_x`r1B@7T1LuaSYy}}zrd!M+?X($<6F7y|1@syTy)qo_LXH-&aQ|%di4HKn8Dzskd14UD3xleps7jCzA z(bIxWeqMwo0H#xy6Du+Y=|URv40!qEVXsrL>1Im?+?JH4>#*gdj%V1G>X@O^%%>B) zdX_}qKG$_3zm%m9zd`4Es3S4so$`3c3p=0kCzcIexOBh=#?_AwT8R>*HXdr9kLZgDW(sbGJ=h(h1Bb)k1x}<{mGb4cum5 z`QnPtd2&icP&yin$L4M3U)L(e4vzno!zh&Ro_ZX5GxvEjm+8>*_DC?^bU9ztxY#KW zC4^+>_1E%+$WK7UcxeBdD0F4Giv9XwOT)G3jKDbiI8cXM8lqG9HSdY50Tv|^CGb+~ zo6av3G~UDSM~PBQqJ+}7q`?`&rE56uhyGB&f0LMmf<~mrW{^MK7ag_Z4JDyra=47I zxu_{&!;G*sYvZ#!`+6utHSW`_NZ+3#r|;l7KkFO*qkTIqtF#RnrNeVd{ib7YXBrOu z=+1vvtc)W|B`3s_kuJ09A7W2Fdn`XItk3m0i!3XZfJ))g_5s3brlqJ~N~=-EX-qvT zGEj>iUwIx&PvIqeXVQ;Er;MOSLH^~Yd;-^J{5w~wjN$_PQ)E8s4E+pYHb~?P*@kVY z-Y7qJpK4e&RPZ^`QSiTSH{7ez6%IU1~ENGqQ=Mmp7DvJ0_9@$PCyCS>-ONO4z0{p<8h}w()eT*w7`&_6+DK3jL% zxzIaaD!ls1=mb}yYZ_|zKW*}j3D;MTgO85Zg)LYF_g*7Og+O7tc2+7?y4m-@V zJ)1UjJuc$nq0b}fPQ-7mW}210PTwtf-K65PqaMv-kgjM!zC*nT5tUYUF+NO?GWl0i zR8-V;LtT`8B}3QV?y$~+3B0I{-FPdz%F944FOg=_KNRg*`vR-m}$+K zEcT(W_9YS?njP)^I#ZIm@%JfFC3{i>BKTuurz~(qEChrycl23bxl6j03E(;kF}iZO z!#jGeV5ixYSeja|8kc-y@1My|{K(=}cAy4Rmh(bKbI0QaSCWP#tJ;wcokXYPf@3*a zHC~*7an5&%Md0yj3sLgZ>^eNQ^UQL-tWjQ(XkNKYxOEm+26BD@^pGk+Fd?qrziMb0 zXL)7qcy{p_;?v+)bwJ`aqVsJK`FH}GY%*i)O9u-__TGqcTS6prUzRdGe0`u0^0EVA zaF95bv;bd@({`x=sSZ}*JYEQvX-@p;$Kz|39yyIWRa^WmP}$9s`So>GJG*XibiT!* z?w3>-zj;oCM2rv$(h9hU;r|-a#s-jK-&srd0ZbmvOh;v!M0{+K33Rg8HuTxGY3a|m zI=nbTP9)AmT|U}RBTcyCaY1rrEVi}HU8xI-COkCPRUKvtn-g`kjLSdEsNUUA*0~HQ zXWKSOn;-!_R6)dHXXg<>`O*(yBUbdB#!ek|)LUuE+5iUbP+QD~G)f9^XG& z5}?$7QlDM}m>1auSS)xrAhv%psDC(|g;J-iQ|@k;p^@?Xje^)uQ^`Kd|c z?hd75qfFd$8p@^fZr$U?-pH=#+(^tL`!a#A^tn&1uipyj7P4KJx6e#@5Wa&^rs&x z1*{xI(%TkE+@a3i?2hATrm8zrV>8uXIM`5tk^*O^nEpC6Ii0B=*u%i!QY;9wZ~r`% zwot+d!Xct)a;b;<%&{6K9-*VA!CPHfeHVwTgJrIuvS!kA3vOGq4CWsGXh5*pl~baa zRI5t*T?V-565sRK%ZfyX{d(HvQZ@9}2s?I?r@G(JQWOZHpRJNS(i7akj$FsdqD@%X z9}n(E>=U-8tc&*J#SGnF*heo-J+pgs$(C8XdcA`S86RzVq;F!L*w8!ouCz1KX?H(jGiMyGXC<|7?;crOyqTgj{AyO3#AP(abu zn%dYhb_#7!iAbDwO;H0?k~b=rRT6DL!0~u>6qF9un!oUfe~OK!9?KMe6>b_risU~G zIaU=JtUX;ywh5Oj3_jVr7lsQ;W33(2NJ=LS-6|+NCzaH!P)FaszgbS9nEYEkE4gWs zK_B*-)A%;YGo-=o`7oSxm;#v&6y{+lVu@j?ko1RrE?G4ZkX8Q3ZimI+U22=ns<<7| z{VPnOhx}P8P!cIcrOz8TrvpP2{&VSCM5$I}BCLI|$)g#!-zR-M{`bfiITTRCM8*if ze@%;QmOymP?4NiM5S1ltiV$7;c^cWryPK{8i&&xH)S;*{~k!{6g3l~|5(Y1 zhk7|^PZwh{;i}S9=wGcI3fp@sWgQDZuQ`#$Y@J}EQk=mM{xPShZB+7T4SBIaezKuyjrQ#$g3yAcYK-O=zM>DRP7A8ZCu%Epa0op+siyZX)odE zhr35@Y|W-?=x@F?T@z-?I}JXJ)m!zF5Ch zQ|rb@1I(Q_JHZ*+e+S`8_d5A@mi#%QC6*O?W5sJa4^PvDfPSylMc|B(UQ@S(sJ+9# zn%5>0JIdW573T1I8)kbQ`gkX1FW1n|{#k&O7dy2xf3SZs z`0JG81S?WgOmSphy^l(KQ@$WE<=R}CWKdo6`Nlz|+_2Y1hGy`oPW;tgLy2BK`8v%QgHtTne~6@?ibHS)8wWW0N+(Y6xJxb}i{Fk7t_V`Vf0sSzk->i<#SN;5T0DgMh$`jDyq*4S zy$hX+dpo=#^wa12MbIl6iNvxpi-%?6^^29ppKoP!#7*3VCns0kZq+SIqh>-6iYB(j zH$hwCO`c_Q0R%Hk!3eA|r?M^Edbajev20ovFU{c5Ts@}A<>SSI$~iTY{9kHE+Y2vc zp3DB>3oyE^40uIN!JnS>Roo5H8QoL~NYf37AGjq`c!gUv_X>}+WYBmSBtVVve2_=1 zJXn$p?`$q@p1w#$@Eg6_XK@c`d}6My74f9(s(>05Z8;V@oSJfU7u;!k-(PSv5|O;2 zZ%IhZftE^9+Yf5nv_2W)NrM@$@Ixs-MB}n6r2#ss3-&o@%qzz^Vy{g0(tCJ%f@=Th zB@L#=JwLWyZ+cVEqw?6F=i#FXRrkGTB)lEz{sIma>Ig5y3Mk#Gzq9%ofNwdo>Y)$I zD2SbrW3;mSccT8AjrJxhIB@{-nCts<0_Pb@)a)XBO;!GN`q=^}8ZU){t;67h+7$R1 zIB?7eoJX2w1CL$GqtMgs!xuxFXc>GZYWu9umF?ICf$~ct+Fj`U_$!sbM4e#TYsRK@ zI%sxZQ>@DZyN)HhPO$q>+SIYqjq{T4n6zH8Z;!@LgG|bP(5p^*etgOxAH~XdJ#&1n z#Q(TMkzjInwqE+0l_0X6riO3y)%x-Ov@;B!UeRn(p6LDp$O7GKxI&;|3>d7hf5tw= z^k6lG$)~)J9J-D~#nh)-k0RTE_)DP$e|u{Y+MPm+ zqKbf2%lf@k*3@5fWtz4^+c-GSSiU zgA{z?er73v<`m5>ZzTXI&~R@pYudfgvuO7H82!|`-_!LjSC*NSbUe#{*0LElX1PI9Oxm(G-Dl`oN6A4m*gLCj{+?Hu+VezR$5rh8|&ZRU_ieGqI+cc{;8WpP!nM+VBz{nR#oc8RIcUP<0UiIt;Kx-)np<+js@^H_5<(G**z1G9e)bR=#*vGB0N8fv{=o>0;3F z_C1<_&Ek|fJG{$D2P^)B$iunlE7;w75H+Zp6UV+D}u`fs*k-Q7}@A~vtg+PIru zKm#ted=x3NX@03pe{-ks5Dbb@K4s%Ej=Fttl=3rfBGK%fB(ed7xYBy%*}b}aa_6dq zH`UWr`t*bAUA3igxS8PnS{>OSPlKh97FkwdHN&)za)Fb3jf@>gF^Jydy4nZrR%6M+ z0-(5AlS0Dye}|khB2g{q*v#c$xM0#6SZp8>KS9t(DwcR@vLZa^pHa&)i7dC@zko>1 z{PrMxVd2^2oo#{K`suVq$I~}YGtg!}XFYd(whTDAb<^T;@Y<7_ngHSsqd4WQs5<4b zClbOxm^O`%)r>4H0eNY8<%oIYjh@0kI}9$J<~jENFjE^opZWaS?|b@Pm|z3f&zJML zx0KW;xrR*@3vp3w{@~j4@?$!G!VK>?$oL_72!=8`{svwUvo^D}^3=~-p)=DQUnc)c zS7xw%@%~+jzS`KW%=6`u!be9; zcJK6cniqGCUTJW4xIt9TwmfTIDWVsGNm00pH<2HzCJK}PQWyHH`dGh`39F~wxw}pS zJ;#R%SQC@;U|DFHq4%_<^;)y)HRL`ple{&@ev3U|@;*a&Zl2fBI|25c)xk_oG8Ey7 zGK4I+v#5hIovhcWvm6pUa&E<#H^5G78c}G|_IRKw_9&avK1Kf&#sh}vEXbW&<~cFM zeB*mmJ0HV$U2gJ9u5GmE0ZM_VkUA4%+@_rv)`;*>+itu(#bZEL_7K7Z6nafbh-zGV?ZHf&xi5Dg7#Cd$=^_Y$R%q zewm|R^?a{=Wkg$`qyl*o9d)tI7zKS>z*>0oFbq>M1G+g2!?n~bg7_*-pOa#goMV85Ny)s1sa^h|O2 z388aJ{c+Qu5-;%CNh4uT#iZG!?j3l};b2);A{aL(|L7fY>2JvkZ`h=-G9M<> z1XCAmFR@ZUwg2EkSN>d&`og7tBfm48hVegs)4Rh(mgHvYe=$_N+h=8B(NN?v5xiT4 z$!m0VGR5~%C5d^;(>_)&fQ09V~0dA3Kfs%sH{!cLeQpA^Q8fupknC(K=v zRLI+<0-Dy-Nj8ci=(iIx7|lImSWQ(!)BRxrB9w|57a* zKe&J={!pecumg?Y4W(c-Mpl*dFV0J+uIq_EHcwtoi*)_$+Y=~aBIs8sku(x=F%O>~ z|AtTL!eP>%%Wl2XYpOje^U0BsS^&~sL$qGM7Jk+;ohT2(uJAquP=5hj5(Lm(R{uX= ze_XGx37MtMR;#Yr?0=1zz&l ze4)SXfB%~8!;~b6bmvRvzQ^TPQZlp>})}f=}$Nb`&4=W+>N1r}9&9<(b zzL#xH9!WZ7x-^_r;*SuqKCra?RmiG%Rp5A~0M$GZ%H-xErF3A)y74#k%h9mG_L2Gv z+p#ajq+e3aj$p;=$PmI>g1mFm{E?w7UD?|0G>A?nzfMo?@{ye(rMQ{&(^?U0_VKSU z&SC5Ks6b_k?Jc%rspMx3sPJzLE~HQ9P5drjTfD+l%xr=>fRPF0gPcH)S9m_JR91hx ze_@1j^%%pOM*;6yM#6^@!awF7L1B!r;(%K8m0L76;#9fd8d6lCFa#o{qWo#y^CiN!i z;G}mHUjs$I(U=9Hae?md~d^ zH9JL#EOHA*tujm~1`?dBT7)B@iMBD~H9Rhd-RIa3*1^QyDtvTZ=IhpD2Z~cCS^2G} zRfsx9T#+>O=zY1Io0$AOp$R4xHDV2~r_eC_CJi!hoSe7P2C+2EB(SVcAjfevmx-_~ zw-xAQdyd@J>;Ykhv|5dmZsDk&u##n!+L?S^4Nsk&?^&3S zG;8SYQbE|-`rs@<%M}SGio2he>Zdi)gUNmnXh$&Qqgk1y;UR2WAInr6NVI0|^0#jr zbHX&U!gf`hcV^*lm$J!zKsOD!GW0uTw3EYWbwe!!L<*H(HxM)lX~7Ih6Uvw-k=@5W z;_ywRrjc4jo1tJUhQ{`oVDK-zvT)1mOzQPz;z zw#2vfi)Tcs_4y4tbm^vIu^sI(xDB`drjqvac1cH<1vFqh=*v#~P4}z?6F1MY-VG$d zIXmDA9m@l*Cq(lNE7lB zqa9p_FKRB27E}zYgw0orfu_BSJ&r9||6%bjY+t#ra+2Sihf$!v})rn z;F<7|N}~Pt>RNuO2J}?kSzIVj{Ze#`fh0+!@z_+K&hUP$#>GZR6Ta+-(hq;HCS0w$ zxm~gN3jiP+%`o-+6;`_`PdHSg90u#cgu3FY<*s|Hcz-n zj%`!bSmPB_)G(r-AsX#-Yy^!oQh#~}a}HkNDm;WXq~r7-@T@I$E^kd@T~VZmvnEc; zADK@okX>y1|LFSec(&W`{Vp989Z#z@+fsX!8nu$PYVSQF($)+@)gGbaDJ`|P)ZQae zBX;OCf*?kS)fyop)Rsi}c|PCq8=vp*Kd=1feqZ-F*E#3D&XrqY?tM67KZ;3SW%y5# zQr--1f4@*Oh7eT%P~R!M;v!G^o5?HpRFU2ViI27oquLZ>;aOY(@lfAKOJ}Hb#N!C< zGKWcZ)mh+YpOK)Ib^ciIk8j5sapoxe$jXf75#`V}K*4ynHjgj1G5k_tBNkgSmPC5} z_qCBC0rq9~&*b&*oGsxjHty1qvstPGtK50FZn8))-!i|L?#hp;09Rr^&}HMtUZWJC zYk-|!_nAl@<`C`MktPcp@gM;M26>OS*M@S^w!nz^>h`P|j&e#bPrwlvC6S#huhem{ zxczq4*w$oH1W1d*8HR@+Xam|0!q9;sS)@3N7;pIha9 zF^kJ|uWnz^&)HGH$Xd?lH)cc-#$w_VV^hb9#@%C=A+RC(=nZI?AAeH##89)1hw-E% zqxIpE1`7`@q~Vt-M%Pvau>b9x`Uz{o30gTAaWF(PpS`;5aI0Z&0+Hs=Palk0mHGwT z*XU$2UAn?8+#0qU-{F$!T$i>5WwJwOLnhaJsOwj=@Bj6;bcY{i(qYh9|Njj!LV`t#v=FU|f@Jv_AjQ$YGORzhkWTVojPg~DKN0Es;7rNM1Q zTPD-#NIWx}a72oM)c$IbSyTw@>e3{L#rB@_w0`($EEVMlsSyx!14F?Qw=9yeJYJB9AJ6fEF z6MzEfL-8a=aws4Iz2;EYoEKcBaH}D!m5wm5M~?yaA2ChHENtNodD@eA3%B0W(+*kQ z#_tEw<1<&iUmzvOgYkMrkR>oR%OmTmyF^4WXir3-4x@GiZS!})t0#Dt2pAS^m4^?O zHE>d+YF8S=205=S6m~xLeHcz*UnKr`;$cRxrzITd9#6QqVw35D+0Xyl^T%h0|JW~J zh^e+a6b~x)^tv_xPWI1_qg{>#L)+EFAN#w-SG8GuVoDW%?|kB`)bUpdq|AVUwI+kL zYh3<^c!kZ}RS0%tor|indASuBJ`w)0$iHXUQ`}oeQo9$3y4PebhMH(1`PhTUmbsK3$|SwY?WfFHmySPsgbW)PsT7Q)TF? z8nc<@*>8GwNMY#V*_SEXdBgYmZex+(qUs5P2qeto9FR}D znWVLvWDfjk=X)!N7ToK(Vqd;h-)UHt6A=#=NUz55H+cugHsef_b{M%zvwwVxJ3&jV z6P9`HZ|W3sanx`vLKu@;oww?JQvz1Ifd}e)-{cYjE6JDoLCTcLgcDn0;Uj{OoxS+> z;UQ%chPq>A=t<@;9pK22g3Wwogt{m)b6ctC;A3z(gI#uH_Ncac=_I!+X5`Tlrm6J! zHf#IgQo-4Kevsl0$DhU#-ek2(-<(6sODuW44ZBV4;PE!Dz?v)O{o2!k%l28gMcYQO zIa;{b7=9&v|99dx=4m?_U;@x9J)F7hNZz^Kc%hh<*KxG%$T~5Sc6DIu&Ee0 zfb!kDIFP?`*Z)&O+&b~v`Ha`G@yD+p0+ZygL99YnnY;UD;~g*RW%!1Pw=!#2)BVN7_fq8*0iOAfG!m*oukYP)_=^!K*t-u?^%HJGSb2#U%5Jme2VislCRd z@8UjJw~NUHk9wqgh6Pv3yciWG>KlRfi^9kqaXSh6YLBg`8jV;XfvEy}=)N#tQq1nA zF}+A><*^mCTN7Alq_|yN>sN`~eJYqaZgG7vUt;B3e2dzOcNp=R4p=N6R#yGj!)FKE z7FwL8>?wQ_4>Z4C3PE7_Y--?3UJz`l(jCy8@yqRRyb6|2-xDhjrvmuIUlAwlK<}#U zE5CF;Ir(L8)i&XSUu!C5{Pc6l`FR9IN*6&Uq#xu@Mk#fUn}II(tFw7xJ_AH=5TC=< zGsdHny@h=VyCS<}XfM7(O$ zb{bh9@jb+xcK^QU*|~f$=gqrWU$Cq0&6J_2;udXN%tMQ&YYj)HxS_yW*WBr%sP^Eg!AR` z0Z%RgLpJsE82K{Rha9R+!XSL33<2D zlV-3;4QBdMmY<%fXw7N#g@!Dd^ac-A{16G zv^amig}2MGoXCL|NbdNsXm{@L*YK}W-vY1)Y;KuYuv64%E@84{wxrJ!lzrcF=_Blx zTF|f=*mYa@F&TevA59>Z`u2TemGXZXt)i>a88XAf+5wID8GQNH^+7p8sT$ z{h-xoIQ3vSLtW--joQ{n>hl6iF3Z)>5hOL9L1!B03HCe(j!C z28ry!;CkBP`oIU5PP2S#(A)gj!y58|{v&Oyw9{-$Z_9Gt!fX1I{`{pb)MYS$Sb|^J8>_)uBwm?zT!~^QBWs|zA`gwKbNwukE}DIQqqg`{g^8hA z52(6Wg8o+%l|2vCzVb9KRjf(N!$f@~+5lvqZdP7Vg%clb{3F;OtlE>&xTy{5^C9Df znXG58q{~E1%7jR1hdyo4`gd_ECEiK;`>GQw{`4XOs_3i_!w+4Ks9Jj0I)W5mVEQ#SsF4vQ+Q(PsfyhX3-E z3g?!A)s`Cu)Ej$cLaQcpidEmd_(_OZ^-xTj^)b`|rd>Y0F+4V7K0*67p<;}b@rgxO zcz6=$s3k2Cx>I=d=+*OO=*@<#tHf==dsJPA!ezcc{&}~6=poSG$FFsP)JG^*Kg&V1 zA~TQLahQIDKPX}G^^mL=`(*@l!r$;%bLTt@5b2A@?bQQ*H1arZeX&mn{m?iQi3l?^ zf4CFO0qyUOs4G{NiBO0_muqHi(Etx1);}L@84+MeT{1pvL>W2ky*0tHkQ_i*ni^J^ zu*F8+H09|D5x$=)-M=NWE65((OlP(oxzS!%TW3owmZGu&1aKo_gL6Y?$#&QSWQG_U zQl)kEW#vAX(1JQNmGuT}6}7JGZb|wWB!xx4iU(`uD_8pFuWf9r=k8{lE4I(lprg!v zpDeBT#f=sX*OX}VZQ!(8N{4GYt$lF={^1;hfr7H@!w(qB#04kod2)!n0QCVXI13R^ zO`r3TNxaCl#F7nxf%p9lDIG+4<6g=OTPcLkq2j>BltW|afmBJT4SJ8ZLb!(@sCW~J z_*pXmr(yY#6qM;JY!{(&E(4;%RqQcy6&k?vyuiO|w`T3THuHrwe6%EF$1v<5lttAF zluhuvi@)Kfc178HP9VI>E8`U-l~6(1(6aO1ltibWB^w;qY~WIfMT@)|{Z_0z@Hs}; zdIZ`wXJ7gf77lz0Hxu~&Ec@yY?Q(kA<`1>l4$A!;hcqfy+hb?=Q=P37>^$Iz?U-rGta4u*rGwX+6xpP!;p91*yhis2yW)G5&C z5oH_ZFy>9W@1VMAeX8-Mc6cvpEq)<=lla4m`M#s0Ej#rmJgQS8VmLbRb9(hLaF4*A zdq!k!c>E+Kg-(LM_jfE0`$L1lO~m24)}Kcd+8h@-HvdI{bj~k-QmFr_;8qqV_g~G^ z|L&uH`Rg@Epj+y}qQKM!i<9kj$fZh2{(G9Ld>+P-CoB46+dtvmX(7*V9k6*E$tl4R zJQ3si4O4fkTRxjo%)tA?wmrJS+uEV2<*2C<>4g+`wn_S2{i?ZbY1@K^aGd^4_=E(1 z@yurd#qXt`Z5yoHr$&VR(iYt-Vcl_9=llE(MKbtd;HVhwbEP~{yP|HU2m2 zemhFDjM7sSY1O_$Ijl4E^}-V9-m` z0rhe3Cw9u%qgutchmvajsPANI*M5CVol)rPRwX~45|Lc|G`xJsWx!0`1{9)Tyy~~K zQ*tLZ-KNP@*VU+`msvAkwmAYwsX&Is2k1ExvIeCerRpl)*tfCC+^77`Ra9^C1q4*p z404LrSLO@V>qiB^5=#oVp;EoyKS5F?cP`2|4QZ;MqUFK!yzH;NFO-d;bl7y=I5WTf zOA#6T_0O-9>}jxYzJ}NM7E=&o)_jz#X3Fsq64iF6^HzKU`UAxWpC6SYcFT}6T+l|x zaQc<&s)p~At#3|?<{@Rsxf?@O9kBF+3zQk3aJ4JqlRqowR+W79G*}}R zNqW`!r4z!`rM7NZBmU^O2#GMBbuAfuB|1xX8zd6;nZ}^%eg_BW~6r3R*jdVY?h(Q8(>ERg|Do z+P7jJco^V=vOYUU-yW^qM&Ar~Q@bnBKC*PON53%_0go$2Uw`S&12hmNG@W|`eZ%3o zR;cFVYH%wp#uUStQp@F*#5TjuU^7x$(yunZrcRbSk&IZQH>OG1vn-kylS^MnZ9;b{-f z4}l^MbUrk0F|J?r7-%gKLeZcc!m;FGG+H1FQp2HE);IQ==;@fe&Miar5x9#{u>#v4 z5o(J4SH8Mk|517C31-pUSK=2fV|;3vo*N?5y5h^tkk83F?Vf>885oJFmA@{_50JcB zmLv~UxjCw@7%*Ddk(GQrYR+YVnvXWOA|~2GMpmBu*DiqPM=Fssl2s~9iK`u3sz+!V zb(Ri`NN(*kKjKcFsjc=%I(MD))P~&e)km!yuU^&+jZ>zCTwslL4m(m|dh>rdW}kxivzq_~|JwKxdd!bQ4}%5*K-Ipi)Q%>7H%|KO0ee7?@xwo_k!Erz;J5lE(@* z;nQT^e2?LaVk#V`uc@G$DON(Y&ko7$2~RgXTXrRcI=G2Z(oOK{dbIV^V1+Fb7b&TgTq~4CV~H%;v=%(bN55Ek~Nz0LKBKQ_h0m zsH;jnowEYv=SVe`zNK8NdZonucrLXUQ@HPk^KbPbOJsXuhqoC|u5VUguu^*1)D>+e za~LT#;rjM?#`(hF%t4FnFOyHM=PE?s{9i-QfAY4$m0Jm+Cybfrf@o2JV6LttvBe@Q ze^UtJ!h}vCfcT_UE;g)NoUfK=$CQhjlD2zC09wyR8?3IEjF}g$oJ^;t5U$r^qrz-U zHeX6mgalfhur0OyCJwd4LijkxgmcSd0NyR|B{Yms9VZ3}Q|6C9ZLutK0hg^*)v3t0 zpH8Mf1}eqA0#&n5S2Rvf+G?J;n=Z%7o)G7d9S1Jc_rKxXj7`p$yU+ru&7GXM&*3c) z(B{Bx;ef3){3YTVnu^!g&LZ(ILQZRn#nT; z!;G`Otp-+KHmTP6u&*B*W*)kTj*{*b24Tmo(ik^NpI5t0>bst|+b1z&*z zr*o%p&f?8CPAjFPYDY}DjGJd)(`jGoh_=kBNalQ|QERof*$h~1;+GgM3E^XuG^pUB zobP~RK#eP06uOk(h>WeL&sBZ9fZt($_}eakn{M2#LJ7ocMM$}<5O>Q*1dl2c7#=NN z{H)esSV1zqiDS{c6OepGDUVXzsR~v~ey^`n(fH1Y1w+nn&@zJ!M?FanUkvM|&)+kX zX?VM!onluJeWi#ms;Vd{Y`^l#q!WHJ2+0)-j_AT-Ob>PJlTm59VkX^Us>OAydbxSM=uFJ-ZJ(frzrEs*iESTGl)mVZ1_{jd8t?>;gg{L=n zv>g{W&Cf^5+r`k*R#bU4(8h2J(+9f4!rfSbRYahu>iktWTFm3^K=8=G-P_*g8 z4~i_s?^_3+vRn0b@Vdq(k4{9zxMkR*u30(fa1KeERpPCdjdt(mk_?q{@Jo?xx{PO< ziuy0XAools$NY7(e5BvNj&NuNtJmgjTH?yFw#iV{JFJ*C*}-fj#UC!mXU#G&vvm*G zl+<*VM8Y>kvLHu05bto3YeeVFM@HK^|9Ka_7Xp1bi$&qj*aQlz*PicZ!jlDv3y50$ zD0b;A{%XsUyRhQ^9}3U}4w!B{u_qE0i?2)!eC=6nnVuEYYo+ntuG~*3@LM~g`}IqD zjn?MSaFyoSWSMM&uJ-vNc_%FXTQpa1;Oyf^W619}tQ*r7AFDOGUqznmNk42z<#7qS z$Tn@pb=*-z_$qBkH?nj%@*0-aC!Ql9=o!VFHwWO-2lsezGCWDgZf~X7D&&#G<1}m_ zCois6@3CUE8INVr6_$qZ-GQTTvsfl}$)V;TSE;(4UQu0{?hsN=o1SG(lYq?9S+ATr zr9%Dmnf8i@p)Av4HsF&lk+G+;k0gLz-=s?HaV;6}5#QTFZ*@PW;zOjnOB!@Wg|C`I zH+#e}QveIT+NAA%*6%JLp^1WfXkGea;e)`V1Bct^T!{kTe*3O~I>7bT;?4Ct&T7Bv z=)aAwZwo0<7-i8vN>kdVX}>DvNk$(HyCjV7NSvoVK}rY|EpKT#=E;s^Z3zLw%%Gjc z!FckDr%fh0_NK|?;3xB2(9OneDe`7OnxXcP%C!_{X{Dz^lC3~ZOgd%U@-FX@AgCD# zdxmx5V&*O7SZ5q_lQsq>^PnfR7`yl>(NYNGF3}1#1_b1UtQW4&7>@=+EA24p5TwKC z-^OYFur*^5b6?gYFx2ID@C3FiJLktF92bB+#HKr;JtUaACF{nu5*@6wNzdvKlM+%) z6Uc}}<2)EPsUQC#bh4@>DHYc?Nt<{()vBp-OF}Bim}oYeRQ(mSLu{EB>J>SD0`pqq z%3!mX+q}KPlXw4C(Pe17dbp;XqL^@#;RvwcV44bW@@u$c#wD1G!*5rIEz9(}(>5RY zJ+ZB?jPKq2Y}z12vE=V6_FU!_XQSDbNXLg1YlL9wvNy@R!#pa2$ervyA6jlw(%zVwlFuiWc}?01ujujk+rsU!TJ5Eitc9`o?x-q!LVR1Cx{0#& z6NHY2QF%kHJ#BC)#QwQ`r$_CZm$+1wx@dILA5Hq>QtTa>;@gE`wy61hsRcopbAb=| z@}lq!TVs#r0)XGx9Xd+BU1szxKQ-SRh7Bth#0pALoCJ48bCW9!J<$$)UHoz|_iIB| zhv}etQ*IC$ba#ej#QK^uLrx|;)y?I4+IRP&+kE7Zs>hSEr8u#twt=nu)$2PMaRK}k zyP~6SUeENF-Ufu*z(oq|P1Dt3&TM@CW6T=PpO|{W!-;Yh>jwcHN&0Ld?zsiC;@%Yd z*B2WyO(~1YFD-PkSo3@*FOnrj{0`IJr1sv_Pz^4CzmP$QSJPQ`*;xE5H|Cvpv1zOe zRwkreu>=O^@do!`wuSa}?_nnA(FRbyV?XBjXHAxuz`Zh0cUpV$Go_}|!$^b3zpBiN z$;$(w{ZWJF-1=KWWcMY5^BK6JhW;Q!5J~&Dw(L>?W~Z>yKi7C5e68Tuy zmTMDJKLRF&KK_1=jVW*mA$WiAV}Zg2H(W_Df>gIpSX#5K*y7crtAH2nK)jPeO7&h& z_rq*mIzH8@g8@LQ2Ia@!+ge_jo}=}{wccZE=Gnu^b){;|Zq~WBqaVfxL0dc>G@kx} zWA{TE;%#o|wtWbq5Tk72A%tVMd%?7{4qEaLHth982b&Ijz4V9wf%t&4dIh_J?H%rm zCAb-PAtU-}=8sbUj-sqwb6y#M{#mE8P5ujQC>zmv2qeXjN?1LM4V0Jte+i%@Q zj5Sb@me#^QGG?|{qMRiImWDQF-U{V7J6#jXF0ZhOxaJZgwmNdaPUq2M@k&rA7JHBcL6M(J6w`d?ccA= z!Bd>G`|J@)l49`}4ExY)LVWRCgA*6F^35Pu^p;@ZgFe=RL|w+|=m%-VRvH;#iBv7B zBe9^Zo;MO(n_v#kD&_M$6Y0iZ?wLxlXsgxGM z#}FDV*5gCFQaF{9dfPt0{a#N6Bpl|tDS#LG$&L?=nQfy4R8K`4QXeeG0&^MpbtnGW zP*cM|s=(ND{%g|jI-DmStM-6qpKp2ktS$-;7-P8iPsbt)vi}a^7QP=9(G zdu(iSnEBUJVuO~4D~4HQ!_pXvsd^&B3*-Bh$(3TC5*IV@XC3YlK3M5)Q?_ig@ORS|0usCZfGPB{#L z(oTX_gY)H26M{+$)VL-uB*<47!IJqDu&h?eyDXAQt{r<$qAsR$_H zcgN=gr4CwrKPHr`UmLKQNCF-$9o5nLXZQO_LxoZNqT$ZoG2t$crsxFewW*;H?CYuG z%eDig>5{nA%<-gI-8@f({>$F>#M=IZ6|4B0=}fn2P(Ms_M9(}3F`;QWCip?rtUcn_ z-SRS#6l~+B?Y`*>sIxEy!%k}wv4G6axTS_SZn;*Oss?tRY4IP7GgJ+L_J``Ton;Yt?hrAAL*6bKzgDIUo?pvN+t`1RHO@RTm5?JldRKT&%ZNi z<1_Mv$Yt}=DSJh&LZPgSyRu$CxwC{fR}SbTcIf}+DIvWltq{*JJM*&j>&Q3tt(s+z zKZ4%ri<0XrwKzEeDPOl>UOT&wfhpKL1ta(E@^EeERN;KDW2>+SA9sUQG<|?D7->$; zdNkm@KR%W3JWwxIJ$LxgzG<}KQu5qw#xS+)-En#QS@qN++z9&4*HZVb+W+~)_n&Z5 z_4t_2Sh`Vl8xBt8^$jQpz)IThWmNFgURANQCh1QM-h|qgDiWhsBC*&8Z2W`;xIW&( zxzZ~ih_|zbw^bY#rVG#uFn%rjc+6i?(fVYbWMfXAzUisN{Py5faVxTCx!Xsr%BD-w zwO)g{o@FMj+*NZwGJXf0m$wHx=Px;ohE;&?wqP`TyUF8Gg?4jK_6491uJqqQ_Ra?- zk1!>#If=~uo*q;dCO+Xl9EFy}ZdR#8Px^%i+jt^K1~%{g5OuW;{vlm_=HN? zHdH$NrZ*zjZ;VsT9Gp}=xr3JE*R^I{!1)KP(ywc}rZ!E|Z{W>SdkB)mp4L;D?)M7= zqQ?3X{*3#>bpk4?oG6Y7Olw3fYV5b#{gIH>0&tN(h>rk^bc)1&TT56IP4=Q5b9>JA zj!mpYzrgX)E}B;&&cHdXV~Yf+!ahiQ_F6p!$udF@oxc4$VZTc$b_ z!ytZ}W%Zz10sP&Gu_MoF&yK#RK{Bk?y0SiZMQ2&sSx?oOmf&d{VZKSVx%hzBe}<6A z!P!&q+#GwO!oYW^`u54um1G_4#lPq8R@d6AHdbL~=8zB9WDZ6l3XuJXj=_VS*#pC^ ze>QXV2evb*q_v-#Tlt*uGTIL02u(^qc0St^&@KwS70L*#5(#B%4Cnp5n|xL3^;kse zLW`)!yJ!Q7wP}NHL%k)b_&(}u@u5@4><5CY2A3=Ky#|DO+%oUGzKL!85P4c!QbK$(3YV|Q=5Li3-fi8{o(iakdwyUq_v|%` z*s`o08P@yg{AuA^Q@pM@$1yi}DsTo%F@t)qWLaPnqyDX*HFDfGX>(W?F@>2u93Gcr=9-y{muWWiOAm-s%x&k)4XECKGu`neH?93Ln)>}h z#EnG$&na+;mNvTx(w`Kqh|$2?_D(xNKXQ}y=ac@p!{W7=oXM5$9PwJ$*2@d{G$ug! z56d1!zn{y;)Jw9`3zSa=z8QKJZ)#pp(9{RH5F}s+;AGqoLU7Ar}-U%gSSH{ z(?9h79wAF6d_%wgcuzVoG-LTJbnjOW?`Rn;DOOU_f zN{ex_<;rr7FfmB>m*w%uijZ%b!P#*JKW4&kFsI=GA8b%-_`Jo443E5$(47cOo39#u ze}7}16gnG;jOycRK=o2HrFbPc{~t z9J&J=FNC;!EwwAkC&26OWX94cV|8pu5%+|}mcug38p|Enz@R&Vo&)m9}a@zMQ+q#PN4cxzD`$mEl9pWezNF9C!bpkQ30ZOS&x!GM(2FAB zO>9dA!yf}>bNf!p8XZ#VZXj;ROL%qtObwKC$l(s0zniJ{0zUn$3qRF48XIm>7c$Lpqkn@Uf+Gw%9%}b&9gTA?hnZ$<{twZ+hmR>&bIwj=ywF z0d81PtQ`4o*VFI#dal{<8lwKmzuw=!8g@K6AEBqN;f$`nZJthP8d0#Qu2rTkE!%2$ z5IqY*`$$8nrRzURr@0S=!&`b}37y|RY@U4lbiAxG-^q5V_u<+*Mbab6-{Ss**CmI; zJXRbpF6CYLk$AQ!j~fc+@0!#BO-^i-`|3-yvVSiTJT4X7Df2?jHbnK$t2$x?zIR&q z+ov2q5tZ%bD|*|g@88s-ysXQ3``wGQCo?xM*S)2OjV(L=49KslLq9lrEQ~KlUzO9F zKYWG?n`r3cl;swem`n*?2N51sXSoA+9s*ZNi0KVoo4<#T`5xlf+*=BChRCo==>{D&M^){@x~TBu!#gV=hMH6vdxl@nGx zolh67q#~aS`gtZrGIdy7{alPk*&ht8@Rr3CD9hc}Z&7{??yG($%bOvh(iJ;l~aJ6CtD@ao0dihL%pu>A)&&<1QZ zWfIZCb)+#}Pjko1|4MTMNj*hB)YoJ!My5p=uLrzSWjsfD6Z&mH-WaCU)-K3lxhJxQ#fD#^O~r96369?x%xkOemY-uY(uk5Fzl0a-Ldk0Ka+Cnfs0LOsmkvGwu;W` z3wM)=;~MnxvzpWG)%_YDBeASpT(!=#*mlAjj4V>Tx5bibT|xZrI*_A4oP{3?h;D_p z1|KZc%rZV@XDhK{!?u^7`4ZkVT}vr%T(&E2+!>fE|Dz8`4cBbue$rmWDH4e-KpE&G zoP)DDK@QTnwO{O2(e35)EV)jSz8?ZqE(#P>8Ch>22_1H(fE>a5_>{S87pmj}D$5*h ze19$-mE)w^kHW`$Ianpja@!Gm{(7gZY9Yd;BXpP8j%i*`vPc84;pwo*^&fC&scNd! z)Lzz7P6Q6e7r%P$cEHk8;Q1k%O4|+lbEX;ZQbypMiGSp`e{R?i8$9kiCh22KFQO_! z4G1kEk;K^X8)9Bvt{2|%n1WGnS)zWrUwnLiAfY*pBiW-vLzEO$w&53tEW-M$*LBh$Pr1@bsN>n#sH* zuSehu-s`J&gOGd)G*wlA-WhgNBKPOBstxf)%iOV@z-!5kMh(xyMwo=KHRIo=fidO4 zCjQ>YVr(r~Y?zeJTAxI5)^inSH@yb6#2gHU3A_X%D^Vrtd)K+sk6`1$i%WkBkglpb zF@PX2hf~1s$JRRfB@!rf>Ad9CK+P)<@+aA@&R5QX9$y+a4_XKNJbUaLi?`4R;HT4A zQeVbbqX~EB%;VO_EVlpl{O3QSJ+H#Q@E#P|o-)&bV1u-IO+GC4t}xbUH|VjhnEZZQ z9k!U%mw_?#la>t*8)bTvOPM@D=kHDbN*l$wh<&~LP0GWW_!e~5FE}vW#E<3nRj3<# zMdH|^PnZF8I!d`+6QNUR2>(Pn#mrLBo2)B=5~+p*#bTLbA_T<)StoCsmrZ_g zvA@&G>pcbFVvCkm*c5;NcCYFH-$^~|>Gm<8wM!YFbLC|l=P9dUwR^Z4mvet1SG1dR z-2Hn=>A0)u>BlNx7H`C}Y-}<>6*AuZ*n7?T7xa@*8%nM~k;)>qVsP((+x_ZuXYQA% z>Z;ENd9iw=F}}0&74u(f#M^74Dp66&eEwRymwk~+9a>Gtf{w{|sDxpndmspM$oGx{ z@K~D~HCUZ@#XFPP)Ki6J4}LIu>CV}*d#@|$ksDItnQ7Ws=@&6cDRP~P`V$N zleX{leWh*d6cX&kraZ@EpHz-)8Hvk4duR2&A!VVur{C!gfe}Wz3!nYiC&uf&HK=0$ z%%}gi2KT@J{9<_P;jO*Pad;b(jRx?v>!lHk+{lBBq};iCMIL&9mCjXUrOftBQ~lvC zyy~<*zuZTKR)s}hW%_*Fe}U)iOE2wX@F*OEXPM)F01y9nsWj4OLyxgUGn6W8Y>~#9 zhOgye-OYba_; z|C51&r&$*7CV1FW-!)vf2HndclgsI5d-ymPSGK7Eu)4$R_4><{4~Sr)9B1B8 zvJ&aJNBZ^ZA+!-SrZB%E&itbhAyM3sZf25HJ$REcJRq^yqp4K|md~z*Fw6xwSG?Z2 z7s7rbd-j?3uW%R?hqqdcCE@!gj;>E1Z;l{be@=l*B#6cG9ug^y*3-3Dn;qu=qB@uI zHVR##46b0Y&PGTRNQ=T))sC;hEiCeW=EIaC0IX*ZEbEqiw@%>H>8&F9uw|c|I$^yC zN*Lzs3Z34Rl4~bk{K>D+Hoe!WWHmJ$mntzSIY3ain<1%dRgDn$%x9cup8RyqEWWRANI~1>%VJLfG2SewdnP4pB4G3&L5h5%PD!|< zVwy(~xI})XQupoqYo6gfn2JhzAu!JN&Qz^ciTZd+qe#BT1@grnX~Jay+A~4yWb|RQua!SJJ z;#&t>m>6G-Yipj~WsgZknN7A$O|ZO-vs$i1Qu}r_AVqFy&8qnHRKZbeJ1R08Ui8*u z@NCB?uHR7u@$$L_Zueq$E~yAF;m<6&mCCoj)gdur56*e18z-f#O}0gp+)DL(yeZo$ zdq8!!?q!#|c+F*!ak)#`KZ3Qi)33fgc3Et6*AJI?%w0lKys2Z?cm_C8yBrT1&!O#U^F{3D5s88)cn%Jr&CRI!H&UYbwE0 zEdepyo>K1LKXhNaq+V78!Z2;PS*vsx(f}5LOd@J6dr^+-TIfv1$kTUGrCSL|f zYeyK=J!x`A8ian9GB(y4xDq`xknllXMZiMvxNo*DlF>Ze&!yZrw#X%5emrLC6j~viccRw82fd zJ0xT-Pjn2Zk|{R6eP2GRP|_*Vo)c^?r$Ie##m$&Z+e~x@Y!N?(f1H@G3f@;8CBSL5 zNWip(y6pAM4kdn#B}#tnHPxBAnj#o^^2$*0s762(w#cp93q-o=OZ)`!KX81ABX&($ zsfhB(H3y5ClPsn9TwQx?0=)*Xs3kZ62swTpxAZQd>#w{A6$+>f~^p zz0@W*q0#W#QYOy1z+FYa&qqrTwA;{n>KN5Rzrv$TTeRM?r>0e_2Z#{@ukKv&{#6= zh}K*&zs2Ly^A83hgJiN`8qHjlT6%u%geI1|$8(WvHbE-sudXl~agS`o^BQ(Y8toF( zL6~c&j&Yq`=l1`QPL7kzi_^zLBuwce2?4y+ePtmY&V@8IlHviTtd#G}q|Jl6JXR0~ zF9+v8XxM7C3*I{p2P*#=4wgL=eNX9&?mD?o99NXLknx2FH~#g#qyO`J<3a!Nz0>Jo za#b--gyBYvRoBwcr*7k|Il~$~IunL|BvbNGYNg9D#A0GtVY~&>)Mq8Zw^b+g*}!Y{ zgK}{_rc06LkUaCq>N$H$=wHbL^tLe-xv_MIW%=W5qbm zRQ?!&sFfotD(;gA2;D5=oOMKPLlKT%ake{DXcji78C|pd`H;c9#-81#h;D}-S(VEF z2rc8P{QISd4E^<#h6*Qm4A;x2GAC)3A^yoa@WG9;e{Tu=vw`CQ_o*{4*P)`=C|Iun zM-Pf&f*wPoa-0FV?o8yo!rnwTE2JTJWh!HpU>|q59P;5suU+QuQ~Uv}%Kq_C;@<{E zU&YF_?9#h7WQ+X;Nc-1ecVZQHd|1nEBw=q2qJz7uE=Kp7=RzX=6IVAITGJmGRhQ5) z6pC-)kwxxJmFk9_SCdi}F4y@j1gKNGYTssIvSiU@#GQNcfh7y&p!hF(TOn*73S+~( z-qxG258o_TIm%ffM2Tp_!IhCQMc+)T&r(L29|BSremyH!UDdwT$nCeZ_mw09dGODr zQayizr>_QjC*nD-a|*HPqbIy=fR1kwi+Jo}L|lR1A9L@V3f!0yCIyaf&9~eWF9r|{ zYeau&*O^n7#?Rkm_nKhde(RiDvZ^&{75EqLJ#}FS$NOi#!}m!&BrT4@M_aRmR{+mi zV@XKh=TxbwQ8j&PTSJ+$Q-JO9o?F=PA<_u?eorNb6HV@fmF@m<_Qf?KLYh~UUs}=( znT6{ABMCckhU3xkuteRogUb1F@9$uWOWfMC-6xy`c-2to4>@by>Y=~Q8`18^(b2Q< zKceFcP*2=$=mkx0F1f_W+W%<4bft{JGv-0{ALNN6T^j>(1n;yv|AfbM`hncI!un1< ztaa2UL}9cSZlCL_{$=KD3w^Q3#Y=`r;4k_Zp;@qcYvmM<*nQovWM;L2ju`M}JJxzCMSS&_=bej7$+#Y28 z2Krr5;iV7q69y2Kf_(5h(xPgl^l{Ue0^u5(ZQ_upnQig(L~1bmvRjP%lI3NicY_HA zQcA*CRY)tm$?=<~i@q{WlowAqxOwjR(x1s;@#3wJvY)4$sOy|th(a<%C{iprx zs7C?xZAcvtkYFp0r<&jP1sntaY%2)^mY}$+9s@t5u9~v7`s=fpE1?Xrl}9&eXZII5 zP!~St*lK7wVD$)BwK-+s@=g>D6L#;w{R%+^fr!e<$z~z~vq46oj4Ou?7TxNTjR>TpxFwe^@2Km-09YP&Bob?9P>G~Ur^rFy3~K3 zVXWD8vg0>?-qTcRrAl!vdGN$IF5%$its;I#hJIiCCKGS zpD7r{-%exWb~(SZzsGUCewy^=rcnCX!7)F|yyKi|X@}!s8mu?0yEMFX&!-F2+}r3w zuwR)U)F6ud`gHNw;=O(0c3j+WrP%TR1P~B^;fs5_qCr!Xm;SvjOOsz%Do>>azf1me z7`fv%@SCA=uPX^9>SL+Ay=}1j9icok>y-%ygIvuWYDpYEROOP1`W3p$nd%8g_?tIh z8E0A>_N22nnTsV$DGxaB&5{iD`-=@T329cNgg9H|`+40{eH*%J7LWgJ8__?N(5Lgq zW%QnW!f{-@u$|V=JN#vg*tInogI*_|+wlYOD1f2SZwYRF22_m*zu{5N>vrTLOeBL zywxMdvfYL5lB=289l3nLSx#3Hb8<03v~X|yGdHl}xgFcPYaML>Fa z+)HZfK*MY>x}(WWg_L-M_og13tiz4|al^u~l3lnpvBs%c`t+o_CwxTz+NJF)GhF_n z9@2i|2>E^DMXX-yaPK^t@g^R=njw-5;cNYs>%Ha1jTk3PnUaW8;#S+Z{+-|{wt+nL zp~e0yr>j$EVNLx`#kP(U?4JoQvh)mxJn|YD$s12}(R0hq|AF9mjwu!>Zc-^xSqFYa zI?*)}+Y$oI%qYidaa1pbZ5fa!ncr|gw!7*NuGMn+_IQ@O8J2UI6 zV6|TBw%gLi&lHHYVi_r;FgjfjeTnhP#M}$hJp}ojsSXvy=VZGxTGOig)22)2m-Eo$ z66sWppMc;;Tu{#j?-vM-WzLzg!tc7i@yHf$`l#dB$_=MG_~9WVYkVwhN_G3h>BG&q zhx2!HilJ6{2ncmaJ_{cWn2VMh7AfOZ%1cRfMvT{{^tUM^J!D>)8dWMU-r`#YD-o)?8!x-BwPxj;nOj_ZJ0@T4Z=Q z%Jgy5w;dB%!frKkyRlS=)Jiojp!pq|VEYJaX_<;)?mNk009In9UTQj(YFz`U``v1! zt={k!{5=M9^c?0R}-ii>;V6*dxLJ?Y`681(v&zKQ)oIwYaL&pZ3C zTFc)T2%m9)-2+C?^1;H!r(nf>N&t!q&)=9kc0S7iVM@3jB4N}FsD1ok z`dREtmDo9wZB<1QtDIk}P~s~cabRyDkgOa#eU71*_&~1sV@h4 z@F<)_%1Q?qMuBi?me_L)na3kAPNck0E*k4<;9Y&rBXLAardZrZngfAxGMYACRGmr< zwmfg|***E$>VGa1{IAk7OYoVpWlUE)#o*n|$12^_$OF%pKF7z}XmQ7tC0%49Sv|v5 zO4sL7VlGQ+dX^8iw_ixv;L0)_av?L|i6G#Xts0mOced^-70H=VGv}NnAG4dIwIWKAeyui|D2j_=VB`-O~O8 z$(RaDs|fJO)w{}hv!pNw$lsnbiAZg(RpEU$% zJqTz6+@0;WjtY1<1FuuphZ=TW_AZKC%XUE!`UL)*(yg7w22bZn0kG9pY)hZcRY`Fb z&Q$XUEO>+VAwq4;oXD>8H*JsGaQ`} z2b>HbSp0-&Punbn?y`i(Ja>%AYIwXCtB!rrAMtr7Wh?QCDIqdQ^DXsRC|3tNsU1Ah zOmSEQ9+WTf{UWRQIrCu3{Qx>ug3kRyL`O0wFC$NJ1Q5Oxu{7Z}Y=!Q8fCO@Jw8}kJc`GU<;Ln8LeuhH>_{#dhcW8($C*)JwYG*E0_6c1>8X0!>+Q&-71>RBxjCU;A}+>D6sIu zY3roov-dxvtq<@3H;6Jq5Bb&Oa-V_sy`h|)@gzaIiG^WrMO#WFajxvO%pMxt&J_)Q zzwJ)9zPn+$>q6X1XqI$j0PLtYKWhe0!VfN)KAW5ssB#jB7t@6}t=Y*?HJ)O@65NgW=Bd-Pb!87oKTz9cZ z4#Fzrb*YbbzTO7xR-f&H?-u4BbSUV}M5yJi0~e%(FaR(*;xWgJ$^d(TNHTFVl3k1q z{t1@8ziQSn0`X|^*w}ljYz4);tW-Sg7fBC%I43hor+ludaWYF{D|`$m^@tuOaU(@J zsqa&DP2n55i0&itvp&2lDSq$tQW966I^Yj07=tB z5BK)GSP$&FcMZ5bfTot-^F9^H6gihUCbGal;!o%Z*l*-A!b`$dkWs@|UaxjPfGOI- zdqQ=f`6dmKIu>_p)5<=5J^C0OcKA{H<>g|_2MyoDTB5E;{niDL9Qxzh9zr!6YYxAk zbc7T40j^f1ca!JBfKP_J$|itn-|RJrT^5h-5!3QD|MjMwFOjGrp7-U^`F0q-dy4cq4JJz$WjZ%(bS$62vLYy{K0da5vo9C1@qK{w=` zAQ6EJ8H(s$Glu@WcT*T75yE4ylcCILTawLlPn}5`pJv~}r!I0E+-?hVmfdx@6th3H z<2mErUaS(BlB-*b*^kc=E;@akKpGdrN~((YnAJg06FT`MR5Qk zEh*7CuejIbF8Y~i`4cA>pkajO)3{p>RnLtfkmL8e`E_ZbE0yD;eip9ty~T<;dk8@W z7sJXVQA1gXA+QMKkv4Rla3pM*&nKX?t1KhANGXV#=dK9Vt!)9?{jFw)L{lEp=6RWX z+V1zk?H2|GwLmcS0l|IG_RM^fOL9m^$okC<^li?VOInm%YbXnIe~?p&iZk4;D5VPl zNQJ$i0&Yj0E+j~ud?oj#ZyCs=0@|HXHlJgQLg`s2p8 z9Qko%+OI+Wl47=UKKC*jZFMTS9``-i&CT+Dff7Wo`!E+M%J@0X7h>wAT3;R1euX$D z9)TRH=>DvqvfKk2KG&I+voj)IN@b-&OOFN(_t}&BobU{~`x#2Qn)>dupb$sV`^dhS zIdh^3CST@A{*Y++%7~uG6bYA@6?|CJDa1&ue6a?@weD`9vm!)WR`i8#NfSId;IT{2 zu{=~qt4n=1;v6yC$L>^2RIJfcr8GL!@Q{I<9J`+dp?<}+RIb-r z{pxg(J#C*WoJ%Jg_?(O}1G1)iuNKKc`ua?h)PCV#^I7-Rt6$x&;6Xp+e+FYaH5)=MlymEYG< z{&I;whcCQ1g6fOBq~U!-ofJ1!(TnJynMIPJrrQzB<5&dT4uck&`?a%SZ@#0)6Q^lv z^b#@Ka+xm_tbLWN>GyhyaUe-eGtPY0;$u!YO~Jh2qwWLCOS(=^hP=5Wji%U_H0j#R zvju0al8B?UT-L{0TAMcg)Hr9K#`-=KPZYO>(KC?XM{6x^f76b;fw$^i=yBYTMrdns^6LhBb!)tI z8ZA<;JYU}NEB@C!v!fV{9i@YdJ#tyOj(k&j^RM#l-ddZA%VtvnRIptv^e9t6{wMGI zbaVtF7AOu`9fGnW0DsX%p0we7oB!qY$;mo`;9rU1Kb6uxNS^U>g|D?{Sag2UFcS~K7%`XB{)LCl^5>+?c7GU;R-WMyTR+H+uCjb=l5pyS1|O+5#Hg(} zq%t<9j9zyR1J|gnghRK#!#3+Qx4?_OI-lxtVlu}F)@5Jum0O;#DM#bdgzIsRC!(Y5 z!YRuHTU^d!Q(xWjS9ncw{o5$iUUBx|s3FD;OTqu~M)x%@YofNWR7}Zhfl}~8>i4B0 zh6dJN596_a+;{k!^ZZG6{F@JGPeYDj(`F~IR|Q=%LwUDE_MYS(UEgaA5f!6FltV}k zgiPN5Xxdye&4f(n589iO?FzO5_Jh_mw5Eg3WY4U<4=wFizfG8{3=@={=Hx~^O_?uQ z0b-oh$fy}D*0#gATFGZ{Ukwl2OXBD|^+`LdXiF2mZhnZ696IxCAPeEo6!kwW9Ep+@ zKfOlUKv5`C=LHq^?nwFh%hCisBZS+!y;9PnmlJQtxt{DQ;q&YHwaa3^IpwRLo7+p* zn?k|Zvwn%P%9D))3!TE)A_V@tw)!)l2#0~;yY@mhb)3Sf!bUiZDYFc##XIz^{VfoE ze}w6{*q9Xed}k3gS}=i6dYJMwP_Ss;dz`WEiP*(P&H1|hqK^BE?^+T;)Cen>t4&69 zuuZ1B`q813C|_@+&9?A<&=lT}U9XO)p|^6a1M#;fbEbUK1AcS&=BG9CU#J^?BvIWs z&v{pAlQxoHCQ=MuRD5qZ2vSlTy?aKL4a2T~{M+;OAnvubfdQ2X7VfR1`{r208F~L7j`;x|wx~B$~aOyu1^v}!t zt!E|vx|`-77{;HmXjcSp6@!a{(ue+0)$Dht`r9r9{W!lwk#CIi52XLo1F-8S;f2rE z$quEBzlL~!{=Yv<|0go=6~~5eC-y`I%71F+&vO26cmrw$ufxMVUEc98Q1;(^{qJy~ z_)$$n-k$kI%JaWi6!6D^Lfg}W@n59^zc8^s?9>wWk2*3U$mSpM0lWeAq5br<=KOqT zXGh~SdFb~~vns+nqxRer|MM6BVGV!&bp{14l*>EZoPWKu_Yk{&+>9Lxgoj#xe)j*Y zmH*mxehN5J70W+8{ntqKX@3cqqKki*Q&W|1(BBR{DCZPMsp>RU?NWtLxKVaPX$BoTYLrHk)_FsVFH%ox$IfxDs zRhf_e4;WYc(XhcOru#<`*MC{rZ}R_t()h!E{D0E;BN+cLq*3Spn~OSuyQsL6gdYKK z5DlCOGo|lc{~NO-juAn$55hroL_&enpke$EVy4z}`ZuwV-2~z`IgnSL{9GA}U`TYy z?p*r&4*yPf4WQl`XKGk*HP6hIF zS#-8kpZ3uo;pb)$GSa_cycrr_WN3Kxl){ffkTEjU2Eo`GT&Q_{A&TS+RizRk`8WJU zE1iM4{UU(j>Knm;jDSGDIg~2QRO^ zb65qlb(8(@r|N-e2Z#_&qz6P|6zT77v%Q2ovb9ogK^?5#53g?j(Nq42oZ5HE;j4Rm zVbAYAzfX;RCEyHtJ_JcIX9IrQ=XMGj0&JqT{dTf+C&dSRY2f|6>{x3@Q&Ur+0bY|I zh{)2vOHEI`=H+|Wrfz1G31F=cUm@1YD>U|Nr$Jm7h%el@pg9X~0bJde9EUc!lr?yN z_ksb71#vg^O>Jz6c38#9&oRG!BK#vG{DXgTIYIr~KiD-PONjIU zFm>l?{mQw_RjiX^~*2fLQ{tZpY?#fy5BYI_KJj)E6BR5kSA8trm`=8FS%Nx zXy~Cue1_b~Q6MAln_w0d2g*xstL1W=5^z81Id^4rnKhU%>MV6%ql(pqj!Ps179rR@ zB=Dl)Ph5BV6NEK99aFDDEl=fVTQM&{KVd7l6+K?9*j)-wmM~36|1w1^)Zg{o38Qzg z0F5kgPI{hO^|D$)XSDmkKDkkx_D6`|n=Anz;$s=nZvD`~!0n#Pr4&%u+nv>UJM~@d z@J<-?8PD7!hxpq##qkus1}X!&$RN0mIPC%3VTG^5y!Z@t7$?l6bmpNSs;4G8Lg1r0 zzJ3jGq2BYLoCGWFpe}{$nNp~-&zymuz`Im>zp>C?$;{B-r*-0}to8;{@;K9X`QpkN zVz7;#@Co^E+Pmlb0O!3(1ms4rU5LQHgtw#B=9SoVBs8V*z$hO)0pN&Lf>h&z(aGux z_?C7=t@ab#Tx=$u6mm()k;AZkv z`WWs)fmkMPWE-)JG{=6Y)VmniRC$1j$ANqC27B0b8oY#B2r4;>M2mw@>!O2jQb&5I zzN`L8^1ZZ*916ByGbk<`YegaYc*xT8h+#RT3gBJg=&-bf{S#{AN%MK9SG~L95n0ZK zH$ByTA@b1!o2;fKxXUXk*z&fAUsF}qDnY2m6xEcJNMK$dK*0gWX%HZJW) zD=hvdHk-hKvt|x?&7SFPsSfjz$v#a{aeM{S`990nS_vu!rA>J{Xn%&>b%ZXLKuy6j z;U48OS)TJTB730TQOuvHDf)RvnbisP&{FO)wSmbco{7363z6Ck3v>!k2wd{p4bZ=m zt?%vQ_l8Sg&*8_lXOm5pO2)PFiQvls61Xl5ZJ2F8Icn)l^|FgikbU$)s3}Vmv}Oi) zAFWzCy20PA=CZHvxWAm{l{4`O+$Erq1$^Zgn3tzq2LCN zuWJL}Opic5?z7$OZB`NSvwG@_+efMAmP~zFrLTm?%F`_`_`=DoJCn~qf@;|3@J^Y> zXXxV{5^Et(hKPR8iL#GRdRkDKOnzda@cQr7U6)^d)4tQyZOy(%SD~Ef_fLh`x@#dt z3T->|x8%Qqk0-?OkB@?h{$2S zxdDyz?m!~bA^Wyz<#%BZ^DEtM`_x++kte$C>d@yZ&od_D*5<>Pshv}Jdh4=3%BeG` z_mU;5wKglNFTAWweN#)VvrrQDF+YHkS{W*eeQ)uYUvRhujKtx0-;SkojdON0ZEt)d z#mhZ;i+8+EpM}7AdR!-VtLKfpv-BvN35GSfI1sEG6;=;G?byaL6j|s`(P8L5|+Q^FcQT4H3atgd7|BZ$GXv@1Fs+F&Y3Hmi4z)&XeG*Lb?%` z6|1UbL2K)0Pk;WQA5VIPd7mHv_?0xsM4^;BD@8qzx#1H56M?30`+SIqrgvKQ6q^Q3 zFz{dj9YJ2cAJ`mrpQj6o4>Im)@i=GZm`ka|dpH}TBlRh#xYVnK5B?5DKNYdgIzr!N zu3?Q3VC?jy;}xMpNW(UdqBzjiWs7b$8m(aR!09HF;(X+Na?SowpPV+gF_6F>M^Qnc zfd!)x76>isJ{rmJ$O55??}bs~t^Gn|k)>8~pORE=aUkGr!Pp#hjLQgYEJtMasbGV# z1wEPDr|;%H(yu;CZpTAKo;6Z3vl3Zc@@h+aR2ftMO3@W0@(?(nm;*T}Br(%KI6V#fVIytI6D2UuY^uF+7 zzx0Srqj4m-Sqbg_!hNWIbLul7Q??{pRa3N?nR6n=d1%Q4RUqPAu7PG7>7WiwK{VN4 zX3+}q#4BGONS0K31U{aye##x2>`&pMdE4kD+E#Ovyv>?ctz0rHPG9UH-ll@@v`Byt zfJ4b~Ly>3mSxHy-2m;(VmX>Qe-67x#(sR<<1L>+nT+ zSYzpz$@eI9SLvIL>@&DD%VI$j+`i_;G9Yd$q$yZY0madho^BNJs1q(qB?EXZ% z>t!Nh;b+GxWW)5qH}xQEw)uK{Dkiw9ojI0IqTQIFMqN;|2T6Oy=!-ZLrp~9kqs>=3 z{(O1$tvc8i?s#g`C0GhA15%}vaJ?TQi|?W0@{BR-1YTe_H1C-{0D5!0A#gPA9F!&= zA0Kx{{mv{LOB6m55&lhXDB&IJDk)CnXPTc-+ix=Ofe~lAP{j8<+DNh<{Yt(AL-O*; zt~~JT)a)oFvP)4MWSghHrH9p{UQB63Ol_l$5)C4TRLBXbxkQ#M(8gGdhInA|5;sGFTWO7>iIPg)!d-F?3n zrUkeTjYQur9pnh;dymr)Ao1VmFcj}T!9r*}tg9XnkU8AN8KUAVB8-^SrFsJMnt3?c z5^?X=+mw1X7>c%a1>%o_D6X$b=nF`tx)M7n7zZ?$9_l8s%#Pbq3wpWrS!>JNU z{m>7X^Ehsr*I4KN=3p`pP8wSqJo@<_>8GM+oP0Cb$hx}l;+?Y4FQG<+I8zDN;B_u< z7Ui#?_NNcCAWC60l)-nrAK24iK1|B998-}ODweExvCDo&JjnP$+QSl0BK;Wx6?Z$6 z+)1V-ePgM~DPe#@Ku4rbK4&#*-j>JNNP@_{(^8ZR0 za>M;c13iwW)H$A%F8B`8={?At2@lHNI%x#J6{({C;xcgzsQmdjFHtuhx%zD%b9Hb(}^$esp_PbX2)1ESF0d!; zYfY`)BjH4g;A}2HoLiD9=CN91p}%P)&p_E&Y{{ZIu|gVDU7@8HMceNfncO8r#d@=O zZI+50&~W0Rx%Bc{wfb`l1WWAr53v6`!)K8AjzwP2)mU4)x$*AKH}UMwR?)5ju18x$ zH({l)jJVNeWqpv4J@}lwyfIc2rZE;{ukE(lXmiGH%+=rZ6jyUbg7a-pSm?85mFCd; zZ<@J0deIt<&(5f2lToQSSK9KYJD-Q9^n^)_(h(CnC@Co9fUnPT^qQP!FX}oHX>Rs_ zkmcoFe*RipvO>-YOP?^m`F9uor$n3BQNLa|P1rpMWf6MAW2I(U3$}ff`K2A#3S&%Y@V%cy9sQ{#!r1esk`2yLsU)dA*Qod0 z_$+Cknf_#7e^Be5CZfqEpXG!4YuvW53Hb!oT9!SA@pvyP_jxqab|W}y0|EkK;Ka@# zzSrU=+-62c5AACN8NE!x^9ZE|x;T0Y+P=ux{Y17UrsvP(lRr&oLpkojFkB_H+lB>s zEwU_61?;2KYlcnBY0k83Yx$L94f?p+L_HexAavlk%gf6f-`TO5Yw>`iCZa^)o$?>n z_7}Pr#5UA1H|%M#GPj&~ud|aTY@?~@o%5IKu*!UI)yMj56 z%5VYDS5icSL-{D9HjRTK>YP*OlpD2<+%sI^ZvjkQbK_iF+@q(FUaBi+f)4mZ+*^?7<~Suf8;|X6rH|FMc<>{tLIw zl_3#eBv3#5*8%=(X_3HEy{?i|cKa54#CDxWS$11~O>;6T*D;TGZ=YS=wcR$jaisT< z1VYbs&}@r^)=C{9n3Wa+6Oytvw^oI$t7ol&d=JAuMCo=lbXl5fOG=^Fa`bM$qp@HT z2eQQvxYJDmGEXe~Xf`d@}`B8H5QdmvnM)dXB? zG_iwR#I#S}Gvr0SgoYj|6sRLcu1YBCSwQOND#MN*-97?cs!<_Ss+EV@1~>pp{B}wv za!Qe=PpeDXSM;MZxmDiZnKqXQfy=|VEyP)V7V#%MN5Ch1`?{{VS>OP)(h=Ode=Nhx z_H8~iug+E}1)rDet5Do+a#E%1Sx{bGPKz;#cBcbrU@ph*wk@_Kl_>`72XNewTtKBVx+tNf~(IUe)yDa)zFn;psZwOu7W zXF&s}Q#*+WcQep@P;Sm9&ByBr5l&Q9csi&cLMr)d^2f(Rm@6f)c|rdf5u4=VzG&=w zgg7Y&FjWL>fCfILh=a(cw8KnZSHo0ZqD1LE{rXHCYTV(eeN$aQzoL{vv&%e0J*oa| zn-TT#o35s(`784haFF@SWk38op34akO+@HOK~!Q#ni?8oYyI)#v5cC~_Q3QO16|z# zsn-l|0#1+CKzbUtjV7_X=}Bq0$zy#Vqryqyp*N6~2ktBDO)18yv|Q82P2LRkJjtNHb0x;v1(;Fn zo}2|$xO8_Bk>!@WF;AWYk{Kszj*ihmajd;_b!BqoV`=psWpk%+oolSd-n{BH$WfJ= z&Y^Hg6kdWn(V}kiEyYq0uTY>lEeNo98c1b~NyM<;d*w@gOuSQDZ7Lmi)+$n`I$_QXB8|;9hl1shG1OZ($ex3QR#gfRGmI;9w^$nETd;q zwH-7NtYdh!1)n3ibx~n#xxceeJsw(rzO#^ew!4^qHaKMFH?S2->pbUrmV40Oj~`Vs zW4QnsaC~FZ>->!g7)>( z-Y5ymM21bMJDR}#N&6E#T!b~tnYqBYLymN@Vl~>U@=xv)d=uQwx5vtBAr+&uS4}48 zT%j$NI!$%S@%PWkZA9iTHJYi#i+GA}TTo%m;NqNCn~HC`K+ag%OP{8`X!6<1s_^^0 zNl_oAiR{UeYAcD9_K$&ZbAprYimP<*PxcML_Zs0oV6#?TiYOd#NhE6^Z)T7dJyKG( zlab7dFxNy;?40zzk)?E0?D=z#XDA;$iscS2wuN=3A{r;(l+48CB3G;QvbW~+L8-qK zhOf_|yHD$Vg3RYW0_Q9kY)|X)#lNW=l_aXBz}fnPXZSryD8{?LJNTHP^fpST4tUuM zxoK{>`SRsm>c_^VK&25QyL#gw0ujKX5T&+&OsC9;=%#N$-+0!7SK{O1p!FzOQoY>= z^i4+u6-=0>iJG`1j;t}Pp|bu&07x*DgMU{7$Uo}1G4LwsM_|R(2A?ihZN5*+pXp(q z^sN)vPloU8+d$wh3r25WHUVZ5eyhhGsejVuV8g)l<&bhDkaw#ui=?j>p4%D`S%kw= zAqtwuM3f_DJ%c43A4;+q@f9Esr66k`TEb4Jin9aIsYMv5uynjYeS% zd@GAkV zBz#VEyLHidY;R~v291irr`wZGYkjd!mq(@>@;C6f7dQ~c_R=dKD`k5)NN@O^o*O{B zgk`?QXuRMvK-!zbq#K_E%4)TUq3Ge97ewmbbL)G1y%V$70^A8>BB(Ve?=XL$sEDR; zX#(WWNW zgHFjm298*$2m-I_1Vvod1nDj$1mPZT>9bfy>*pD@>ssEb;94bZvPAGjs7{#>yPsJI z_Q(Q;Q4Nu#8j*IN6j7ALxYept;9DZhIhs1^+kCY*a&&-BpQKll{`K$QpW8D))D<}o za8d(IkH}jh2Dk}bBUk}c#X0h{A9+;tce?1w1hYfLdbe=mKvr3c$cxr~-C_K)efKC7(*#fH+dQD!^>!`w=XVxD{lx3Zo)-tRJpX!hbdayV4-lIz~ z6X@9A-@nxrM&zg3!nPDhdlPKD85V>oE@hLUA$%S?{O;8J3wxPD$Y9PibsV(TzAslx z`Vfo9v`d@cW#<+DuK(UG(plbvYpiVXAgtVub#wFcIh1w&aZE*4o+Fzca5qfhlAdFE zU2V_KS0vb{%QlgF&y|-aH2K`Gr_82`#s+q&8y^T&FeQCTto7jFJcfb?rU99lkL4+q zl9bw-bE3EGtM5B!qIGwj=(mvsKi1jIaFJw(m-_@l>rv*-k}+#+ZsC!KDSD&k7<>kY zJOvuh?wuLMynZvlD{3x*gOIlm#9N6*_&pYbWBu$9u$9s-SmmLaR1wkSJlCzqzmrwB zdj=y`<*&^HoS~8IG2G~W{F+K_KM;`y^FEC)fPJ#F= zscGX}g@fMDDHsr#I%U^RG+XEfG94!ui#gC@~bo4)N8%Sw*EY2&g^ zPSI+t5!tQ$RTFiqR!cuOy<1onlN^Cwee6*tV}GVnvf`o+WJttuKbh z#UD1^aZCDQGT#@EH1-7}9eowIeA@{cdeD)gy&l|3YSwu@UUXw2U((imkpa>(*qX@s zo_|BZ!zhk)@;#<|WxIZDV!f@a$rs8Ki*%DDU#b{;Gszn*v$eBGg`ob~J)MBUsu_MY z&RTtWp@;IxPa{h!WD8+w53LX8MSzTJ%~Jw$)art&bkd7m&Ka$i5p?X^k>>_KB*DG<;mj+)%8}aNe=I zV$|F(lQk9Ap$&O?xq}uSLDxZ~d@121{7INPu7=-j=Byvh@7+}J?u)u&%+q-e6xv3T z>}pI|``Ji+_^b+qHh*<_;vRCIhZ|Qv33`JS4qF=!FxAx8>$ca8Z!F6jT8YGnD7|uC zkf9d)`tFb1&ol^3-CP|9ZzHoq%KXC%Me~)9w zuLkDq?YQ+NXbY!lvBBz`T$9D1V2@@=!ze1tYyP4ekx%v%NgNnv>yW!kx|C6J9W=1d z?@71ovH3cw!eDcz!bK@*!IZ3B`Tj|*$bNI=ICU*k*;Wnajyz92H_gEzO!TentZ?Zv zz#TL#<)EewcZq0;NHi0l-e_ESX``#R$xh1RU|moct2p$XHQ=p$v24LFxb@#}d2>Fv zt^1Z0NZjbkXxjpA)K{EdnF3eUFW9aH9J@oTvtG*Rt6th~krS*ePiMpC)41u;mvoLE z8RtAMGhT?D_BZNnqW4y@Opk-gPUeM`ZEFpe)e&8S=04`8X>N3bWEW%FG?GkC)~fWc zYF2sHrcLd_q`O<)xexV}l2$>nQE32pcf~r|bL-417=F)D;2@zf1=GRqiST>}+S zlP!`E%h5bndykz@(j0)2E$)7ZtEAUKmxs}(-mJrRdQ<7CZisC$NJTNO^SAe~z2{?=@q4v&IV#cz!ayEMqR<0Ym-9Q{a*FtnA0` za1wdx2vUWnUCv%Xk2CZ%jIVjJ!0BTuS|{V@Lj}+y%`$zEAJFM=L#Q}Oig^0SR7!C? zx>jzBP;RWTlt(VhfePz{nQmXG{Jg#D?z0~fNgI_ksho6plJa!sg9i^>?Z&)(V5O+- zH>yrnkWy#eGWX>Ihm}>Pw2y2k3m+>cGq)$6y9l1ztc-VwpPYK;WGM-j$#&ak$FkR{ zlsd)VxPxPv`L?9^$Z`+9p;9=)4vcY~YpaU9u{*lS1{yqH9B1&IJvu!9K*7_vj+@IJ zA8nPGW_#@3vL9LmMgotV=GQ+v@&w->=k`sDchsu#{A`m|b7BMzlF$zqP+H!{4xyOr zdYiaWZgZPNOduK^8*Q9v1M&Qr#O?uu?k&+1!VA*# z_or?Xxe|IlY%5rey^yfcQaepnOZ7Y|kfLCxp*HWommHZ9ww&#N#a_GJVU=WmR>8c` zr&HGK&RcqjI?K+yu(GUq)T+50{pDUOwJqy$S*(b0p$8yP?6MLy} z__6Hh`bc@U^<=p%t(@5p#t!&cnL~Rtwe5`h5S?bXUMAc`mAPLGHJpK7(WV9`!d-F7=*>bYMS+v{ zU^H~{2&PLP5A)OAc;jDG(Es!bg<8WJefXn8$;(a{L;14~uH(^`7Lw!-SQR^^$=k}K z5L1uuPXN))Kd) ziBhqz*)B9E4yE$PFSx^)cwC)|ibW+)h-K)6drmk#w3%>f>3~Y-H!kRp;jhAO&}TEQ zh~;Ml7Po-i`F7bKT(kskz>V+5WG`qS!^7D*;r1zh;Vp#C=BPgjSC!CTO% zHy&<#7;Uo0(o*r!H}=y-%s9NRtHsiBCw>O$4xd;oV`)R(l$ynZC!~+XI);$!#vxYhdI6=>m!Ee8>s?rW5PEg3x1mk zUI*uX6z3n*{To@^iuOaB6t-Lz&N%^8AzY)~vTp0zx#;()=HoQvy$mMmutIOZd$!97yneI0>oRs(Wt-b?)&m|7 zZNw|plMmss&4HkyX@QF}ihz=W)x!QmO;47Niu@T0`i24DENg3(X7LwI^KADk*@Rlm zHIvxAo%TkZ))R+wsqz#ZP@_0psDL<>`V$p{7#12_7R=xHaB^;~UpWcdh_8OgZZ3jNL_rt<}eco*r#~tXdG*%5h7;1|JBBoUpytD;p@u*1WAH zH11`B@iCKhdg$KBkpy?Nom{Ft*jot%i*?yWPvw**m(o&eo=Rb!evLn_-d1R}K#?bm zW$X?=p?*%5H^M@5BYo6l@zn8}*YALR8F1Sc?(7+D!ZOn7xG1J8m+DUo4ofI^;H)aDWVuBU6WP0}72aW=~0J z=&srsCjaCgXeZ-mV=mFlFgttAg(9xqOY78vbo5wx%pOBHR@&B6n5WMprp(D^dG(3L z7N>S=0rchg2zX@$K_SUbg#?-7>^0`$8iVd1v(w*`zxLVR_56!CwV4ehTB!T_6#jRMFi@ z93`W7bDnT~?)HK4=`Cya8;`xn@Pst&rp?scIbCwEyg83SSvO%jOUv1_qPY13k`?#_ ztu{n5bZ$`N<*0Gcm@*@vnyf1h8SBg& zVC)ZPWOh4XTp=pBmI8?ZTUzCCl&*@#knslSVhngLmi(|hfs;3V#2#yXxamajo;cKw zXk{1PKWk7vr-~5xnC7;g$jQUA;mEWvN6Kk_KoI8=Pf<6A_cZ|pzOMjpbx&}0rsTYlq{I-$LEGVTCwe74qV)Ov3HD)=Yx`-vT}M5QLtD{5FEHtlxGaqC1?DJiqAC8*Zp9(sf3PRr{_Mu)}7Vr{y+QtQ}_ zNV*Q@o4M-&3nQhv=Y_#t;5m<&j!oPfpGUNZi7txA0{z(Byfs_<87Jh&SEsP8B!XFv zM;A*0SbDW@7Zz*lU9X2QtJ=x(vwK9i?(05(s(fGN`MkQa7wK{b*6vI9<12d7c{^EU zzWBvv!V7;-Q0KdVX{F;B;o&R?u=oX)Du&z}=S_CzB$|bTr6T&xExOIw=~#BUEbD-5 zz0GTu~!BLL^%;+sKJ4`@LX|koQ=Jr-2^YK^lOp7U^FI6FBM^lW_Ya@A~9p){~ zS5pNs;~IgyAr0{l!;v1g9+>D)PN#TE4o@EgFbSY9p0MwO30KNXi5gj_zsY%a`#Pq& zXc-(eLSL?Sw8<(OEX2g(aWlyC0fIL^KQ1ftk~L0Ud-Q(rCF!&NMwdyCCvIOdCQ>;k zEwk7IFxI|?v_4;$(Beex-MF88*wI)PWKoX5QSSH1D+bTNBr?1#FM7ILS)(!56K0zE zS=5n-_v91iOQUkqTeY&9z6n@~11desjSS;>tY*COM!`*GvnzwrE_K>+=4+?Z*H?7K z@nz#M!iExE#ouQExSM?NE>P-~KS7mL(u!S_xqiRsE_U+Kb1R>7EVlNU(&3vBVAkb8 zm`#2~R9C`AH9?m^T21+cmb)Kyxb;?Eq*Gczg8YT$>nT%K?~89iI4oibItL8$@6o?y zI$ab`2V>C+SvbB1jS6&a@oxIrIxH)%7XVRXHTN~Mr!6Ilum#!)%hF#98Vb{P^`nX?MLC6&X=a+YlF$wa4Cz^DGYcTCg>i~vn2_%-&hey+Xwm5CV^-5Eu{>A53WBqY~*&=MXgTUauHo zN@>{Xy~(#AV7%lVVqUc4&H|`*oQg^n;HWUzn;wcnL6yS(DfcFGuQTr5Fa-A4IP}rl zzL({t&ClQ*uZg#dW7xpMB~;|PiLRQuU=;Gd&=r`jYA7vC|03t-3pHxA(?~~(h_bhs zG{#Bwv^Dke7h>|p1TyWUB`;_uO3uqm9DjGiLVuhJJ)KKiBP@`d+97VUeke3My1cZ zj@`AL{VX6Dg=s;zyfY6k{2ISXiE}5P_uP4uEtOj>s$MH7LQ!5XzX#{nUJRogm2l=e^}W}QcK2f?eTsGEOgX! zcyd5RJC-)zG&WG0s%ZY)7&JG$y6UKSfBm_WN!59Y`5BF`+y1+`*CoV-gH3&tZps?q zAcvjgn9?x3#>%oj{TJL4u4f-AC{zdzr&>eCa4Dtj19Eo4I-`2ixUU|6!t&VJIV?*3 z>Y~g3|JZu(Z?+#l{JW~9YPHoGHBwcysMs^)U5cWls;JtV+I!PhQMG66RP9l*XKZ4N zz4r()V+BFn`Fzj0&$;h&zCYw2c%8hG=kt0#uIuW8Q%!K?sL84KwkVTb{Cz`^aC-nb z%r=v0`ZPfIIvtQt!a zrT_fsXmxoH8G!j0tY@<_Dc4|sAnX8Kz`5I`?R7yQ790~zxE-pJHbDB#GlOQA8GINM zBXcxeHza4RJ32o@*gJAq=}n2GapzxQ0s6BizYJr#b9T%$`Te$hwW*Duh=Y&hi?58W z(-{Qre-tvpYaIii;e6};ZF$S_m8bhJj@^$cBKnfDj$hG*n11?{@4WMLTZo1d9Ghm5 z+VoX#3c4$@4_WkIy7I-er)}=O-LD-T?AI9;E2a_|rh_gL*qL_fIBiPMLaU2bT^7aupi&Ma=f(^VyX1!Iy46=ZE!fTj#NE*sa*N@GoNC0v74M zx-AvpM7qF)0sBAcEDeO97w4@=!t%X{H||=AfEwBt#a=XbY3^H6y``kT$mo9O&71!> zahi|iuXg^U`)hCawF|`3+9>yR481w852FL!uK3Q zTa z{Q#4t(3;?`Bra8>pO2vNg!VU>`*VG2AT^fG` z3Hk}XO18=-d!fk9?G137yvBC$joNe-hG%g-+*3JxU^EfY@1jtrOa@DF=( zQc-TRRNUV7e(43c{B>$W?3PQ99GB3xAtJ%>C|3QHP@RN`>sDOb;=CArXxJVufw9

J{V|)T;&CD`ixA-XELwz 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 -g@= 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

NKP)XtZ=ytmuP+7Fw@&NY@&lFaHf>aec^fb670C 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%0D!nY+!u7 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 literal 0 HcmV?d00001 diff --git a/docs/reference/indices/index-mgmt.asciidoc b/docs/reference/indices/index-mgmt.asciidoc index b89d2ec957af9..7a78f9452b85e 100644 --- a/docs/reference/indices/index-mgmt.asciidoc +++ b/docs/reference/indices/index-mgmt.asciidoc @@ -3,21 +3,15 @@ == Index management in {kib} {kib}'s *Index Management* features are an easy, convenient way to manage your -cluster's indices, <>, and <>. Practicing good index management ensures your data is stored +cluster's indices, <>, <>, and <>. Practicing good index management ensures your data is stored correctly and in the most cost-effective way possible. +To use these features, go to *Stack Management* > *Index Management*. + [discrete] [[index-mgmt-wyl]] -=== What you'll learn - -You'll learn how to: -* View and edit index settings. -* View mappings and statistics for an index. -* Perform index-level operations, such as refreshes. -* View and manage data streams. -* Create index templates to automatically configure new data streams and indices. [discrete] [[index-mgm-req-permissions]] @@ -31,82 +25,68 @@ If you use {es} {security-features}, the following or index's data. * The `manage_index_templates` cluster privilege to manage index templates. -To add these privileges in {kib}, go to *Stack Management > Security > Roles*. +To add these privileges, go to *Stack Management > Security > Roles* or use the <>. [discrete] [[view-edit-indices]] -=== View and edit indices +=== Manage indices -Open {kib}'s main menu and click *Stack Management > Index Management*. +Investigate your indices and perform operations from the *Indices* view. [role="screenshot"] image::images/index-mgmt/management_index_labels.png[Index Management UI] -The *Index Management* page contains an overview of your indices. +* To show details and perform operations such as close, forcemerge, and flush, +click the index name. To perform operations +on multiple indices, select their checkboxes and then open the *Manage* menu. +For more information on managing indices, refer to <>. + +* To filter the list of indices, use the search bar or click a badge. Badges indicate if an index is a <>, a <>, or <>. -Clicking a badge narrows the list to only indices of that type. -You can also filter indices using the search bar. - -By clicking the index name, you can open an index details page to investigate the index -<>, <>, and statistics. -On this page, you can also edit the index settings. - -To view and explore the documents within an index, click the *Discover index* button to open {kibana-ref}/discover.html[Discover]. - +* To drill down into the index +<>, <>, and statistics, +click an index name. From this view, you can navigate to *Discover* to +further explore the documents in the index. ++ [role="screenshot"] image::images/index-mgmt/management_index_details.png[Index Management UI] -[float] -=== Perform index-level operations - -Use the *Manage* menu to perform index-level operations. This menu -is available in the index details view, or when you select the checkbox of one or more -indices on the overview page. The menu includes the following actions: - -* <> -* <> -* <> -* <> -* <> -* <> [float] [[manage-data-streams]] === Manage data streams -The *Data Streams* view lists your data streams and lets you examine or delete -them. +Investigate your data streams and address lifecycle management needs in the *Data Streams* view. -To view more information about a data stream, such as its generation or its -current index lifecycle policy, click the stream's name. +The value in the *Indices* column indicates the number of backing indices. Click this number to drill down into details. -To view and explore the data within a data stream, click the compass icon image:compassicon.png[width=3%] next to the data stream name to open {kibana-ref}/discover.html[Discover]. +A value in the data retention column indicates that the data stream is managed by a <>. +This value is the time period for which your data is guaranteed to be stored. Data older than this period can be deleted by +Elasticsearch at a later time. [role="screenshot"] -image::images/index-mgmt/management_index_data_stream_stats.png[Data stream details] +image::images/index-mgmt/management-data-stream.png[Data stream details] -To view information about the stream's backing indices, click the number in the -*Indices* column. +* To view more information about a data stream, such as its generation or its +current index lifecycle policy, click the stream's name. From this view, you can navigate to *Discover* to +further explore data within the data stream. -[role="screenshot"] -image::images/index-mgmt/management_index_data_stream_backing_index.png[Backing index] +* preview:[]To edit the data retention value, open the *Manage* menu, and then click *Edit data retention*. +This action is only available if your data stream is not managed by an ILM policy. [float] [[manage-index-templates]] === Manage index templates -The *Index Templates* view lists your templates and lets you examine, -edit, clone, and delete them. Changes made to an index template do not +Create, +edit, clone, and delete your index templates in the *Index Templates* view. Changes made to an index template do not affect existing indices. [role="screenshot"] image::images/index-mgmt/management-index-templates.png[Index templates] -If you don't have any templates, you can create one using the *Create template* -wizard. - [float] ==== Try it: Create an index template @@ -236,3 +216,32 @@ earlier. GET /my-index-000001,my-index-000002 -------------------------------------------------- // TEST[continued] + +[float] +[[manage-enrich-policies]] +=== Manage enrich policies + +Use the *Enrich Policies* view to add data from your existing indices to incoming documents during ingest. +An enrich policy contains: + +* The policy type that determines how the policy matches the enrich data to incoming documents +* The source indices that store enrich data as documents +* The fields from the source indices used to match incoming documents +* The enrich fields containing enrich data from the source indices that you want to add to incoming documents +* An optional <>. + +[role="screenshot"] +image::images/index-mgmt/management-enrich-policies.png[Enrich policies] + + +When creating an enrich policy, the UI walks you through the configuration setup and selecting the fields. +Before you can use the policy with an enrich processor or {esql} query, you must execute the policy. + +When executed, an enrich policy uses enrich data from the policy’s source indices +to create a streamlined system index called the enrich index. The policy uses this index to match and enrich incoming documents. + +Check out these examples: + +* <> +* <> +* <> From 80b3d475a4364643f28e9ba1513d571c086e084c Mon Sep 17 00:00:00 2001 From: Chris Hegarty <62058229+ChrisHegarty@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:08:18 +0100 Subject: [PATCH 033/203] ESQL: improve recursion in PlanNamedTypesTests (#101145) Minor test fix to improve the robustness of recursion when creating properties. The incremented depth should be passed when creating the EsField, rather than post-incremented. --- .../xpack/esql/io/stream/PlanNamedTypesTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java index 1f92a26f58856..b3da177b1c39c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java @@ -671,12 +671,13 @@ static Map randomProperties(int depth) { if (depth > 2) { return Map.of(); // prevent infinite recursion (between EsField and properties) } + depth += 1; int size = randomIntBetween(0, 5); Map map = new HashMap<>(); for (int i = 0; i < size; i++) { map.put( randomAlphaOfLength(randomIntBetween(1, 10)), // name - randomEsField(depth++) + randomEsField(depth) ); } return Map.copyOf(map); From fcdeb219934ce0fc4cf065762b8eeb50444884b7 Mon Sep 17 00:00:00 2001 From: Abdon Pijpelink Date: Thu, 19 Oct 2023 17:14:21 +0200 Subject: [PATCH 034/203] [DOCS] Expand ES|QL ENRICH documentation (#101079) * [DOCS] Expand ES|QL ENRICH documentation * Add examples to 'Enrich data' page * Add another diagram * Remove redirect that's no longer needed * Review feedback --- docs/reference/esql/esql-enrich-data.asciidoc | 126 ++++++++++++++++++ docs/reference/esql/esql-language.asciidoc | 4 +- .../esql/processing-commands/enrich.asciidoc | 76 ++++++++--- .../images/esql/esql-enrich-command.png | Bin 0 -> 132191 bytes .../images/esql/esql-enrich-policy.png | Bin 0 -> 155994 bytes docs/reference/images/esql/esql-enrich.png | Bin 0 -> 143426 bytes .../enrich/execute-enrich-policy.asciidoc | 5 +- .../apis/enrich/put-enrich-policy.asciidoc | 2 +- docs/reference/ingest/enrich.asciidoc | 27 +++- docs/reference/redirects.asciidoc | 1 - 10 files changed, 211 insertions(+), 30 deletions(-) create mode 100644 docs/reference/esql/esql-enrich-data.asciidoc create mode 100644 docs/reference/images/esql/esql-enrich-command.png create mode 100644 docs/reference/images/esql/esql-enrich-policy.png create mode 100644 docs/reference/images/esql/esql-enrich.png diff --git a/docs/reference/esql/esql-enrich-data.asciidoc b/docs/reference/esql/esql-enrich-data.asciidoc new file mode 100644 index 0000000000000..d7a2c81df9ece --- /dev/null +++ b/docs/reference/esql/esql-enrich-data.asciidoc @@ -0,0 +1,126 @@ +[[esql-enrich-data]] +=== Enrich data + +++++ +Enrich data +++++ + +You can use {esql}'s <> processing command to enrich a table with +data from indices in {es}. + +For example, you can use `ENRICH` to: + +* Identify web services or vendors based on known IP addresses +* Add product information to retail orders based on product IDs +* Supplement contact information based on an email address + +[[esql-how-enrich-works]] +==== How the `ENRICH` command works + +The `ENRICH` command adds new columns to a table, with data from {es} indices. +It requires a few special components: + +image::images/esql/esql-enrich.png[align="center"] + +[[esql-enrich-policy]] +Enrich policy:: ++ +-- +A set of configuration options used to add the right enrich data to the input +table. + +An enrich policy contains: + +include::../ingest/enrich.asciidoc[tag=enrich-policy-fields] + +After <>, it must be +<> before it can be used. Executing an +enrich policy uses data from the policy's source indices to create a streamlined +system index called the _enrich index_. The `ENRICH` command uses this index to +match and enrich an input table. +-- + +[[esql-source-index]] +Source index:: +An index which stores enrich data that the `ENRICH` command can add to input +tables. You can create and manage these indices just like a regular {es} index. +You can use multiple source indices in an enrich policy. You also can use the +same source index in multiple enrich policies. + +[[esql-enrich-index]] +Enrich index:: ++ +-- +A special system index tied to a specific enrich policy. + +Directly matching rows from input tables to documents in source indices could be +slow and resource intensive. To speed things up, the `ENRICH` command uses an +enrich index. + +include::../ingest/enrich.asciidoc[tag=enrich-index] +-- + +[[esql-set-up-enrich-policy]] +==== Set up an enrich policy + +To start using `ENRICH`, follow these steps: + +. Check the <>. +. <>. +. <>. +. <>. +. <> + +Once you have enrich policies set up, you can <> and <>. + +[IMPORTANT] +==== +The `ENRICH` command performs several operations and may impact the speed of +your query. +==== + +[[esql-enrich-prereqs]] +===== Prerequisites + +include::{es-repo-dir}/ingest/apis/enrich/put-enrich-policy.asciidoc[tag=enrich-policy-api-prereqs] + +[[esql-create-enrich-source-index]] +===== Add enrich data + +include::../ingest/enrich.asciidoc[tag=create-enrich-source-index] + +[[esql-create-enrich-policy]] +===== Create an enrich policy + +include::../ingest/enrich.asciidoc[tag=create-enrich-policy] + +[[esql-execute-enrich-policy]] +===== Execute the enrich policy + +include::../ingest/enrich.asciidoc[tag=execute-enrich-policy1] + +image::images/esql/esql-enrich-policy.png[align="center"] + +include::../ingest/enrich.asciidoc[tag=execute-enrich-policy2] + +[[esql-use-enrich]] +==== Use the enrich policy + +After the policy has been executed, you can use the <> to enrich your data. + +image::images/esql/esql-enrich-command.png[align="center",width=50%] + +include::processing-commands/enrich.asciidoc[tag=examples] + +[[esql-update-enrich-data]] +==== Update an enrich index + +include::{es-repo-dir}/ingest/apis/enrich/execute-enrich-policy.asciidoc[tag=update-enrich-index] + +[[esql-update-enrich-policies]] +==== Update an enrich policy + +include::../ingest/enrich.asciidoc[tag=update-enrich-policy] diff --git a/docs/reference/esql/esql-language.asciidoc b/docs/reference/esql/esql-language.asciidoc index 10af5a6e9702c..399034737efaa 100644 --- a/docs/reference/esql/esql-language.asciidoc +++ b/docs/reference/esql/esql-language.asciidoc @@ -10,7 +10,9 @@ Detailed information about the {esql} language: * <> * <> * <> +* <> include::esql-syntax.asciidoc[] include::multivalued-fields.asciidoc[] -include::metadata-fields.asciidoc[] \ No newline at end of file +include::metadata-fields.asciidoc[] +include::esql-enrich-data.asciidoc[] \ No newline at end of file diff --git a/docs/reference/esql/processing-commands/enrich.asciidoc b/docs/reference/esql/processing-commands/enrich.asciidoc index 1aaa95367ff92..1e489119d4ca3 100644 --- a/docs/reference/esql/processing-commands/enrich.asciidoc +++ b/docs/reference/esql/processing-commands/enrich.asciidoc @@ -1,8 +1,55 @@ [discrete] [[esql-enrich]] === `ENRICH` -You can use `ENRICH` to add data from your existing indices to incoming records. -It's similar to <>, but it works at query time. + +**Syntax** + +[source,txt] +---- +ENRICH policy [ON match_field] [WITH [new_name1 = ]field1, [new_name2 = ]field2, ...] +---- + +*Parameters* + +`policy`:: +The name of the enrich policy. You need to <> +and <> the enrich policy first. + +`ON match_field`:: +The match field. `ENRICH` uses its value to look for records in the enrich +index. If not specified, the match will be performed on the column with the same +name as the `match_field` defined in the <>. + +`WITH fieldX`:: +The enrich fields from the enrich index that are added to the result as new +columns. If a column with the same name as the enrich field already exists, the +existing column will be replaced by the new column. If not specified, each of +the enrich fields defined in the policy is added + +`new_nameX =`:: +Enables you to change the name of the column that's added for each of the enrich +fields. Defaults to the enrich field name. + +*Description* + +`ENRICH` enables you to add data from existing indices as new columns using an +enrich policy. Refer to <> for information about setting up a +policy. + +image::images/esql/esql-enrich.png[align="center"] + +TIP: Before you can use `ENRICH`, you need to <>. + +*Examples* + +// tag::examples[] +The following example uses the `languages_policy` enrich policy to add a new +column for each enrich field defined in the policy. The match is performed using +the `match_field` defined in the <> and +requires that the input table has a column with the same name (`language_code` +in this example). `ENRICH` will look for records in the +<> based on the match field value. [source.merge.styled,esql] ---- @@ -13,12 +60,8 @@ include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich] include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich-result] |=== -`ENRICH` requires an <> to be executed. -The enrich policy defines a match field (a key field) and a set of enrich fields. - -`ENRICH` will look for records in the <> based on the match field value. -The matching key in the input dataset can be defined using `ON `; if it's not specified, -the match will be performed on a field with the same name as the match field defined in the <>. +To use a column with a different name than the `match_field` defined in the +policy as the match field, use `ON `: [source.merge.styled,esql] ---- @@ -29,9 +72,9 @@ include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_on] include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_on-result] |=== - -You can specify which attributes (between those defined as enrich fields in the policy) have to be added to the result, -using `WITH , ...` syntax. +By default, each of the enrich fields defined in the policy is added as a +column. To explicitly select the enrich fields that are added, use +`WITH , ...`: [source.merge.styled,esql] ---- @@ -42,8 +85,7 @@ include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_with] include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_with-result] |=== - -Attributes can also be renamed using `WITH new_name=` +You can rename the columns that are added using `WITH new_name=`: [source.merge.styled,esql] ---- @@ -54,8 +96,6 @@ include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_rename] include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_rename-result] |=== - -By default (if no `WITH` is defined), `ENRICH` will add all the enrich fields defined in the <> -to the result. - -In case of name collisions, the newly created fields will override the existing fields. +In case of name collisions, the newly created columns will override existing +columns. +// end::examples[] diff --git a/docs/reference/images/esql/esql-enrich-command.png b/docs/reference/images/esql/esql-enrich-command.png new file mode 100644 index 0000000000000000000000000000000000000000..934258430d97497f252c05a3b6089eb4ba52c886 GIT binary patch literal 132191 zcmeFZcT`i`_AibI0wMw`q7sUrpi~is(6Jy&m#S115s(sk?-oQPs0c{M0w@Sb4ZR6O zM2hqtG)NCcdVg#0=()-Lo;Sw1?~mX3y*I97c;t|swdb0Be&$>&SW{hzVfT^U6ciK; zS1w=FqM)EgQ&7-crria8^P<$$mV#n;w$+6TnpZAd;L>zN-LbMUr=YkT91%sMqdmUQ zf7`3=!g-ZP&gW5q&XkwwUQug4-79{6AJ>aRJ74ug)UsT=s&nz4g4eE5 z>DY_~3Q~S4&gSChpP>x;STa+(+!X6H@-?b{Lu?*5zrFmG+F$kOql+g$a#Osoc6NA_ z#V5-z^$^WQb9Z5eQnQ=xo2JumOsgyk$XNr_J^4ZQU2{8)t+mG*4AK zl%BEUueM-{a8&%^OraUXT;Y4C*z!4wTa|@}fxBO4QDQ=sn!>NArA!6;eo>vj{|r|tbk>EM@#b6MQ|E47q}3R)sM$QOQH^t~ zt9Nun(w$BiYYz;$q(-t$E%w?bWY;e&Hx zfYA_M6>vPhp6Dj0^GB-b-PZ&z_S`ym{+3jH zaQ|t!c%fl~rsTeBJ>8V2JSejqh7xEPP_qcG$~Gi;6nfb&le8RMuakc(j-+ z7oJRUVlSoryo^3~)5|3_6F&cQgH*fyceneCx6trbQg&0&3{lGXOU%-oK+)>;^A*S(VCnD)-}YJ4dSxziSmz%I%V<)E|Nj<-eYO<$0CoU9+WN zOkm0zp%!6Fd1quyh)J+Xi15XzS7AwPEnO|kEk2ef*Q6gl`BwANEKbx$^#JdV@ZkDS zFK+OQF20<6N%L~YS0%xNDxZ#+v1j{sTiWb%Whr1PVKfXXdqSvLGE;Iu#vN%wj6SBX zcCa5Wl?}&}KjVlS{0bJiJ;Y<+rKz2|a|VRUL-y7%SPG)H-A#c!z}PZ)jS{c`h*%NGh3m9r^1 z@2|8|PB`3^)=hi;;a2LtSI?8RG9Aw)M?HLf_*-}00Rv{jfsEKCb95(S1hEw-&i8Vfo3AhF&+Dn^^<{RxTT*4uuuCbwEUWEPvF5VK%#ijlWS2gV zDgH&>ezj|Ds$v(NB1VojiZ@;rvK2BFde+I2k#4}#d&ZWLaEuV&%bFkLaze^ZiZVW4 zD$3d9o9>vZ=kBr>zM;Y$9qo&45n~+fwq5L9Vx9C|eed*BmsJzrHMfLy;ojq3i=1r{ zy>{bcZ&KY*@X@NLH(y#7_I>QI|N6Q6M5|@))i>XsW~YhvA+d#Zh1mNxKkRZ^-&PNo zpX_{bGsdR4Iq%aR@mvX&GXfGN)_Y^6U($bFj&^IR{pu1U5N9a5{EE6h(CXREt7hLU zuH^2?>$mK&T)mS}Rr=DSZ#2JY-Od~pam%H9;(dNje#PRY$vcy7;&;T2VjW}G3vL^7 z_PG?O6`%-T2`jGa1dbl2+ho}M$O65D-PBL0+m zg8F1!leCYktG&9L+|P~FkOwL`4OgRHt=7%2&NDX8xOF)FEaY$|xV$N;E#92!oXnhT za>%mx`~GUCYX05Aq1!R1!(U=wqGU2;Y7-U{%mghuk93-gyok!uFV`=@dqR1R}jn}smud0SA4I~Jg<9x?|J%jJ}qwT z39Y&J+3zhLe5tE9`CG)u+r>p%}a6SKaI%0qIyjJ z^4jVC(_h5o%rD(eXwv$!?@qpXtU0QlcfYR~Q-YG$ z`k)i1-Cto=g?<>n5WgxdKV?upsTXWqA23ie@O+>wIUrfhYtNER-+aDc_SCr|3y%Sl z#xlbtGyzYT?48N3Sn}@-2;6DvdvIf8ENQ0d;qEt&pI_*{a5%_>1C8uoqsK>0C@+2q z{DRW0P8;Mos+6fKt*6h~A!DGkv+MHRhhajJoeuqz?%ElOgeoZm<0?A)^Nv3{>bt`S zQYqE*~LCGe|RqVL?+3u-0ulSOZNlgY7}63sZr za+f7>yepnPNSeWR$#$`#ciR9r-7;w3HD{+Cq^;yMy3rJ3JZ-E$?J-c|km$4BbngM9 zRLQ3jPFbU0AD5YDzO~?AU{Q&;We)olm(#R`Z>!E-uXk!3WvPhX((9UR@p{#qo<&d` z3rXGV-|DJJ-Q05FT0XvNUZ2!-WASNY%j(Ulenu%229v?#N;r(!5gQM39Bs2`3te* zn30(sI4@o;KHiH6QH#3=6prr3Vt3AF?xt`&Lvi5tLoVTjg!idbFA&^i<{?o!}KTXW}|T<*3uc1}|6vfS_!Qs8Ik*CO0p z@FULFvfS6yG`TLI9L>4Jg-;2e;+EUZ#l9i&*2PP|zYhLSmfOGY zC>Lj0Zf@vBfBhlSY3^?IuQ%B_{oWSXpa}Gdh^X)>k-ydkUzLG=E2U}WZfyKyI_^8B2K=UsfBNvP@c9WH`__BnGS&M-w9myp)Cmt~#T>Ds41VE% z^+LON8oL#O`}pL{ZI*hj+DaxXm3yJ$*Ul?9-q}u?R-L-)yC=Ag$s&ScAV!q^J;VC} zcHX>aW@7xw4)=_yr>+)H4B~5W-kCF-gEN~BO=Y7VO_RzI70dlj3G?12i5e`6@qJFX zrRFmIU)&1*6gw!XXqmXkf2cU%9y`=6`0u|aKB#bj38BQ`xBuV1^ZUbn2sX;USrL3X zwx0>H?BIUk?_U{cCPq%fUsU-}2IA%RqW%Yw@B<3=HoN}*lOzoV`+n?qhQHe%6)i2g z@o#rdMY}*pi(W^T9{qc2s+iIv})qu=2ebyrV-{1v82pN9%P?dCo)%6otBsU@HS z*!KZbSLmWnvp6chHuk36r+@LBrabAl4323U*>9B1_=xzdHkdA5dwh@~Is-Di$_dID z?@{Ttrg-VL6t!4A`JTh%=AU#k;B_E=;@bh!)i3%-cyndoWi8b!-w4Fy=AR~g3GEXR zO;vlrGaMD;O~=3%16=88!}moJ0q63`n9I{j4xJt@R?oV_lFB?6?1G>_`1;Xff--7 zG_;3{_K+>gtIzTK_D1t|=>|rg_A58j=)~0a;;f)bk+@iZgx}Wu0zMa&yx(F_Rb+eE zW^KO9SIU99LoBRfAz5U#sxD z6v=7ltSC))fU6*K1m6u#x9@o&=I<`5SF>f}LvnGN}pY0C$ zH!U;dnjCU(9X+BS;IP<#87G*q{4#&rS9EAU0#jjHG0;p2ogYBs!>y&(u=>N55z!9n(26Q^=us%+lV|j60!*8uxA`~l&^BCCoXh-5u zp3AplWWg%&X0< zVtpVR;X2(PEw@s|h<3>gFFwh%Q?ANE!Xha+4R1t;j%8}{YLu5;_4NE9vUTSJxA&yG zATJ*?DUAxDl-GA*!@c{uB|@#^A_JoyLv#&YYA(uy=#s=+IF>k6cuW~$qtCR9L%Q*2 zf0|DfBC8x0)$kK+*z~eAc#})qA7d2utWaUotyaB_0-zNfI*Q!}w+49x_AYZ6XZJQ> z(E9s-Ns=jQ`^l%~_8Za7-n_s?$R8QE@6gpxR^s$Iqr2_Nd`*oD5@|=Cw_K(|oIurF z;oSl0na0Fz?X|CNTQX&f^V_J~b8HfJrL&3FJQ}}_k}b{j?h04Qkb6H?XXTEb+WyvO z=C>;OkkgmjMm6t1iy=wK^bYV@qAcOOIvYufkzZ8QCBgFkn33gBx(cNZ6_yJ2-$E|f@ zE=i$!^G^C~%_nktpc&~d1{|8DL%(@aVZr>!={Pb$F^8>dYes2{f3qtwj{W+SlH9Ln zI-6tZtmON$GywZraY|e_l5(Buz8p2N{VsDW%vyvJzq`g?!(JvcW?pYUw4jMv!>Gox zFV_ri+4lshy}?gZN*QZGu2QKAu2pkOgg))XIQ-SD38lF-PUc_Sr!rwt;?pEiXJJtS z8EoOjxA7%EViWVZsn+}c9 zrmq1usnOUSO`krBz&VXoJo&I3KjUq&w$@c4_Y1-KZ`LD4^>`1OkyFknDDFKCN>=-VTPv ztOpcqd;0@JW*hve*m&ecST-K)e}sk%JwoxO_VfP5pH>NUwHr0eBj;bsj09+ zHjjlGO=0a^U61m_LfWoXVuUgd#CytSTq5I38$B&L72{-njuLVvtnJ;@QTxF1r9*da zJ9X<%F$w2(W8$v2%^y~-p@bNmG3x?^5>i%BaegLB9CC9L zoYum0D3iPs3O_L4W5Z#!ayma#Y5M~>sdYBrUklNBa{<^j#;soi&w?Ub>#Q?5)~=7! zP-BzbuKAE|^s|zJeVj(tRNgt5F>LpzyIUc?Y&K?<+m^1kIBnAStxj*14B~oF?DXg$ z^!5^vSlF02i`a)OTK9O1lUMnMF<;5cdUB+R;?MoYk&F443G7nR?Q;! zhR;TsMykth-PopP_kcXd8?tB^um>p4;WPCIV_9K`?lF_ue-x~@qHiq!>q~In&kzk^ z&hFB!VtT)d*)L&HVX{6r+40WBE|H3vFPwq+OlO05QUWb2fFL(zb3RGJRwF|*i|*%! z*Hgn~Z@+h*9aE{0IyIb?JcxPf<~M~tn)t%!vb~Y3pRN)quj2q!k}eGe=}TCw&c=l0wrK~RA5AEd$RKR?B}WYvstl0lHN8SyNIUQXJ9U^(Adc?D1Osof zrTB>vvFU5jyWLnmjVc_O7!*T`sEBOLUc}Au$<2Msb7^bz!7BUlAcf05O^{_8a-bgb z_B2mrfK{}g%NZMqhF6@&OiRB^SgmUC3IB*%9~kFsj@KwD_bi-oU!1~QOt0U@Ww+vJ z(JC_^*GQf6Q=s$6M3Ww?doYO-v;*16P|(ws@>o;tU8ywNsck>?EZ(Y7T3Ch6ji6@X zwZqXn-g~1;T{iQ)J)&d=MXKRU*WUUMxj$Q*9ykJPmSJ1ulmuj=ajhS*I{hAPq2oiX zo%Chp^Vxhf9QqF1#xoiv)5SvT*AHPRKv2?ZBHb)SKnidWYV?Nk^zdrAme&u)J^{N` zx0>giQo(AJb#|3Zh_dwAdEg$HDVazaM!RpHVRxwkVj4?lItRhC=fmFpl zD+3eKEwL#$36mD9H9c4iFE-P?ts~{WkX(MW+q0o@Jea^qkapefDvw|CnW-%|Q^BB= zOXOXZaM@arGzcGx$)o`aL}W!mkHb!)G${JN9mgBF$RY=4D+iq?7TA2kPff+vd;+W+ zmk3`!cycOqsPN-M{vgsoc^J3|F}s9}EoS(JN7DIX zr_U1o_*BunPa*?-^t$?qVX)HEOZ@jpSE2@$I=$uVeRp89ba&Md*f`GxF9m|QgM-}? z!c&eQx6ogUVcja5Pho4TO3na!>kafaJN+&xzM+r+^j2p5PS;sDlo*NG1x1B8b#x3B zu2q#-%ddZ~Jb>`9JZW1xG7*692)K#U%mjP1X)d#}oxwdf4a7e$Nf#&8wBh4i$fk5t z*S@E)A7nV{X5WLI|H9 zf4oV$^L==yJW1YXo)K5mknuW0Sey7RmwToBtPBAYv2ORWhpo!t4nwXn=?Vt~LEI@U zQf4ED*|~u@Ndq88)_Zsc#woQ_^@?vndB9~@^$E>PN*A=o-mIEOHY=G}l_AjdXs5B~ zvZNVB5*v`D-NN;VblA6+?v4z@9(?4~kw3KZS%9xKQbbPj#@YHeyn3ESwQb7-;daJ; zo0YxC5lo1v7R9q9nXf|99st@L_R*<*LiCQ0#($ZE49S=t?Kb7d&n~mpnxnXgJr?%9 z(EU4Ly5Kv4pA_2srp<~oS#fLp8I^42HLYgtwH>*%4-wUQb&-@VE>l2Pd0FoK5o##S&15dX ztJD@vXxVRkWi(MYTxLg%ZPjBrp;abQ49L6@26&^HtsWmW`2Ah???xYh zUmqVP8*Q@kJ-EoZq6Vu=iyl2KWp|z=a_*os1D*q`;HbO`e=`a<yJ$P8o<} zK4tqj$PBj6>|gJ}G%@!Ed~x`<3( zyPHj?#3x^G)TEF^Dqn%0b7!)lL)lOvi|t1`QXjxH29VXz9RU-5%!5k><}_7vryt1Q zIq=9y*VDdqI@k2FMRWYj{yxm+bRHpcd67MKv2lMhhZ>YYprR)nSg=d|v7Jj>jhn}j zO=b6%G)UwW;0W@hp9@;Y#faI~&bKP-R6mR&5C~4>+GbwfcC1gnK5D`nc;njUIO(X& z%_$yMobYx@>N4Udd)es8(XD-m(ZO@;WFnFTB69S6|NJl&WP}MjT9bW%h?MuDgJk7y z&96vh|u#ex7bi!9`%7KL~vq-9Jwnb|I6Eo=uDZdTnpVffW}a!%4X&EY);Cqb$d#h}v+J=sMx{N^^wr-AnR@^F(f89Yv3w@M zd2($s3OBkGG=#*UdqhYV{lWtLb)R8`8W*(RIVyW~_LEGbX*EOc*savosy8Bwhd&f; zjj5-n?TQ5T3} z51RF+!J6zpFHzX7w_g0*jZVGqM9zeEVNj~Zw+BkXe2p`s=_?wAN;lV~ceW+_5SR>} zB~srSFb!NwNuO5)4Q#PHpU7JPF#zBoI7f_?9xdH=_W`LdOfzS#wFlTp7;xkT!4e)Y0ut={Lu1!Ezg!_&e`{GarBTOKE>Ri zFQS~kCMhuW=1cC(y0rjf5qZ6HbwJxb&a`>zC8>0if$l(=J1-1Bx^qkM zg)u?O!?EM%8*{*D;91GJUj^2umZ&835xs}Br&hHv#D0}A%lWgk=vWC?8Zuvf2z1Y6 z62fzL6aCigo#yOPNZTZX$M`G}b{WHeP6CW> zsbMC4oax#V@JrF@>E6?@@*8%>J;CgUiZlnF(Lxsh=VcE(CfzU9C9q+oUn?I4V1BwK zPd0WP2G94Gy3x7)j%Xj@T;Ac~oUr2ZM zkUR6lG&y$u30TV~9mHfDEU$QLKQoYo??Xt`8MRjj;$Qc^KSa9U+n@?>D)tdKje;L{ zd2hf6l%ZSZ2@e;NL#bYpTr(Mg0A*ebyjdiM-g4s}8yK*TEf8{k6NrECtAreOr2@%f z<*bDpZ0OU|h<0NS$j}Fh%`^Fs!ns*#WMvQkcRBa&I{9^3@jQpq1hq@1dSPyv-n`0BO zPs>!vpK_tXnlJ0SkUsnQ3aDVUF;n@XuokeHP4`zrHW+WctwT+ZPQB$sE~PU7478)@ zc)kiR9%s7UTa1U4vcUDd@jCFZ>71@)9#$BXp(Xu3`6BREOTYdIJ4_8Jy+zH`=bwRi z^cjO_GIA6^3ur|ex2nEtq2MxX&}-Ck3$GX#IS>7sp8fvm0$S=Kxt&jdAk{T#bru%( zseGW+ccoZK-L}6%Bj8`mIUypflLDUC5EzvrRSu?ihTy&3+ia)-Gp#Mpx$Aie4o1Ee zqq;Za1e%cvQJ#a#b5HQ4>lS1g+6llrBQ1%J)A_Jmw8e^N_rc(n?(2vgh`D|X;{VcL*@rCL+<5+)8tU60N|c}!Q7P~Vo=w_vk9MtT-eR2 z^O39w*4E4Bq$Nud0OTBYsOeNs!CKoqZS}yE7J`XmA3FeB!Jm*MLkOrwj&dOrf^xj_ zOkj`Ei4llpfbe&Vwt@$CI`*ZSz7nL#7)1b({oX@cQ$#F#ApTylKp0TNOhe4qMW9(r zy1Hbdyb3hStZS;s71k{4QNmOptXb^$f_JkbG-`g{CKG|BCa7?^GLz?FU9H)Lz2O1d3f>pNmD~N2=nXj6wbE>Fof&k zCp%K#-U9r+LsUuDk~#c*)b$O~H*kOD$xHSkBm4emmjuE|Nk0rSQZdWtLgFHD;F}*b zF1i4@3;kj_!}vga&xR3cwzz|G5s*Kbo2OL=%hjU)bEK^-M80{W)wnAF&uyRBk~X4} zAb+t}+_;{I<}t1zPshXLI6pKl0lUaYr$>>JLjV?m!AQ7@E5YQC(weShKsHK`a#j0D zhqeyPBdf7#zXBv7A-aX~IGpJlomc9+3X?DG*}7y5fa$@b>ttz-8{p=^c)qd{u`;0i zqO_k5?p|E%>ItXD#@m)vljhkiQh=M22DdJRFu@H3X7&^F36OBjmkOuTfQ&;7!br)d z`fbt0uVE_@PRpkk;sxw+f0glhWF!!8V_^PD0P#Qo&_f6RmO>l5r`PY{DSOTRM^mO24q5&akd&Sd68egPC)VQ)w5dTJG_p$(O$cgd5`E))ff{g#Mh7 z+okaU*`P&-m8}_g{^((~nC{M>Ddyg6upW%>EVH)lb#Lqt%Zh3PWW^PmXOS_(5HJJa z?-Ilm%AK+V{1=wTtTtj?;!+37NXZdY@l&N^Vc~2ZD@ltffl}Q8@@B=JR zi@6qnCn`PqrBfZ;l;lvdDRolb8%B-AH~Qz2dJ%;r55W8Ry6L9@u&5575eyfqE1Gb$ zm$4&Q3`#{cmAlBruZ>4LZ&Wi9Hm^&$tcAWBFe&!Y?%m9me&`!Hf3C`*X&~2)lnE~Y zLMoh|m=;aU;ZHIfi*qrJ-tC`tzVYnI-VY0S_uO{hp|Rz*O;g#prUYquE{CaSULosM zGF5X5vF!h9Q3K#4fExHAR;rl-0Ey065kNF6a3*NmxV@2i7T>yugX~#1zu{Ugn4k4HK z_L7Zyd|?J@W}8aHS7JH3=czCOZp%);iC$@pb{cJkif@kV4#TgK^X6jG`Ao7hSb;n0 zJ-klp-4&K0cQje_IEW}2s?V?96RRxlsgkiKY=SD>g310@3&zox2ia8QwM16z>yi9%8`<3aen4$9%61+2y_gCc5lG(M&pQXwiig78pQ>SYo489q?t#6)ZCly-&eesnR~BvM7TY#M)%<6C zJxBRk^UM5Hyg*ep`OcG+iBGct42m1<@d$+-V;61TT=EYi7fEFKc4l8)!u?!Syb9LD zN2m<6CWB0JSIfFmeYV%8xi8?M45Ot$nwZW0S&|;WH6j!PLVd*c%=%jRBpO~WA>Yq> z(D!<;ePf4J7if%OGAn#Le;W7zG_Qk@ElFzI&Y=6$|K3%=^*9^~^tBwlwP4v^S@d<; zikBF8Sup76)Z0%V+O>3k_@6gBK+klrGl+%@Gm!9x&fl06_t z?wTZlhZ)^7?Mi@$4hrLCzc}avEEj=j)>^M`L%mIw-x_J5@PI&n9>&es*BpjNkxg@c zZG>o2Lo){@Zo^c18ZU8l;k-<07O0``M$S6dvgJ+P`-9_FW=_2GSq9B|UiErIRXP9Nnss1}#571(%n({k-FmUX5KV9Gm?1o#hU3hS({xMENJA9)yf9yu>g@iA+TU>>NNoSI#a4ec}kpPGLQi zI6;j)UP^^M?tGPjObspoHMqp73n~L>s7vftvG;Y@TB6NUg;Mn6aVXP`&$5j8ET;I@AxZoI@~z z%4aG@mMq6p1$joak8a0(VvhG{bb$pf%Jpv_nY-jx`lhODF+Peu5#;U2|`N6p63du=#KByY;h^kt$NgCA3r@XPa{=rXQJH1G9-q{=95sjF;Mz=5&1_Kw3}G$F9N7UoQW|%}OX5h5 za?Q~wLZOd#8pqY_B|;SA>Y14ff&U2(!0TNyLWoLkoWzB@dJjpoW=v)!&Q5r%vm!>@ z1f-9XcB96DakNT*D9SwVA#eHebTPc&}TA+P33B$9R+J zHmjoVrxh)hs<~wDK~fR>SH1?43VH5^(L36$}s3&dSV&Y?zfDlyq(%$CWvVQK1FgZ_P=SPvOQL05*QTfAGXP*rxVL^`ehJ3=qp{^({`QoVJ-{A|!|%yZ5UZ#+ITGd+2Qy#U)q)I>gUL zrnqxJaeo+osB|DIuF_XYQ1bwv6vi`x`1i9K0R;g1=xe20zxEDxgJ8|85)g<4s`gM8 z!25sJF#@VRrr^7L05RGnCQUX>_#P0WC7qrb3=@;^sHvU@mX(sis^xWnzNvm^B-4d! zAHim;80^xOBRBOJ=BM$v=e!Gq;YrLGSF`^oQK*)a z2Y3t~dv9`h)d}Qj%@KW#z5K8;7?!-il|#xoKZygEPasY17kiOmt(5>jm^;OSl_KV~ zqrJFmRgm(8in8W^1icqmezIdW*FoBrYnRyAMbtIpv`%O;PMi@BO%7aj+)JwWv|m98 zm0>hrwk(79nt{?6XM+^EDaDq4;2pqwHta|he+MN<{Sl`&dW>PWP$Es^qR#=@A;vCx zk`B)ENbT_H8<5~*Gkcr}#00;ouZJ4SYNVxi?Pn22COm4WCo_{4&{QcrXQVR919L{s z$9i2IdcJQk!*xo-z8@{uO-)Ar+ha&^YAR27b++JQSS=^DguK|R9C|i&*-4*u1dYSd zb|EiYq88M>l*ZUY0O4q_ylzz>{(ZL*xs(_HyVw-+Z~MZFk9JiQOvCtQhVk95aw;rg zB4~gtb#I_l+=a!hHn|n|Yv)68Lm`EdbFC#vZ(#NNryzYjJOseTB|B*MXG zflmK#K|%P7NgN=c%A)ni8}U8VU z3q(}AT0SYMTqv;8;AZZ63~bPubRDNp0(0wD_;26l|6hHZ|At^F3j8+&`z!o61pgm~ z;N+RG@Kjvk>{h!*vk`GX(FGvq89wXgKBF!_!Dy~PydEEEoahw+-B`b{Df_*!<8R(k z%7cLl(k;cd^rRK}(iJVKU!V4mulQ}3I4@<)Ur4>FCIY62t!LCR<;$|;=-4eq2#&wd zEI>#aD$GNtku?ZcLAg6mh>!0&0Y|vdkL;%WO}bsgc{Z=DHmC6i`o1z9;QA?QtO{oP zQm<#jdP=ceR0tKe$oY#LX-`M990bb^j&%Dya4g1BIDNSW8g|7;5Q#EAyh4QSFm0N; zsPkDRzaB3vGt~iNyx|_CM~7Ix;U*2!n)ksA@>mbU1G+LOp^#=tTD= zOMoM{kb2OuECaHWNM$GpDSK*5C)z zO<-Cy!Q=*X{vrLjZu9BPaz&%f6N9x~bNZ%i0ye^;G6xZ+ag^KSSyNzbyy1j;LST1-OZ$28NFB|3N^q!*`e=x{6p0U6$ z1Xj*!x5AS0P2t8M$nQKtN=yY{_|nciB&Qz$Wgw&Kz@K#WS)6V0)x`-8f{8t7jw{Hw zytQf~XfblhPb_}oLH#EaFW==)R#+Ol!%J6Pu~gWe{I3s5*Vp_2xY^ywZ?g~PCf_rs z=@Z0`OFCzD9~k_`c`QD?z zRukv#OTPz{U8rCwO<%s?y4B|bNL4uq^xoJ{MrI2rLfCdvc5HK(5^Njxf}xt-eqRAnnFf~Z ze#4W7V&n9?J^Cb2z|_QNa;3Lf?zH{+X=tXAF|VD}f6y{rdkQvuZq}|(7*>G3j@URi zFf!c^NTa~iqu5O2f*bulim=Ns_CoXh18tz)fCeWJ9ewD~PJ}ogQqUnu2IOiUvbx_6 zz}`q>$Jp~%z{tU*Wg<9{Vl`QQ&u1}e_Vc6iz>$?p|Fo3hKMN3cYn=Le`SLB!5eU@Y zD{M=U9%=mD*CanP*9htfFh~`&2hJ9ol5R%Zh&JiIubTTf$~L-H_(%~Q1)oeI1=V&? zf=LC$*^PngFbhDwpU=txvtYoY;wk8f;Vh!o8QT|G#6~5+^wqr*vQ(Y+11RH6p4HJ9 z6Pscx@u-4Mr2g?;Sb@z`gJ42j)mo(D!}b32`e0zA_nJ8=p2l?nFzlgP?$0&YxHEB> zbb-1eeo>^H$66>j#Z|VGAe7x1z|AHF+&pb(4w(*|$_A3Vb(-}B(K`pTo2aTmvy|F@ zdZj*vFp}$c+X5$ws$&IZE9$2zS@CPB2O0<;vO2_wBNyQ*<{ulO+IP>9&Oo8vXu}pN zLvoazo-)wvo!ogkW5EDE)JA%+RoP4Je)=j+^Zm zDRyQ7O4oR+{PRl0Zmseci*NuzOn_Q%HExO;Ywfior)wBE()vat@@{$~D4b#)^6%b( zHP)iD^dL$CnwoV`toSnyJz1Ttwp9Lwi{1xT_bpUHPx)atxG0SVb zhEL#u;6JTIshq!j%={f-XmjrBP2@zsDhqK94C&yj_irLZOT^!3IV4cRjvhl!odK>UtFb$A3*3N=s4g-)fYL!nN8IOMFvyrE2yz*69%zls17gaTx-1k*?$YRbK!zUClSFW z-TEpA>XKFrdU<0ys1427ua&;3yxjYH_`bt4%R_`0nSd!=C)1QlWuPe$w-GZh;2;=( z*XKGPA}Lirf$leq zCoA6}b~AQej#>g^IbfyC-MHncZ_eBLjLV00P@P7;*NeflMDs=?e2}4UiCxtKH{o~e zl1tdWa$BR0qsYtyrKBmELJ|X@QQWzT;~?BTo~!P=`gIrRZh|L|V16s~!zFvrtQz+# z+l+kgdkUp7gI#uA_Pvklw*GU5pogk~!j}!!zGayH@d^>)yVz9p20$q; z0LbLHo(;~3rr@)-1~g+Pp6y#kt?G|UbX8b%P^_xX#MW+qubo*guivoPDSzM1VeW1j z&$g?Q!TM5z2*;N-aG0o*kn2I}9}hsY6KU~}qxE1c&Wgp@?LFw+f!r&i3hF5ed2L(P zeq4UtHnP~29NJ|R-)USVzS(?qWiwqnD|zC2ok-~~d`RlvP0a4l#KF~I6AGW&mS(5_ zpc9fjR7m;08V{P6cRarb@#!r@|GdXgR=HY<#tHDB$&>*D0ICnHNfSDSBz53-<6}C% z5Q(n;w(7EKt~duJc+a0*Tz-QTs^`^`)5SLR?CA>x<&V@n<+`p6b3izxyWLFUi; zer7O(cu4ZBZM`oKoUQAteJJ-S9UULl-}qMQz*YuQTT|L6C~&%~FzU8j!Vt0~vgZ`3 zb9@Z&L)Jcn2WoJyaPF966FZpvNNcE?Q+xw1evnV}5oKSU1*cxE z=Do61j?>h!3Q=KMk8h}u^+H3S0jT1g)41KRmoohJoLd|kfa=G!@?VtO!fgx3uLVi8 z%9k(7x7H6hJ`w|NczerGu{8Z7;bwoukG(!Z zY8aXiL5zDMmv-wC{J2Gp^J)$}i*Pzb{t~c8rJ_%J6JDnv{;&yPIRc3Rm@?m)78jSNB7A!*K zN1jbh?gMpjo@fE75(n%B#xC9Me4LTkg$2h2oMCd>4!aWg!TktQnI@#1%+m)+uIvY?o=+&n>**}Ul5z_bnLcD3=sK*u`7LUN5K$# z&=lD?=KxSXx703vcH$u$FbAo244xHZHnz|>h;S=d+)X-_tS||ZmJI7)1|eeUt>~Qe z)eF*us2+TA`aVRL&|O(l@|j+Oq*o>*Zt^iPUw_vroc9u9&w~Q)*jRdWu2n=RX>Fh6 z4is%pNN)%U>oI6*#W}#xJ(k%C{qJ{tDHsA`Y{&2W2oh%w9~8WP7Yk**tR1~|kC_n? zm#lA+69a~xWsbgVYaouTeB}^%odyx3=*(5&wHtI8c2ud8TvOsubkV zVwZ)#gR*yD@w;85{Coi>+J)bQrM)Ch6&)5eE*FG!2Re7a3|u2KQjR=lF4}$IFv4$C z8W%0_Iu?F&fD{#U-XNp+>wh~TKu`XAg5^Ix!Sdfu$bUN_aMt(VPRRc!!wAq3bFanx zg_>NEruHOjoQq=d$d`Ppfs)P?h6+~~sKC2pAkq~AP6xU+m6K+XngEI^EKNwWB$g7X z6wdXU0R28rgZ*=j%&T2(9R1+RA#fZ%whfi(r%_zMdcy4|II9QF#gZOJ3|IolV;_!3 zI_@5vr8!Z|d=`QtTPkX;)5O2jy1_P{_9->CtvOhLlmvw&6A;qN67(8~8G~wUceO2) zF*FY*tZk6ME;zVOFazjkrZoA%vJhze`22vZArWnx>0#YJ`bkouQ`-nzYC%DR?TgG&!O4A6Dse&gTtMcCG5vY6OjPu(iv9&=}0i)4_%_? zvsv$=1RXWF+GpaoziO^F#*Gu)IwJ|)I^(l#_xmQ6|NPv*SR=TG0gaj3eDew?H#lFy zsV*&N-*9mrrbO!4N-8~6m>mi%Pd*x)}LEWw6pqB z=)qw8#KSVup@^hDAbGoG)5QunZS20*4xgql3Y++IGlb!Xx6qwk>jsnLm+uOKe6v)+ z8)HX2N=g`4nU;nM+|1Nia807P+()+?U=SFEm>K&&8NTiGp=lG7cP~PP{^{hDn@N^~ zn>V`8dXWMQ3ekZ6)9Yzzbi@*Zsd-!go{Gx)d}fYDKz4Ek4C3C|=J5!`2V<+KNoBy* z0vZ;DN3S)E5GRIA%B<(@pp0;m&|ci^do*6wtsM1jrwX`pD%NgFIGYT#KCw(Q5tv|o}gE44RVerpIPjd#g#h_|SvFoNMEf0!;wNATk>u#&SC4H#k zpF@;ex8gx3`Z615cOPc#U$>svfm=_CT)z5}Ixx*lAdmjRG#g5I4L*OTiOax;f}u2> z{eDP6Wah#8f$R1%b0-SPP#mfwV8ls+%DShNSTv`**$&#qrT6b_yxR1MgY9ZO?sj7! z{wn(%`2`P5FO5OLRPu^tH}N2h|HFc}_mTQ)58s6d>)RWd(s<(wb^^^Vz$5~W7f0QOkwl15&Uz4dXaTfX8vAO z8_@Ihg9G4%#`uGv$TjrOr8)_;)8xxdvbxa-EK*da+e1Yx)hI^WpAk#7_q<#7&`p2S z7n*^De;*#`aTCNy1mgRp?o^P{asm|FMs_G&apIA%{gsj~Z(+pfa$2zb2Y|l%Od`u8 z?Qkz#!3B2iCrSYL{102afVo=Xeg&WvJKjDnu_i3@G4HHx(Rf|CSrIsgG}IE@!zu z0ITz2)*HWD(OzcJvhXPxq;?m;hh2N_7RkV_rztncw%-LRh-KM(oyJ!4pw>Z1#O%w* zX(zw|IO~6O01lwAZaHfObKou}wrgWegN#4er#4TS(1BwwbjCVl*C>AhaX0Flk}hc0 zLN}^Bc`0m4JXUxodLk@>G01EGpF^|8J2ECbUYm(acS zEdXD{0fq>BCD+M3uLqFhFVD2sCy8>TY&m`bmO#+H4A0r$N2>O295QkF4*3p^nT?CZ z1k|GPi$wJ_KdNkuaCcQluT5_#$xqjjc)n`!^ zg6yjp6~za2x~~H9uL!Q>A~^^EE)$Ak!vYPs$X{CI>tcsc&aA;&$P3}N8lu|TM9Sh! z+<#&POf(OmcYiljAHJ^4V4bwh&=sNXH*G&p)CJ-{^qS|AxjO^k?wq|?B>v{QToceiY}`T0 z7}`8V5Y!^oyRqkCV>YoAcw7wMyKyzc_QRnn2eZ6`q{>ebKnIZ5{ehBYs&K4on405G zgP=>3i!{GVQsFxnMM+pkigUhX4#b%kPZ|W*!hW>U6((MK2Kl zB-Ub*^!XF2fIZhPM2N-1n$UK*_^u{A`45gH06csyDVT@!ajuVtz%Ot059FH@_3ne@ zVH9-h2{`i56yj+1YCvRu%sMW zeE{EG&We!VnEf<9_U#Ri_4Y!ld_1_`IqZoHHi+sq%%7`uxLo)oLNj+ysCD92Y=zqP zR(J5TTD$&2QCV=EJWul+*@Z{6Oz$p0S3x(JZaIS6rOVIexNZk60{C$+@xUDI(+k?f zn+p`X--m34v6`y;ZS`s>bsB9(z3%XnoFK*30b_|5I8VD(#4b$_NVg_?md|l+&vAM+ z&0RjuN$dfUURJ$fy}i}A{cH=fz22-Zc`@h(G^Khu!H*UB@ zSt=?L#?q>+$u@T7ZWUTA*+Zi2*~T7)3d5Z{5`$96zGdH)DTES3b|WME*mv*wrMi>f z^SsY<9PjZS@ADqNznEjb^Szeyyw3G=oo5YCkgoM&U$|9l=kjo;-kTAN*6de-9*e}p zND?K*lhwuF{Bh4WU$t6?G$lMKigk@2RvijvvE$vEy6_r`MRSVOe~>se^6tU46g z%?2iNho%R~7cMV7xWvTevTNU}6F@$CEDG|A2>(3PajbRgvsxBgDlp=%2 z)G3nU@V&LtZ}5rKx`=)vMax#aTE&sMCL4SAseoi4<=p9c+KR+AWhl#P1^Ic9QIVT7 zq7^$A=j2|ct`M$%9}CoDD@YiS%aL6s2L`$o@A=&(2RuJJh2}OqQ>lsUMA&2u=aQ+p z9~`BDQ+IV_@?Xri`pK_uTzc3qDo7JvQxL6IraEJ-W+;d`&K`ja3Cp$#^V#X^uTwKT z&5@rjBM+vQxbK(dAo-39ylc<8b@gkSfkHkso)B29L!^bG3c2{;>d|(5`Q7%YFe37I zcJb$*W@21{o&n!e-NysJ3ocJ{eh#cF7QK93=gD}jW3e!>k^Z>;o$UD3?BCM{o&q$` zr$FQ5{Ew5=`l|T#6U3;aWu9Fm$weiscr~m?n^5zeihj_Q>lHrfg!e0TP+83p9LL@z zv2I9fnqG;13MiD_9O4o@O^CMegnEGiNWH-IqV$>kwO8b?`=$JK9nT&wV#3(Kwh7spygJ=kM;&U?TCU+VjjG(| z`FaKVx&4KwMN8e*i0HWzRa6idnVd4JurjQrbT1Ia_-5ZO<%^~w-PEy~OewTyV{S1% ziZ41}sz!fEoc~M8J^G^4I$PuUuP0C|`Bf+F-D$>@6tHVMYo@EDZ|Ug{vpJLDei5@ z;xi}vAEn>dKz6bJg?wT4Y*J<|t(7a0tpF`;v_7HYPqmdDh0m`n2sFC8+J=C5B3u$V ze|G1uqxMC)O}BKPS8=`)>T{Ftk6lu{J_fa)YlY;CNs%t)c-iT6>rS_s0CAT!KO~uE zs5$*$1g7WV$?I*bdD!3c>hjl5dzyO@&qvujSUl@7d3OJ@-yk{4d)4lL$?(9D5ij+S znCS;CHcWgtm%BN4#zi_O6!=~3y1o7eq}HzYC(xFs)w$PC?T6#q{~A2qIB#L|-!o3X zl${8GMJz9<7yS39|J|k^j`M$6;{U%VCuP6OSXY_#+_z9)8JFq%v&)kWD)i!Q)(Yt5 zS)y51&!zt;BF0Ov=v-NzVb0e49Zs?;<+cNaStmUqr^DF{y)cxi7zj;m z9CQ1gnO&G^Id@lH)Kj;>!JEyhFx}d^Fhtx3OR z+0TD`0NR{#tq~*M>qNrk*xR}Ob$ZdU#W<8*ud~3>>Rkxx%w72~5Q-Pyt*gq8{Ja#w zk}|c=47V~vR~=2~EGev_rgM={lgs=Fp+Q;7!pN~i*|3sijxR9a4pkH^%9;sO+=Wt6 z|KjrJ&XwK-uS#^W+ADdsBn4j(Kuk17Qv^Fc4K!gI^=srdJ_mgeH?fvFVyjn)dM| zzYH#40Ddgb-ly7*AqI3eXCru^5YfCl3bc>s0i@oSOGj27|Xl+^(=hevj0wz2I4 zV&F>CT#|;6AP-pcOD(ma$fG=`L0|dJ>YV#8hZ4FpTIMT zoB639rY_6RTrNzXc+?l#aD7%ZnSa#Uq*+xdSC^QS z;@790omyp|R^{4bf2BzuXOSLhAJU(Gp(Liuc~BBF+wD7O=Q!JZ`JALM0Ubi#-O9f7 zw_)GY(|gkeCI)0J9XrfgOb+X5BnsFpl5Kb6_69gP>krv}HfUVN{eIJFm7U`cG7O+1 z7Se?Z!*<#yQ2H2e{(?qh4L#*Io%1!>EQ~yiig`o7+q~ zZ=i>?kNv4JwaTIOFhVZ*)hoRL^WwWugra+QPzRKX)pu+qs#ce$**svcNdDlU;Z$H!UrBg*^4erV?E}Mx&7!P!G)xx-6*_V<(Hyr%$>I2Q4JfM zhkeWMwMSQ%{Zz1v1s|gtJFq+I9a|JmD$jp}qRrW1q&NHfjB(usb}BFJIzgPJpruDQ zojzt3dgsi=k>@hVPVo(OZR|6`w87;^Cvgk@tz;XreaE#k&oRw;wxo-kHqzv|qV&LA zRrb20Nz$&hO%Lgo_eAZISsLoU{1nwQtrj|K{uNF5V9ZDvf;~20NxGmSpg;dP^k()u zliEs?xe$IEeb*-2fx~*X4HFOQW^f&QQ!9vAsg$gRTq~WDj4g)R#&0+|rD$bx`u3() zH!JG|5>84B>V-Bq5GQ)x>`A?spok((W~LDB=SsKG*XYpMpwjme$!}#Vi4W`KBXi!8 zWCA(pgsmJ6T+FT7O;GsszgnFXTSzj~Y*G9i`qd$5QbN>B#)QT1r5aatiR$jBQO8<^ z`Gdx>P{gU{=q5>5U#r3ibVqlt>9GP~N7deBv7Ta{>cWDfXE!%>r@s|DyT8MzI#P{I z<_PIHhoWo1u$pdFpPIew#Qae(}bjb<{C-M5)w?PG_2@*hm{&1dy0UlTtHk7LrsY#5pqGAAW{k@(Vc~l{)KY zutEKa9P7HwBx%%$j;;{y}|o0jg1~yR0gG1Wtj7&Crc6^4u5nxW7|los$7oM z9q@l{-eG*QFC*GA19yV7Z`$42_EXHHyPvSItcwE*palYO)Kk*>*icDxnaONF=bS8HVn=ymH;3jev#ghl3syr7uaSxcAi|AJX44n4j?1 z62H&7Ef9E}z_S_Gh0$z$v`~HOgT0yf$bLP}K|W)G@nV~*=8-yFp+MrI&CSaT6U*`G z<|)BZPspOW5bd2RJZ<}6p=w#>9#7Pq@$lE*+Ze10jiYeAF7$OxONb;o#hLqNi*=9g zbK-HcMdv0>lPBz2&FXt^zne&;H1zhlpx#GiSIr>=u){K)wAUZtE?v$O2PK~p;$X%A zdyO%vQtZn89`Za#pvhvsxsGwv6P~ExjJr9vF~!t*LlaD+HQ7IntzQo9GsqDUE-A62 z2>s4|^%?1^c?CIq+x<>zB}g%Ik211vv#X9Az$c?C_rj$D zhQS4?>XdY257T3Ca9C6{-3+ylg=EwbGURGr_G_^H;hoC6!+x`h3euS+E?P|{ZoTB^ zqwX-%4Ob2y6-W{>)#W9|Wd@q@IYSYr#ccFj*kkR>3)IYt%yo`Uhf)Q<9>}Sk?b%n= zme`ar^i^~3h&sDf3&~iMx^RHxu(&OlY*tm^6&JOHTDV49(TN`*q5t4RE;%sB0-mL^ z9!X)9=V#T@WXM*wju=RJmvPpVkB&fv_v4W%uxco?DU39_#v(_b-T~&?CnICeJ~Ubv zpvKN4NmpzX3yYzZo2#x}d+K;zfI#ptenw3;^xdagecr^S45@ii&dqg|=a7BAUR>Jw zpx{im&nvIeNEw%MFmE-`cNib}T)wN`ELQZ(I2H>Wao8SnJs~Z*wyM}|p&WXN(#83o zdUc%kAG+39jjGDsf#>a6+MEZIyAh+vuE1O;RM$Z>cX7O$j^s~K!H|=h|JNv}fy2&c zXLza;-lTk<&d0ya$Iq(>q>p@(__lk=P!3TG*?cpjopMH6uSyc$_(#1$EcN;ui{|f! zkA=iwC3#TNn6&yYFU$_qmW+_ZoRvHT=-9>59)FJa?7sDQRcQaqMg57Tix*oWZ+T*T zzs1*jd&FEan@#JY1Q%pa`MDS9{$mv-xD*q6L|y?dPk%CbQiPS;A;T zPpZ>pSzVZxC#LJleFCA5-#{2-fEF8S^n?mOg4P>RCq2lx>v(xne$p_8V4>Mnz$bzw zz5QA#7x~tN9Z#YPZ5QQ33oPr?R_hFoIO53}ylw7(aNTvL&2?ywS0ZYmYEdo)Rhd^p z16~hlCdA0wyfQ%rQj{L2T?(<+ytTqZ9QP+)$nvGXFy~LiSVX#xe-N%)mT7D0|JaWu zGeB|C;xX_2db}}d7&C2GxuUnET`F>M7)F$avz~oRUd$ztw?Mx9x+1-Aw%XI|HeYYW z9S&CrQ%F)u`g<)l^)J!5f>f_R$&w)0`Z(js{a#=5wM9 z!Pz$z3Yh;6m|tZVJN42Sp4zHL7?U|B+D4(_KrwqVWS4sF)USC(CYW0X+MMTTE z!cOef_7)Ze{P(+-nytoV!(Q`ElUl1jDOVNh(YX|EacZnb*B-OC zhLtcXYRZJR)0Et%Ms4~2eb9=%fpLPc^-sqv8Udg9T+^IY{f2$7wk7_w0&jt~ohHNH zR$ey6?Rg0i1*Xz|U-cZsZCi-^3AwdwDq-Lpshi<~9JAjjXzML}CDh)J6^c&D_$Pq7 ziT`xJ*4}Kv1>#>|R&O1w4#_CC%=;E%9M`fzL-hXx1D}!b*^5%?WXZFXcl>mk`Qv>b zHLvUw1mnP6*Hp9jNvd>`kD()(c`>r5>=5e4Y(5~tGmYWSs6tEP0O zeS;W1o`#9bwXP501D%ur1ZNf>LKUGl;;$=uS4$)-qu_Sp{j0%75BE3U!0fx%sTSI7 zscCxcUEI)Irckq=ZBoI-S}RFu>_UP-VpIO@g+Y|KW+oXG>K$#o^rXGJ0J?(Q+>6%` zoyDQ*dU4g{K;)z6Sl~+1XS%1RjFA)aj7TB^+H;yuO5_Vw0UmL9E;1#mEXza*_pYCZ3<)84% z=9vgHm&(4nS1*vHY0O8QuDXpIa4I0ms=M*iVMArk<|n19xxY_K!sS~0y~G3Y;G0B( z+DiQ8=@vf=v86oR_F}y41Hw%^E&H&YrAE=k3DpTk)*OGZ1=7th_e^qi`P(@*Z75@BdqfQ>@;_vXk zHLA4TS5i5hz=!Dm9?|?NQM-?#gc|?D**R65+Y|gdh5pdNNYVZPvuHk3HZ5yZMZ9D; zxv(Ccef4kz+eBy!yrN{^g<3OSA8T^kJ`6T6kQk-IX%$7N#B2{vB{!3uf>RT>dIVdf zE));3k`bwlHM#>9I(V=h%(IeFa=&L+EzvOgG;>< zF?fsnnRT)*vB~}H?TG@PV$9vs1JEIe4Axe9XXDSYa4B&(!RV-zKF?RwQ!R5ne8L7bgBq``ZnEaMIR$&K(|b8{?4jiw6|x< zffyjZH=if*8^%25uFIg-Q|bL!-t1kebs2{vwWn;EN$De`>E2+Kok*yI?ZSfz8Hi$J z`x8ZF^4=B`->{nr4@<}jxR1US`y;^t*6k69iHWKj?GyddgTyL&GjHy*?^U z;*#3Eax$4n*f+1_OCY+b>Eqo(z3c7o5y5lmHlH*z(^O6TF2wiDar<+l+`RbM^l9Qe zo=8k5+dDt(c50JlU&fl9a9f;o-hMK!F2IO0&}M!%SC&J=wZwEM(TK=-?nvFkRuX@D z5xlbbOkr$j0RL7zaSUCXQ{#9iLQRRgj=a8i5y;zkmnlT^wDQ0ofRb#HL(xJ!1Ha}U7km#(Aiz6cXW*xfFn8J5= zMTSiFN9wnmEG36t@7@+L0O9PDS3_;L8gh$GQ-U{}x6CH^8@XN_GRr8MmDST2b|`kz}x`IT|ZpLkg^xZ%olTN8Ta(`k(=hNua)m3N$X!*zP-D*`xF7|uLz z>g_wzf#+oQBSyN8m<_ow7PGqxm>*=XyCvfMmh2yj#-bxH$4hr){Y~f>&Ss_O!tyRB z7QB_H%tp$k| zmE|yUg!}w=;a2lMj^C<=<5GeBjkFgx`ww%Wq^?2gO1?^8pMPClEbOWiQg|wQ~9llG6#R;}DWdEFMa(e~ERf12&xwcfOYmQDZhy-#`J zUW201pB%X+b;bb|N78i6T!%ILX_J%!4w$UND)n=1>s}ue2Y00F^266(;Qcx6KG+7* z=jQblOMd*`QySLd)YP8zA6O3jCV&k~hgR0 z)=m4rBwgK(|22|&dX*Z7>h2XNSsX9V{{93~Tq19Y8*GBj6cvD@r7x+xdG0zS&N0XS z-m-4X5w_Qa%u*&#&WGJc;nC27<_o{h!jFI6Llc<@vu_{6OIPE171|rw=-C- z<8WP?+@)ylwc!aZctT5O=aJ)3eVlxJ@V5GfC)9wY6RRWwOYef6w1p@3^#&%~4dQkW z*KIHPPI%NUHJZFZLVSiZ)8KqNKwc1@DB=r0x#0;`2=0lWNi?8XfH#>c5C!eQU7n)F9Eyd(u)QlP$zxIb+(Y}(d(``@petxA}Fs9&60@lHsD!UCRc zecZGm))mbO8}I(*!%-R$s3u|gj|OqWFdgty7mX8Jjs!t#q=*hxzYR~wg7R2ZUqR>K z3F<4t8=mk4(rmkOu7N8J)ES0dhO|K|`#@a+4n3uh59E>i6&5Gfqx=woy%p}GaM(F1 zVdX{P2~qB|f5y@&@4#k~uH2e9l(yPR4Y>J_iOBoFYPb7YR#75d;~Ne%ZIF*hJbogj zq-9StsLTR}4X2Ir2>dXZwPVkFRaok+Zl(35eBk-x(2HAoTjfoP)wW^@*?Ak58Uka4 zvPpQAcEP*u38vF*c;Yr}xtWc`)6(PcL`71f=7y!J15>P89a|999{)6U0|T7{(?a=f zJPm~#$c33AugVQ!igTd0lm}mnQWSxTn#nNVxg&kXB431m_zmPaB?o|1#B3`8Rj~o`(gW2d1qL#^Hu&0#G8jw3nR8u z{y6yq@Wg?0A2$f9t@FEc?lb^)+l@R{Ic40Y+5xAjKQlv^O6j)_X9> z6G)ql`fs8V6pgUl#t76O=wh&T0RJAL`08VDDFuiJ3RJ-cW@Z6#D9o3CY|m4;_M+l< zZG-vQi?qcoSiY%pT~Xe8=+s8j=qYfH%597a*Hu_*thczupC_&q1!JT3h10B+P5Iil zL7rZNuDp7dz0?%<5LH+F_Q<(E6<9n_E9Kp{>l76*8hvKU%?(Sv1}d&FA!34hI|v}@ zXv1f;f6|`I6Ktu?FOE=8!WFT$8CupqgURw-P}m-w9M?oWc?F)R&R`Mw^BvJbut=jU z)+69-kVEPzn9%uop%2-;9oN)Ar1;QNElgd z(hc44lAR!iq;yaU2kQ}Z3;(U94SYHe1v5s@;`|@W#{i$6aT47)3^eJ@bv!J5o#Hzz z_`A}Z4aDn*0x!9BjlvET4`FW}a*1w8!tZ+k>*ewm*+DS~%OZTT75H=GuOrnQyBojw zQ-4A3XC1Mn*ziO>JaICV&`Yff3S7hX4&$Fsa52MX3Oo^0p`e3#&{+?yL#856-vB&Q zZCZcq#p`g-M4I35N2Pw#t6i;~%MMU3v7j7~P~|I&Q7gi2j-77%Ef?SxKx5zW$)Eff ze|Rj(M27dR2V7v7D4!l~t-!KNWjC3Le(t_Ug=ZJQKHp^u2dOP2%YE%EdZx>7_pvIP zYO=1AnK55tvzqyNJo+=`xBp!yIhb_v{(CQ?tp3AFWv;)0L3h2p`QxXrD2SzKpURP= zdR5;xtsT2)JVmPOH{OaoQe?uZhzY;lvYbvTgQJ%9LCZ?Y%K&(B})m3(H|FRUI z@Yup9V($^js2q#dy*+Y2;xYABo^>9}6W#jD)gCRQ=l99{9L>Npr)b!sJI-T1j%~zv zs*ehv*eVeW$6}__!vl$ZmO}z?=S-#?f5Sb*`W3 zeYlrO=?`&6ZC%)J-78o!I3{1RJUS}9*ue%zMw%E^+&lK&#I3)(Vzg*~8nzVg-W^{N zxAINHqkGK7%13Z)`0R?tY>eET9{TwF%)&~YzHjHkxX~MK_9&;t(dG7%!6EuS=;Rst zg@*DO^MmK)4*)?#U&IZOUj{4F0#-?hl>?wq*ym)(a8@iYEpW%>Ov!fGyUw&b(gm8_ z_~GEUGe7X?$9CH^9^P6hw{Q@DpWwet^C&T4#|pZPJZ4t-kcAd2{#ZILmGbQ|0>WXz+oh+QeOG5exve+XTh(3Y&E zZr#ReZzcI?^SH0<-0)c5%6th=;9Q$+-41dXURN8;lEeq(8-ff9jWJl~MGyP1ZI4e# zTMDC7cC!YO;1&@*cTuKuTgocN9Uw|YfO1E7Sm@Z#ofT~3(3>bU(U57g2?H(^=EW#B zFrjiNahh;nw}w{FD8IyhA*bA9v44gKw~JFV-J~vZ>?OC%>(68h_Csq%2oLRBysCL; zana{bP|h#gizxZrh7xVvbr-Lun!g*hrXa)wt*06_d{sQ=8=h>vQ>L(vZ0qCC(pVvH-VB2KDTlqLxxScdkswmL%9=m`5e|qBMAL_o%bD87dn0q3{GA z#YbbgP&C}q6mHTs8%=TY>A$P_k$sTY@|1LBD`-F`FJ02!gQ9JWxK1}g}GJ!HL7?;e(q49n6v!dV*a;{p4+9~2}Nqz?~e4-uc z2h1zl*b@_HQ%J1L$|KTwrBHNQG`?@gI_)bfq#yFeuCOJ4UYp!(Kgq z+XU<#>DS#$Cp;aOzm|9Z}9<(Bbufw#=!*6f383en%W#Eh-mCa)2$ z+wuGm<9AC-W|>zGz-f}9(_a30BL1fXb>=T=cgwj?y3o-}f#fewU93vwla5V>_Qu&S zrxdO$c^=u)0%_l1Bc0&7?`tHMSD+|x7^Wpl;Qd1Momy1xbIQ%XKkeImHX0lq0qbk$ zp)Q{?{cC;kG$Vv&fGq zn#j?yOu2fWT`M=c07SW zZ*LGPA5?ahkl9Mo$DU?80MCG@?fDml5@pulR=kq`v0b4*wYCox^eEj{Ifc=K-gzrL!K1$wg&4$Q5P(XogN2*Udx&tb^yRSBtv zzlr%_K&*ui{x0q%JnpLUY*hk!jqL0B^)Ix8m*z98J8+<`LxqypOZgi~t zCcLJSk5j|ExvHA;&VvQ1@%ZHSbJo%iPB>WZhAN0pUQTR*!i>d#Z9R7#O>vB(Eg#@Q zvc6Y03$F?5H3!?{Y9=#E{BXEq0W?e-?EC^{n3X>4%+W0)Tbr5Pq?A0CW*MZ-jn>Oa zRi`bPur4y3)ZC`klXreeiKO2d&)cuV&1T-_~O~i``BUy)QVdSs;sG zVa_!c2Cx9m`%-NxeVL8BsFe}iXcqg&x6!u?b8Sk5jiPAxwZqbuhF&8gmZ#LhS9v_z z1rFvr(Y%4|qHa$)k6i0|8C?fQ)~W1K2m)*70*7yL2Xhv zGe3^&?3^d(L~-glmrr}+d-Ja%Ad{JTI8btDe!wXmw5ZsJL~R9 zZCWX1>FKA|y`bG3-KXRY7Pi&8=%Pvg1k<%9-Txobv;LWU0x~(R4|Z#I#40`c>m&&6 zDB1KCdRnQJR(prp%9zd_%pSAB-ot_t-Lqp4b5;xu!kG|NiTI$xDS0_O=-MUPcsY+U z8#iGSNK3sa&4=0MzBBy3mK#H&p*no^Tj<%hO9?pX+Yn8MBY8Vmv7mzu@b5Hy&z{*l z-aB6UW^^+5(fOcHE&%8Cyru*@rCc8W_c7Q6#s`hBfVW@$wG$9{4af;QMO4yIURgUC z+VGX3Oclpf^0VMbPRru4h~zE$6UBoj92#V-u%nM(ovTy&^BMvqu=F?WP|~+VPlWR?ae7RGijHgkh$7W0-W&0{lOFQ>0ZaS24IP$`R-jO;FnVs| z`{?G;wfQQ*eDQvlr=LDL7-jtz#(MJTBl4ZGPiGZ5HON2tlx)yaLW|8Tp=8|*rb+oT zw-_(tkf7(+OT_B*v_@Dy@gmD495wcfIax#Ye~XrCFa(1-8mE z60X+?gS4$3WPNujotdyY=4{>BM_0?A$juCtXGdsI;w3an^781g;h7)vjG+m5qrSz6 z7qPJ|;`H6ptyG2>*A6F=Hy|nA9DlUxqTH$GHvqZlJ*>W_DOOt`EY8~hIJ?fX$U?9XLO+!D38O7*Rc2_U-OY8ohoZpMjmje{jGXH^jV-BLl1oid(Oh6u!MGU3S9-( zC@`#rL;Roi=O|7Mn>sZsy4EwPd4f=oC-v^lKIoQNDLSJL1?2QMPmu)CDOP>ke+z>- zLs(&FcWgoNK7lq{#plj`99M;8$3#a9WZUK%w-6hX35jRqf*i~GeaG+ULj@)M%^z!k zef`Zwl4-HFB@_d7`qny~H7sjZ(5h2A33a+qr!+S${^0z%@<$;uDLsd|QJk{mX?-1x z-cBhhyo74rk_2*PsqXpNGMk-h%2M!0OXc?}arKuy+wKv!aY*iS&>1fd8)y@rzg$Q> zBZti~4V!u3$3qgh?MS^qiC2g>oauvDke-sy@dIUKhJ@x36x8T-9t`*y2Zkii4w_yL zlt)*lqqm>j;s-BxT!D{9k#=5v_a$k?-p&QaASBcf!3Q5Vj|9WOwJWl zc#FH*73NPl_wK<`&*)5Lr&&xsBLd5(4}7fCv~DZ3!OFlKljfam0c1cT{eHRF`9P4F z_2R=N4~E*jk-jh<8S7ywGv6Yz4U?2Wv842Pza!o7x5ZrAGMnxzX^(#-W~e}#dsbXP z{!ktA{Vm{E@8PL)kGPK&KbHH!_z7N5L?TkG6^`?3oC@?xcD>!+2OtKE+w?Jlke(Yb z-TNCi^6h#rgm&+Ge$N1ZcvSm9@XZ+>$K!F|oidXl$2BPT1$y-;BoEyr9Y%K~d2qTg zqh``?SN_)NSKh)Cb&nW6GH;k`UB~|>SzV$#;S+tY?(zBOg>8GGKpT^lNinqLjj$a& z4c&d;5u#GNFm4L`zt4UP@(M!X=)5&agFfpDPKQ$iibsDNuSx+lvBP02<8m*e`qOQV z54$jzzKO8>c0BuNdiSF0TrR#dQ+7Uy9)Bo@4t)K2`fTw;`)AS-(kxPC?n0>lz1v`K znR!VRJ_ajuckn#~tF>oe$8WL!<2s&gUPQ;m(9WF%x4sE)FWH|$&7d`bms{_SJ!b5= zFI`*-u?E-OBdo92EH(+9dFy!#!{%tP;zZd=J=mvI>v{Op5Ptjbf4K%qaKjWssS22} zktbch^=rO87LsV$%9c_%Z0jA1buHi3U1rv)`M>o(MpUQGsHb(MOKQ~Xa+#QdfIzl( z`v^m)>vlP0>?d7(gQ6wU5J*LTbJJBY$*vx~ZL}5?b%Pt7n6RLw-{`;n6jxl=>`&Y2 zxg|G;%f;LYUFN;n$pu%Yi7uoF=R32dDTL%!JF8lteynN{YkXI(#aG0$k6#~78sDuay03>EbYW;@qiH@Pjqs0HTsct3&jl)^2q?esHMO)4=5gRK~ zH3A}IT+8~MswDW3ceE{|sPMz#r23dHv@cH_4*noXf`6H>KVDt{>Xs1?>fI?Pcki)U zd3T1V!NDWoqG7-p(12YM_CFvgW=M3qw0aW!+Vz=|Jepnxl-yW-*(ds@BCFUeB`9;! zcD{wk(^I@H#m!_dLW#gs%G6_EZX6SRBMLF_z{7<|22kV49n_ByC6Y8L+WQ{3eganH zw_i5J5X`PR>8o?>CL`OKqvpoR2KU^UQ1sbZY`CXgn3%;4LUO`-Wlx&W3yLT)8c6>y^}hK-0W63Z!u?9xVT&Yo8(mjYlt>c(9oU zyX|sitrED6d5`Vf<-rvUeZT3q{OY;=$0xgHBjuL0Fy9MO!=K+qcD{DxhkOpypf7R% zA)1%Q<{3JvvA)+DKh~YME}UcCZbRus+^_zoMFMJjPc^PqjQBwS_Fem&tzF9^1AW3r z^9!Qg5J5_hjTCL0JTYp+#9Z9-kM*~@qWte8;$OiE{IzIHJ3}vG!1zptErgt_Z`S|D z&R>||jXH!1@+~;yw@`1cK=g=NCg~Np2yr$wubH54QKQ(8NFcK5EMR&F3%K9A3J&dq zaGM4w%;eYE^@R$*qi1Ky2=RPhVcJWN|}pg7>j-$2<|N zD)l^jxDs9o|NYvbn{8Q8Z>mQeZIAS8*kYk&EnQzOCczJ(pZu>!sEM0UP>O-K z+Hrq=Y=IHM!-PM5{@dVjkWNTtUzW>)!!b#QC90 z{p=29UF$Zk5&7`F(DE!zNm=_%9Z7Hh+Pyy_AqgXINu03ZN{W2u4GRj?_|&py0%1%*t#U%AK+W z5qR2Qjh1yGRxg%Bd-4Q-;jKMfX^-!Y);wvB00YqT3>@5mw40srUFZeH=v@##JDAiW z4i4<{?)V*$N>=Kl-2cD~IlGNiu&$gthhTosK&t)V85|*3d5m>@^G=u6udOEwK@+!o=1N77yOReY&zh7>^3rZ zK@yS%sWGA7^G*d_q_g@8fYd`>QnrW1 zxFK&Hy$gQ#Yv)WUe;@kx7BGi`C4gwG|0SCA;Q#qVgQ#@Lgj7kxw#+WC2$Ov5;VBa z@KS#Ni)FjlCeKLf+1jD=&$kC}^5larHUDWj^dtH47s$WAf+R!_Qhs=iU_?su;e-*G zR#U~FX;f*ja*O#Z{JOqcRi{dYv6)A02ZJa5iA^6e`|DRfem?J9X>+Aknstv??`4zB zL-Je!AP*o;3PERiTHs^a(-s#&0>zz9ii7ZR+@&v-2!2NE?feM{shrrqU_yDHOZbo& zSQUUE9<5o!xJKK)%rf4fg1ldd{eYZ>Le?derbX@`7iHM~bfk8lEl8o-HMDaw%u$=W z?u9~`&0b?mx5r+qYrJF~aXKtm`HFSKKf-~P_qm+DwD8d|oIBVhB^(fg1ZjdGbrx~z zGFt;|qT$6#}hF}@K(LbrRw?EtgV&o|=9P%}gv?&6I`L(FQ zV6dK*BTxH!5&5bj>|pqp5l$jUC_lv1-aLO-o)u&>SVR!N@nnRN7$1H80US~R<*fWX zJ^&;QIqSicF??x!YPyb^`sB@XNOl-7v7KRS*lPJf_!1%9>Y}uxUUuc$kMl_~qD(IL zy@cVpztzMD?LWt1FQ!OA`574IA~Bu``0V96vN^A(sUUzp1ovckoROzKp)B-B1FrRC z`_FEWFvY1LmfdVXg5w&cj(uxL?)rR?`@a^|#C;5HiIeu*Nd*iq@g0UgFEL?yk^1pj z+{ee8JVk)f8_>6rE1&Dh7P+3L)j-qg`$JK?7`$qNnh{WK9{h3*0PfiYn)278a%9i{ z#p;ld#P4192IJ=xaNRR0L%RgT90lQO~G0C#kI8?8a z&}hX0Gt!Xb&5q~DbZ}8HgKSgQBL^c{=$r7xz%&L>hRH8J)}4g_9J$Mx!E1ZU%Oii? zmA?lEj_qwDf(yKjgk*k(P*PuK&5n^c+*C0iXz;#%br>h~NWX<2eeI1@sMmZdM_LJi zgNNCHGxV~aCJxTygD}Obr-F2NEFuxX2ImoEH*y{if*2)2-0GLhemgtWi)gOc+5eUb zA25EiHMa}h`Z-nF2FZNRMINR6kTZQ#`L4Vhh}Wr)5s<}(a+83=Ggd*#A)+X)~cn?o=YekOC4;QLt3AmhK%86A|BkP9g2tVfpJpusho{ z)q@YnN5_GPSMYY}C3Nb(_S#l5VT$RM>fAxU{H|>5TQcX@g;Pia~LgRzbrcDg;{_xj)_DA!vP?p!Q6V+mO#e zvkzc1Y3vUG8?ZPDO0YiZkK%|QT&i`aB|k1(tb zWPS)J`Or-*GQ6P2Mbi=aJpmR#ZE!k*8yp5bP9K)ahQh0-gvvQsAoX}&gmH5_Mp8Y%(qA+^hA8S;s3t48K&+wo* zoV^?BRNxkX-55XZ$*uZ^d%)Cs4hGZxr_Uf{Uiu7~=H1s`M7n8Lkv3OCyLSRLc9aiu zZGRfL4O>rvQF4ky3U>bzoCD3SG{m?F04I^2OowgI^BF-9 z>Te*MW~6ZnagvXy`nJbH8}liP8xE&}&Ri1(TTvdkVrN!b?CHC!_d0y5LU_;u(1~1$ zIdV@e`uki_p{zPPl#uv$bSpJ5R1)sYMZ%HHndYt2q0TsggCe=_FM;~^IW2BXK`9B~ zcv;nFjAi5>Db3D{$xG-+D9*3LD3%0R6FqRr6oI)E?+!(R4_Ft}oOThc;*0N*iW1TS zT~ZS^bfdsvX^2tuoc1L(HuWbbT$pc*38d98?ny>CA(se&#T+?E&MfpvOoFNTyy_MS zA`F9Jr_D|U@l}~WrldZ!8-&E##R>%1FMB`N(pq}dk~rvqW3QE&I!(jVn0jQ@+}1zC ziyl#YH7}}MW>YfI==}+;ptWqE#q%z^;{e%?AZKLXrYl>!BH2!F z7fTv?*-7g%F+xGWL9eb{>tla_YF;7O8j8mXkKpvY@D+u1^1;0F&f#U@-z;RqdW$Yt zyNBFnSdb#(XuJzE_l0#P(40l7%=+VpME(^E~~s@97*wU{BT zP5s!oMd{flPbMH|3N=Xt-YK%W)_E+1%Qa{I9+hStnIoopON|zlOh$w*;)c8P#c?rZy#-;61S1$hD;8;BZ*;NkoJ%Sz~a6NY4v`K90{9h+kZOCFG zc4cIru7$Q_hL@|zX3DR7$8as#xd_tXxIQS8&e+;WLd4{vb?s|z-%x+(z!1YAF$39) z1*ZC%MMyyA_H`t5p=Wrbm*LaY#|$|I>c>lmHv#Xnf*3WsZr@dZZyUYOtQF*8wO1sM zA5s8=^f~wu`}=0X0mk^wq|kLT{*lH1zGx2#-0$=57cA9_h^hP@7sX+j+wEh$NX83V zmF}XXo`HmpU}#!sOR8PH1*%8H;Q=k3Gln@+xs_rG5!(r^G8%<%?ri3Cnk?AA zyf6-85y2u<;PhpV&$I=WEG_ayIYAeAm-Ef(&=eQ5LprV&qTO&K)CjpgwA26N1l4`6 z35y{F?aI77>2Y7qwZE>s$Yc4^Y^IgW?&Tis&bHelw^ZD#ng;3RmRPz+rH8}1N0;ht z%WWgazu4#BvGjv|)_$w^Jn{5%a&8`mM24D}fJ32S=!b~m>?JbC+>~Rg&bw_K&S)JB zk^j}^aRjZ>Mo#f~)^5a0iRaZ<{s}!IQ~{$s`JN$j**e1FyG9hoa{v3o3$jehtws8p z>`CQe0OYz_!B2`PnS8%O{%Cc7Le_5SGqGngVP$cyyE#ssp1JyKiew7o85I_)=R#Zo z-Tl>pD}Rsmn8?L_mL{>Qev`yGTH=P~reXxCHHKW(a93&Z?$F5kL>s1#xq`$w8#6W~ zx8iKj`Qix?JNJA{QO>EVN_p^$%ctC);(d2Z28o=f?>sO z5X`%|3QVZ((4BlneL7Apach>bfxh`*RLMZin3b%(_VX6Z*#Qsq1n1steyw9>1p~Ry zM4T)8clPSDe@q>TVM)-5^OH^((H2E_2=-IJ#z-|jlz35#Pe)% z&MMhgr}eCLR9~;nTq(722h5zqWhgz?_mvw2Rl&LCJf+Np;&h$-lt%1H%!%}r)-0ye zx4DozPSTUfyH!6*g})~-T6u&U-LY>eS_&f9`Go-&l_4BBn8x;zpYqlTMt1#UYFoZ$ zqzPv4Qi#(Sos*yK=uh7kTx~Uy&m}!&wba!cn>V`3X|Vx4X!qe7@h`N2i8^t%Lbr#m zL6@)HtShDQmS-2ch;J79`h^brg9&}!|d>NhP$6!XVtI@*Ut zI^LLRfZ$+zh6;h`x#@X%YgcMXHEWc1!R^WSk9gxaG|8)qE`_|smH$aex+MfhKizCk z>p~BXi;DqB(J1Sl1sGS5gc470I~A01X_7@nN)oj)X>vrteVl$d+4{rlI6;e4z9B#E zclEE$BUXBiQxg{BeKWENG}x$x9FL>YgU8Ma;I1z2%FSK;-oLFo_TK1+VB$4P*NVI+ z_r?^b-EWUi=8cw!(zdR2c`nc{7*V@??%&0r77``hC-Ee}$i=vp=x$gdx3GuXJIZ-F z{Z*X+)^o@zATmd0G^~}psPD0q_k?0P!$viE22(fzN!rt4ZNT zwZsq5*Xeb)jORQE<@uYnKdzTOcmbwMzd^DWTpBp1dGzbME_BF9sO3VySSexo<)wE# z`wgWHa9rs%Q1K9r%8A>f}DA(gcX>Z#l?Se;Bd+Zo}lbM|0T;3!Y}3EZH*M56A=2 z`rM?IDGv`)H0k!8VF(DeW`d=FWEPc$HNY;xCux1)r#wb?`l@fJnqV}rLZsBPL2?GC zZsI%!m_JcgFS^FGy$fACNLXGVRam)^XL?#I6dQ%uWeq-r+xHc{{9Zniaa@!mEv2Eb z;A!g0+(bpXira02?ZLcCwFTxWuLs2>LQ_iWV8)7546Kf?Su0OPv%!`r*q>wh<=F}{ z&c45V{r_U`J)@dzqWw`(6cB0BR6-L0QHqE_q$?sK0@4JesR$@NAiXJy^q|s2nu_!$ zARU58iFB3TAwiH%=$-!#zV)ni&OPU@`{CYo&j*)zSjjUpyU(6Izr8gKL8&PSg2sf? zI3%;WB%=nQP)SQ-K2g>&N?|9%lG9XVFpz7Fl z)H@Zm^O2G1%F*iSUPjXC>!V!Paj#oE35{Q}S%uz6mh(V`reNOqN}OKo_E_h$x|sYf zktf6syENLorhlLE-du@_wewi3>GRJ0X#Ew+x;S;59UP$izQyaSG$q>4V8CpQ2a2*B zzh#Xv`GgD1CjH(;TJ|&g>BDOW5-QFbY>lY37*KZ2NJz%IPj1QHX^s81NCg{l=8uWh%n0XC z=cL&qhl|G#_J5u~2sc*e+@qlMtQ3KWk{*25;L=5LH0B7}c=g>>wYXyILYzUmOp)tC ztm|A?^lG_SV_}r1jYc!RZFPv*Ygx01vD6TN%)%C#5X{#(k%IOYFgzD*Vs*(#-5?7r zqlJNu3?N6XylA7r*RCH%C;D&$6g9FSbp;fo+wsp+xE@~)({~RdI0^%llfdY12PdEq zLs7m?D!aP3foqazKU#$M&(=ydr_fnsyb;foSVc~YTg248(uzEXNZ;l*>n44nvO}5$ z(mAbywnCeURZA|KhLmkHV}*hjTC#9s%5V2J**)o1gsM3YaSxobH?7T1CFa;{`RdM( z{RSRmE%U^1ztlFB1?pk`tQ*?B33Wqo52d*o&T3<4?&!DdsYe+Cfu+ouCQvuVQO6KSzhbU_ljuGV<`xDi%R0Gk++ilh2$S8$X2{^q_i<+z%CS8NJi7 zkRI8f&RfoVY~l7^{_v%S%ipufusZ#sn@xQ@-uzijrvW9;sX~CVDkTT2Fr{)R74Idx z(2?_uW^gBsFxhDtQTCaVmtT+1;V44rlI25ZJ?rE$wja)8OZ~mN%qNQ8WzqmfB%Chl zV2_;`A_n4kv`l)5szfiKc_#W!tbYXj8KFChenQCE2X`f;_^VKUL2VuHeuB*VzBi;n zH_omZFllAezl}VeM2>D`BfQvX=>TiCEOMI(vD+Ss;V{Tu)YgvCb?ayMPFzF$a`^%_ zVzPgc=qQ$>N^Kl?JF8erJk1LY_rF8+;WoIw*}|?#WVvh@YNOZu1Fh9OE{m`}D%U37 z{BCa>p+LLeE=JSn6JZe??Oc5D)J#cJ@(sUP1+{_1kacUTxyx@Z#0c9+vz5huWOqGw zv076x$Fx!6(!FSP*m`9SBuLz4D2|U8v@;Fph-&pCcNK~zsPic&CEESw2R3STyZ-9; z)-G_Ll8X6l=)gFUPEkN7yko(pW|41Bzqj$oQTNs3w&X%yD^TlFcXo<2!1O9y;V>OB zK}=Cq+IhLOXpRjjNye*nbO9j?pt7GFa3?(qT(oNMCFFrX?d>jg_fqFt7}@7Fis)G$ z8Nx+xNyO@s^$2gg`wf?7N^kx?$>5ZTJp_XP>|6mXfsx(T;uqym_rc)ibQUAKC6OB>}7 z8AINE6FmV9+4T1x`g~&=##raB%kGA^F~#ecj(1_!udQVX?HG-GOYhPc`S2ujbS19m zYR~UGlmI|r4;EFgcDDJtU4O-!BX_TWxz);bcYx~493DgVm&3(VjFZD_5JH3ex0|M& z@Q?KMMDatNG1O*Ft$MsY*M98C&Pp~Vk)Ed3I#int!IS+c1olM2_syZ5rO@g)nto=s zwcdnaKgTuEWv(-IR5>DZD3(T}P0))Nti)!9k@D2i_YMb#%@(Ut>dW|K-vYHe-QcV4 zb~$V2gWbRG)xCWf?PoZo(yVG%&bPj^0yz-d88NYiy`b`fs25ZLBJe^3D=Z?M(@+GO zN&i;N`qMAf2}jG^mhy}h0VDvIns#09VfT)V3&VT8gTw?BojinNm50&=PEb5Kef^); z(@J6R(tBvw1urpQ(ySuHyJL>oYDlG$*#vX&Svd6ZS3#7*R)TjtUc}XyZasI<`Mti=g zay{g7+aFt5rp>dK?rl|s(YG1Tr^3dyTq}XRpV&hulJ(~2laI~5k+k_+4@Y4!L3huFGS+=Pa`q7Y|p!M zb9147tVVyv6B6ZYL*WI6qSI$Z0>@>x`PBH9)J=a%dN1pYOnJktJo$0ygp#G7%jTtxtyK6b9D(D z-w=#lQ~R3D)vmjK-ri^fni6vGWO?6Ty%q11h{W#OfdTq+Jb0Rf1`d9 zLTTXCW6Hu_{o)YdNoa_ciNFPFXA9vEFClHK)jZpzVAmgad&%^a4<4hHE{|{8GsI9p zg;gH!aF;np|7B+^oAQgMqAb+PcT(JJ4DO^r<6nEtRC>#7buxR~N|k4I<$W%*`+WE; zSP=_nYf`NDRtOG%4cDE_u6X|9ATeNz52xg-){l zojuUMzaEEI!QKe^_U<<&>3GETJE8!x3>}j`yEbl%!ORy2vLBO9#uX`sW*igR^G!bZ zTq|dOUPFfrr*cyAo;K=QXz;G3&I=egK8xeMhLfiT<+VFrQOvkYnkub0*(Rleg%!H7) zoz&;WB{g3l%V$tECxfRNscrRY=`19M`dn}|rG3$7Kq2&%%hvSIxmy$#U1_U%bXn)) zO+nA$wye=|cFEYA`N*R3q(LP#&$&Cq!Eyif;6g|?!vmg2(5wB6wRUaHUPX2X=e7dE zzHq0+V+yR}IMnS7?g$vV4)|r$3~iYa$fo~P|1;8(Yd`=YmKaD}0L-)-zy2cF z`O7Qg;Gk9YQG3@RScyPCNB?;{80vM4vtQ#6ViPUjhC*qtVNO_UoFVTu287V=z2ZMi zjS%AXWOTE6D4YC3mTd=RQdgSu-J{&F8950t%rA9Q0U12<&{wAYq?e)Gq~h>@?MRpr zq1jJlAZPGH02+*o=17Xe-Tzi{U@_ok86PpdYawO1fZ}Y_kCw)9d9OvxR}F2qH=$96 z=1v~o)e_6`U1KipJqEnH*MwyRqd+CLud_?J*iDfwtjr1RCx`Z5a414Wh!J_)18KO+a4S)bFKTziE z2c}7TrVd3aAw=ta?#C-tI5BI{P!5GH9DC8cAb^D+_kz$odxS9&>XB7oW^me2+{2 zolf(>3+ZBpJ!zak=VYG|lnz8L5m3*O}Q19lw2;Y|`jC5tpVa z9LHn$GAko3zpDF@3_pox?VHEi%91ZF_9ZHaoKa&WN%}z^$dPtux4UKi`{w4rT*bK8 zTuJNrh$H>x_TXa0YHX`(#oTJL>$c$d*{cZq2R23NV|`wwSEje_=uGOi^~S^&2}-t= zYS1sd<^1GsHt>=&bc*M?H!XS0J{)p-E9Q+fu9z?H?rIQ{T5>npEEH)Im|3}f620AA zm>ccObLo-BqbBvl+GpwsX%U3j{NzAXwc^r@ChulDg+dLXJYs_VVT|;$6u}p-x^x}U zY8Y>{s*`)&X7j@p;CWZ?=qS-inA}qH6k8EMJM+NR1Ni z3k5HArX*`Yh7NaJ*Gw;8{Ln_S%fq&f@GTu|>f&62tIDk72Xu#_vSYpB0AgxVzhkzS zsaHBbz~cp0?;VA->3T9bRCQHw36UG&A!x=3(R?j$|R>T+Sc$&GAocX`~2|u`yH$;k=KLf~|t>B6QeyJjQni-zm|?F=Ra! zT5K?Sd9Y7_+Ci0fFK$xu6>gNW=dqbG5lz=uacB_DRrdIv2Q?4>N zq|%3y?QgTu%`c_8MHY%5ep#X+sU)tmr6{9^6J2(4ZnfCoq;he_!pR?>{k2R|5Z660 zEs-Gw>r6x%m80TZ>=brReD=q0<>)b@5X%^sYKdIC=E=)|YlvrQky5-N?Pk!f@=m7) z#c2>hO+6E@M-e$v|J6kPCgKnY`3G_iwC1%3sZaLdiVyiedU^6OQkC=FyF;+x6C8&+ z58{v?KTo|DJo`R9gn0iu##Twbg7Uh6}=_bRv4%kSFrz5Kr5Hzy~7|e%NZJDqTEV5AF&VD-F3e#ju}_1 zzk@-ekt?D-Hsij?*~rSEL@u=9jV%0|mmI8QpXX^GKh`8IHWy=IUMSYXEK!pT!9qQi z+tKSB=$WroMQ3QS7;X!!2QAiVbXbGY-z4fs-iKSK{JAgPJVeSdOiBVQ!9J6+i5KRQ z`^>!G-^BRUCrdVeld^^Rq!Tr{LQ1`&G&Q+~uEjU0aNWM){26o<_w+cz50H@Ss=$;b z2^^t)BhZH4E)J%=OgfXvjRJA+9{bU+RaRa$0C>99`Ycne2=6P>X9GMk#*f%R@(R$O8q;Q+5t!>0b%h;1w`4)Fcs~SXzT0MFK@r# zK$9v&!?29RVc8EX6nl;ZlauHGr3{rCvLxuRHa~w}FMwfR*=MNM zkWk=s?}P(`9$klF#n)Z}_sq4(yH)q0tlTf#bowEkM8x|tt|I#4U&JhPq6eC?8q?{p zhCiGiS)KAvtWp;v<@iPcM2@?|evHAjpVj^9^T%1FH);p~f?;mC-hPmTTyEcf+Q<7y zZ*$vCBt?y(SD^*S7+YH%iT9GXy`>39-7mF$6h9JZ5-!8{{OO0|kEeX!aUVr(HXWzi zF9t%^7fW4m>GwN0?y!*5_TyDV-4;7`pnn>EeJxu;cjLrMei8k^%a666A89{o!}y^kj@cX+iSf>Ja!{$T`%_OYmBs&qxD!3;BP13%i%B zYi6gY*Gxf|Zt|C%V`;Seufh_pzDA~Uz!EOqLD*b@6+R1idiW@;khE^Z-z579j`Rgb zZ8Qq32dyA5%I1E~D4|(=h@Z`K>3fw=M_3(=?AEy-E-%P!Ifi9G8aI#kA`jH$hNKl6 z)8o3gk66c&5LJ3QJAfBG(}Hc7e8F+6?y@?lXA&&!_K6#ip|Tl}_!yt-*87@jKka-F ztSm>$IvNa67}L}j-Qab7EVq= zPAU&R{YHD1Dg&zj%

*@(40|#|(M}L{VU0P!%oKsHS`N)C-Q81+xo4KkNPdA_Fa< zlbriVDuH&}4Q-J<9B6B$FF6vlSg)TC8?9)ug{MYTS^Z6%^YW54PWg+f-y{cV-3LUV zes(1QcykV@S9N7z@(LX`3rB5st_F2Zy+}g629;41>vkYC%Y<8hAF0#uo}Y6`MOx4LVcFn8 z*42F|gAjJ}see8i`lMGhF?oi;J3t2hHmE;n@1bT|%`BJJ(|*x@y?k8dSF6 z-G{_uVDOF{vi;L+#)3;xeYy%pi3+|T_Cbz{=uV~~9#%E~aD`x!VMH%v(|@wlW;d&Q zv21(d>HS7J4Mc+2Y(8$0mt9CO!9EbV{W`Z7f7$8^!tXVdL%~*}6YVPNTPv*`WiF_w&K)OdX2fBW z;CeSgFjgAkF{?+nn9;~W2{!W}d4wd3p}8*?T2g*)_OqZr-g#w~mDHo=qkOHuJT+^- z?Nsxi8nwUlCe0D*E^XGQ2fvCF-lTVGni~^N1eNFHKbP1%bKdWlE=zV#D$}xsytIOa z90J>hmnP+a#oebP*F&^MeOZf7YBhg%+)Bstct(nUM<`CAuh-e>SpC?|pTam~sfJ5d z7)1Z7Zcj{M|!i5yDLQu!x_2j zR^cSW+Yrn+l%W>g?+|aJn5&luTP4F5CFf>_3FR*DTQT(oM-B6_jZ;5`c;nBgvP$!0 zl=VB&XS=RnT{dv~k#^O}A?OE!)-a>8m%x&{xE+_q_1@B|C0bK+=xz<_F!P?^bG+rG zO-Htb)s4q{#1>!P86IFvh&dG%i`0-;6+q?c3)q*% z4(DTJ_T^BAd~m4wU%$mG>_f#WJYTu7Uo60c`;FIHqxQWR1{Hq(Zps#4jpm5a5O*8}vm`?wZDDMNEydvq~VV~M>D2Kn2? zkw4W7BS*^_qI@&FUs{Rvh8pLTx)MW8ds@)NCvVB0@t`|sM&e{v%X)*&RW?OXp$z3* zvYxM39Kz}(*}SjvudS!Izq5!AVy;}LsoJ{3hC@11e7ku7>@OAV#7UxvT)r$%4Ug0u zGOF~~8XA`9E??MQGB_TPnxR<`L5G?w=M~SS-z{)cFr`b~(CF)GiBVspXt~dH{X-fr zI%8g4t?+82{IC1{RTj-V;({wTTD~5Q)t>b4x|zhTUHJ+tTkPgE!W)`_j=8V1^v2r9 zQcLsu?e~lAmaGQvr?k76!Scs|i% z`L%V|VuLvy>rLuuX3J3xCfnKWcs|#l2BGB-1Qj*Gb`{UJD)syYVly@B3CE?(+BW9` zibh?xBRke2J>HAjW{p%)|BQjPzG{h7CYM?ZGJWdD$D=v=gO2{^iPpdXT&|K#K)I1% z*NXzJ>glxcfNGlKoo%6$=Pu^>&$yLbwd+idWOyxU?beBvo{^|ppC2+3$_`W3tr6G8 zme{K%FRHJn7i5KPABnOwOc32!tBSqRedYamBelv^iFe0kWm+!W?v)pK_%1t2rw|hn zt^U%oHar83EwjHE>gAYP!jQ)Ori_BQfM>mXMDa-M4}@k!cONTXu==Fj#=>qm25pI734BXYLK;NfoIoXiKq)wKy*17nxt!Pk8sJvxso z=9-VE2|}(&4WpC&khA3F)zv4#wdG{|2+PyZQPjjCmBsijxts4G2tCA!{uO-*E7Njn=pJ*HMa6^>`RjqDk_x$286i9zQ%5F-Fr4u$HI&L`*BBeT|tus3> zcx^;Zo%h}+gwtADt`3!DN*VIX>0Tu4#A}WlJVFk_pIUqf1iBaHRb zq?kP|N~k%zg(%zH@+SR@Ph*m0k3@Auxt(0P&9}TL7B?K+A{?i73!^TpFc#JkHKEzA zBj|SR>}{3SP>Yt^WZhbKR`I=aigbyDM4e^Urj5jahXUz2{DKORmg)ulg;r_Zdqs_g3@py% zqx|X3_2Nwaz(6I|zSLUF9Xbt+R z6Dw_?sbc}30yL@n6qg&Pn>(^HVQ;htiB1dJZ z%ez}G85=4&Wtkm#CeW%BbK^?J2`*(jrid=RuAqmkg!Rb{4P`?quBf4NJ>hb<8eLKy zeL8Nf-2P6{rDPvBz8kLAH*T$Gpt39Xa@yFr`Az40_uk?rN2ZSOCSJ^a$dA6SIlC8a zU4erttls{j`jikZ%=pgK{@j%r*6*{Qr#wouYD4Vn4=0G% zN}HRgQ2j@Aa@;%W8%Cc^dAX^swx^^SsB|*T>{?Bk5#|;WO0h-IBa}jJK;w8(LxH{d z4J7BF%DYu10%M%j$>m3S;V2maG)4F!G|Gf??OB=Vydztq- zhQ`TLVv3%rIhDP#Rj>tGxwyUAB5OK=9*!1 z&(PEQAb0AgrfM?}2DyEqHm&!MS1KxginO6~l9T5|A2?iAn|2bd|7@oIroV}rkCnp- ziGWB%r47nsor}hQsL?x;ZRkdg#g@y!CIq%BqU<8~uB5Ao{)#L*_tLbt|0%hI~l?qk=!5Xuw?7=m3u2VcZGu#<$=Yw`X7`v z7QOIreKwFUtmgg2e7D~2v``3pWg?d@KsHCJ#>aUX!rw0J`S5sJ?7LHjcqvC8;U3R; zx$A60w#IPT?`Yw6or+g-Sef>yez|vCxEP&9DSyLZ6+>fF2mZb&Rl80;_o?X&{&AOC zg~6OK+5OIKVU(IFukUN|cBMW(e|!ofuN@f^V4?1vxt6!Z`DK(n3d|YjU*^mQ*_maF z_+n3|UT0YTUmS55@dm&0ef>3a^rGITMA~hqb)QV(8@(}ZlX@XpVLm)+VUu~M8wa0b zCK+N?Ry&fBx9%-%=44-&&dSJU4J&u#$2fObp(cL`rw3{1M^7m@t9D4d$2n@8Kq0=5 zFz%&XPwwaR$D7!06cVlIgN6e+oLh2+t80UM2x6eUPZ$KWj!Wu#qpkmn(7}@d*qYflJab7 z`m9h)X6$A`#*|g-&VhT|WRkUm2gqvBp)3Bzm6ginB_{5Z4|`MDVb%s2oyJRU-vw;% zE?{3ihM?Gt*h{^72bh`K(ZSm|#lE}^@rPXkQQznMmTqj^ zU=vXml>ewHs7O1sa3w`BC~w;OmohJM@mP&(?U49@4kIC*lu&9YSxY<*QPDVJ8}WWk ze!beYK8XppSnp>0stBzRv7s%XkQ0?$DPQUx{TSI&YVn6*CmE_Gz$UzQ!eLJ3VEH)N z(Dj1vH`yV$^W>j+2q{Y!|ATXruoce^H9alOiI!I?t#h4xrk>|c9~~jZYiZ1jZfvN@ z6kh0c{Ly%y3RmuhPna5uF51cXYo3i}1CNXqYBOHd$_~yycX`-nM#v7YH9dveatj!Y z8O<_L|FrX<{8z!R<`u+doGn*=8%5%%9MAc)uBx3F zRkw(U06mk@0&#(g_B97%T^^&JPuVF9HMz8=`Pqx5->GbGAN*|vcftsR>BYclARYWKZ; z%U4Lfx15xBhYSGz%o0)@a%7D&2Xm&sFnmw zJ=r(>9Q*vQkJ73?jS#Qr{_cL!$6Ckt&a-1yA+(=ge<0CuIG4rOE_k|kN}rC8YS@tC zvdKp?QG0{4ciMF2zT=juG6gRv<rJRbu+IpjhvWBE*C z`nQ6TS4)NeR+1}5ch6p8g&E~uMn4pol-MLn(sMxhvC8qLC;d+dl>M7`2&n9BmAplPj@|;jU z>%1`&wfo?wn90Dfrg6Ds?AF=EE=oUyi(K*It3_<@Po*WxT^-|(qUfCMjg+E6clkT4 zPg1sn@I_9t%M~+T6j8SC{SdDcVc-ZmxWLx}&<5 z;=S6P@Y`=-#*{CGg(EbSe2!9!b~ib+@9O5@vQdL!MNH)M>|2e(hSrwK_jYMk$03( z?ZTYiW&`MCGaF9lOY=Uby1bRRdkdAIuo%@}x}DujzuTHv9fOv<`&}?gXt!>6Lv?Lp zV6MM&);rqQNG;mecuG~JPbfn{b5r<}Hj|x=1ypmBxk{(t2IRwy3 z4pQjcmPfOSHb)XF$FoaR4q_b;dwB16cwXGILr{9p@goO?DPO1zz?Jbblu8BEOHZ21 ziR0l80AAuJAjfKE+bEixr92komrifSxT<$y7bDa(+rKrs6&FK5H}ASMwDR{Rh5a-g z(bo@cI=yMW+Q}vp8YI)EBTA zv_HRac11watS2u|(d?7sxPx9_>%}Gi`MKQ@K2yV<+qZseRR+Iqf6egPV!k3eR?>ax z8p`5!Om~Zfb!5qkCl-Z}?{OWhYyEbTr3$4bPVAN>XT;$*y4 zPEC53{h9arDq5hgyfg3EHRD}h**ASF(VH`s^F8&Z&IhsPpLSAu9MD~!1`0g`uN>JP z-u!dU!!YOKX@fKevi1IY-Pe4Z3q{3BpvooGbcm>scOePyJUCeUHfN{5pGO@<%-x;e z>m~Mr{u(Dz#(mXsq*vdzF+}2-gOP_-@zS`LJ;>(@+zgx1vv`>Ec-SS!*ZKFgpBqU~ zq<$vC1{G^cVv@VQC!nT_rp6uxHp7qVv-%R^%BKZ~)`B~|r8dXs^-f1DI}hx0kIRkd zc%Qa?toIBhQd}4*Sa!f^0HzyCNkG6Yhx1*WK6pW|=YhK?;jVAxE^nxJbTVL}g)i)N z7mAiUTt$$S^|ExSwr%w;Tj$KjYV}`qH}Fkk0i6-gP6MoO)$Jp{%(egy!yu(Ueb}X; zW9ESsOptKX@k=I7rg4nxZm8{Z6XZ5d}EV7 zUA122ZY$TG-aEFv8tP$FJo+pud^XHj_un)Q8tz<|SL}%^?Qy-CGH9ADPm0ZLT`zl@ zFMK5?SytY3#&iZ?yeV=3v4Glk7YWn;aE*c?08^@hI7F+7o zosKP&^F}M>S>E@t=bY>m4NR*o~8XD@X}DI`!t zhXN?b{9l|vA*Kksv*~u@p2jWDE5?_D@o0*<{^f-dQ|s2z1MQhJmMs^#Vm$yRadH0o z+r84n@R?fxIFR$+w2xSg%$CW26BfaeMzkDhZ$jVA2$j#hv391wZsVA(D$G{O@#*GN zSTVukpIm*Hp}qIOP22AS2^pjq;UgZ4yACE3x-A}6p>Rt#fxS%Y;y(mS6p0z_oC&=g z;M|G%$({0yS!z))q9BB&#$iU*d)B#V;AQ+!2^eq#zr$#gA)yk-G8F?%L+efL2Z%m; zgyKPZfaxF~J*WXi#c`EUgKGg>-&V^j@vpj`%hUAdTD!7PW|u5GjKmIf*;-Dbd$`V1 z$4Q?tY0#88x7(ZJ?baYKTF3?pFMW^q(djDG{yY5WO4Zle{V6S5 z@5>nR7~w~5KNpQ@y+%K$`0kpl7<+B}5XaEjtl`*wV|M0Oe$Wcex^GV_&rYf;@49YH z3)9PrZsr`LuKTvJm|rF!J73%CHkpLngI zuYJcy!{S;J(Ry7xv)&#k?p&tP9aU!+MUVk)a&Tiu>!f?{7J zO8duBxieLSJ-`pl$in=$#G!b3iJh<2U;K<#gB%)r-(0jSw9s6zw z?rLOzz7Q&ej5jm%V~ZGe`E}T8kZ=$iqmy6dF7=3f0kcb3pU>W1d6`5_Xle0Gn13eS z=dmd{#77mBf9Q>rvFIV)U5}OE(VXU4gpru1_iPDvC|0`FrDy3AJM!F?U&2OoLs{~o zb$G}7hx6GJWQPXz?4;~Sc-&m72v;a2=iwsM0p>dgXoQf1pWjmr{LFit+nus! zO0~yi&%4VI81ofX87j4GBE)co1i=>L_=EPXo|9Xw4UWA5BLveTFZ+>!YRBHMg#ped zjHSv<@+Q?;dRBTY^#tDZh7!XXhp?x3-4s*j6m!Q+x9u+8=9ce0hD+uZs{?&vSwxHb z?kOJmqI85%cxjyJN=y{O>&n`+@lMGwavUc>RD*EJ4^Tc_I)!Zbw65Ul>@m?e;>&DY zK(+Ov2hTSfE1i7OR|z28A~x9*hl{-z8PEU#@~&Y94R@BZ&5ve991N~~(nXOUxS1)z zE}yb%xx($N?Yu2$u}Tc9T&5^??Ixl|1bW3Qr3SS(c6zwZHL!8L7|NdZEZ1K!PuQig zkZmjOzWVoi+F(8T60LZFPO%p3BEFot;=bCs6RoRh2eGJNl!4B6WrE>4&AIPm%*ONY z_hP{A;9-|gpIu@XmnUbQg!|whpgOR&RJL0)F@?F)#5jya?ILPMGE{cCwyZi@$B>0> zqjQT(cm*N(60XhyB2AyFFeUM(d(q?60BdmF)~A@XAR;>ZICD^tXu^v2B8xN;c6LL; zh-|bs+jQRcy*pcKyR*4{@t4Z264D)ow>G*5yl+d&q2F~Wn;ZXHrOX$q(q9(Ddl)xZ zd~f$UVyz;IM2qTka|lSvCZ9lq)YP4;O0=J`^*86)5&zDCaNCZ}2$MDI@DXHqSh}Yq7QeA5e z_J|*i$42}|m$4_K6`FZ`&p7z&R}8ZBh-bd zk;}S`=AS( zQCAP)2#`i!7%IF8zqANVZPzbVX)_T0!1G2;2jWo4K#g=^lEPC3Veqzb(efMsoXSqTA9E1^ju3uC#Cd+k`RI~x>~;9K1c1R_v667h zyob!zXf4UrG01+4h8@Fu&THhQU)O-?t3C=euU=;r87#V-G;A7X_R4hT(M@Rh<(kq{ zOnd*0qv)Caq@{H8(LEsv=E~ZM zDpbUaWfh2cRobUDxq%c%^Dq#t2nN2+VW9QVI@&{o-_-}67441pc^D>ArF^cQn*9I9 zy8Ev*%V@(3&Tvk}fI*2#?MZhsJlQDp(j-k zTRe@r^NJO4?(;IA(pt8pBKm(`mVKn5(HI(ij0A^cJtTpw&pC`5} zc?uUH{x%(C?BT;M^G9#w9U$73f;O-#l0W0j@Rwga?CeM-r~P&Hv$>gg_H80^5j}`1 zz38SEUF(6)gA=h$D@W zkCGFJM|~c(%xP%A&i(p68FaDcw@i5ot-2Z{o9lcz+A=fL={UZH0N-s43cy4@;Q@;B zSgfSM{TLkzy&)p>@ZWSMZoS_7uU{wOyvF^KiWsbL4~sYiePMwG9|3U4zonMd1EltE z)~bR?&xSj{38B1{tf!NBQtmAaNka5Mu|)*X=RXL{zv)bDAFV*Q7y;_}TQct`e>NXu z@+Gz+JqF!}{^p*<8gTD#*2Yo8-BrH13oOh&H}bQu0!)}V2woYggV@3D&A$ca-*hIT zRR^6MI}2z-5m&^B`G-iLkHBGDBG6MYDCJ+=`_H_BAZfIjVMySPWW_fpwESW z3(UXiY)pikS7_2XOn&K1fECf%3IKid83sWW@7RHG{>8niVc_21taXxrAUUPH&B}oM zd~@juMw0yl9)M^;Y|vBS?v%oNI3U;{nx#m16u6Y! z9!z^G$%5r9$^NOsYfT0^-*$-RM(wSt-BzE_?)Mey)TC3SQg~#*`Bj-~t;pFpsel83 z)qT{|#_>fwRB)T00Py7>srl;AwE+#r)#*3B^zTOM7Ky)2ldU{BpXcwz_Ie=tum9op z`beK~^j+g8n&r!2BPkzd!biC|@JxgCVaT_`zsIaX&5bG{0B!n8v_31KO>4JY0F>|# z>gI{3@fH1h{j>R0`I6?KYO%mhE=`oPBuh$Gy2)o_n}@TVI3H#wk< z&Y$B0S)tR$#Wo_s^wqc!K$vzS2IId$v_Ek|+#s*c@<+fx#QPCrC{X{y<@}BIi21`stj$Si42p`=bj968TuOR{W>hO})OK_k2ukj~g{4D!qA{2Uv1Pef+&w%5x z!tOy#KlnH2{>|2$-=RgN3gBjj)a(ZdX#Q>-^OTa-8Ro&ANw{V6q!pC1k zEObfNqhx?Wu0A$M#`ll$GhqCphUlvRqkUo0DjeW0iSZ!o9#Z(*e{-(y-)#LIiEfL* zK0xd!B{5RE31Q3S6KCP$H`sC%J1?A~hTZ=ArV1JWnEz}1TQI&&gh)u;M zVU=sYGFmNDIEnb=k3VaG@@lZ1WdVU<`O;&Z2FlA98bJlpNHubFP{l6Rju1l)oldk` zykK>wfrC+Hy2Qw?5WZslyB9|snIXu1*i@z~ih5N{=a>IxcM}!6ITW&u=6a^3t z=6YQW5r2_YdQ*^y;`%DrgB7MA;r4L$EK)Up)-nW|axf*tfr0p_0vY<>EPVu_$LurF zb!diEpRhk}AK;+fRl%rNp%zj;H$bbyi27;BVp7A3-V(PVWqa+KKA(gapFG~!MND)i znhz;}q$-?`ByOsV6or~S0f4O2GfN%%tKsrRV0wixy`tZWsS1h{_mPL#{bOD8Ej@Jq z;LWUu?jNc<47$f*R_CKxNwF>Pn?1x*n=VaDq7)DTT~C>;OTajeOVee5_)&hXZov`W zHT5g01aO?2OLIgmstC6|oOporVlchm-26riQt3NKSr`$#-iIIU|L0=QLYE93p8Ti7 z-4}@_V$ZxFcyZ8ueq%)#2OaOp{7>puTv%m9r06*(!a(ptK9*vG@jl2@ze z??Ahvrh3*ZcCiNa?#Dk47c-A3!IM;){fQ&Lzq!)N zgf*5Y?N9N3UU*!C!$_5_^a;qZ6;*p0d!OsSxcRC=&3hz%exgSt44)%;_VTd>dXQ`mL~(*GR37uOC~S_X=T0tzWz! z)ni$W?y)=*HjIq8zA8yXOdd=_fZ@EJ)W-rn&!v?G{_&!7_rN^4SE2Y9Q_PItI4|Us zyX)7sx)O3nek5$xTQ{bZ!XC7vfm%|K7*lCVmkzt6N6gq5_~2))3jWdW@UuUVXyy<$ z_mlY?M$o{0%GyC&btc=SieRwb{0R(^4TXk?0}%KkwTLKuRrH0`pB+<)R(}k{vJXJ3 zxBxO{V!@ghYnm^KZjB#sjs0?pm^0oEg`9Dv>tRI(BAuOp&VSgrKS>e{m?-^Tb_iw9 zI_<9KGy^+W2W{EE8I&x+g^af_^<#~KM%BA%Y=*=|If+A$JLkgNf14z^JH(zXHei-p zB`~WE;=BZIXq8B}{X#7jKrkp69j?gJAwpA+vHXB8htC^PFZGJD<~BVH=Fkf|b8_AUwztMl`Bs3ewdqchJXNS7%x&Jk7%O+@ z6wF{2kan=zu_Equgrt0PRyEI~2(^kY)xm1e)O@o&6cUtfzJ2U~T0IIQKc9f5jDtRMd~#~+jAwflnXj6$9h8^ywLrTW-Hs9L zDI^=_kmmH0VV6Md8K)aSG|z%R0`5IGehqj$a1d*1{KEO`6S8NLLKnSHb-xD#BR%b+vzTk{I3@`s+JkQT%Y)6hhybtpxHJCg^Sc6sjApO7=WIhz%3S zdh!oFdI5n5^t~FF*Pk0e^+$eiS^zedKVOCHBf9U^jTF`@lth7;#^1Jp_{K6ywCBY^ zyzFh`&1E&-Z4tBi-{X~^AV%6g9;kCg{_9MK;JG0iIY1e+P*AcV;2xp1R ztGWz&#m?_Xf74J{nMm_|J?KIPw|a5QXUnd5D+7tdEE=ZPDKLg6~7PWi7rgLY2_c3v-$u7#||4D&!#I{u%RfH6bi!;)E zb-537L@09O#Q_>}cB$P=yI<3yNx%t>f=!l;;bPgTa5T7vwBDIuXrujmm-Htkj+MZK zT+s{7ixn znG03r{!`y3AR5r2M9;xd&%E*OaA-A|?IwO6&UEtUulTJ>MK>wqcx zKkU6{Sd&ZJ0H_E`Q9+TSgmzSrjs=Kx

>NrwYPR{e~B(NA6;vfGm{{E|v0Wi>wR4VEq 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. [WARNING] ==== -Enrich indices should be used by the <> only. -Avoid using enrich indices for other purposes. +Enrich indices should only be used by the <> +or the <>. Avoid using enrich indices for +other purposes. ==== // end::execute-enrich-policy-def[] diff --git a/docs/reference/ingest/apis/enrich/put-enrich-policy.asciidoc b/docs/reference/ingest/apis/enrich/put-enrich-policy.asciidoc index 5981bf9f6d7f7..ee33b0b320905 100644 --- a/docs/reference/ingest/apis/enrich/put-enrich-policy.asciidoc +++ b/docs/reference/ingest/apis/enrich/put-enrich-policy.asciidoc @@ -53,7 +53,7 @@ DELETE /_enrich/policy/my-policy ==== {api-prereq-title} // tag::enrich-policy-api-prereqs[] -If you use {es} {security-features}, you must have: +To use enrich policies, you must have: * `read` index privileges for any indices used * The `enrich_user` <> diff --git a/docs/reference/ingest/enrich.asciidoc b/docs/reference/ingest/enrich.asciidoc index 7b87333cbf416..4688b21d37e7d 100644 --- a/docs/reference/ingest/enrich.asciidoc +++ b/docs/reference/ingest/enrich.asciidoc @@ -67,14 +67,16 @@ Directly matching incoming documents to documents in source indices could be slow and resource intensive. To speed things up, the enrich processor uses an enrich index. +// tag::enrich-index[] Enrich indices contain enrich data from source indices but have a few special properties to help streamline them: * They are system indices, meaning they're managed internally by {es} and only - intended for use with enrich processors. + intended for use with enrich processors and the {esql} `ENRICH` command. * They always begin with `.enrich-*`. * They are read-only, meaning you can't directly change them. * They are <> for fast retrieval. +// end::enrich-index[] -- [[enrich-setup]] @@ -115,8 +117,9 @@ include::{es-repo-dir}/ingest/apis/enrich/put-enrich-policy.asciidoc[tag=enrich- [[create-enrich-source-index]] ==== Add enrich data +// tag::create-enrich-source-index[] To begin, add documents to one or more source indices. These documents should -contain the enrich data you eventually want to add to incoming documents. +contain the enrich data you eventually want to add to incoming data. You can manage source indices just like regular {es} indices using the <> and <> APIs. @@ -125,29 +128,38 @@ You also can set up {beats-ref}/getting-started.html[{beats}], such as a {filebeat-ref}/filebeat-installation-configuration.html[{filebeat}], to automatically send and index documents to your source indices. See {beats-ref}/getting-started.html[Getting started with {beats}]. +//end::create-enrich-source-index[] [[create-enrich-policy]] ==== Create an enrich policy +// tag::create-enrich-policy[] After adding enrich data to your source indices, use the -<> to create an enrich policy. +<> or +<> to create an enrich policy. [WARNING] ==== Once created, you can't update or change an enrich policy. See <>. ==== +// end::create-enrich-policy[] [[execute-enrich-policy]] ==== Execute the enrich policy -Once the enrich policy is created, you can execute it using the -<> to create an +// tag::execute-enrich-policy1[] +Once the enrich policy is created, you need to execute it using the +<> or +<> to create an <>. +// end::execute-enrich-policy1[] image::images/ingest/enrich/enrich-policy-index.svg[align="center"] +// tag::execute-enrich-policy2[] include::apis/enrich/execute-enrich-policy.asciidoc[tag=execute-enrich-policy-def] +// end::execute-enrich-policy2[] [[add-enrich-processor]] ==== Add an enrich processor to an ingest pipeline @@ -208,9 +220,10 @@ Instead, you can: . Replace the previous enrich policy with the new enrich policy - in any in-use enrich processors. + in any in-use enrich processors or {esql} queries. -. Use the <> API +. Use the <> API or +<> to delete the previous enrich policy. // end::update-enrich-policy[] diff --git a/docs/reference/redirects.asciidoc b/docs/reference/redirects.asciidoc index b0a1c157c8cd0..a79ceace09233 100644 --- a/docs/reference/redirects.asciidoc +++ b/docs/reference/redirects.asciidoc @@ -1952,4 +1952,3 @@ coming::[8.11.0] === Tutorial: Natural language processing (NLP) coming::[8.11.0] - From c5be59991d7451268cb915a183551ad90b1256b3 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Thu, 19 Oct 2023 17:23:57 +0200 Subject: [PATCH 035/203] Mute ProcessWorkerExecutorServiceTests#testAutodetectWorkerExecutorServiceDoesNotSwallowErrors (#101149) relates to https://github.com/elastic/elasticsearch/issues/101144 --- .../xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java index 096d0b7105ce5..6e976a889141f 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java @@ -126,6 +126,7 @@ protected void doRun() { } } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101144") public void testAutodetectWorkerExecutorServiceDoesNotSwallowErrors() { ProcessWorkerExecutorService executor = createExecutorService(); if (randomBoolean()) { From c1aa78bcab6fdb351a39c7f3343e072271767bd1 Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Thu, 19 Oct 2023 17:28:20 +0200 Subject: [PATCH 036/203] Don't downsample in flamegraph test (#101146) With this commit we avoid downsampling in the flamegraph API test as this is a rather basic test that checks whether the API works in general. Tests for downsampling are done in other places already. --- .../elasticsearch/xpack/profiling/GetFlameGraphActionIT.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java b/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java index f0df6c69ba09e..308e377c2826a 100644 --- a/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java +++ b/x-pack/plugin/profiling/src/internalClusterTest/java/org/elasticsearch/xpack/profiling/GetFlameGraphActionIT.java @@ -8,6 +8,11 @@ package org.elasticsearch.xpack.profiling; public class GetFlameGraphActionIT extends ProfilingTestCase { + @Override + protected boolean useOnlyAllEvents() { + return true; + } + public void testGetStackTracesUnfiltered() throws Exception { GetStackTracesRequest request = new GetStackTracesRequest(1, null); GetFlamegraphResponse response = client().execute(GetFlamegraphAction.INSTANCE, request).get(); From ca6295e582e7116ec855cbde47475afe224bd99f Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Thu, 19 Oct 2023 17:53:13 +0200 Subject: [PATCH 037/203] Remove more explicit references to SearchResponse in tests (#101092) Remove `assertSearchResponse` which was just an alias for `assertNoFailures` and then cleanup many spots in the result by combining the hit count and no failure assertion into a single method. follow-up to #100966 --- .../bucket/AdjacencyMatrixIT.java | 12 +-- .../bucket/TimeSeriesAggregationsIT.java | 13 ++- .../pipeline/DateDerivativeIT.java | 20 ++-- .../aggregations/pipeline/SerialDiffIT.java | 4 +- .../script/expression/MoreExpressionIT.java | 41 ++++---- .../join/aggregations/ChildrenIT.java | 9 +- .../join/aggregations/ParentIT.java | 12 +-- .../elasticsearch/join/query/InnerHitsIT.java | 18 ++-- .../elasticsearch/aliases/IndexAliasesIT.java | 8 +- .../index/store/ExceptionRetryIT.java | 4 +- .../indices/IndicesRequestCacheIT.java | 8 +- .../search/StressSearchServiceReaperIT.java | 8 +- .../AggregationsIntegrationIT.java | 6 +- .../search/aggregations/CombiIT.java | 4 +- .../search/aggregations/EquivalenceIT.java | 9 +- .../search/aggregations/MetadataIT.java | 4 +- .../search/aggregations/MissingValueIT.java | 38 ++++---- .../aggregations/bucket/BooleanTermsIT.java | 8 +- .../aggregations/bucket/DateHistogramIT.java | 76 +++++++-------- .../aggregations/bucket/DateRangeIT.java | 26 ++--- .../bucket/DiversifiedSamplerIT.java | 18 ++-- .../aggregations/bucket/DoubleTermsIT.java | 42 ++++---- .../search/aggregations/bucket/FilterIT.java | 10 +- .../search/aggregations/bucket/FiltersIT.java | 20 ++-- .../aggregations/bucket/GeoDistanceIT.java | 14 +-- .../aggregations/bucket/GeoHashGridIT.java | 14 +-- .../search/aggregations/bucket/GlobalIT.java | 4 +- .../aggregations/bucket/HistogramIT.java | 64 ++++++------- .../search/aggregations/bucket/IpRangeIT.java | 12 +-- .../search/aggregations/bucket/IpTermsIT.java | 8 +- .../aggregations/bucket/LongTermsIT.java | 40 ++++---- .../aggregations/bucket/NaNSortingIT.java | 6 +- .../search/aggregations/bucket/NestedIT.java | 9 +- .../search/aggregations/bucket/RangeIT.java | 40 ++++---- .../aggregations/bucket/ReverseNestedIT.java | 9 +- .../search/aggregations/bucket/SamplerIT.java | 12 +-- .../aggregations/bucket/ShardReduceIT.java | 32 +++---- .../SignificantTermsSignificanceScoreIT.java | 20 ++-- .../bucket/TermsDocCountErrorIT.java | 96 +++++++++---------- .../bucket/TermsShardMinDocCountIT.java | 10 +- .../bucket/terms/StringTermsIT.java | 50 +++++----- .../aggregations/metrics/ExtendedStatsIT.java | 8 +- .../aggregations/metrics/GeoBoundsIT.java | 6 +- .../aggregations/metrics/GeoCentroidIT.java | 4 +- .../metrics/HDRPercentileRanksIT.java | 8 +- .../metrics/HDRPercentilesIT.java | 8 +- .../metrics/MedianAbsoluteDeviationIT.java | 8 +- .../metrics/ScriptedMetricIT.java | 38 ++++---- .../search/aggregations/metrics/StatsIT.java | 8 +- .../search/aggregations/metrics/SumIT.java | 12 +-- .../metrics/TDigestPercentileRanksIT.java | 8 +- .../metrics/TDigestPercentilesIT.java | 8 +- .../aggregations/metrics/TopHitsIT.java | 39 ++++---- .../aggregations/metrics/ValueCountIT.java | 8 +- ...ketMetricsPipeLineAggregationTestCase.java | 16 ++-- .../aggregations/pipeline/BucketScriptIT.java | 34 +++---- .../pipeline/ExtendedStatsBucketIT.java | 4 +- .../pipeline/PercentilesBucketIT.java | 8 +- .../basic/SearchWithRandomIOExceptionsIT.java | 7 +- .../search/fetch/FetchSubPhasePluginIT.java | 4 +- .../search/fetch/subphase/InnerHitsIT.java | 23 +++-- .../highlight/HighlighterSearchIT.java | 3 +- .../search/fields/SearchFieldsIT.java | 5 +- .../functionscore/ExplainableScriptIT.java | 4 +- .../search/functionscore/FunctionScoreIT.java | 14 +-- .../search/functionscore/QueryRescorerIT.java | 3 +- .../search/geo/GeoPointScriptDocValuesIT.java | 8 +- .../search/morelikethis/MoreLikeThisIT.java | 21 ++-- .../aggregation/AggregationProfilerIT.java | 18 ++-- .../elasticsearch/search/query/ExistsIT.java | 9 +- .../search/query/SearchQueryIT.java | 9 +- .../search/scroll/SearchScrollIT.java | 3 +- .../search/simple/SimpleSearchIT.java | 82 ++++++---------- .../search/sort/FieldSortIT.java | 19 ++-- .../search/stats/SearchStatsIT.java | 4 +- .../FieldStatsProviderRefreshTests.java | 8 +- .../flattened/FlattenedFieldSearchTests.java | 55 ++++------- .../bucket/ShardSizeTestCase.java | 6 +- .../SharedSignificantTermsTestMethods.java | 4 +- .../bucket/AbstractTermsTestCase.java | 6 +- .../metrics/AbstractGeoTestCase.java | 4 +- .../metrics/CentroidAggregationTestBase.java | 14 +-- .../SpatialBoundsAggregationTestBase.java | 16 ++-- .../geo/BasePointShapeQueryTestCase.java | 26 ++--- .../search/geo/BaseShapeQueryTestCase.java | 48 +++++----- .../search/geo/GeoShapeQueryTestCase.java | 12 +-- .../hamcrest/ElasticsearchAssertions.java | 29 +++--- .../xpack/unsignedlong/UnsignedLongTests.java | 26 ++--- .../FrozenSearchableSnapshotsIntegTests.java | 6 +- .../DocumentLevelSecurityTests.java | 9 +- .../integration/FieldLevelSecurityTests.java | 15 ++- .../search/GeoShapeScriptDocValuesIT.java | 6 +- .../search/ShapeQueryOverShapeTests.java | 22 ++--- .../spatial/search/ShapeQueryTestCase.java | 18 ++-- 94 files changed, 788 insertions(+), 861 deletions(-) diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java index ae800b23b12e1..2f3b87d73a223 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java @@ -35,7 +35,7 @@ import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.avg; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -102,7 +102,7 @@ public void testSimple() throws Exception { .addAggregation(adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")))) .get(); - assertSearchResponse(response); + assertNoFailures(response); AdjacencyMatrix matrix = response.getAggregations().get("tags"); assertThat(matrix, notNullValue()); @@ -134,7 +134,7 @@ public void testCustomSeparator() throws Exception { .addAggregation(adjacencyMatrix("tags", "\t", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")))) .get(); - assertSearchResponse(response); + assertNoFailures(response); AdjacencyMatrix matrix = response.getAggregations().get("tags"); assertThat(matrix, notNullValue()); @@ -157,7 +157,7 @@ public void testEmptyFilterDeclarations() throws Exception { .addAggregation(adjacencyMatrix("tags", newMap("all", emptyFilter).add("tag1", termQuery("tag", "tag1")))) .get(); - assertSearchResponse(response); + assertNoFailures(response); AdjacencyMatrix filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -180,7 +180,7 @@ public void testWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); AdjacencyMatrix matrix = response.getAggregations().get("tags"); assertThat(matrix, notNullValue()); @@ -309,7 +309,7 @@ public void testAsSubAggregation() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java index 3100db781172a..381c8d23a1764 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java @@ -58,7 +58,6 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -178,7 +177,7 @@ public void setupSuiteScopeCluster() throws Exception { public void testStandAloneTimeSeriesAgg() { SearchResponse response = client().prepareSearch("index").setSize(0).addAggregation(timeSeries("by_ts")).get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); InternalTimeSeries timeSeries = aggregations.get("by_ts"); @@ -204,7 +203,7 @@ public void testTimeSeriesGroupedByADimension() { .subAggregation(timeSeries("by_ts")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); Terms terms = aggregations.get("by_dim"); @@ -232,7 +231,7 @@ public void testTimeSeriesGroupedByDateHistogram() { .subAggregation(timeSeries("by_ts").subAggregation(stats("timestamp").field("@timestamp"))) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); Histogram histogram = aggregations.get("by_time"); @@ -272,7 +271,7 @@ public void testStandAloneTimeSeriesAggWithDimFilter() { .setSize(0) .addAggregation(timeSeries("by_ts")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); InternalTimeSeries timeSeries = aggregations.get("by_ts"); @@ -304,7 +303,7 @@ public void testStandAloneTimeSeriesAggWithGlobalAggregation() { .addAggregation(global("everything").subAggregation(sum("all_sum").field("metric_" + metric))) .addAggregation(PipelineAggregatorBuilders.sumBucket("total_filter_sum", "by_ts>filter_sum")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); InternalTimeSeries timeSeries = aggregations.get("by_ts"); @@ -351,7 +350,7 @@ public void testStandAloneTimeSeriesAggWithMetricFilter() { .setSize(0) .addAggregation(timeSeries("by_ts")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); InternalTimeSeries timeSeries = aggregations.get("by_ts"); diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java index 628863387b157..40bde4b2fea21 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java @@ -39,7 +39,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; @@ -125,7 +125,7 @@ public void testSingleValuedField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -170,7 +170,7 @@ public void testSingleValuedFieldNormalised() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -235,7 +235,7 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstStart() throws Excep ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -293,7 +293,7 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstEnd() throws Excepti ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -353,7 +353,7 @@ public void testSingleValuedFieldNormalised_timeZone_AsiaKathmandu() throws Exce ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -421,7 +421,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -504,7 +504,7 @@ public void testMultiValuedField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -562,7 +562,7 @@ public void testUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -580,7 +580,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java index 324716f0f25ce..b7b93ed5b681d 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java @@ -31,7 +31,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.max; import static org.elasticsearch.search.aggregations.AggregationBuilders.min; import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.diff; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; @@ -231,7 +231,7 @@ public void testBasicDiff() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); diff --git a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java index 69b0046e5e207..218b0c7c5157d 100644 --- a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java +++ b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java @@ -44,7 +44,6 @@ import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.bucketScript; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -88,7 +87,7 @@ public void testFunction() throws Exception { ensureGreen("test"); client().prepareIndex("test").setId("1").setSource("foo", 4).setRefreshPolicy(IMMEDIATE).get(); SearchResponse rsp = buildRequest("doc['foo'] + abs(1)").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(1, rsp.getHits().getTotalHits().value); assertEquals(5.0, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D); } @@ -118,7 +117,7 @@ public void testScore() throws Exception { req.setQuery(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("text", "hello"), score).boostMode(CombineFunction.REPLACE)); req.setSearchType(SearchType.DFS_QUERY_THEN_FETCH); // make sure DF is consistent SearchResponse rsp = req.get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); SearchHits hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals("1", hits.getAt(0).getId()); @@ -223,7 +222,7 @@ public void testMultiValueMethods() throws Exception { ); SearchResponse rsp = buildRequest("doc['double0'].count() + doc['double1'].count()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); SearchHits hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -231,7 +230,7 @@ public void testMultiValueMethods() throws Exception { assertEquals(5.0, hits.getAt(2).field("foo").getValue(), 0.0D); rsp = buildRequest("doc['double0'].sum()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(7.5, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -239,7 +238,7 @@ public void testMultiValueMethods() throws Exception { assertEquals(6.0, hits.getAt(2).field("foo").getValue(), 0.0D); rsp = buildRequest("doc['double0'].avg() + doc['double1'].avg()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(4.3, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -247,7 +246,7 @@ public void testMultiValueMethods() throws Exception { assertEquals(5.5, hits.getAt(2).field("foo").getValue(), 0.0D); rsp = buildRequest("doc['double0'].median()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(1.5, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -255,7 +254,7 @@ public void testMultiValueMethods() throws Exception { assertEquals(1.25, hits.getAt(2).field("foo").getValue(), 0.0D); rsp = buildRequest("doc['double0'].min()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(1.0, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -263,7 +262,7 @@ public void testMultiValueMethods() throws Exception { assertEquals(-1.5, hits.getAt(2).field("foo").getValue(), 0.0D); rsp = buildRequest("doc['double0'].max()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -271,7 +270,7 @@ public void testMultiValueMethods() throws Exception { assertEquals(5.0, hits.getAt(2).field("foo").getValue(), 0.0D); rsp = buildRequest("doc['double0'].sum()/doc['double0'].count()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(2.5, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -280,7 +279,7 @@ public void testMultiValueMethods() throws Exception { // make sure count() works for missing rsp = buildRequest("doc['double2'].count()").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(1.0, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -289,7 +288,7 @@ public void testMultiValueMethods() throws Exception { // make sure .empty works in the same way rsp = buildRequest("doc['double2'].empty ? 5.0 : 2.0").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); hits = rsp.getHits(); assertEquals(3, hits.getTotalHits().value); assertEquals(2.0, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -327,7 +326,7 @@ public void testSparseField() throws Exception { client().prepareIndex("test").setId("2").setSource("id", 2, "y", 2) ); SearchResponse rsp = buildRequest("doc['x'] + 1").get(); - ElasticsearchAssertions.assertSearchResponse(rsp); + assertNoFailures(rsp); SearchHits hits = rsp.getHits(); assertEquals(2, rsp.getHits().getTotalHits().value); assertEquals(5.0, hits.getAt(0).field("foo").getValue(), 0.0D); @@ -640,22 +639,22 @@ public void testGeo() throws Exception { refresh(); // access .lat SearchResponse rsp = buildRequest("doc['location'].lat").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(1, rsp.getHits().getTotalHits().value); assertEquals(61.5240, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D); // access .lon rsp = buildRequest("doc['location'].lon").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(1, rsp.getHits().getTotalHits().value); assertEquals(105.3188, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D); // access .empty rsp = buildRequest("doc['location'].empty ? 1 : 0").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(1, rsp.getHits().getTotalHits().value); assertEquals(0, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D); // call haversin rsp = buildRequest("haversin(38.9072, 77.0369, doc['location'].lat, doc['location'].lon)").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(1, rsp.getHits().getTotalHits().value); assertEquals(3170D, rsp.getHits().getAt(0).field("foo").getValue(), 50D); } @@ -678,14 +677,14 @@ public void testBoolean() throws Exception { ); // access .value SearchResponse rsp = buildRequest("doc['vip'].value").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(3, rsp.getHits().getTotalHits().value); assertEquals(1.0D, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D); assertEquals(0.0D, rsp.getHits().getAt(1).field("foo").getValue(), 1.0D); assertEquals(0.0D, rsp.getHits().getAt(2).field("foo").getValue(), 1.0D); // access .empty rsp = buildRequest("doc['vip'].empty ? 1 : 0").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(3, rsp.getHits().getTotalHits().value); assertEquals(0.0D, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D); assertEquals(0.0D, rsp.getHits().getAt(1).field("foo").getValue(), 1.0D); @@ -693,7 +692,7 @@ public void testBoolean() throws Exception { // ternary operator // vip's have a 50% discount rsp = buildRequest("doc['vip'] ? doc['price']/2 : doc['price']").get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(3, rsp.getHits().getTotalHits().value); assertEquals(0.5D, rsp.getHits().getAt(0).field("foo").getValue(), 1.0D); assertEquals(2.0D, rsp.getHits().getAt(1).field("foo").getValue(), 1.0D); @@ -712,7 +711,7 @@ public void testFilterScript() throws Exception { Script script = new Script(ScriptType.INLINE, "expression", "doc['foo'].value", Collections.emptyMap()); builder.setQuery(QueryBuilders.boolQuery().filter(QueryBuilders.scriptQuery(script))); SearchResponse rsp = builder.get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); assertEquals(1, rsp.getHits().getTotalHits().value); assertEquals(1.0D, rsp.getHits().getAt(0).field("foo").getValue(), 0.0D); } diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java index 49d3d04b3ee5c..521a2d31b4156 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -36,7 +36,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; @@ -50,7 +49,7 @@ public void testSimpleChildrenAgg() { .addAggregation(children("to_comment", "comment")); final SearchResponse searchResponse = searchRequest.get(); long count = categoryToControl.values().stream().mapToLong(control -> control.commentIds.size()).sum(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Children childrenAgg = searchResponse.getAggregations().get("to_comment"); assertThat("Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", childrenAgg.getDocCount(), equalTo(count)); } @@ -68,7 +67,7 @@ public void testChildrenAggs() { ) ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Terms categoryTerms = searchResponse.getAggregations().get("category"); assertThat(categoryTerms.getBuckets().size(), equalTo(categoryToControl.size())); @@ -107,7 +106,7 @@ public void testParentWithMultipleBuckets() { .subAggregation(children("to_comment", "comment").subAggregation(topHits("top_comments").sort("id", SortOrder.ASC))) ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Terms categoryTerms = searchResponse.getAggregations().get("category"); assertThat(categoryTerms.getBuckets().size(), equalTo(3)); @@ -204,7 +203,7 @@ public void testWithDeletes() throws Exception { public void testNonExistingChildType() throws Exception { SearchResponse searchResponse = client().prepareSearch("test").addAggregation(children("non-existing", "xyz")).get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Children children = searchResponse.getAggregations().get("non-existing"); assertThat(children.getName(), equalTo("non-existing")); diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java index 26a8d44759513..7f780090cbd3a 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java @@ -27,7 +27,7 @@ import static org.elasticsearch.join.aggregations.JoinAggregationBuilders.parent; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; public class ParentIT extends AbstractParentChildTestCase { @@ -39,7 +39,7 @@ public void testSimpleParentAgg() { .addAggregation(parent("to_article", "comment")); SearchResponse searchResponse = searchRequest.get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); long articlesWithComment = articleToControl.values() .stream() .filter(parentControl -> parentControl.commentIds.isEmpty() == false) @@ -58,7 +58,7 @@ public void testSimpleParentAggWithSubAgg() { .setQuery(matchQuery("randomized", true)) .addAggregation(parent("to_article", "comment").subAggregation(terms("category").field("category").size(10000))); SearchResponse searchResponse = searchRequest.get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); long articlesWithComment = articleToControl.values() .stream() @@ -121,7 +121,7 @@ public void testParentAggs() throws Exception { ) ); SearchResponse searchResponse = searchRequest.get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); final Set commenters = getCommenters(); final Map> commenterToComments = getCommenterToComments(); @@ -201,7 +201,7 @@ private Map> getCommenterToComments() { public void testNonExistingParentType() throws Exception { SearchResponse searchResponse = client().prepareSearch("test").addAggregation(parent("non-existing", "xyz")).get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Parent parent = searchResponse.getAggregations().get("non-existing"); assertThat(parent.getName(), equalTo("non-existing")); @@ -218,7 +218,7 @@ public void testTermsParentAggTerms() throws Exception { .subAggregation(parent("to_article", "comment").subAggregation(terms("to_category").field("category").size(10000))) ); SearchResponse searchResponse = searchRequest.get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); final Set commenters = getCommenters(); final Map> commenterToComments = getCommenterToComments(); diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java index 0f8bf4e701e09..23c7802507ae4 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java @@ -52,6 +52,7 @@ import static org.elasticsearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; @@ -638,15 +639,14 @@ public void testTooHighResultWindow() { createIndexRequest("index1", "parent_type", "1", null, "nested_type", Collections.singletonMap("key", "value")).get(); createIndexRequest("index1", "child_type", "2", "1").get(); refresh(); - - SearchResponse response = client().prepareSearch("index1") - .setQuery( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setFrom(50).setSize(10).setName("_name")) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures( + client().prepareSearch("index1") + .setQuery( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setFrom(50).setSize(10).setName("_name")) + ), + 1 + ); Exception e = expectThrows( SearchPhaseExecutionException.class, diff --git a/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java index 2155192414246..9aa78f44881e2 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java @@ -64,7 +64,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyArray; @@ -288,7 +288,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { .setQuery(QueryBuilders.matchQuery("name", "bar")) .addAggregation(AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("test").field("name"))) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Global global = searchResponse.getAggregations().get("global"); Terms terms = global.getAggregations().get("test"); assertThat(terms.getBuckets().size(), equalTo(4)); @@ -299,7 +299,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { .addAggregation(AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("test").field("name"))) .addSort("_index", SortOrder.ASC) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); global = searchResponse.getAggregations().get("global"); terms = global.getAggregations().get("test"); assertThat(terms.getBuckets().size(), equalTo(4)); @@ -310,7 +310,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { .addAggregation(AggregationBuilders.terms("test").field("name")) .addSort("_index", SortOrder.ASC) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); terms = searchResponse.getAggregations().get("test"); assertThat(terms.getBuckets().size(), equalTo(2)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java index 8b1c44703569f..b1b3a6850c061 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java @@ -39,7 +39,7 @@ import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -130,7 +130,7 @@ public void testRetryDueToExceptionOnNetworkLayer() throws ExecutionException, I dupCounter++; } } - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(dupCounter, equalTo(0L)); assertHitCount(searchResponse, numDocs); IndicesStatsResponse index = indicesAdmin().prepareStats("index").clear().setSegments(true).get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java index 8ad81104eab2f..93a19e5d921b5 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java @@ -35,7 +35,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.dateRange; import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -67,7 +67,7 @@ public void testCacheAggs() throws Exception { dateHistogram("histo").field("f").timeZone(ZoneId.of("+01:00")).minDocCount(0).calendarInterval(DateHistogramInterval.MONTH) ) .get(); - assertSearchResponse(r1); + assertNoFailures(r1); // The cached is actually used assertThat( @@ -86,7 +86,7 @@ public void testCacheAggs() throws Exception { .calendarInterval(DateHistogramInterval.MONTH) ) .get(); - assertSearchResponse(r2); + assertNoFailures(r2); Histogram h1 = r1.getAggregations().get("histo"); Histogram h2 = r2.getAggregations().get("histo"); final List buckets1 = h1.getBuckets(); @@ -530,7 +530,7 @@ public void testProfileDisableCache() throws Exception { .setProfile(profile) .setQuery(QueryBuilders.termQuery("k", "hello")) .get(); - assertSearchResponse(resp); + assertNoFailures(resp); ElasticsearchAssertions.assertAllSuccessful(resp); assertThat(resp.getHits().getTotalHits().value, equalTo(1L)); if (profile == false) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java index d09fb6e32e40f..c194674d1d99f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java @@ -9,7 +9,6 @@ import org.apache.lucene.tests.util.English; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; import org.elasticsearch.test.ESIntegTestCase; @@ -19,8 +18,7 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; @ClusterScope(scope = SUITE) public class StressSearchServiceReaperIT extends ESIntegTestCase { @@ -45,9 +43,7 @@ public void testStressReaper() throws ExecutionException, InterruptedException { indexRandom(true, builders); final int iterations = scaledRandomIntBetween(500, 1000); for (int i = 0; i < iterations; i++) { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(matchAllQuery()).setSize(num).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, num); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(matchAllQuery()).setSize(num), num); } } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java index d6b9cb0ac267c..0c940ad1f3154 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java @@ -19,7 +19,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; @ESIntegTestCase.SuiteScopeTestCase public class AggregationsIntegrationIT extends ESIntegTestCase { @@ -44,7 +44,7 @@ public void testScroll() { .setScroll(TimeValue.timeValueMinutes(1)) .addAggregation(terms("f").field("f")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); Terms terms = aggregations.get("f"); @@ -53,7 +53,7 @@ public void testScroll() { int total = response.getHits().getHits().length; while (response.getHits().getHits().length > 0) { response = client().prepareSearchScroll(response.getScrollId()).setScroll(TimeValue.timeValueMinutes(1)).get(); - assertSearchResponse(response); + assertNoFailures(response); assertNull(response.getAggregations()); total += response.getHits().getHits().length; } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java index 683ccb975b9fb..9c8786206264f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java @@ -24,7 +24,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.missing; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; @@ -66,7 +66,7 @@ public void testMultipleAggsOnSameField_WithDifferentRequiredValueSourceType() t .addAggregation(terms("values").field("value").collectMode(aggCollectionMode)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggs = response.getAggregations(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java index 4352c0962c7df..f8e6ed5193e9c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java @@ -57,7 +57,6 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.IsNull.notNullValue; @@ -367,7 +366,7 @@ public void testDuelTermsHistogram() throws Exception { .addAggregation(histogram("histo").field("values").interval(interval).minDocCount(1)) .get(); - assertSearchResponse(resp); + assertNoFailures(resp); Terms terms = resp.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -427,7 +426,7 @@ public void testReduce() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filter filter = response.getAggregations().get("filter"); assertNotNull(filter); @@ -493,7 +492,7 @@ public void testDuelDepthBreadthFirst() throws Exception { ) ) .get(); - assertSearchResponse(r1); + assertNoFailures(r1); final SearchResponse r2 = client().prepareSearch("idx") .addAggregation( terms("f1").field("f1") @@ -505,7 +504,7 @@ public void testDuelDepthBreadthFirst() throws Exception { ) ) .get(); - assertSearchResponse(r2); + assertNoFailures(r2); final Terms t1 = r1.getAggregations().get("f1"); final Terms t2 = r2.getAggregations().get("f1"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java index f5283b979c722..0cfd27f03c6e1 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java @@ -22,7 +22,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.maxBucket; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; public class MetadataIT extends ESIntegTestCase { @@ -46,7 +46,7 @@ public void testMetadataSetOnAggregationResult() throws Exception { .addAggregation(maxBucket("the_max_bucket", "the_terms>the_sum").setMetadata(metadata)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Aggregations aggs = response.getAggregations(); assertNotNull(aggs); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java index 96636b57f2774..6ad46d7a77ee8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java @@ -30,7 +30,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.closeTo; @ESIntegTestCase.SuiteScopeTestCase @@ -57,7 +57,7 @@ public void testUnmappedTerms() { SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("my_terms").field("non_existing_field").missing("bar")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); assertEquals(2, terms.getBucketByKey("bar").getDocCount()); @@ -68,14 +68,14 @@ public void testStringTerms() { SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("my_terms").field("str").executionHint(mode.toString()).missing("bar")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); assertEquals(1, terms.getBucketByKey("foo").getDocCount()); assertEquals(1, terms.getBucketByKey("bar").getDocCount()); response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("str").missing("foo")).get(); - assertSearchResponse(response); + assertNoFailures(response); terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); assertEquals(2, terms.getBucketByKey("foo").getDocCount()); @@ -84,14 +84,14 @@ public void testStringTerms() { public void testLongTerms() { SearchResponse response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("long").missing(4)).get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); assertEquals(1, terms.getBucketByKey("3").getDocCount()); assertEquals(1, terms.getBucketByKey("4").getDocCount()); response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("long").missing(3)).get(); - assertSearchResponse(response); + assertNoFailures(response); terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); assertEquals(2, terms.getBucketByKey("3").getDocCount()); @@ -99,14 +99,14 @@ public void testLongTerms() { public void testDoubleTerms() { SearchResponse response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("double").missing(4.5)).get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); assertEquals(1, terms.getBucketByKey("4.5").getDocCount()); assertEquals(1, terms.getBucketByKey("5.5").getDocCount()); response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("double").missing(5.5)).get(); - assertSearchResponse(response); + assertNoFailures(response); terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); assertEquals(2, terms.getBucketByKey("5.5").getDocCount()); @@ -116,7 +116,7 @@ public void testUnmappedHistogram() { SearchResponse response = client().prepareSearch("idx") .addAggregation(histogram("my_histogram").field("non-existing_field").interval(5).missing(12)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histogram = response.getAggregations().get("my_histogram"); assertEquals(1, histogram.getBuckets().size()); assertEquals(10d, histogram.getBuckets().get(0).getKey()); @@ -127,7 +127,7 @@ public void testHistogram() { SearchResponse response = client().prepareSearch("idx") .addAggregation(histogram("my_histogram").field("long").interval(5).missing(7)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histogram = response.getAggregations().get("my_histogram"); assertEquals(2, histogram.getBuckets().size()); assertEquals(0d, histogram.getBuckets().get(0).getKey()); @@ -136,7 +136,7 @@ public void testHistogram() { assertEquals(1, histogram.getBuckets().get(1).getDocCount()); response = client().prepareSearch("idx").addAggregation(histogram("my_histogram").field("long").interval(5).missing(3)).get(); - assertSearchResponse(response); + assertNoFailures(response); histogram = response.getAggregations().get("my_histogram"); assertEquals(1, histogram.getBuckets().size()); assertEquals(0d, histogram.getBuckets().get(0).getKey()); @@ -147,7 +147,7 @@ public void testDateHistogram() { SearchResponse response = client().prepareSearch("idx") .addAggregation(dateHistogram("my_histogram").field("date").calendarInterval(DateHistogramInterval.YEAR).missing("2014-05-07")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histogram = response.getAggregations().get("my_histogram"); assertEquals(2, histogram.getBuckets().size()); assertEquals("2014-01-01T00:00:00.000Z", histogram.getBuckets().get(0).getKeyAsString()); @@ -158,7 +158,7 @@ public void testDateHistogram() { response = client().prepareSearch("idx") .addAggregation(dateHistogram("my_histogram").field("date").calendarInterval(DateHistogramInterval.YEAR).missing("2015-05-07")) .get(); - assertSearchResponse(response); + assertNoFailures(response); histogram = response.getAggregations().get("my_histogram"); assertEquals(1, histogram.getBuckets().size()); assertEquals("2015-01-01T00:00:00.000Z", histogram.getBuckets().get(0).getKeyAsString()); @@ -167,7 +167,7 @@ public void testDateHistogram() { public void testCardinality() { SearchResponse response = client().prepareSearch("idx").addAggregation(cardinality("card").field("long").missing(2)).get(); - assertSearchResponse(response); + assertNoFailures(response); Cardinality cardinality = response.getAggregations().get("card"); assertEquals(2, cardinality.getValue()); } @@ -176,14 +176,14 @@ public void testPercentiles() { SearchResponse response = client().prepareSearch("idx") .addAggregation(percentiles("percentiles").field("long").missing(1000)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Percentiles percentiles = response.getAggregations().get("percentiles"); assertEquals(1000, percentiles.percentile(100), 0); } public void testStats() { SearchResponse response = client().prepareSearch("idx").addAggregation(stats("stats").field("long").missing(5)).get(); - assertSearchResponse(response); + assertNoFailures(response); Stats stats = response.getAggregations().get("stats"); assertEquals(2, stats.getCount()); assertEquals(4, stats.getAvg(), 0); @@ -193,7 +193,7 @@ public void testUnmappedGeoBounds() { SearchResponse response = client().prepareSearch("idx") .addAggregation(geoBounds("bounds").field("non_existing_field").missing("2,1")) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoBounds bounds = response.getAggregations().get("bounds"); assertThat(bounds.bottomRight().lat(), closeTo(2.0, 1E-5)); assertThat(bounds.bottomRight().lon(), closeTo(1.0, 1E-5)); @@ -203,7 +203,7 @@ public void testUnmappedGeoBounds() { public void testGeoBounds() { SearchResponse response = client().prepareSearch("idx").addAggregation(geoBounds("bounds").field("location").missing("2,1")).get(); - assertSearchResponse(response); + assertNoFailures(response); GeoBounds bounds = response.getAggregations().get("bounds"); assertThat(bounds.bottomRight().lat(), closeTo(1.0, 1E-5)); assertThat(bounds.bottomRight().lon(), closeTo(2.0, 1E-5)); @@ -215,7 +215,7 @@ public void testGeoCentroid() { SearchResponse response = client().prepareSearch("idx") .addAggregation(geoCentroid("centroid").field("location").missing("2,1")) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoCentroid centroid = response.getAggregations().get("centroid"); GeoPoint point = new GeoPoint(1.5, 1.5); assertThat(point.getY(), closeTo(centroid.centroid().getY(), 1E-5)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java index 795a72310cb2f..3f94cde9cb30a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java @@ -15,7 +15,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.UnmappedTerms; import org.elasticsearch.test.ESIntegTestCase; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -79,7 +79,7 @@ public void testSingleValueField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -113,7 +113,7 @@ public void testMultiValueField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -149,7 +149,7 @@ public void testUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); UnmappedTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java index 62a8a5c9dee98..8333fcd37ea37 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java @@ -60,8 +60,8 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -229,7 +229,7 @@ public void testSingleValuedField() throws Exception { .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -270,7 +270,7 @@ public void testSingleValuedFieldWithTimeZone() throws Exception { .execute() .actionGet(); ZoneId tz = ZoneId.of("+01:00"); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -333,7 +333,7 @@ public void testSingleValued_timeZone_epoch() throws Exception { dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.DAY).minDocCount(1).timeZone(tz).format(format) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -366,7 +366,7 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.key(true))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -388,7 +388,7 @@ public void testSingleValuedFieldOrderedByKeyDesc() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -409,7 +409,7 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -430,7 +430,7 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -451,7 +451,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -513,7 +513,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -537,7 +537,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -561,7 +561,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -585,7 +585,7 @@ public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -641,7 +641,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -686,7 +686,7 @@ public void testMultiValuedField() throws Exception { .addAggregation(dateHistogram("histo").field("dates").calendarInterval(DateHistogramInterval.MONTH)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -730,7 +730,7 @@ public void testMultiValuedFieldOrderedByCountDesc() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -781,7 +781,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -836,7 +836,7 @@ public void testScriptSingleValue() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -876,7 +876,7 @@ public void testScriptMultiValued() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -927,7 +927,7 @@ public void testUnmapped() throws Exception { .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -940,7 +940,7 @@ public void testPartiallyUnmapped() throws Exception { .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -1130,7 +1130,7 @@ public void testSingleValueFieldWithExtendedBounds() throws Exception { throw e; } } - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -1187,7 +1187,7 @@ public void testSingleValueFieldWithExtendedBoundsTimezone() throws Exception { .extendedBounds(new LongBounds("now/d", "now/d+23h")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat( "Expected 24 buckets for one day aggregation with hourly interval", @@ -1245,7 +1245,7 @@ public void testSingleValueFieldWithExtendedBoundsOffset() throws Exception { .extendedBounds(new LongBounds("2016-01-01T06:00:00Z", "2016-01-08T08:00:00Z")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -1322,7 +1322,7 @@ public void testIssue6965() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); ZoneId tz = ZoneId.of("+01:00"); @@ -1370,7 +1370,7 @@ public void testDSTBoundaryIssue9491() throws InterruptedException, ExecutionExc .format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX") ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("2014-01-01T00:00:00.000+02:00")); @@ -1395,7 +1395,7 @@ public void testIssue8209() throws InterruptedException, ExecutionException { .minDocCount(0) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(4)); assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("2014-01-01T00:00:00.000+01:00")); @@ -1432,7 +1432,7 @@ public void testFormatIndexUnmapped() throws InterruptedException, ExecutionExce .extendedBounds(new LongBounds("2018-01", "2018-01")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("2018-01")); @@ -1455,7 +1455,7 @@ public void testRewriteTimeZone_EpochMillisFormat() throws InterruptedException, dateHistogram("histo").field("d").calendarInterval(DateHistogramInterval.MONTH).timeZone(ZoneId.of("Europe/Berlin")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("1477954800000")); @@ -1469,7 +1469,7 @@ public void testRewriteTimeZone_EpochMillisFormat() throws InterruptedException, .format("yyyy-MM-dd") ) .get(); - assertSearchResponse(response); + assertNoFailures(response); histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("2016-11-01")); @@ -1586,7 +1586,7 @@ public void testScriptCaching() throws Exception { .calendarInterval(DateHistogramInterval.MONTH) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1606,7 +1606,7 @@ public void testScriptCaching() throws Exception { .calendarInterval(DateHistogramInterval.MONTH) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1622,7 +1622,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(dateHistogram("histo").field("d").calendarInterval(DateHistogramInterval.MONTH)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1686,7 +1686,7 @@ private void assertMultiSortResponse(int[] expectedDays, BucketOrder... order) { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histogram = response.getAggregations().get("histo"); assertThat(histogram, notNullValue()); @@ -1730,7 +1730,7 @@ public void testDateNanosHistogram() throws Exception { ) .addDocValueField("date") .get(); - assertSearchResponse(r); + assertNoFailures(r); Histogram histogram = r.getAggregations().get("histo"); List buckets = histogram.getBuckets(); @@ -1746,7 +1746,7 @@ public void testDateNanosHistogram() throws Exception { ) .addDocValueField("date") .get(); - assertSearchResponse(r); + assertNoFailures(r); histogram = r.getAggregations().get("histo"); buckets = histogram.getBuckets(); @@ -1764,7 +1764,7 @@ public void testDateKeyFormatting() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); InternalDateHistogram histogram = response.getAggregations().get("histo"); List buckets = histogram.getBuckets(); @@ -1782,7 +1782,7 @@ public void testHardBoundsOnDates() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); InternalDateHistogram histogram = response.getAggregations().get("histo"); List buckets = histogram.getBuckets(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java index 27fb5c49df605..d0e7a74c9feab 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java @@ -42,7 +42,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -136,7 +136,7 @@ public void testDateMath() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -171,7 +171,7 @@ public void testSingleValueField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -217,7 +217,7 @@ public void testSingleValueFieldWithStringDates() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -264,7 +264,7 @@ public void testSingleValueFieldWithStringDatesWithCustomFormat() throws Excepti ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -318,7 +318,7 @@ public void testSingleValueFieldWithDateMath() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -364,7 +364,7 @@ public void testSingleValueFieldWithCustomKey() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -420,7 +420,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -495,7 +495,7 @@ public void testMultiValuedField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -541,7 +541,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -661,7 +661,7 @@ public void testScriptCaching() throws Exception { ) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -684,7 +684,7 @@ public void testScriptCaching() throws Exception { ) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -706,7 +706,7 @@ public void testScriptCaching() throws Exception { ) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java index e9180f5d8b9f2..ac0dbf6f5d0a6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java @@ -28,7 +28,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sampler; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -101,7 +101,7 @@ public void testIssue10719() throws Exception { .subAggregation(sampler("sample").shardSize(100).subAggregation(max("max_price").field("price"))) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms genres = response.getAggregations().get("genres"); Collection genreBuckets = genres.getBuckets(); // For this test to be useful we need >1 genre bucket to compare @@ -133,7 +133,7 @@ public void testSimpleDiversity() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler sample = response.getAggregations().get("sample"); Terms authors = sample.getAggregations().get("authors"); List testBuckets = authors.getBuckets(); @@ -154,7 +154,7 @@ public void testNestedDiversity() throws Exception { rootTerms.subAggregation(sampleAgg); SearchResponse response = client().prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH).addAggregation(rootTerms).get(); - assertSearchResponse(response); + assertNoFailures(response); Terms genres = response.getAggregations().get("genres"); List genreBuckets = genres.getBuckets(); for (Terms.Bucket genreBucket : genreBuckets) { @@ -186,7 +186,7 @@ public void testNestedSamples() throws Exception { .setSearchType(SearchType.QUERY_THEN_FETCH) .addAggregation(rootSample) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler genreSample = response.getAggregations().get("genreSample"); Sampler sample = genreSample.getAggregations().get("sample"); @@ -217,7 +217,7 @@ public void testPartiallyUnmappedDiversifyField() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler sample = response.getAggregations().get("sample"); assertThat(sample.getDocCount(), greaterThan(0L)); Terms authors = sample.getAggregations().get("authors"); @@ -237,7 +237,7 @@ public void testWhollyUnmappedDiversifyField() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler sample = response.getAggregations().get("sample"); assertThat(sample.getDocCount(), equalTo(0L)); Terms authors = sample.getAggregations().get("authors"); @@ -256,7 +256,7 @@ public void testRidiculousSizeDiversity() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); sampleAgg = new DiversifiedAggregationBuilder("sample").shardSize(100); sampleAgg.field("author").maxDocsPerValue(Integer.MAX_VALUE).executionHint(randomExecutionHint()); @@ -268,7 +268,7 @@ public void testRidiculousSizeDiversity() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java index 1e4cfb444d47e..1664db2060e0d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java @@ -54,7 +54,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -282,7 +282,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception new TermsAggregationBuilder("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(allResponse); + assertNoFailures(allResponse); DoubleTerms terms = allResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -299,7 +299,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -320,7 +320,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -345,7 +345,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -374,7 +374,7 @@ public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -421,7 +421,7 @@ public void testScriptSingleValue() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -453,7 +453,7 @@ public void testScriptMultiValued() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -480,7 +480,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -505,7 +505,7 @@ public void testPartiallyUnmappedWithFormat() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -537,7 +537,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithSubTer ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -578,7 +578,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms tags = response.getAggregations().get("num_tags"); assertThat(tags, notNullValue()); @@ -619,7 +619,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms tags = response.getAggregations().get("tags"); assertThat(tags, notNullValue()); @@ -759,7 +759,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -789,7 +789,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -819,7 +819,7 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -863,7 +863,7 @@ public void testScriptScore() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -930,7 +930,7 @@ private void assertMultiSortResponse(double[] expectedKeys, BucketOrder... order ) .get(); - assertSearchResponse(response); + assertNoFailures(response); DoubleTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -990,7 +990,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1009,7 +1009,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1022,7 +1022,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java index 4d799cbbea6b5..065bb5924e049 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java @@ -28,7 +28,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.avg; import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -81,7 +81,7 @@ public void setupSuiteScopeCluster() throws Exception { public void testSimple() throws Exception { SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", termQuery("tag", "tag1"))).get(); - assertSearchResponse(response); + assertNoFailures(response); Filter filter = response.getAggregations().get("tag1"); assertThat(filter, notNullValue()); @@ -95,7 +95,7 @@ public void testEmptyFilterDeclarations() throws Exception { QueryBuilder emptyFilter = new BoolQueryBuilder(); SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", emptyFilter)).get(); - assertSearchResponse(response); + assertNoFailures(response); Filter filter = response.getAggregations().get("tag1"); assertThat(filter, notNullValue()); @@ -107,7 +107,7 @@ public void testWithSubAggregation() throws Exception { .addAggregation(filter("tag1", termQuery("tag", "tag1")).subAggregation(avg("avg_value").field("value"))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filter filter = response.getAggregations().get("tag1"); assertThat(filter, notNullValue()); @@ -132,7 +132,7 @@ public void testAsSubAggregation() { .addAggregation(histogram("histo").field("value").interval(2L).subAggregation(filter("filter", matchAllQuery()))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java index 68acd22ce6c55..9e86c4ec80d4d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java @@ -34,7 +34,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.avg; import static org.elasticsearch.search.aggregations.AggregationBuilders.filters; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -107,7 +107,7 @@ public void testSimple() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -134,7 +134,7 @@ public void testEmptyFilterDeclarations() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -156,7 +156,7 @@ public void testWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -206,7 +206,7 @@ public void testAsSubAggregation() { .addAggregation(histogram("histo").field("value").interval(2L).subAggregation(filters("filters", matchAllQuery()))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -273,7 +273,7 @@ public void testSimpleNonKeyed() throws Exception { .addAggregation(filters("tags", termQuery("tag", "tag1"), termQuery("tag", "tag2"))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -303,7 +303,7 @@ public void testOtherBucket() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -334,7 +334,7 @@ public void testOtherNamedBucket() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -360,7 +360,7 @@ public void testOtherNonKeyed() throws Exception { .addAggregation(filters("tags", termQuery("tag", "tag1"), termQuery("tag", "tag2")).otherBucket(true)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); @@ -394,7 +394,7 @@ public void testOtherWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filters filters = response.getAggregations().get("tags"); assertThat(filters, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java index df3f1b3e0fdb3..a7dee3b1507ee 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java @@ -39,7 +39,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.geoDistance; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -144,7 +144,7 @@ public void testSimple() throws Exception { } SearchResponse response = client().prepareSearch("idx").addAggregation(builder).get(); - assertSearchResponse(response); + assertNoFailures(response); Range geoDist = response.getAggregations().get("amsterdam_rings"); assertThat(geoDist, notNullValue()); @@ -191,7 +191,7 @@ public void testSimpleWithCustomKeys() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range geoDist = response.getAggregations().get("amsterdam_rings"); assertThat(geoDist, notNullValue()); @@ -240,7 +240,7 @@ public void testUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range geoDist = response.getAggregations().get("amsterdam_rings"); assertThat(geoDist, notNullValue()); @@ -287,7 +287,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range geoDist = response.getAggregations().get("amsterdam_rings"); assertThat(geoDist, notNullValue()); @@ -335,7 +335,7 @@ public void testWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range geoDist = response.getAggregations().get("amsterdam_rings"); assertThat(geoDist, notNullValue()); @@ -462,7 +462,7 @@ public void testMultiValues() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range geoDist = response.getAggregations().get("amsterdam_rings"); assertThat(geoDist, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java index 151de82117bc1..078cc9761fb26 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java @@ -36,7 +36,7 @@ import static org.elasticsearch.geometry.utils.Geohash.stringEncode; import static org.elasticsearch.search.aggregations.AggregationBuilders.geohashGrid; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -136,7 +136,7 @@ public void testSimple() throws Exception { .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); List buckets = geoGrid.getBuckets(); @@ -163,7 +163,7 @@ public void testMultivalued() throws Exception { .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { @@ -188,7 +188,7 @@ public void testFiltered() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filter filter = response.getAggregations().get("filtered"); @@ -211,7 +211,7 @@ public void testUnmapped() throws Exception { .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); assertThat(geoGrid.getBuckets().size(), equalTo(0)); @@ -225,7 +225,7 @@ public void testPartiallyUnmapped() throws Exception { .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { @@ -245,7 +245,7 @@ public void testTopMatch() throws Exception { .addAggregation(geohashGrid("geohashgrid").field("location").size(1).shardSize(100).precision(precision)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); // Check we only have one bucket with the best match for that resolution diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java index 1ccb47a8517c4..80133961ad73a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java @@ -21,7 +21,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.global; import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -65,7 +65,7 @@ public void testWithStatsSubAggregator() throws Exception { .addAggregation(global("global").subAggregation(stats("value_stats").field("value"))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Global global = response.getAggregations().get("global"); assertThat(global, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java index 3f47240fb0f9d..b92320c48ed1c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java @@ -51,7 +51,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -238,7 +238,7 @@ public void testSingleValuedField() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -290,7 +290,7 @@ public void testSingleValuedFieldWithRandomOffset() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).offset(offset)) .get(); - assertSearchResponse(response); + assertNoFailures(response); // shifting by offset>2 creates new extra bucket [0,offset-1] // if offset is >= number of values in original last bucket, that effect is canceled int expectedNumberOfBuckets = (offset >= (numDocs % interval + 1)) ? numValueBuckets : numValueBuckets + 1; @@ -324,7 +324,7 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(true))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -345,7 +345,7 @@ public void testsingleValuedFieldOrderedByKeyDesc() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -366,7 +366,7 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(true))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -393,7 +393,7 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(false))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -424,7 +424,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -467,7 +467,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -509,7 +509,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -551,7 +551,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -595,7 +595,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() thro ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -633,7 +633,7 @@ public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -689,7 +689,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); final int numBuckets = (numDocs + 1) / interval - 2 / interval + 1; final long[] counts = new long[(numDocs + 1) / interval + 1]; @@ -717,7 +717,7 @@ public void testMultiValuedField() throws Exception { .addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -738,7 +738,7 @@ public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { .addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -763,7 +763,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); final int numBuckets = (numDocs + 2) / interval - 2 / interval + 1; final long[] counts = new long[(numDocs + 2) / interval + 1]; @@ -799,7 +799,7 @@ public void testScriptSingleValue() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -823,7 +823,7 @@ public void testScriptMultiValued() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -844,7 +844,7 @@ public void testUnmapped() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -857,7 +857,7 @@ public void testPartiallyUnmapped() throws Exception { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -882,7 +882,7 @@ public void testPartiallyUnmappedWithExtendedBounds() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -990,7 +990,7 @@ public void testSingleValuedFieldWithExtendedBounds() throws Exception { throw e; } } - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -1067,7 +1067,7 @@ public void testEmptyWithExtendedBounds() throws Exception { throw e; } } - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -1110,7 +1110,7 @@ public void testDecimalIntervalAndOffset() throws Exception { SearchResponse r = client().prepareSearch("decimal_values") .addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05)) .get(); - assertSearchResponse(r); + assertNoFailures(r); Histogram histogram = r.getAggregations().get("histo"); List buckets = histogram.getBuckets(); @@ -1157,7 +1157,7 @@ public void testScriptCaching() throws Exception { .offset(0.05) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1178,7 +1178,7 @@ public void testScriptCaching() throws Exception { .offset(0.05) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1194,7 +1194,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1280,7 +1280,7 @@ public void testHardBounds() throws Exception { SearchResponse r = client().prepareSearch("test") .addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(0.0, null))) .get(); - assertSearchResponse(r); + assertNoFailures(r); Histogram histogram = r.getAggregations().get("histo"); List buckets = histogram.getBuckets(); @@ -1291,7 +1291,7 @@ public void testHardBounds() throws Exception { r = client().prepareSearch("test") .addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(null, 0.0))) .get(); - assertSearchResponse(r); + assertNoFailures(r); histogram = r.getAggregations().get("histo"); buckets = histogram.getBuckets(); @@ -1301,7 +1301,7 @@ public void testHardBounds() throws Exception { r = client().prepareSearch("test") .addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(0.0, 0.3))) .get(); - assertSearchResponse(r); + assertNoFailures(r); histogram = r.getAggregations().get("histo"); buckets = histogram.getBuckets(); @@ -1321,7 +1321,7 @@ private void assertMultiSortResponse(long[] expectedKeys, BucketOrder... order) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histogram = response.getAggregations().get("histo"); assertThat(histogram, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java index 22cf163c44005..1768773ba7696 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java @@ -25,7 +25,7 @@ import java.util.function.Function; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; @@ -73,7 +73,7 @@ public void testSingleValuedField() { .addUnboundedFrom("192.168.1.10") ) .get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -106,7 +106,7 @@ public void testMultiValuedField() { .addUnboundedFrom("192.168.1.10") ) .get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -139,7 +139,7 @@ public void testIpMask() { .addMaskRange("2001:db8::/64") ) .get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -166,7 +166,7 @@ public void testPartiallyUnmapped() { .addUnboundedFrom("192.168.1.10") ) .get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -199,7 +199,7 @@ public void testUnmapped() { .addUnboundedFrom("192.168.1.10") ) .get(); - assertSearchResponse(rsp); + assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java index 568f18b15ecec..51733d98fd80a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java @@ -22,7 +22,7 @@ import java.util.function.Function; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; public class IpTermsIT extends AbstractTermsTestCase { @@ -64,7 +64,7 @@ public void testScriptValue() throws Exception { SearchResponse response = client().prepareSearch("index") .addAggregation(new TermsAggregationBuilder("my_terms").script(script).executionHint(randomExecutionHint())) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); @@ -92,7 +92,7 @@ public void testScriptValues() throws Exception { SearchResponse response = client().prepareSearch("index") .addAggregation(new TermsAggregationBuilder("my_terms").script(script).executionHint(randomExecutionHint())) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); @@ -120,7 +120,7 @@ public void testMissingValue() throws Exception { .addAggregation(new TermsAggregationBuilder("my_terms").field("ip").missing("127.0.0.1").executionHint(randomExecutionHint())) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java index fa5a23ecb322f..d65a246aedc5d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java @@ -53,7 +53,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -268,7 +268,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception SearchResponse allResponse = client().prepareSearch("idx") .addAggregation(new TermsAggregationBuilder("terms").field(field).collectMode(randomFrom(SubAggCollectionMode.values()))) .get(); - assertSearchResponse(allResponse); + assertNoFailures(allResponse); LongTerms terms = allResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -285,7 +285,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -306,7 +306,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); // Scripts force the results to doubles DoubleTerms terms = response.getAggregations().get("terms"); @@ -332,7 +332,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); // Scripts force the results to doubles DoubleTerms terms = response.getAggregations().get("terms"); @@ -362,7 +362,7 @@ public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); // The script always converts long to double DoubleTerms terms = response.getAggregations().get("terms"); @@ -410,7 +410,7 @@ public void testScriptSingleValue() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -443,7 +443,7 @@ public void testScriptMultiValued() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -470,7 +470,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -495,7 +495,7 @@ public void testPartiallyUnmappedWithFormat() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -527,7 +527,7 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithTermsS ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -568,7 +568,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms tags = response.getAggregations().get("num_tags"); assertThat(tags, notNullValue()); @@ -609,7 +609,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms tags = response.getAggregations().get("tags"); assertThat(tags, notNullValue()); @@ -749,7 +749,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -780,7 +780,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -811,7 +811,7 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -882,7 +882,7 @@ private void assertMultiSortResponse(long[] expectedKeys, BucketOrder... order) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -942,7 +942,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -961,7 +961,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -974,7 +974,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java index 5deeeb40953b9..d015825d775d3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java @@ -28,7 +28,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.core.IsNull.notNullValue; @@ -154,7 +154,7 @@ public void testTerms(String fieldName) { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); final Terms terms = response.getAggregations().get("terms"); assertCorrectlySorted(terms, asc, agg); } @@ -183,7 +183,7 @@ public void testLongHistogram() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); final Histogram histo = response.getAggregations().get("histo"); assertCorrectlySorted(histo, asc, agg); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java index 823b4f8404176..e9ec509e3b4c7 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java @@ -48,7 +48,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -182,7 +181,7 @@ public void testSimple() throws Exception { .addAggregation(nested("nested", "nested").subAggregation(stats("nested_value_stats").field("nested.value"))) .get(); - assertSearchResponse(response); + assertNoFailures(response); double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; @@ -231,7 +230,7 @@ public void testNestedWithSubTermsAgg() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); long docCount = 0; long[] counts = new long[numParents + 6]; @@ -284,7 +283,7 @@ public void testNestedAsSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms values = response.getAggregations().get("top_values"); assertThat(values, notNullValue()); @@ -313,7 +312,7 @@ public void testNestNestedAggs() throws Exception { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Nested level1 = response.getAggregations().get("level1"); assertThat(level1, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java index 95f60df960ab8..b94ed2aed4486 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java @@ -40,7 +40,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -142,7 +142,7 @@ public void testRangeAsSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getBuckets().size(), equalTo(numDocs + 1)); @@ -200,7 +200,7 @@ public void testSingleValueField() throws Exception { .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -241,7 +241,7 @@ public void testSingleValueFieldWithFormat() throws Exception { .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6).format("#")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -284,7 +284,7 @@ public void testSingleValueFieldWithCustomKey() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -331,7 +331,7 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -403,7 +403,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -457,7 +457,7 @@ public void testMultiValuedField() throws Exception { .addAggregation(range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -517,7 +517,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -581,7 +581,7 @@ public void testScriptSingleValue() throws Exception { .addAggregation(range("range").script(script).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -622,7 +622,7 @@ public void testEmptyRange() throws Exception { .addAggregation(range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(-1).addUnboundedFrom(1000)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -672,7 +672,7 @@ public void testScriptMultiValued() throws Exception { .addAggregation(range("range").script(script).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -730,7 +730,7 @@ public void testUnmapped() throws Exception { .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -773,7 +773,7 @@ public void testPartiallyUnmapped() throws Exception { .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -816,7 +816,7 @@ public void testOverlappingRanges() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -930,7 +930,7 @@ public void testScriptCaching() throws Exception { .addRange(0, 10) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -950,7 +950,7 @@ public void testScriptCaching() throws Exception { .addRange(0, 10) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -963,7 +963,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(range("foo").field("i").addRange(0, 10)).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -980,7 +980,7 @@ public void testFieldAlias() { .addAggregation(range("range").field("route_length_miles").addUnboundedTo(50.0).addRange(50.0, 150.0).addUnboundedFrom(150.0)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -1011,7 +1011,7 @@ public void testFieldAliasWithMissingValue() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java index 5a8c560ad98f0..d40afeb433e38 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java @@ -35,7 +35,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -157,7 +156,7 @@ public void testSimpleReverseNestedToRoot() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Nested nested = response.getAggregations().get("nested1"); assertThat(nested, notNullValue()); @@ -339,7 +338,7 @@ public void testSimpleNested1ToRootToNested2() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Nested nested = response.getAggregations().get("nested1"); assertThat(nested.getName(), equalTo("nested1")); assertThat(nested.getDocCount(), equalTo(9L)); @@ -371,7 +370,7 @@ public void testSimpleReverseNestedToNested1() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Nested nested = response.getAggregations().get("nested1"); assertThat(nested, notNullValue()); @@ -714,7 +713,7 @@ public void testFieldAlias() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Nested nested = response.getAggregations().get("nested1"); Terms nestedTerms = nested.getAggregations().get("field2"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java index a9604ce1c62f3..e7f1f30fd8ad3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java @@ -26,7 +26,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sampler; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -99,7 +99,7 @@ public void testIssue10719() throws Exception { .subAggregation(sampler("sample").shardSize(100).subAggregation(max("max_price").field("price"))) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms genres = response.getAggregations().get("genres"); List genreBuckets = genres.getBuckets(); // For this test to be useful we need >1 genre bucket to compare @@ -129,7 +129,7 @@ public void testSimpleSampler() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler sample = response.getAggregations().get("sample"); Terms authors = sample.getAggregations().get("authors"); List testBuckets = authors.getBuckets(); @@ -151,7 +151,7 @@ public void testUnmappedChildAggNoDiversity() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler sample = response.getAggregations().get("sample"); assertThat(sample.getDocCount(), equalTo(0L)); Terms authors = sample.getAggregations().get("authors"); @@ -169,7 +169,7 @@ public void testPartiallyUnmappedChildAggNoDiversity() throws Exception { .setExplain(true) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sampler sample = response.getAggregations().get("sample"); assertThat(sample.getDocCount(), greaterThan(0L)); Terms authors = sample.getAggregations().get("authors"); @@ -186,6 +186,6 @@ public void testRidiculousShardSizeSampler() throws Exception { .setSize(60) .addAggregation(sampleAgg) .get(); - assertSearchResponse(response); + assertNoFailures(response); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java index 1dba19319efc2..63f31d2fbb972 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java @@ -36,7 +36,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.range; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; @@ -96,7 +96,7 @@ public void testGlobal() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Global global = response.getAggregations().get("global"); Histogram histo = global.getAggregations().get("histo"); @@ -113,7 +113,7 @@ public void testFilter() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Filter filter = response.getAggregations().get("filter"); Histogram histo = filter.getAggregations().get("histo"); @@ -129,7 +129,7 @@ public void testMissing() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Missing missing = response.getAggregations().get("missing"); Histogram histo = missing.getAggregations().get("histo"); @@ -149,7 +149,7 @@ public void testGlobalWithFilterWithMissing() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Global global = response.getAggregations().get("global"); Filter filter = global.getAggregations().get("filter"); @@ -168,7 +168,7 @@ public void testNested() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Nested nested = response.getAggregations().get("nested"); Histogram histo = nested.getAggregations().get("histo"); @@ -185,7 +185,7 @@ public void testStringTerms() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); Histogram histo = terms.getBucketByKey("term").getAggregations().get("histo"); @@ -202,7 +202,7 @@ public void testLongTerms() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); Histogram histo = terms.getBucketByKey("1").getAggregations().get("histo"); @@ -219,7 +219,7 @@ public void testDoubleTerms() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); Histogram histo = terms.getBucketByKey("1.5").getAggregations().get("histo"); @@ -236,7 +236,7 @@ public void testRange() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); Histogram histo = range.getBuckets().get(0).getAggregations().get("histo"); @@ -253,7 +253,7 @@ public void testDateRange() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); Histogram histo = range.getBuckets().get(0).getAggregations().get("histo"); @@ -270,7 +270,7 @@ public void testIpRange() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); Histogram histo = range.getBuckets().get(0).getAggregations().get("histo"); @@ -287,7 +287,7 @@ public void testHistogram() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram topHisto = response.getAggregations().get("topHisto"); Histogram histo = topHisto.getBuckets().get(0).getAggregations().get("histo"); @@ -304,7 +304,7 @@ public void testDateHistogram() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram topHisto = response.getAggregations().get("topHisto"); Histogram histo = topHisto.getBuckets().iterator().next().getAggregations().get("histo"); @@ -321,7 +321,7 @@ public void testGeoHashGrid() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid grid = response.getAggregations().get("grid"); Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); @@ -337,7 +337,7 @@ public void testGeoTileGrid() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid grid = response.getAggregations().get("grid"); Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java index c8d89785fc4af..1932e640e785e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java @@ -57,7 +57,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.significantText; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -130,7 +130,7 @@ public void testXContentResponse() throws Exception { SearchResponse response = request.get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms classes = response.getAggregations().get("class"); assertThat(classes.getBuckets().size(), equalTo(2)); for (Terms.Bucket classBucket : classes.getBuckets()) { @@ -287,7 +287,7 @@ public void testBackgroundVsSeparateSet( } SearchResponse response1 = request1.get(); - assertSearchResponse(response1); + assertNoFailures(response1); SearchRequestBuilder request2; if (useSigText) { @@ -390,9 +390,9 @@ public void testScoresEqualForPositiveAndNegative(SignificanceHeuristic heuristi ); } SearchResponse response = request.get(); - assertSearchResponse(response); + assertNoFailures(response); - assertSearchResponse(response); + assertNoFailures(response); StringTerms classes = response.getAggregations().get("class"); assertThat(classes.getBuckets().size(), equalTo(2)); Iterator classBuckets = classes.getBuckets().iterator(); @@ -428,7 +428,7 @@ public void testSubAggregations() throws Exception { .subAggregation(subAgg); SearchResponse response = client().prepareSearch("test").setQuery(query).addAggregation(agg).get(); - assertSearchResponse(response); + assertNoFailures(response); SignificantTerms sigTerms = response.getAggregations().get("significant_terms"); assertThat(sigTerms.getBuckets().size(), equalTo(2)); @@ -504,7 +504,7 @@ public void testScriptScore() throws ExecutionException, InterruptedException, I ); } SearchResponse response = request.get(); - assertSearchResponse(response); + assertNoFailures(response); for (Terms.Bucket classBucket : ((Terms) response.getAggregations().get("class")).getBuckets()) { SignificantTerms sigTerms = classBucket.getAggregations().get("mySignificantTerms"); for (SignificantTerms.Bucket bucket : sigTerms.getBuckets()) { @@ -598,7 +598,7 @@ public void testScriptCaching() throws Exception { .addAggregation(significantTerms("foo").field("s").significanceHeuristic(scriptHeuristic)) .get(); } - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -623,7 +623,7 @@ public void testScriptCaching() throws Exception { .addAggregation(significantTerms("foo").field("s").significanceHeuristic(scriptHeuristic)) .get(); } - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -640,7 +640,7 @@ public void testScriptCaching() throws Exception { } else { r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(significantTerms("foo").field("s")).get(); } - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java index 46c2c693132cc..d010962385232 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java @@ -28,7 +28,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; @@ -278,7 +278,7 @@ public void testStringValueField() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx") .addAggregation( @@ -291,7 +291,7 @@ public void testStringValueField() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertDocCountErrorWithinBounds(size, accurateResponse, testResponse); } @@ -310,7 +310,7 @@ public void testStringValueFieldSingleShard() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -323,7 +323,7 @@ public void testStringValueFieldSingleShard() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -344,7 +344,7 @@ public void testStringValueFieldWithRouting() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountErrorSingleResponse(size, testResponse); } @@ -364,7 +364,7 @@ public void testStringValueFieldDocCountAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -378,7 +378,7 @@ public void testStringValueFieldDocCountAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -398,7 +398,7 @@ public void testStringValueFieldTermSortAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -412,7 +412,7 @@ public void testStringValueFieldTermSortAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -432,7 +432,7 @@ public void testStringValueFieldTermSortDesc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -446,7 +446,7 @@ public void testStringValueFieldTermSortDesc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -467,7 +467,7 @@ public void testStringValueFieldSubAggAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -482,7 +482,7 @@ public void testStringValueFieldSubAggAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -503,7 +503,7 @@ public void testStringValueFieldSubAggDesc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -518,7 +518,7 @@ public void testStringValueFieldSubAggDesc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -537,7 +537,7 @@ public void testLongValueField() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx") .addAggregation( @@ -550,7 +550,7 @@ public void testLongValueField() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertDocCountErrorWithinBounds(size, accurateResponse, testResponse); } @@ -569,7 +569,7 @@ public void testLongValueFieldSingleShard() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -582,7 +582,7 @@ public void testLongValueFieldSingleShard() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -603,7 +603,7 @@ public void testLongValueFieldWithRouting() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountErrorSingleResponse(size, testResponse); } @@ -623,7 +623,7 @@ public void testLongValueFieldDocCountAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -637,7 +637,7 @@ public void testLongValueFieldDocCountAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -657,7 +657,7 @@ public void testLongValueFieldTermSortAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -671,7 +671,7 @@ public void testLongValueFieldTermSortAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -691,7 +691,7 @@ public void testLongValueFieldTermSortDesc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -705,7 +705,7 @@ public void testLongValueFieldTermSortDesc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -726,7 +726,7 @@ public void testLongValueFieldSubAggAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -741,7 +741,7 @@ public void testLongValueFieldSubAggAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -762,7 +762,7 @@ public void testLongValueFieldSubAggDesc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -777,7 +777,7 @@ public void testLongValueFieldSubAggDesc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -796,7 +796,7 @@ public void testDoubleValueField() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx") .addAggregation( @@ -809,7 +809,7 @@ public void testDoubleValueField() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertDocCountErrorWithinBounds(size, accurateResponse, testResponse); } @@ -828,7 +828,7 @@ public void testDoubleValueFieldSingleShard() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -841,7 +841,7 @@ public void testDoubleValueFieldSingleShard() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -862,7 +862,7 @@ public void testDoubleValueFieldWithRouting() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountErrorSingleResponse(size, testResponse); } @@ -882,7 +882,7 @@ public void testDoubleValueFieldDocCountAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -896,7 +896,7 @@ public void testDoubleValueFieldDocCountAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -916,7 +916,7 @@ public void testDoubleValueFieldTermSortAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -930,7 +930,7 @@ public void testDoubleValueFieldTermSortAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -950,7 +950,7 @@ public void testDoubleValueFieldTermSortDesc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -964,7 +964,7 @@ public void testDoubleValueFieldTermSortDesc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertNoDocCountError(size, accurateResponse, testResponse); } @@ -985,7 +985,7 @@ public void testDoubleValueFieldSubAggAsc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -1000,7 +1000,7 @@ public void testDoubleValueFieldSubAggAsc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -1021,7 +1021,7 @@ public void testDoubleValueFieldSubAggDesc() throws Exception { ) .get(); - assertSearchResponse(accurateResponse); + assertNoFailures(accurateResponse); SearchResponse testResponse = client().prepareSearch("idx_single_shard") .addAggregation( @@ -1036,7 +1036,7 @@ public void testDoubleValueFieldSubAggDesc() throws Exception { ) .get(); - assertSearchResponse(testResponse); + assertNoFailures(testResponse); assertUnboundedDocCountError(size, accurateResponse, testResponse); } @@ -1057,7 +1057,7 @@ public void testFixedDocs() throws Exception { .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1112,7 +1112,7 @@ public void testIncrementalReduction() { .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms.getDocCountError(), equalTo(0L)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java index 85cc185d1f558..9b8da69a13e1e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java @@ -25,7 +25,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.significantTerms; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; public class TermsShardMinDocCountIT extends ESIntegTestCase { @@ -72,7 +72,7 @@ public void testShardMinDocCountSignificantTermsTest() throws Exception { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); InternalFilter filteredBucket = response.getAggregations().get("inclass"); SignificantTerms sigterms = filteredBucket.getAggregations().get("mySignificantTerms"); assertThat(sigterms.getBuckets().size(), equalTo(0)); @@ -89,7 +89,7 @@ public void testShardMinDocCountSignificantTermsTest() throws Exception { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); filteredBucket = response.getAggregations().get("inclass"); sigterms = filteredBucket.getAggregations().get("mySignificantTerms"); assertThat(sigterms.getBuckets().size(), equalTo(2)); @@ -136,7 +136,7 @@ public void testShardMinDocCountTermsTest() throws Exception { .order(BucketOrder.key(true)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms sigterms = response.getAggregations().get("myTerms"); assertThat(sigterms.getBuckets().size(), equalTo(0)); @@ -151,7 +151,7 @@ public void testShardMinDocCountTermsTest() throws Exception { .order(BucketOrder.key(true)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); sigterms = response.getAggregations().get("myTerms"); assertThat(sigterms.getBuckets().size(), equalTo(2)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java index 60af5cfad0004..de458c1e2bb76 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java @@ -58,7 +58,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -305,7 +305,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception new TermsAggregationBuilder("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(allResponse); + assertNoFailures(allResponse); StringTerms terms = allResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -322,7 +322,7 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -343,7 +343,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -368,7 +368,7 @@ public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -397,7 +397,7 @@ public void testMultiValuedScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -426,7 +426,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -473,7 +473,7 @@ public void testScriptSingleValue() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -504,7 +504,7 @@ public void testScriptSingleValueExplicitSingleValue() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -536,7 +536,7 @@ public void testScriptMultiValued() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -564,7 +564,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -655,7 +655,7 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms tags = response.getAggregations().get("tags"); assertThat(tags, notNullValue()); @@ -697,7 +697,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms tags = response.getAggregations().get("tags"); assertThat(tags, notNullValue()); @@ -760,7 +760,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms tags = response.getAggregations().get("tags"); assertThat(tags, notNullValue()); @@ -823,7 +823,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms tags = response.getAggregations().get("tags"); assertThat(tags, notNullValue()); @@ -972,7 +972,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1004,7 +1004,7 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1037,7 +1037,7 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1073,7 +1073,7 @@ public void testSingleValuedFieldOrderedByStatsAggAscWithTermsSubAgg() throws Ex ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1157,7 +1157,7 @@ private void assertMultiSortResponse(String[] expectedKeys, BucketOrder... order ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1188,7 +1188,7 @@ public void testIndexMetaField() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); @@ -1241,7 +1241,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1260,7 +1260,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1273,7 +1273,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1297,7 +1297,7 @@ public void testScriptWithValueType() throws Exception { try (XContentParser parser = createParser(JsonXContent.jsonXContent, source)) { SearchResponse response = client().prepareSearch("idx").setSource(new SearchSourceBuilder().parseXContent(parser, true)).get(); - assertSearchResponse(response); + assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); assertThat(terms.getName(), equalTo("terms")); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java index 51d0d5411d6f7..9a8eb1e7b45cd 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java @@ -38,7 +38,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -873,7 +873,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -892,7 +892,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -905,7 +905,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(extendedStats("foo").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java index 12bb521358642..2fef88b7610db 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java @@ -14,7 +14,7 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.geo.RandomGeoGenerator; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; @@ -30,7 +30,7 @@ public void testSingleValuedFieldNearDateLine() { .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoPoint geoValuesTopLeft = new GeoPoint(38, -179); GeoPoint geoValuesBottomRight = new GeoPoint(-24, 178); @@ -54,7 +54,7 @@ public void testSingleValuedFieldNearDateLineWrapLongitude() { .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME).wrapLongitude(true)) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoBounds geoBounds = response.getAggregations().get(aggName()); assertThat(geoBounds, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java index d1caf6d5cb80a..d1ae1589a5c2a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java @@ -18,7 +18,7 @@ import java.util.List; import static org.elasticsearch.search.aggregations.AggregationBuilders.geohashGrid; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -35,7 +35,7 @@ public void testSingleValueFieldAsSubAggToGeohashGrid() { .subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); GeoGrid grid = response.getAggregations().get("geoGrid"); assertThat(grid, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java index 0bdd75f73e743..59393da57666e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java @@ -39,7 +39,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; @@ -584,7 +584,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -604,7 +604,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -620,7 +620,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(percentileRanks("foo", new double[] { 50.0 }).method(PercentilesMethod.HDR).field("d")) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java index 58a22a32ae13b..19a1ce91fc4c8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java @@ -41,7 +41,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -554,7 +554,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -575,7 +575,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -591,7 +591,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(percentiles("foo").method(PercentilesMethod.HDR).field("d").percentiles(50.0)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java index b9eacda5d3750..844b77334f70a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java @@ -45,7 +45,7 @@ import static org.elasticsearch.search.aggregations.metrics.MedianAbsoluteDeviationAggregatorTests.IsCloseToRelative.closeToRelative; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -517,7 +517,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -536,7 +536,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -549,7 +549,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(randomBuilder().field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java index a144acf809b53..41dd285e900f4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java @@ -49,7 +49,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.scriptedMetric; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; @@ -363,7 +363,7 @@ public void testMap() { .setQuery(matchAllQuery()) .addAggregation(scriptedMetric("scripted").mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript)) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -411,7 +411,7 @@ public void testMapWithParams() { .reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -463,7 +463,7 @@ public void testInitMutatesParams() { .reduceScript(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op list aggregation", Collections.emptyMap())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -517,7 +517,7 @@ public void testMapCombineWithParams() { scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -580,7 +580,7 @@ public void testInitMapCombineWithParams() { .reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -648,7 +648,7 @@ public void testInitMapCombineReduceWithParams() { .reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -707,7 +707,7 @@ public void testInitMapCombineReduceGetProperty() throws Exception { ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(numDocs)); Global global = searchResponse.getAggregations().get("global"); @@ -765,7 +765,7 @@ public void testMapCombineReduceWithParams() { scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -815,7 +815,7 @@ public void testInitMapReduceWithParams() { .reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -859,7 +859,7 @@ public void testMapReduceWithParams() { scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -917,7 +917,7 @@ public void testInitMapCombineReduceWithParamsAndReduceParams() { .reduceScript(reduceScript) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -952,7 +952,7 @@ public void testInitMapCombineReduceWithParamsStored() { .reduceScript(new Script(ScriptType.STORED, null, "reduceScript_stored", Collections.emptyMap())) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("scripted"); @@ -1012,7 +1012,7 @@ public void testInitMapCombineReduceWithParamsAsSubAgg() { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(numDocs)); Aggregation aggregation = response.getAggregations().get("histo"); assertThat(aggregation, notNullValue()); @@ -1150,7 +1150,7 @@ public void testScriptCaching() throws Exception { scriptedMetric("foo").initScript(ndInitScript).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1166,7 +1166,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(scriptedMetric("foo").mapScript(ndMapScript).combineScript(combineScript).reduceScript(reduceScript)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1182,7 +1182,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(ndRandom).reduceScript(reduceScript)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1198,7 +1198,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(ndRandom)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1214,7 +1214,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript)) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java index 06149028e3fb5..1b1bba3fc2529 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java @@ -34,7 +34,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -261,7 +261,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -280,7 +280,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -293,7 +293,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(stats("foo").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java index 75e2870c58ff5..9ea866f889720 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java @@ -38,7 +38,7 @@ import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.VALUE_SCRIPT; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -238,7 +238,7 @@ public void testScriptCaching() throws Exception { sum("foo").field("d").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, RANDOM_SCRIPT, Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -256,7 +256,7 @@ public void testScriptCaching() throws Exception { sum("foo").field("d").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_SCRIPT, Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -269,7 +269,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(sum("foo").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -286,7 +286,7 @@ public void testFieldAlias() { .addAggregation(sum("sum").field("route_length_miles")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Sum sum = response.getAggregations().get("sum"); assertThat(sum, IsNull.notNullValue()); @@ -299,7 +299,7 @@ public void testFieldAliasInSubAggregation() { .addAggregation(terms("terms").field("transit_mode").subAggregation(sum("sum").field("route_length_miles"))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java index 6909bd719f6dd..8a1ee38630be9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java @@ -39,7 +39,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; @@ -495,7 +495,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -514,7 +514,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -530,7 +530,7 @@ public void testScriptCaching() throws Exception { .setSize(0) .addAggregation(percentileRanks("foo", new double[] { 50.0 }).field("d")) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java index 3fc5bf9863256..5a2f246735a32 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java @@ -41,7 +41,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; @@ -468,7 +468,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -488,7 +488,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -501,7 +501,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d").percentiles(50.0)).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index 5829f75d45edb..685e269516b0f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -65,7 +65,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xcontent.XContentFactory.smileBuilder; import static org.elasticsearch.xcontent.XContentFactory.yamlBuilder; @@ -320,7 +319,7 @@ public void testBasics() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -354,7 +353,7 @@ public void testIssue11119() throws Exception { .addAggregation(terms("terms").executionHint(randomExecutionHint()).field("group").subAggregation(topHits("hits"))) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(8L)); assertThat(response.getHits().getHits().length, equalTo(0)); @@ -388,7 +387,7 @@ public void testIssue11119() throws Exception { .addAggregation(terms("terms").executionHint(randomExecutionHint()).field("group")) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(8L)); assertThat(response.getHits().getHits().length, equalTo(0)); @@ -409,7 +408,7 @@ public void testBreadthFirstWithScoreNeeded() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -442,7 +441,7 @@ public void testBreadthFirstWithAggOrderAndScoreNeeded() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -469,7 +468,7 @@ public void testBasicsGetProperty() throws Exception { .addAggregation(global("global").subAggregation(topHits("hits"))) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Global global = searchResponse.getAggregations().get("global"); assertThat(global, notNullValue()); @@ -494,7 +493,7 @@ public void testPagination() throws Exception { .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)).from(from).size(size)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchResponse control = client().prepareSearch("idx") .setFrom(from) @@ -502,7 +501,7 @@ public void testPagination() throws Exception { .setPostFilter(QueryBuilders.termQuery(TERMS_AGGS_FIELD, "val0")) .addSort(SORT_FIELD, SortOrder.DESC) .get(); - assertSearchResponse(control); + assertNoFailures(control); SearchHits controlHits = control.getHits(); Terms terms = response.getAggregations().get("terms"); @@ -541,7 +540,7 @@ public void testSortByBucket() throws Exception { .subAggregation(max("max_sort").field(SORT_FIELD)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -578,7 +577,7 @@ public void testFieldCollapsing() throws Exception { .subAggregation(max("max_score").field("value")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -632,7 +631,7 @@ public void testFetchFeatures() { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -699,7 +698,7 @@ public void testInvalidSortField() throws Exception { public void testEmptyIndex() throws Exception { SearchResponse response = client().prepareSearch("empty").addAggregation(topHits("hits")).get(); - assertSearchResponse(response); + assertNoFailures(response); TopHits hits = response.getAggregations().get("hits"); assertThat(hits, notNullValue()); @@ -718,7 +717,7 @@ public void testTrackScores() throws Exception { .subAggregation(topHits("hits").trackScores(trackScore).size(1).sort("_index", SortOrder.DESC)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1077,7 +1076,7 @@ public void testNoStoredFields() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -1139,7 +1138,7 @@ public void testScriptCaching() throws Exception { ) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1162,7 +1161,7 @@ public void testScriptCaching() throws Exception { ) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1180,7 +1179,7 @@ public void testScriptCaching() throws Exception { topHits("foo").scriptField("bar", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "5", Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1203,7 +1202,7 @@ public void testScriptCaching() throws Exception { ) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -1216,7 +1215,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(topHits("foo")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java index dda06b9f422ad..62f28211c674a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java @@ -37,7 +37,7 @@ import static org.elasticsearch.search.aggregations.metrics.MetricAggScriptPlugin.VALUE_FIELD_SCRIPT; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -241,7 +241,7 @@ public void testScriptCaching() throws Exception { count("foo").field("d").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, RANDOM_SCRIPT, Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -260,7 +260,7 @@ public void testScriptCaching() throws Exception { .script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_FIELD_SCRIPT, Collections.emptyMap())) ) .get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), @@ -273,7 +273,7 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(count("foo").field("d")).get(); - assertSearchResponse(r); + assertNoFailures(r); assertThat( indicesAdmin().prepareStats("cache_test_idx").setRequestCache(true).get().getTotal().getRequestCache().getHitCount(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java index cc4cfabfffa54..4947f9a3a43e0 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java @@ -37,7 +37,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -134,7 +134,7 @@ public void testDocCountTopLevel() { .addAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">_count")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get(histoName); assertThat(histo, notNullValue()); @@ -170,7 +170,7 @@ public void testDocCountAsSubAgg() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -208,7 +208,7 @@ public void testMetricTopLevel() { .addAggregation(BucketMetricsPipelineAgg("pipeline_agg", termsName + ">sum")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -250,7 +250,7 @@ public void testMetricAsSubAgg() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -308,7 +308,7 @@ public void testMetricAsSubAggWithInsertZeros() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -355,7 +355,7 @@ public void testNoBuckets() { .addAggregation(BucketMetricsPipelineAgg("pipeline_agg", termsName + ">sum")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -385,7 +385,7 @@ public void testNested() { .addAggregation(BucketMetricsPipelineAgg("nested_terms_bucket", termsName + ">nested_histo_bucket." + nestedMetric())) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java index 5c4caf5f242a6..56101a7c7a4ce 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java @@ -42,7 +42,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.bucketScript; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -174,7 +174,7 @@ public void testInlineScript() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -224,7 +224,7 @@ public void testInlineScript2() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -274,7 +274,7 @@ public void testInlineScriptWithDateRange() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("range"); assertThat(range, notNullValue()); @@ -320,7 +320,7 @@ public void testInlineScriptSingleVariable() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -366,7 +366,7 @@ public void testInlineScriptNamedVars() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -413,7 +413,7 @@ public void testInlineScriptWithParams() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -463,7 +463,7 @@ public void testInlineScriptInsertZeros() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -509,7 +509,7 @@ public void testInlineScriptReturnNull() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -552,7 +552,7 @@ public void testStoredScript() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -602,7 +602,7 @@ public void testUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram deriv = response.getAggregations().get("histo"); assertThat(deriv, notNullValue()); @@ -630,7 +630,7 @@ public void testPartiallyUnmapped() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -683,7 +683,7 @@ public void testSingleBucketPathAgg() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -732,7 +732,7 @@ public void testArrayBucketPathAgg() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -791,7 +791,7 @@ public void testObjectBucketPathAgg() throws Exception { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -885,7 +885,7 @@ public void testInlineScriptWithMultiValueAggregation() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); @@ -940,7 +940,7 @@ public void testInlineScriptWithMultiValueAggregationDifferentBucketsPaths() { ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java index 2950276e5c552..87731e40b9a90 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java @@ -26,7 +26,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.extendedStatsBucket; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.IsNull.notNullValue; @@ -104,7 +104,7 @@ public void testGappyIndexWithSigma() { .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(1L)) .addAggregation(extendedStatsBucket("extended_stats_bucket", "histo>_count").sigma(sigma)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo, notNullValue()); assertThat(histo.getName(), equalTo("histo")); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java index 53ebbfc0bb016..ab9987f7d1255 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java @@ -28,7 +28,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.percentilesBucket; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.core.IsNull.notNullValue; @@ -73,7 +73,7 @@ public void testMetricTopLevelDefaultPercents() throws Exception { .addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -110,7 +110,7 @@ public void testWrongPercents() throws Exception { .addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum").setPercents(PERCENTS)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); @@ -208,7 +208,7 @@ public void testNestedWithDecimal() throws Exception { .addAggregation(percentilesBucket("percentile_terms_bucket", termsName + ">percentile_histo_bucket[99.9]").setPercents(percent)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get(termsName); assertThat(terms, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java index dba26d0560a14..6279450e99f49 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java @@ -32,8 +32,7 @@ import java.util.Collection; import java.util.concurrent.ExecutionException; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; public class SearchWithRandomIOExceptionsIT extends ESIntegTestCase { @@ -193,9 +192,7 @@ public void testRandomDirectoryIOExceptions() throws IOException, InterruptedExc indicesAdmin().prepareClose("test").execute().get(); indicesAdmin().prepareOpen("test").execute().get(); ensureGreen(); - SearchResponse searchResponse = client().prepareSearch().setQuery(QueryBuilders.matchQuery("test", "init")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, numInitialDocs); + assertHitCountAndNoFailures(client().prepareSearch().setQuery(QueryBuilders.matchQuery("test", "init")), numInitialDocs); } } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java index a5dad457005cb..fff6386b643f6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java @@ -39,7 +39,7 @@ import java.util.Objects; import static java.util.Collections.singletonList; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.CoreMatchers.equalTo; @@ -75,7 +75,7 @@ public void testPlugin() throws Exception { SearchResponse response = client().prepareSearch() .setSource(new SearchSourceBuilder().ext(Collections.singletonList(new TermVectorsFetchBuilder("test")))) .get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat( ((Map) response.getHits().getAt(0).field("term_vectors_fetch").getValues().get(0)).get("i"), equalTo(2) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index 183f925ced9d3..2a3b8aeede733 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -50,6 +50,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; @@ -952,9 +953,7 @@ public void testUseMaxDocInsteadOfSize() throws Exception { QueryBuilder query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1) ); - SearchResponse response = client().prepareSearch("index2").setQuery(query).get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures(client().prepareSearch("index2").setQuery(query), 1); } public void testTooHighResultWindow() throws Exception { @@ -966,15 +965,15 @@ public void testTooHighResultWindow() throws Exception { ) .setRefreshPolicy(IMMEDIATE) .get(); - SearchResponse response = client().prepareSearch("index2") - .setQuery( - nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFrom(50).setSize(10).setName("_name") - ) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures( + client().prepareSearch("index2") + .setQuery( + nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFrom(50).setSize(10).setName("_name") + ) + ), + 1 + ); Exception e = expectThrows( SearchPhaseExecutionException.class, diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 9da65214599e1..642dfc2f6cf54 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -93,7 +93,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNotHighlighted; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; @@ -3349,7 +3348,7 @@ public void testHighlightQueryRewriteDatesWithNow() throws Exception { ) .get(); - assertSearchResponse(r1); + assertNoFailures(r1); assertThat(r1.getHits().getTotalHits().value, equalTo(1L)); assertHighlight(r1, 0, "field", 0, 1, equalTo("hello world")); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java index aaac1d8964877..41917a672206a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java @@ -63,7 +63,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -1033,7 +1032,7 @@ public void testScriptFields() throws Exception { ); } SearchResponse resp = req.get(); - assertSearchResponse(resp); + assertNoFailures(resp); for (SearchHit hit : resp.getHits().getHits()) { final int id = Integer.parseInt(hit.getId()); Map fields = hit.getFields(); @@ -1271,7 +1270,7 @@ public void testLoadMetadata() throws Exception { ); SearchResponse response = client().prepareSearch("test").addStoredField("field1").get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 1); Map fields = response.getHits().getAt(0).getMetadataFields(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/ExplainableScriptIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/ExplainableScriptIT.java index 08697bc1470fb..e9ce09f7455a2 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/ExplainableScriptIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/ExplainableScriptIT.java @@ -32,7 +32,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; -import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; import java.io.IOException; import java.util.ArrayList; @@ -47,6 +46,7 @@ import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -137,7 +137,7 @@ public void testExplainScript() throws InterruptedException, IOException { ) ).actionGet(); - ElasticsearchAssertions.assertNoFailures(response); + assertNoFailures(response); SearchHits hits = response.getHits(); assertThat(hits.getTotalHits().value, equalTo(20L)); int idCounter = 19; diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java index 88e205ce3747c..e32abeb481a2a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java @@ -40,7 +40,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -94,7 +94,7 @@ public void testScriptScoresNested() throws IOException { ) ) ).actionGet(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getAt(0).getScore(), equalTo(1.0f)); } @@ -110,7 +110,7 @@ public void testScriptScoresWithAgg() throws IOException { searchSource().query(functionScoreQuery(scriptFunction(script))).aggregation(terms("score_agg").script(script)) ) ).actionGet(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getAt(0).getScore(), equalTo(1.0f)); assertThat(((Terms) response.getAggregations().asMap().get("score_agg")).getBuckets().get(0).getKeyAsString(), equalTo("1.0")); assertThat(((Terms) response.getAggregations().asMap().get("score_agg")).getBuckets().get(0).getDocCount(), is(1L)); @@ -201,7 +201,7 @@ public void testMinScoreFunctionScoreManyDocsAndRandomMinScore() throws IOExcept } protected void assertMinScoreSearchResponses(int numDocs, SearchResponse searchResponse, int numMatchingDocs) { - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat((int) searchResponse.getHits().getTotalHits().value, is(numMatchingDocs)); int pos = 0; for (int hitId = numDocs - 1; (numDocs - hitId) < searchResponse.getHits().getTotalHits().value; hitId--) { @@ -219,7 +219,7 @@ public void testWithEmptyFunctions() throws IOException, ExecutionException, Int SearchResponse termQuery = client().search( new SearchRequest(new String[] {}).source(searchSource().explain(true).query(termQuery("text", "text"))) ).get(); - assertSearchResponse(termQuery); + assertNoFailures(termQuery); assertThat(termQuery.getHits().getTotalHits().value, equalTo(1L)); float termQueryScore = termQuery.getHits().getAt(0).getScore(); @@ -234,7 +234,7 @@ protected void testMinScoreApplied(CombineFunction boostMode, float expectedScor searchSource().explain(true).query(functionScoreQuery(termQuery("text", "text")).boostMode(boostMode).setMinScore(0.1f)) ) ).get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(1L)); assertThat(response.getHits().getAt(0).getScore(), equalTo(expectedScore)); @@ -244,7 +244,7 @@ protected void testMinScoreApplied(CombineFunction boostMode, float expectedScor ) ).get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java index 109d391fd17fc..ecb4be2685ab0 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java @@ -49,7 +49,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFourthHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSecondHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertThirdHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; @@ -85,7 +84,7 @@ public void testEnforceWindowSize() { ) .setSize(randomIntBetween(2, 10)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertFirstHit(searchResponse, hasScore(100.f)); int numDocsWith100AsAScore = 0; for (int i = 0; i < searchResponse.getHits().getHits().length; i++) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPointScriptDocValuesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPointScriptDocValuesIT.java index 55994458388a4..90b942c576a82 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPointScriptDocValuesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPointScriptDocValuesIT.java @@ -36,7 +36,7 @@ import java.util.function.Function; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -164,7 +164,7 @@ public void testRandomPoint() throws Exception { .addScriptField("label_lat", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lat", Collections.emptyMap())) .addScriptField("label_lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lon", Collections.emptyMap())) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); final double qLat = GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat)); final double qLon = GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon)); @@ -208,7 +208,7 @@ public void testRandomMultiPoint() throws Exception { .addScriptField("label_lat", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lat", Collections.emptyMap())) .addScriptField("label_lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lon", Collections.emptyMap())) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); for (int i = 0; i < size; i++) { lats[i] = GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lats[i])); @@ -247,7 +247,7 @@ public void testNullPoint() throws Exception { .addScriptField("height", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "height", Collections.emptyMap())) .addScriptField("width", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "width", Collections.emptyMap())) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Map fields = searchResponse.getHits().getHits()[0].getFields(); assertThat(fields.get("lat").getValue(), equalTo(Double.NaN)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java index 5a8b506b99296..659f6af68d612 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java @@ -43,7 +43,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertOrderedSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertRequestBuilderThrows; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -609,7 +608,7 @@ public void testMoreLikeThisMultiValueFields() throws Exception { .maxQueryTerms(max_query_terms) .minimumShouldMatch("0%"); SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, max_query_terms); } } @@ -642,7 +641,7 @@ public void testMinimumShouldMatch() throws ExecutionException, InterruptedExcep .minimumShouldMatch(minimumShouldMatch); logger.info("Testing with minimum_should_match = {}", minimumShouldMatch); SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); if (minimumShouldMatch.equals("0%")) { assertHitCount(response, 10); } else { @@ -672,7 +671,7 @@ public void testMoreLikeThisArtificialDocs() throws Exception { .maxQueryTerms(100) .minimumShouldMatch("100%"); // strict all terms must match! SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 1); } @@ -698,14 +697,14 @@ public void testMoreLikeThisMalformedArtificialDocs() throws Exception { .minDocFreq(0) .minimumShouldMatch("0%"); SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 0); logger.info("Checking with an empty document ..."); XContentBuilder emptyDoc = jsonBuilder().startObject().endObject(); mltQuery = moreLikeThisQuery(null, new Item[] { new Item("test", emptyDoc) }).minTermFreq(0).minDocFreq(0).minimumShouldMatch("0%"); response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 0); logger.info("Checking the document matches otherwise ..."); @@ -717,7 +716,7 @@ public void testMoreLikeThisMalformedArtificialDocs() throws Exception { .minDocFreq(0) .minimumShouldMatch("100%"); // strict all terms must match but date is ignored response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 1); } @@ -746,7 +745,7 @@ public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedExcep .maxQueryTerms(100) .minimumShouldMatch("0%"); SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, numFields); logger.info("Now check like this doc, but ignore one doc in the index, then two and so on..."); @@ -761,7 +760,7 @@ public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedExcep .minimumShouldMatch("0%"); response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, numFields - (i + 1)); } } @@ -785,7 +784,7 @@ public void testSelectFields() throws IOException, ExecutionException, Interrupt .include(true) .minimumShouldMatch("1%"); SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 2); mltQuery = moreLikeThisQuery(new String[] { "text" }, null, new Item[] { new Item("test", "1") }).minTermFreq(0) @@ -793,7 +792,7 @@ public void testSelectFields() throws IOException, ExecutionException, Interrupt .include(true) .minimumShouldMatch("1%"); response = client().prepareSearch("test").setQuery(mltQuery).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 1); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java index d89f39a1452b6..e981c1fd5c0c0 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java @@ -42,7 +42,7 @@ import static org.elasticsearch.test.MapMatcher.assertMap; import static org.elasticsearch.test.MapMatcher.matchesMap; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -127,7 +127,7 @@ public void testSimpleProfile() { .setProfile(true) .addAggregation(histogram("histo").field(NUMBER_FIELD).interval(1L)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); @@ -171,7 +171,7 @@ public void testMultiLevelProfile() { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); @@ -258,7 +258,7 @@ public void testMultiLevelProfileBreadthFirst() { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); @@ -329,7 +329,7 @@ public void testDiversifiedAggProfile() { .subAggregation(max("max").field(NUMBER_FIELD)) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); @@ -398,7 +398,7 @@ public void testComplexProfile() { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); @@ -615,7 +615,7 @@ public void testNoProfile() { ) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(0)); @@ -652,7 +652,7 @@ public void testFilterByFilter() throws InterruptedException, IOException { .subAggregation(new MaxAggregationBuilder("m").field("date")) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("dateidx").numPrimaries)); @@ -724,7 +724,7 @@ public void testDateHistogramFilterByFilterDisabled() throws InterruptedExceptio .setProfile(true) .addAggregation(new DateHistogramAggregationBuilder("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Map profileResults = response.getProfileResults(); assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(getNumShards("date_filter_by_filter_disabled").numPrimaries)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java index dd3e160054f6b..45f8b47efd870 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java @@ -31,7 +31,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; public class ExistsIT extends ESIntegTestCase { @@ -115,14 +114,14 @@ public void testExists() throws Exception { final long numDocs = sources.length; SearchResponse allDocs = client().prepareSearch("idx").setSize(sources.length).get(); - assertSearchResponse(allDocs); + assertNoFailures(allDocs); assertHitCount(allDocs, numDocs); for (Map.Entry entry : expected.entrySet()) { final String fieldName = entry.getKey(); final int count = entry.getValue(); // exists SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.existsQuery(fieldName)).get(); - assertSearchResponse(resp); + assertNoFailures(resp); try { assertEquals( String.format( @@ -201,7 +200,7 @@ public void testFieldAlias() throws Exception { int expectedCount = entry.getValue(); SearchResponse response = client().prepareSearch("idx").setQuery(QueryBuilders.existsQuery(fieldName)).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, expectedCount); } } @@ -233,7 +232,7 @@ public void testFieldAliasWithNoDocValues() throws Exception { indexRandom(true, false, indexRequests); SearchResponse response = client().prepareSearch("idx").setQuery(QueryBuilders.existsQuery("foo-alias")).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 2); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java index be265cb3985e6..c35bfee6e73d7 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -104,10 +104,10 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFirstHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSecondHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasScore; @@ -452,9 +452,10 @@ public void testTermIndexQuery() throws Exception { ); } { - SearchResponse request = client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexNames))).get(); - SearchResponse searchResponse = assertSearchResponse(request); - assertHitCount(searchResponse, indexNames.length); + assertHitCountAndNoFailures( + client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexNames))), + indexNames.length + ); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java index 3b459f8d57934..6b6192ddcce96 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java @@ -50,7 +50,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertRequestBuilderThrows; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -517,7 +516,7 @@ public void testStringSortMissingAscTerminates() throws Exception { assertSearchHits(response, "1"); response = client().prepareSearchScroll(response.getScrollId()).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 1); assertNoSearchHits(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java index d71345a42dd2c..61bdeb0f6ade3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java @@ -46,6 +46,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -190,29 +191,25 @@ public void testSimpleDateRange() throws Exception { client().prepareIndex("test").setId("3").setSource("field", "1967-01-01T00:00").get(); ensureGreen(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d").lte("2010-01-04||+2d/d")) - .get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 2L); + assertHitCountAndNoFailures( + client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d").lte("2010-01-04||+2d/d")), + 2L + ); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lte("2010-01-06T02:00")) - .get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 2L); + assertHitCountAndNoFailures( + client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lte("2010-01-06T02:00")), + 2L + ); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lt("2010-01-06T02:00")) - .get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); + assertHitCountAndNoFailures( + client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lt("2010-01-06T02:00")), + 1L + ); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("field").gt("2010-01-05T02:00").lt("2010-01-06T02:00")) - .get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 0L); + assertHitCountAndNoFailures( + client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("2010-01-05T02:00").lt("2010-01-06T02:00")), + 0L + ); assertHitCount( client().prepareSearch("test").setQuery(QueryBuilders.queryStringQuery("field:[2010-01-03||+2d TO 2010-01-04||+2d/d]")), @@ -220,12 +217,10 @@ public void testSimpleDateRange() throws Exception { ); // a string value of "1000" should be parsed as the year 1000 and return all three docs - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("1000")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 3L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("1000")), 3L); // a numeric value of 1000 should be parsed as 1000 millis since epoch and return only docs after 1970 - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(1000)).get(); + SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(1000)).get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, 2L); String[] expectedIds = new String[] { "1", "2" }; @@ -245,37 +240,14 @@ public void testRangeQueryKeyword() throws Exception { ensureGreen(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lte("B")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 2L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("A").lte("B")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lt("B")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 1L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte(null).lt("C")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 3L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("B").lt(null)).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 2L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(null).lt(null)).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 4L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("").lt(null)).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 4L); - - searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("").lt(null)).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 3L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lte("B")), 2L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("A").lte("B")), 1L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lt("B")), 1L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte(null).lt("C")), 3L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("B").lt(null)), 2L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(null).lt(null)), 4L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("").lt(null)), 4L); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("").lt(null)), 3L); } public void testSimpleTerminateAfterCount() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java index 3b6f29fd1ec90..2adadc5e34fb6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java @@ -63,7 +63,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFirstHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSecondHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; @@ -126,7 +125,7 @@ public void testIssue8226() { .setSize(10) .get(); logClusterState(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); for (int j = 1; j < searchResponse.getHits().getHits().length; j++) { Number current = (Number) searchResponse.getHits().getHits()[j].getSourceAsMap().get("entry"); @@ -140,7 +139,7 @@ public void testIssue8226() { .setSize(10) .get(); logClusterState(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); for (int j = 1; j < searchResponse.getHits().getHits().length; j++) { Number current = (Number) searchResponse.getHits().getHits()[j].getSourceAsMap().get("entry"); @@ -184,7 +183,7 @@ public void testIssue6614() throws ExecutionException, InterruptedException { .addSort(new FieldSortBuilder("timeUpdated").order(SortOrder.ASC).unmappedType("date")) .setSize(docs) .get(); - assertSearchResponse(allDocsResponse); + assertNoFailures(allDocsResponse); final int numiters = randomIntBetween(1, 20); for (int i = 0; i < numiters; i++) { @@ -197,7 +196,7 @@ public void testIssue6614() throws ExecutionException, InterruptedException { .addSort(new FieldSortBuilder("timeUpdated").order(SortOrder.ASC).unmappedType("date")) .setSize(scaledRandomIntBetween(1, docs)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { assertThat( searchResponse.toString() + "\n vs. \n" + allDocsResponse.toString(), @@ -1765,13 +1764,13 @@ public void testCustomFormat() throws Exception { ); SearchResponse response = client().prepareSearch("test").addSort(SortBuilders.fieldSort("ip")).get(); - assertSearchResponse(response); + assertNoFailures(response); assertEquals(2, response.getHits().getTotalHits().value); assertArrayEquals(new String[] { "192.168.1.7" }, response.getHits().getAt(0).getSortValues()); assertArrayEquals(new String[] { "2001:db8::ff00:42:8329" }, response.getHits().getAt(1).getSortValues()); response = client().prepareSearch("test").addSort(SortBuilders.fieldSort("ip")).searchAfter(new Object[] { "192.168.1.7" }).get(); - assertSearchResponse(response); + assertNoFailures(response); assertEquals(2, response.getHits().getTotalHits().value); assertEquals(1, response.getHits().getHits().length); assertArrayEquals(new String[] { "2001:db8::ff00:42:8329" }, response.getHits().getAt(0).getSortValues()); @@ -2080,7 +2079,7 @@ public void testLongSortOptimizationCorrectResults() { .addSort(new FieldSortBuilder("long_field").order(SortOrder.DESC)) .setSize(10) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); long previousLong = Long.MAX_VALUE; for (int i = 0; i < searchResponse.getHits().getHits().length; i++) { // check the correct sort order @@ -2092,7 +2091,7 @@ public void testLongSortOptimizationCorrectResults() { // *** 2. sort ASC on long_field searchResponse = client().prepareSearch().addSort(new FieldSortBuilder("long_field").order(SortOrder.ASC)).setSize(10).get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); previousLong = Long.MIN_VALUE; for (int i = 0; i < searchResponse.getHits().getHits().length; i++) { // check the correct sort order @@ -2120,7 +2119,7 @@ public void testSortMixedFieldTypes() { .addSort(new FieldSortBuilder("foo")) .setSize(10) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); } String errMsg = "Can't sort on field [foo]; the field has incompatible sort types"; diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java index fa996a6bd0e89..28e3cc3cf1b87 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java @@ -39,7 +39,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAllSuccessful; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -193,7 +193,7 @@ public void testOpenContexts() { .setSize(size) .setScroll(TimeValue.timeValueMinutes(2)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); // refresh the stats now that scroll contexts are opened indicesStats = indicesAdmin().prepareStats(index).get(); diff --git a/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java b/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java index 5c35733feedef..b1b04550d22f2 100644 --- a/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java +++ b/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java @@ -18,7 +18,7 @@ import org.elasticsearch.test.ESSingleNodeTestCase; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; @@ -48,7 +48,7 @@ public void testQueryRewriteOnRefresh() throws Exception { .setSize(0) .setQuery(QueryBuilders.rangeQuery("s").gte("a").lte("g")) .get(); - assertSearchResponse(r1); + assertNoFailures(r1); assertThat(r1.getHits().getTotalHits().value, equalTo(3L)); assertRequestCacheStats(0, 1); @@ -58,7 +58,7 @@ public void testQueryRewriteOnRefresh() throws Exception { .setSize(0) .setQuery(QueryBuilders.rangeQuery("s").gte("a").lte("g")) .get(); - assertSearchResponse(r2); + assertNoFailures(r2); assertThat(r2.getHits().getTotalHits().value, equalTo(3L)); assertRequestCacheStats(1, 1); @@ -73,7 +73,7 @@ public void testQueryRewriteOnRefresh() throws Exception { .setSize(0) .setQuery(QueryBuilders.rangeQuery("s").gte("a").lte("g")) .get(); - assertSearchResponse(r3); + assertNoFailures(r3); assertThat(r3.getHits().getTotalHits().value, equalTo(5L)); assertRequestCacheStats(1, 2); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/flattened/FlattenedFieldSearchTests.java b/server/src/test/java/org/elasticsearch/index/mapper/flattened/FlattenedFieldSearchTests.java index 709223d19788c..20d307a0d4cb1 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/flattened/FlattenedFieldSearchTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/flattened/FlattenedFieldSearchTests.java @@ -40,9 +40,9 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.cardinality; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertOrderedSearchHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -131,17 +131,12 @@ public void testQueryStringQuery() throws Exception { ) .get(); - SearchResponse response = client().prepareSearch("test").setQuery(queryStringQuery("flattened.field1:value")).get(); - assertSearchResponse(response); - assertHitCount(response, 1); - - response = client().prepareSearch("test").setQuery(queryStringQuery("flattened.field1:value AND flattened:2.718")).get(); - assertSearchResponse(response); - assertHitCount(response, 1); - - response = client().prepareSearch("test").setQuery(queryStringQuery("2.718").field("flattened.field2")).get(); - assertSearchResponse(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(queryStringQuery("flattened.field1:value")), 1); + assertHitCountAndNoFailures( + client().prepareSearch("test").setQuery(queryStringQuery("flattened.field1:value AND flattened:2.718")), + 1 + ); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(queryStringQuery("2.718").field("flattened.field2")), 1); } public void testSimpleQueryStringQuery() throws Exception { @@ -159,17 +154,9 @@ public void testSimpleQueryStringQuery() throws Exception { ) .get(); - SearchResponse response = client().prepareSearch("test").setQuery(simpleQueryStringQuery("value").field("flattened.field1")).get(); - assertSearchResponse(response); - assertHitCount(response, 1); - - response = client().prepareSearch("test").setQuery(simpleQueryStringQuery("+value +2.718").field("flattened")).get(); - assertSearchResponse(response); - assertHitCount(response, 1); - - response = client().prepareSearch("test").setQuery(simpleQueryStringQuery("+value +3.141").field("flattened")).get(); - assertSearchResponse(response); - assertHitCount(response, 0); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(simpleQueryStringQuery("value").field("flattened.field1")), 1); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(simpleQueryStringQuery("+value +2.718").field("flattened")), 1); + assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(simpleQueryStringQuery("+value +3.141").field("flattened")), 0); } public void testExists() throws Exception { @@ -226,7 +213,7 @@ public void testCardinalityAggregation() throws IOException { .addAggregation(cardinality("cardinality").precisionThreshold(precisionThreshold).field("flattened")) .get(); - assertSearchResponse(response); + assertNoFailures(response); Cardinality count = response.getAggregations().get("cardinality"); assertCardinality(count, numDocs, precisionThreshold); @@ -234,7 +221,7 @@ public void testCardinalityAggregation() throws IOException { SearchResponse firstResponse = client().prepareSearch("test") .addAggregation(cardinality("cardinality").precisionThreshold(precisionThreshold).field("flattened.first")) .get(); - assertSearchResponse(firstResponse); + assertNoFailures(firstResponse); Cardinality firstCount = firstResponse.getAggregations().get("cardinality"); assertCardinality(firstCount, numDocs, precisionThreshold); @@ -242,7 +229,7 @@ public void testCardinalityAggregation() throws IOException { SearchResponse secondResponse = client().prepareSearch("test") .addAggregation(cardinality("cardinality").precisionThreshold(precisionThreshold).field("flattened.second")) .get(); - assertSearchResponse(secondResponse); + assertNoFailures(secondResponse); Cardinality secondCount = secondResponse.getAggregations().get("cardinality"); assertCardinality(secondCount, (numDocs + 1) / 2, precisionThreshold); @@ -281,7 +268,7 @@ public void testTermsAggregation() throws IOException { // Aggregate on the root 'labels' field. TermsAggregationBuilder builder = createTermsAgg("labels"); SearchResponse response = client().prepareSearch("test").addAggregation(builder).get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -299,7 +286,7 @@ public void testTermsAggregation() throws IOException { // Aggregate on the 'priority' subfield. TermsAggregationBuilder priorityAgg = createTermsAgg("labels.priority"); SearchResponse priorityResponse = client().prepareSearch("test").addAggregation(priorityAgg).get(); - assertSearchResponse(priorityResponse); + assertNoFailures(priorityResponse); Terms priorityTerms = priorityResponse.getAggregations().get("terms"); assertThat(priorityTerms, notNullValue()); @@ -313,7 +300,7 @@ public void testTermsAggregation() throws IOException { // Aggregate on the 'release' subfield. TermsAggregationBuilder releaseAgg = createTermsAgg("labels.release"); SearchResponse releaseResponse = client().prepareSearch("test").addAggregation(releaseAgg).get(); - assertSearchResponse(releaseResponse); + assertNoFailures(releaseResponse); Terms releaseTerms = releaseResponse.getAggregations().get("terms"); assertThat(releaseTerms, notNullValue()); @@ -328,7 +315,7 @@ public void testTermsAggregation() throws IOException { // Aggregate on the 'priority' subfield with a min_doc_count of 0. TermsAggregationBuilder minDocCountAgg = createTermsAgg("labels.priority").minDocCount(0); SearchResponse minDocCountResponse = client().prepareSearch("test").addAggregation(minDocCountAgg).get(); - assertSearchResponse(minDocCountResponse); + assertNoFailures(minDocCountResponse); Terms minDocCountTerms = minDocCountResponse.getAggregations().get("terms"); assertThat(minDocCountTerms, notNullValue()); @@ -359,7 +346,7 @@ public void testLoadDocValuesFields() throws Exception { .get(); SearchResponse response = client().prepareSearch("test").addDocValueField("flattened").addDocValueField("flattened.key").get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 1); Map fields = response.getHits().getAt(0).getFields(); @@ -409,17 +396,17 @@ public void testFieldSort() throws Exception { .get(); SearchResponse response = client().prepareSearch("test").addSort("flattened", SortOrder.DESC).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 3); assertOrderedSearchHits(response, "3", "1", "2"); response = client().prepareSearch("test").addSort("flattened.key", SortOrder.DESC).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 3); assertOrderedSearchHits(response, "2", "1", "3"); response = client().prepareSearch("test").addSort(new FieldSortBuilder("flattened.key").order(SortOrder.DESC).missing("Z")).get(); - assertSearchResponse(response); + assertNoFailures(response); assertHitCount(response, 3); assertOrderedSearchHits(response, "3", "2", "1"); } diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java index 80eb0c4ac6bbb..8b7731c6b3505 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java @@ -18,7 +18,7 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.is; @@ -79,11 +79,11 @@ protected void indexData() throws Exception { indexRandom(true, docs); SearchResponse resp = client().prepareSearch("idx").setRouting(routing1).setQuery(matchAllQuery()).get(); - assertSearchResponse(resp); + assertNoFailures(resp); long totalOnOne = resp.getHits().getTotalHits().value; assertThat(totalOnOne, is(15L)); resp = client().prepareSearch("idx").setRouting(routing2).setQuery(matchAllQuery()).get(); - assertSearchResponse(resp); + assertNoFailures(resp); long totalOnTwo = resp.getHits().getTotalHits().value; assertThat(totalOnTwo, is(12L)); } diff --git a/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java b/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java index 1ec8a48b5d168..9ef137cefeeb3 100644 --- a/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java +++ b/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java @@ -28,7 +28,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.ESIntegTestCase.client; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; public class SharedSignificantTermsTestMethods { @@ -51,7 +51,7 @@ private static void checkSignificantTermsAggregationCorrect(ESIntegTestCase test .addAggregation(terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD))) .execute() .actionGet(); - assertSearchResponse(response); + assertNoFailures(response); StringTerms classes = response.getAggregations().get("class"); Assert.assertThat(classes.getBuckets().size(), equalTo(2)); for (Terms.Bucket classBucket : classes.getBuckets()) { diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java index c2c2f9dbef7f5..9bfe15cb7f0b9 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java @@ -15,7 +15,7 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.elasticsearch.test.ESIntegTestCase; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; public abstract class AbstractTermsTestCase extends ESIntegTestCase { @@ -41,7 +41,7 @@ public void testOtherDocCount(String... fieldNames) { .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(allTerms); + assertNoFailures(allTerms); Terms terms = allTerms.getAggregations().get("terms"); assertEquals(0, terms.getSumOfOtherDocCounts()); // size is 0 @@ -59,7 +59,7 @@ public void testOtherDocCount(String... fieldNames) { .collectMode(randomFrom(SubAggCollectionMode.values())) ) .get(); - assertSearchResponse(resp); + assertNoFailures(resp); terms = resp.getAggregations().get("terms"); assertEquals(Math.min(size, totalNumTerms), terms.getBuckets().size()); assertEquals(sumOfDocCounts, sumOfDocCounts(terms)); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java index 1af8eb8dab82b..571e269bf532d 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java @@ -30,7 +30,7 @@ import java.util.Map; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; @@ -234,7 +234,7 @@ public void setupSuiteScopeCluster() throws Exception { .addSort(SortBuilders.fieldSort(NUMBER_FIELD_NAME).order(SortOrder.ASC)) .setSize(5000) .get(); - assertSearchResponse(response); + assertNoFailures(response); long totalHits = response.getHits().getTotalHits().value; XContentBuilder builder = XContentFactory.jsonBuilder(); ChunkedToXContent.wrapAsToXContent(response).toXContent(builder, ToXContent.EMPTY_PARAMS); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java index f211216226285..5af696fb644d3 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java @@ -18,7 +18,7 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.global; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -38,7 +38,7 @@ public void testEmptyAggregation() { .setQuery(matchAllQuery()) .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); CentroidAggregation geoCentroid = response.getAggregations().get(aggName()); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); @@ -52,7 +52,7 @@ public void testUnmapped() throws Exception { SearchResponse response = client().prepareSearch(UNMAPPED_IDX_NAME) .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); CentroidAggregation geoCentroid = response.getAggregations().get(aggName()); assertThat(geoCentroid, notNullValue()); @@ -65,7 +65,7 @@ public void testPartiallyUnmapped() { SearchResponse response = client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME) .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); CentroidAggregation geoCentroid = response.getAggregations().get(aggName()); assertThat(geoCentroid, notNullValue()); @@ -79,7 +79,7 @@ public void testSingleValuedField() { .setQuery(matchAllQuery()) .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); CentroidAggregation geoCentroid = response.getAggregations().get(aggName()); assertThat(geoCentroid, notNullValue()); @@ -93,7 +93,7 @@ public void testSingleValueFieldGetProperty() { .setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Global global = response.getAggregations().get("global"); assertThat(global, notNullValue()); @@ -120,7 +120,7 @@ public void testMultiValuedField() throws Exception { .setQuery(matchAllQuery()) .addAggregation(centroidAgg(aggName()).field(MULTI_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); CentroidAggregation geoCentroid = searchResponse.getAggregations().get(aggName()); assertThat(geoCentroid, notNullValue()); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java index ac366576c8975..1b318eb4952c2 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java @@ -23,7 +23,7 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.global; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -41,7 +41,7 @@ public abstract class SpatialBoundsAggregationTestBase e public void testSingleValuedField() throws Exception { SearchResponse response = client().prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get(); - assertSearchResponse(response); + assertNoFailures(response); SpatialBounds geoBounds = response.getAggregations().get(aggName()); assertThat(geoBounds, notNullValue()); @@ -60,7 +60,7 @@ public void testSingleValuedField_getProperty() { .addAggregation(global("global").subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Global global = searchResponse.getAggregations().get("global"); assertThat(global, notNullValue()); @@ -100,7 +100,7 @@ public void testSingleValuedField_getProperty() { public void testMultiValuedField() throws Exception { SearchResponse response = client().prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), MULTI_VALUED_FIELD_NAME)).get(); - assertSearchResponse(response); + assertNoFailures(response); SpatialBounds geoBounds = response.getAggregations().get(aggName()); assertThat(geoBounds, notNullValue()); @@ -118,7 +118,7 @@ public void testUnmapped() throws Exception { .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); SpatialBounds geoBounds = response.getAggregations().get(aggName()); assertThat(geoBounds, notNullValue()); @@ -134,7 +134,7 @@ public void testPartiallyUnmapped() throws Exception { .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); SpatialBounds geoBounds = response.getAggregations().get(aggName()); assertThat(geoBounds, notNullValue()); @@ -171,7 +171,7 @@ public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() { .addAggregation(terms("terms").field(NUMBER_FIELD_NAME).subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))) .get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -194,7 +194,7 @@ public void testSingleValuedFieldWithZeroLon() { .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) .get(); - assertSearchResponse(response); + assertNoFailures(response); SpatialBounds geoBounds = response.getAggregations().get(aggName()); assertThat(geoBounds, notNullValue()); diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/BasePointShapeQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/BasePointShapeQueryTestCase.java index 69698bc535b06..6abce04556316 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/geo/BasePointShapeQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/geo/BasePointShapeQueryTestCase.java @@ -47,7 +47,7 @@ import java.util.Map; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -115,7 +115,7 @@ public void testIndexPointsFilterRectangle() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, geometry).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -123,7 +123,7 @@ public void testIndexPointsFilterRectangle() throws Exception { // default query, without specifying relation (expect intersects) searchResponse = client().prepareSearch(defaultIndexName).setQuery(queryBuilder().shapeQuery(defaultFieldName, geometry)).get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -181,7 +181,7 @@ public void testIndexPointsPolygon() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, polygon).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo(1L)); assertThat(searchHits.getAt(0).getId(), equalTo("1")); @@ -223,7 +223,7 @@ public void testIndexPointsMultiPolygon() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, multiPolygon).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getHits().length, equalTo(2)); assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2"))); @@ -234,7 +234,7 @@ public void testIndexPointsMultiPolygon() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, multiPolygon).relation(ShapeRelation.WITHIN)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getHits().length, equalTo(2)); assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2"))); @@ -245,7 +245,7 @@ public void testIndexPointsMultiPolygon() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, multiPolygon).relation(ShapeRelation.DISJOINT)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); @@ -255,7 +255,7 @@ public void testIndexPointsMultiPolygon() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, multiPolygon).relation(ShapeRelation.CONTAINS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); assertThat(searchResponse.getHits().getHits().length, equalTo(0)); } @@ -283,7 +283,7 @@ public void testIndexPointsRectangle() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); @@ -341,7 +341,7 @@ public void testIndexPointsIndexedRectangle() throws Exception { ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("point2")); @@ -354,7 +354,7 @@ public void testIndexPointsIndexedRectangle() throws Exception { .indexedShapePath(indexedShapePath) ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); } @@ -621,7 +621,7 @@ public void testIndexPointsFromLine() throws Exception { .setTrackTotalHits(true) .setQuery(queryBuilder().shapeQuery(defaultFieldName, line).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo((long) line.length())); } @@ -647,7 +647,7 @@ public void testIndexPointsFromPolygon() throws Exception { .setTrackTotalHits(true) .setQuery(queryBuilder().shapeQuery(defaultFieldName, polygon).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo((long) linearRing.length())); } diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java index b1319e59e8515..bd8f369e32046 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java @@ -43,7 +43,7 @@ import static org.elasticsearch.index.query.QueryBuilders.existsQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -150,28 +150,28 @@ public void testShapeFetchingPath() throws Exception { .indexedShapeIndex("shapes") .indexedShapePath(defaultFieldName); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); filter = queryBuilder().shapeQuery(defaultFieldName, "1") .relation(ShapeRelation.INTERSECTS) .indexedShapeIndex("shapes") .indexedShapePath("1.geo"); result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); filter = queryBuilder().shapeQuery(defaultFieldName, "1") .relation(ShapeRelation.INTERSECTS) .indexedShapeIndex("shapes") .indexedShapePath("1.2.geo"); result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); filter = queryBuilder().shapeQuery(defaultFieldName, "1") .relation(ShapeRelation.INTERSECTS) .indexedShapeIndex("shapes") .indexedShapePath("1.2.3.geo"); result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // now test the query variant @@ -179,19 +179,19 @@ public void testShapeFetchingPath() throws Exception { .indexedShapeIndex("shapes") .indexedShapePath(defaultFieldName); result = client().prepareSearch(defaultIndexName).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); query = queryBuilder().shapeQuery(defaultFieldName, "1").indexedShapeIndex("shapes").indexedShapePath("1.geo"); result = client().prepareSearch(defaultIndexName).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); query = queryBuilder().shapeQuery(defaultFieldName, "1").indexedShapeIndex("shapes").indexedShapePath("1.2.geo"); result = client().prepareSearch(defaultIndexName).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); query = queryBuilder().shapeQuery(defaultFieldName, "1").indexedShapeIndex("shapes").indexedShapePath("1.2.3.geo"); result = client().prepareSearch(defaultIndexName).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } @@ -220,7 +220,7 @@ public void testRandomGeoCollectionQuery() throws Exception { QueryBuilder intersects = queryBuilder().intersectionQuery(defaultFieldName, queryCollection); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(intersects).get(); - assertSearchResponse(result); + assertNoFailures(result); assertTrue("query: " + intersects + " doc: " + Strings.toString(docSource), result.getHits().getTotalHits().value > 0); } @@ -350,7 +350,7 @@ public void testEdgeCases() throws Exception { .setQuery(queryBuilder().intersectionQuery(defaultFieldName, query)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("blakely")); @@ -395,7 +395,7 @@ public void testPointQuery() throws Exception { SearchResponse result = client().prepareSearch(defaultIndexName) .setQuery(queryBuilder().intersectionQuery(defaultFieldName, point)) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } @@ -408,7 +408,7 @@ public void testContainsShapeQuery() throws Exception { client().prepareIndex(defaultIndexName).setId("1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get(); QueryBuilder filter = queryBuilder().shapeQuery(defaultFieldName, innerPolygon).relation(ShapeRelation.CONTAINS); SearchResponse response = client().prepareSearch(defaultIndexName).setQuery(filter).get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(1L)); } @@ -424,7 +424,7 @@ public void testExistsQuery() throws Exception { ExistsQueryBuilder eqb = existsQuery(defaultFieldName); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(eqb).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } @@ -461,7 +461,7 @@ public void testIndexedShapeReference() throws Exception { .setQuery(queryBuilder().intersectionQuery(defaultFieldName, "Big_Rectangle")) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -470,7 +470,7 @@ public void testIndexedShapeReference() throws Exception { .setQuery(queryBuilder().shapeQuery(defaultFieldName, "Big_Rectangle")) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -492,7 +492,7 @@ public void testQueryRandomGeoCollection() throws Exception { SearchResponse result = client().prepareSearch(defaultIndexName) .setQuery(queryBuilder().intersectionQuery(defaultFieldName, polygon)) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } @@ -539,26 +539,26 @@ public void testShapeFilterWithDefinedGeoCollection() throws Exception { { QueryBuilder filter = queryBuilder().intersectionQuery(defaultFieldName, new GeometryCollection<>(List.of(polygon1))); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } { QueryBuilder filter = queryBuilder().intersectionQuery(defaultFieldName, new GeometryCollection<>(List.of(polygon2))); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 0); } { QueryBuilder filter = queryBuilder().intersectionQuery(defaultFieldName, new GeometryCollection<>(List.of(polygon1, polygon2))); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } { // no shape QueryBuilder filter = queryBuilder().shapeQuery(defaultFieldName, GeometryCollection.EMPTY); SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 0); } } @@ -627,7 +627,7 @@ public void testIndexLineQueryPoints() throws Exception { .setTrackTotalHits(true) .setQuery(queryBuilder().shapeQuery(defaultFieldName, point).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo(1L)); } @@ -652,7 +652,7 @@ public void testIndexPolygonQueryPoints() throws Exception { .setTrackTotalHits(true) .setQuery(queryBuilder().shapeQuery(defaultFieldName, point).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo(1L)); } @@ -685,7 +685,7 @@ public void testNeighbours() throws Exception { .setTrackTotalHits(true) .setQuery(queryBuilder().shapeQuery(defaultFieldName, center).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo((long) polygons.length)); } diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeQueryTestCase.java index 37ab420849804..b8f5cdf00da34 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeQueryTestCase.java @@ -36,8 +36,7 @@ import java.util.List; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; public abstract class GeoShapeQueryTestCase extends BaseShapeQueryTestCase { @@ -160,11 +159,10 @@ public void testIndexRectangleSpanningDateLine() throws Exception { Point filterShape = new Point(179, 0); - SearchResponse result = client().prepareSearch(defaultIndexName) - .setQuery(queryBuilder().intersectionQuery(defaultFieldName, filterShape)) - .get(); - assertSearchResponse(result); - assertHitCount(result, 1); + assertHitCountAndNoFailures( + client().prepareSearch(defaultIndexName).setQuery(queryBuilder().intersectionQuery(defaultFieldName, filterShape)), + 1 + ); } protected Line makeRandomLine() { diff --git a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java index 885a1aafd89b8..3379599c381e5 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java +++ b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java @@ -267,7 +267,7 @@ public static void assertSearchHitsWithoutFailures(SearchRequestBuilder requestB public static void assertSortValues(SearchRequestBuilder searchRequestBuilder, Object[]... sortValues) { var searchResponse = searchRequestBuilder.get(); try { - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHit[] hits = searchResponse.getHits().getHits(); assertEquals(sortValues.length, hits.length); for (int i = 0; i < sortValues.length; ++i) { @@ -312,6 +312,16 @@ public static void assertHitCount(SearchResponse countResponse, long expectedHit } } + public static void assertHitCountAndNoFailures(SearchRequestBuilder searchRequestBuilder, long expectedHitCount) { + var res = searchRequestBuilder.get(); + try { + assertHitCount(res, expectedHitCount); + assertNoFailures(res); + } finally { + res.decRef(); + } + } + public static void assertExists(GetResponse response) { String message = String.format(Locale.ROOT, "Expected %s/%s to exist, but does not", response.getIndex(), response.getId()); assertThat(message, response.isExists(), is(true)); @@ -659,23 +669,6 @@ public static void assertFutureThrows(ActionFuture future, RestStatus status, assertThat(extraInfo, ExceptionsHelper.status(e), equalTo(status)); } - /** - * Applies basic assertions on the SearchResponse. This method checks if all shards were successful, if - * any of the shards threw an exception and if the response is serializable. - */ - public static SearchResponse assertSearchResponse(SearchRequestBuilder request) { - return assertSearchResponse(request.get()); - } - - /** - * Applies basic assertions on the SearchResponse. This method checks if all shards were successful, if - * any of the shards threw an exception and if the response is serializable. - */ - public static SearchResponse assertSearchResponse(SearchResponse response) { - assertNoFailures(response); - return response; - } - /** * Check if a file exists */ diff --git a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java index 3594dde42229b..727441f5dba10 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java +++ b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java @@ -41,7 +41,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.range; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -104,7 +104,7 @@ public void testSort() { .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchHit[] hits = response.getHits().getHits(); assertEquals(hits.length, numDocs); int i = 0; @@ -119,7 +119,7 @@ public void testSort() { .setSize(numDocs) .addSort("ul_field", SortOrder.DESC) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchHit[] hits = response.getHits().getHits(); assertEquals(hits.length, numDocs); int i = numDocs - 1; @@ -135,7 +135,7 @@ public void testSort() { .addSort("ul_field", SortOrder.ASC) .searchAfter(new Long[] { 100L }) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchHit[] hits = response.getHits().getHits(); assertEquals(hits.length, 7); int i = 3; @@ -151,7 +151,7 @@ public void testSort() { .addSort("ul_field", SortOrder.ASC) .searchAfter(new BigInteger[] { new BigInteger("18446744073709551614") }) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchHit[] hits = response.getHits().getHits(); assertEquals(hits.length, 2); int i = 8; @@ -167,7 +167,7 @@ public void testSort() { .addSort("ul_field", SortOrder.ASC) .searchAfter(new String[] { "18446744073709551614" }) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchHit[] hits = response.getHits().getHits(); assertEquals(hits.length, 2); int i = 8; @@ -203,7 +203,7 @@ public void testSort() { .addSort("ul_field", SortOrder.DESC) .searchAfter(new BigInteger[] { new BigInteger("18446744073709551615") }) .get(); - assertSearchResponse(response); + assertNoFailures(response); SearchHit[] hits = response.getHits().getHits(); assertEquals(hits.length, 8); int i = 7; @@ -218,7 +218,7 @@ public void testAggs() { // terms agg { SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(terms("ul_terms").field("ul_field")).get(); - assertSearchResponse(response); + assertNoFailures(response); Terms terms = response.getAggregations().get("ul_terms"); long[] expectedBucketDocCounts = { 2, 2, 2, 1, 1, 1, 1 }; @@ -244,7 +244,7 @@ public void testAggs() { .setSize(0) .addAggregation(histogram("ul_histo").field("ul_field").interval(9E18).minDocCount(0)) .get(); - assertSearchResponse(response); + assertNoFailures(response); Histogram histo = response.getAggregations().get("ul_histo"); long[] expectedBucketDocCounts = { 3, 3, 4 }; @@ -265,7 +265,7 @@ public void testAggs() { range("ul_range").field("ul_field").addUnboundedTo(9.0E18).addRange(9.0E18, 1.8E19).addUnboundedFrom(1.8E19) ) .get(); - assertSearchResponse(response); + assertNoFailures(response); Range range = response.getAggregations().get("ul_range"); long[] expectedBucketDocCounts = { 3, 3, 4 }; @@ -281,7 +281,7 @@ public void testAggs() { // sum agg { SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(sum("ul_sum").field("ul_field")).get(); - assertSearchResponse(response); + assertNoFailures(response); Sum sum = response.getAggregations().get("ul_sum"); double expectedSum = Arrays.stream(values).mapToDouble(Number::doubleValue).sum(); assertEquals(expectedSum, sum.value(), 0.001); @@ -289,14 +289,14 @@ public void testAggs() { // max agg { SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(max("ul_max").field("ul_field")).get(); - assertSearchResponse(response); + assertNoFailures(response); Max max = response.getAggregations().get("ul_max"); assertEquals(1.8446744073709551615E19, max.value(), 0.001); } // min agg { SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(min("ul_min").field("ul_field")).get(); - assertSearchResponse(response); + assertNoFailures(response); Min min = response.getAggregations().get("ul_min"); assertEquals(0, min.value(), 0.001); } diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java index a6ef720e90777..bd39393d7f338 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java @@ -70,7 +70,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_STORE_TYPE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xpack.searchablesnapshots.SearchableSnapshots.SNAPSHOT_RECOVERY_STATE_FACTORY_KEY; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; @@ -470,7 +470,7 @@ public void testRequestCacheOnFrozen() throws Exception { dateHistogram("histo").field("f").timeZone(ZoneId.of("+01:00")).minDocCount(0).calendarInterval(DateHistogramInterval.MONTH) ) .get(); - assertSearchResponse(r1); + assertNoFailures(r1); assertRequestCacheState(client(), "test-index", 0, 1); @@ -491,7 +491,7 @@ public void testRequestCacheOnFrozen() throws Exception { .calendarInterval(DateHistogramInterval.MONTH) ) .get(); - assertSearchResponse(r2); + assertNoFailures(r2); assertRequestCacheState(client(), "test-index", i + 1, 1); Histogram h1 = r1.getAggregations().get("histo"); Histogram h2 = r2.getAggregations().get("histo"); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java index 3baec135d107f..be3162ac98b3b 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java @@ -96,7 +96,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; @@ -541,14 +540,14 @@ public void testPercolateQueryWithIndexedDocWithDLS() { SearchResponse result = client().filterWithHeader( Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD)) ).prepareSearch("query_index").setQuery(new PercolateQueryBuilder("query", "doc_index", "1", null, null, null)).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // user2 can access the query_index itself (without performing percolate search) result = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) .prepareSearch("query_index") .setQuery(QueryBuilders.matchAllQuery()) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // user2 cannot access doc#1 of the doc_index so the percolate search fails because doc#1 cannot be found ResourceNotFoundException e = expectThrows( @@ -596,14 +595,14 @@ public void testGeoQueryWithIndexedShapeWithDLS() { requestBuilder.setQuery(shapeQuery); } result = requestBuilder.get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // user2 does not have access to doc#1 of the shape_index result = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) .prepareSearch("search_index") .setQuery(QueryBuilders.matchAllQuery()) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); IllegalArgumentException e; if (randomBoolean()) { diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java index 27b6a10e0619c..5d7644efccfe5 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java @@ -82,7 +82,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.hamcrest.Matchers.equalTo; @@ -485,33 +484,33 @@ public void testPercolateQueryWithIndexedDocWithFLS() { SearchResponse result = client().filterWithHeader( Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user7", USERS_PASSWD)) ).prepareSearch("query_index").setQuery(percolateQuery).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); result = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) .prepareSearch("query_index") .setQuery(QueryBuilders.matchAllQuery()) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // user 3 can see the fields of the percolated document, but not the "query" field of the indexed query result = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) .prepareSearch("query_index") .setQuery(percolateQuery) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 0); result = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user9", USERS_PASSWD))) .prepareSearch("query_index") .setQuery(QueryBuilders.matchAllQuery()) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // user 9 can see the fields of the index query, but not the field of the indexed document to be percolated result = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user9", USERS_PASSWD))) .prepareSearch("query_index") .setQuery(percolateQuery) .get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 0); } @@ -573,7 +572,7 @@ public void testGeoQueryWithIndexedShapeWithFLS() { requestBuilder.setQuery(shapeQuery1); } result = requestBuilder.get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // user sees the queried point but not the querying shape final ShapeQueryBuilder shapeQuery2 = new ShapeQueryBuilder("field", "2").relation(ShapeRelation.WITHIN) @@ -611,7 +610,7 @@ public void testGeoQueryWithIndexedShapeWithFLS() { requestBuilder.setQuery(shapeQuery3); } result = requestBuilder.get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 0); } diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java index 13a4de7f97fa4..4d526a6a07718 100644 --- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java +++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java @@ -51,7 +51,7 @@ import java.util.function.Function; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -268,7 +268,7 @@ private void doTestGeometry(Geometry geometry, GeoShapeValues.GeoShapeValue expe .addScriptField("label_lat", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lat", Collections.emptyMap())) .addScriptField("label_lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lon", Collections.emptyMap())) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Map fields = searchResponse.getHits().getHits()[0].getFields(); assertThat(fields.get("lat").getValue(), equalTo(value.getY())); assertThat(fields.get("lon").getValue(), equalTo(value.getX())); @@ -323,7 +323,7 @@ public void testNullShape() throws Exception { .addScriptField("height", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "height", Collections.emptyMap())) .addScriptField("width", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "width", Collections.emptyMap())) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); Map fields = searchResponse.getHits().getHits()[0].getFields(); assertThat(fields.get("lat").getValue(), equalTo(Double.NaN)); assertThat(fields.get("lon").getValue(), equalTo(Double.NaN)); diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java index 8903904eeefd7..a011f42c14518 100644 --- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java +++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java @@ -35,7 +35,7 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -176,43 +176,43 @@ public void testShapeFetchingPath() throws Exception { .indexedShapeIndex(indexName) .indexedShapePath("location"); SearchResponse result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS) .indexedShapeIndex(indexName) .indexedShapePath("1.location"); result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS) .indexedShapeIndex(indexName) .indexedShapePath("1.2.location"); result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS) .indexedShapeIndex(indexName) .indexedShapePath("1.2.3.location"); result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); // now test the query variant ShapeQueryBuilder query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("location"); result = client().prepareSearch(searchIndex).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("1.location"); result = client().prepareSearch(searchIndex).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("1.2.location"); result = client().prepareSearch(searchIndex).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("1.2.3.location"); result = client().prepareSearch(searchIndex).setQuery(query).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, 1); } @@ -265,7 +265,7 @@ public void testNullShape() { public void testExistsQuery() { ExistsQueryBuilder eqb = QueryBuilders.existsQuery(FIELD); SearchResponse result = client().prepareSearch(INDEX).setQuery(eqb).get(); - assertSearchResponse(result); + assertNoFailures(result); assertHitCount(result, numDocs); } @@ -288,7 +288,7 @@ public void testContainsShapeQuery() { Rectangle rectangle = new Rectangle(-50, 50, 50, -50); ShapeQueryBuilder queryBuilder = new ShapeQueryBuilder("location", rectangle).relation(ShapeRelation.CONTAINS); SearchResponse response = client().prepareSearch("test_contains").setQuery(queryBuilder).get(); - assertSearchResponse(response); + assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(1L)); } diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java index 1aad094bfe5b9..8a42e157d0017 100644 --- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java +++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java @@ -32,7 +32,7 @@ import java.util.List; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; @@ -87,7 +87,7 @@ public void testIndexPointsFilterRectangle() throws Exception { .setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -95,7 +95,7 @@ public void testIndexPointsFilterRectangle() throws Exception { // default query, without specifying relation (expect intersects) searchResponse = client().prepareSearch(defaultIndexName).setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle)).get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -124,7 +124,7 @@ public void testIndexPointsCircle() throws Exception { .setQuery(new ShapeQueryBuilder(defaultFieldName, circle).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -153,7 +153,7 @@ public void testIndexPointsPolygon() throws Exception { .setQuery(new ShapeQueryBuilder(defaultFieldName, polygon).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); SearchHits searchHits = searchResponse.getHits(); assertThat(searchHits.getTotalHits().value, equalTo(1L)); assertThat(searchHits.getAt(0).getId(), equalTo("1")); @@ -195,7 +195,7 @@ public void testIndexPointsMultiPolygon() throws Exception { .setQuery(new ShapeQueryBuilder(defaultFieldName, mp).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getHits().length, equalTo(2)); assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2"))); @@ -225,7 +225,7 @@ public void testIndexPointsRectangle() throws Exception { .setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS)) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); @@ -283,7 +283,7 @@ public void testIndexPointsIndexedRectangle() throws Exception { ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("point2")); @@ -295,7 +295,7 @@ public void testIndexPointsIndexedRectangle() throws Exception { .indexedShapePath(indexedShapePath) ) .get(); - assertSearchResponse(searchResponse); + assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); } From 9715493c21cd84aa1a0c9499825d642f1e272068 Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Thu, 19 Oct 2023 17:58:55 +0200 Subject: [PATCH 038/203] ESQL: Fix escaping of backslash in LIKE operator (#101120) Fixes https://github.com/elastic/elasticsearch/issues/101106 Fixing a bug in escaping backslash character in LIKE operator, eg. the typical query that tries to match a file path ``` ... | where path LIKE "C:\\\\*"``` Also including here a little fix to correctly fold RegexMatch expressions (LIKE/RLIKE) when the input is a BytesRef. --- docs/changelog/101120.yaml | 6 ++++++ .../src/main/resources/where-like.csv-spec | 14 ++++++++++++++ .../ql/expression/predicate/regex/RegexMatch.java | 4 ++++ .../elasticsearch/xpack/ql/util/StringUtils.java | 5 +++-- .../xpack/ql/util/StringUtilsTests.java | 3 +++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/101120.yaml diff --git a/docs/changelog/101120.yaml b/docs/changelog/101120.yaml new file mode 100644 index 0000000000000..bf359eb21be9f --- /dev/null +++ b/docs/changelog/101120.yaml @@ -0,0 +1,6 @@ +pr: 101120 +summary: "ESQL: Fix escaping of backslash in LIKE operator" +area: ES|QL +type: bug +issues: + - 101106 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/where-like.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/where-like.csv-spec index c513f6670b044..49bf62bf77db7 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/where-like.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/where-like.csv-spec @@ -273,3 +273,17 @@ emp_no:integer | first_name:keyword | last_name:keyword 10086 | Somnath | Foote 10088 | Jungsoon | Syrzycki ; + + +likeWithPath +row x = "C:\\foo\\bar.exe" | mv_expand x | where x LIKE "C:\\\\*"; + +x:keyword +C:\foo\bar.exe +; + +likeWithPathNoMatch +row x = "C:\\foo\\bar.exe" | mv_expand x | where x LIKE "C:\\\\\\\\*"; + +x:keyword +; diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/regex/RegexMatch.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/regex/RegexMatch.java index b7f50100c60db..439bba43b1348 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/regex/RegexMatch.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/regex/RegexMatch.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.ql.expression.predicate.regex; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.Nullability; import org.elasticsearch.xpack.ql.expression.function.scalar.UnaryScalarFunction; @@ -69,6 +70,9 @@ public boolean foldable() { @Override public Boolean fold() { Object val = field().fold(); + if (val instanceof BytesRef br) { + val = br.utf8ToString(); + } return RegexProcessor.RegexOperation.match(val, pattern().asJavaRegex()); } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java index db15bfa3c57d5..beebf0d581444 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java @@ -139,7 +139,8 @@ public static String likeToJavaPattern(String pattern, char escape) { // * -> .* // ? -> . // escape character - can be 0 (in which case no regex gets escaped) or - // should be followed by % or _ (otherwise an exception is thrown) + // should be followed by * or ? or the escape character itself (otherwise an exception is thrown). + // Using * or ? as escape characters should be avoided because it will make it impossible to enter them as literals public static String wildcardToJavaPattern(String pattern, char escape) { StringBuilder regex = new StringBuilder(pattern.length() + 4); @@ -157,7 +158,7 @@ public static String wildcardToJavaPattern(String pattern, char escape) { case '*' -> regex.append(escaped ? "\\*" : ".*"); case '?' -> regex.append(escaped ? "\\?" : "."); default -> { - if (escaped) { + if (escaped && escape != curr) { throw new QlIllegalArgumentException( "Invalid sequence - escape character is not followed by special wildcard char" ); diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/StringUtilsTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/StringUtilsTests.java index ca163cff7b36e..3280a96d246a9 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/StringUtilsTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/StringUtilsTests.java @@ -52,4 +52,7 @@ public void testWildcard() { assertEquals("^foo\\*$", wildcardToJavaPattern("foox*", 'x')); } + public void testEscapedEscape() { + assertEquals("^\\\\\\\\$", wildcardToJavaPattern("\\\\\\\\", '\\')); + } } From 5d14bca37de24363a78b8d9c2345d3230fb8596d Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Thu, 19 Oct 2023 12:09:54 -0400 Subject: [PATCH 039/203] Disable packaging tests in Jenkins (#101088) --- ...astic+elasticsearch+periodic+packaging-tests-trigger.yml | 6 ------ .../elastic+elasticsearch+periodic+packaging-tests.yml | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests-trigger.yml diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests-trigger.yml deleted file mode 100644 index d8c8b557e4514..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+packaging-tests - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/8 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests.yml index a68641f50a174..e6f6cb5c3771b 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+packaging-tests.yml @@ -2,7 +2,8 @@ - job: name: elastic+elasticsearch+%BRANCH%+periodic+packaging-tests display-name: "elastic / elasticsearch # %BRANCH% - packaging tests" - description: "Testing of the Elasticsearch %BRANCH% branch packaging tests.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true project-type: multijob node: master vault: [] From 851e0d4d5119c860f8cb0b0f733939f05ef8f497 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:46:45 -0400 Subject: [PATCH 040/203] [ML] Revert remove rethrow change for AbstractProcessWorkerExecutorService (#101159) * Revert "[ML] Removing exception rethrow in (#100925)" This reverts commit b4698b3ecbf6f566729c1d1defa5a8df641d5cda. We do need to rethrow because it is possible to get future runnables * Unmuting test --- .../job/process/AbstractProcessWorkerExecutorService.java | 8 +++----- .../ml/job/process/ProcessWorkerExecutorServiceTests.java | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java index d5734dc5b0e95..dee608e69f5bb 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/AbstractProcessWorkerExecutorService.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.util.concurrent.AbstractRunnable; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.SuppressForbidden; @@ -20,7 +21,6 @@ import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -50,7 +50,7 @@ public abstract class AbstractProcessWorkerExecutorService e * for execution when the queue is full a 429 error is thrown. * @param queueSupplier BlockingQueue constructor */ - @SuppressForbidden(reason = "wraps a queue and handles errors appropriately") + @SuppressForbidden(reason = "properly rethrowing errors, see EsExecutors.rethrowErrors") public AbstractProcessWorkerExecutorService( ThreadContext contextHolder, String processName, @@ -107,14 +107,12 @@ public void start() { while (running.get()) { Runnable runnable = queue.poll(500, TimeUnit.MILLISECONDS); if (runnable != null) { - assert runnable instanceof RunnableFuture == false - : "Rethrowing errors for RunnableFuture instances is not supported"; - try { runnable.run(); } catch (Exception e) { logger.error(() -> "error handling process [" + processName + "] operation", e); } + EsExecutors.rethrowErrors(ThreadContext.unwrap(runnable)); } else if (shouldShutdownAfterCompletingWork.get()) { running.set(false); } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java index 6e976a889141f..096d0b7105ce5 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/process/ProcessWorkerExecutorServiceTests.java @@ -126,7 +126,6 @@ protected void doRun() { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101144") public void testAutodetectWorkerExecutorServiceDoesNotSwallowErrors() { ProcessWorkerExecutorService executor = createExecutorService(); if (randomBoolean()) { From 7eb5f4426600dbf0080f2cc0ab5bcba43fa7f158 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 19 Oct 2023 20:39:55 +0200 Subject: [PATCH 041/203] Update forbidden apis gradle plugin (#101160) --- .../internal/precommit/ThirdPartyAuditPrecommitPlugin.java | 2 +- gradle/build.versions.toml | 2 +- gradle/verification-metadata.xml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/ThirdPartyAuditPrecommitPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/ThirdPartyAuditPrecommitPlugin.java index c87d7a6b0e730..f6d3787a4f686 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/ThirdPartyAuditPrecommitPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/ThirdPartyAuditPrecommitPlugin.java @@ -29,7 +29,7 @@ public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin { public TaskProvider createTask(Project project) { project.getPlugins().apply(CompileOnlyResolvePlugin.class); project.getConfigurations().create("forbiddenApisCliJar"); - project.getDependencies().add("forbiddenApisCliJar", "de.thetaphi:forbiddenapis:3.5.1"); + project.getDependencies().add("forbiddenApisCliJar", "de.thetaphi:forbiddenapis:3.6"); Configuration jdkJarHellConfig = project.getConfigurations().create(JDK_JAR_HELL_CONFIG_NAME); if (project.getPath().equals(LIBS_ELASTICSEARCH_CORE_PROJECT_PATH) == false) { // Internal projects are not all plugins, so make sure the check is available diff --git a/gradle/build.versions.toml b/gradle/build.versions.toml index 4472fd635f905..94ed94df43818 100644 --- a/gradle/build.versions.toml +++ b/gradle/build.versions.toml @@ -16,7 +16,7 @@ checkstyle = "com.puppycrawl.tools:checkstyle:10.3" commons-codec = "commons-codec:commons-codec:1.11" commmons-io = "commons-io:commons-io:2.2" docker-compose = "com.avast.gradle:gradle-docker-compose-plugin:0.17.5" -forbiddenApis = "de.thetaphi:forbiddenapis:3.5.1" +forbiddenApis = "de.thetaphi:forbiddenapis:3.6" hamcrest = "org.hamcrest:hamcrest:2.1" httpcore = "org.apache.httpcomponents:httpcore:4.4.12" httpclient = "org.apache.httpcomponents:httpclient:4.5.10" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 3200cd14ae3a8..34b3d4e578338 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1186,9 +1186,9 @@ - - - + + + From ca5aaaa16614310d7f085e396834f59f68817405 Mon Sep 17 00:00:00 2001 From: Joe Gallo Date: Thu, 19 Oct 2023 14:41:48 -0400 Subject: [PATCH 042/203] Add debug logging during ILM's check-rollover-ready step (#101015) --- .../core/ilm/WaitForRolloverReadyStep.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java index 887d0d5ec2101..f3d47cea1f39f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java @@ -205,18 +205,16 @@ public void evaluateCondition(Metadata metadata, Index index, Listener listener, boolean rolloverOnlyIfHasDocuments = LifecycleSettings.LIFECYCLE_ROLLOVER_ONLY_IF_HAS_DOCUMENTS_SETTING.get(metadata.settings()); RolloverRequest rolloverRequest = createRolloverRequest(rolloverTarget, masterTimeout, rolloverOnlyIfHasDocuments); - getClient().admin() - .indices() - .rolloverIndex( - rolloverRequest, - ActionListener.wrap( - response -> listener.onResponse( - rolloverRequest.getConditions().areConditionsMet(response.getConditionStatus()), - EmptyInfo.INSTANCE - ), - listener::onFailure - ) - ); + getClient().admin().indices().rolloverIndex(rolloverRequest, ActionListener.wrap(response -> { + final var conditionStatus = response.getConditionStatus(); + final var conditionsMet = rolloverRequest.getConditions().areConditionsMet(conditionStatus); + if (conditionsMet) { + logger.info("index [{}] is ready for rollover, conditions: [{}]", index.getName(), conditionStatus); + } else { + logger.debug("index [{}] is not ready for rollover, conditions: [{}]", index.getName(), conditionStatus); + } + listener.onResponse(conditionsMet, EmptyInfo.INSTANCE); + }, listener::onFailure)); } /** From a7326334bde4acb17316058f3c0abc0ee6424e98 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 19 Oct 2023 20:54:25 +0200 Subject: [PATCH 043/203] Make GenerateProviderManifest configuration cache compatible (#101161) --- .../internal/GenerateProviderManifest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java new file mode 100644 index 0000000000000..e20e00ea3b110 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.gradle.internal; + +import org.elasticsearch.gradle.util.FileUtils; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import java.io.File; +import java.util.stream.Collectors; + +abstract class GenerateProviderManifest extends DefaultTask { + + @Classpath + @InputFiles + abstract public ConfigurableFileCollection getProviderImplClasspath(); + + @OutputFile + abstract public Provider getManifestFile(); + + @TaskAction + void generateManifest() { + File manifestFile = getManifestFile().get(); + manifestFile.getParentFile().mkdirs(); + FileUtils.write(manifestFile, generateManifestContent(), "UTF-8"); + } + + private String generateManifestContent() { + return getProviderImplClasspath().getFiles().stream().map(File::getName).sorted().collect(Collectors.joining("\n")); + } +} From d9ca42bf7d4348b90c9844181a6e1074eea18ede Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 19 Oct 2023 23:18:21 +0200 Subject: [PATCH 044/203] Use custom task implementation for use generate manifest (#101165) Follow up on #101161 to make this behave better when using gradle configuration cache --- .../internal/GenerateProviderManifest.java | 21 +++++++++++++------ libs/x-content/build.gradle | 14 +++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java index e20e00ea3b110..6f07b198e7e8a 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java @@ -11,28 +11,37 @@ import org.elasticsearch.gradle.util.FileUtils; import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.provider.Provider; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.TaskAction; import java.io.File; import java.util.stream.Collectors; +import javax.inject.Inject; + abstract class GenerateProviderManifest extends DefaultTask { + @Inject + public GenerateProviderManifest() {} + @Classpath @InputFiles abstract public ConfigurableFileCollection getProviderImplClasspath(); - @OutputFile - abstract public Provider getManifestFile(); + @Input + abstract public Property getManifestName(); + + @OutputDirectory + abstract DirectoryProperty getOutputDir(); @TaskAction void generateManifest() { - File manifestFile = getManifestFile().get(); - manifestFile.getParentFile().mkdirs(); + File manifestFile = getOutputDir().file(getManifestName().get()).get().getAsFile(); FileUtils.write(manifestFile, generateManifestContent(), "UTF-8"); } diff --git a/libs/x-content/build.gradle b/libs/x-content/build.gradle index c51b91e2711b8..f934d7ce09386 100644 --- a/libs/x-content/build.gradle +++ b/libs/x-content/build.gradle @@ -8,6 +8,7 @@ import org.elasticsearch.gradle.transform.UnzipTransform +import org.elasticsearch.gradle.internal.GenerateProviderManifest import org.gradle.api.internal.artifacts.ArtifactAttributes import java.util.stream.Collectors @@ -67,15 +68,10 @@ tasks.named("dependencyLicenses").configure { } File generatedResourcesDir = new File(buildDir, 'generated-resources') -def generateProviderManifest = tasks.register("generateProviderManifest") { - File manifestFile = new File(generatedResourcesDir, "LISTING.TXT") - inputs.files('jars', configurations.providerImpl).withPathSensitivity(PathSensitivity.RELATIVE) - outputs.file(manifestFile) - doLast { - manifestFile.parentFile.mkdirs() - manifestFile.setText(configurations.providerImpl.files.stream() - .map(f -> f.name).sorted().collect(Collectors.joining('\n')), 'UTF-8') - } +def generateProviderManifest = tasks.register("generateProviderManifest", GenerateProviderManifest.class) { + outputDir = layout.buildDirectory.dir('generated-resources') + manifestName = "LISTING.TXT" + getProviderImplClasspath().from(configurations.providerImpl) } def generateProviderImpl = tasks.register("generateProviderImpl", Sync) { From 881ea032fa4f4a695b68324bfab821a0d98c96c9 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 19 Oct 2023 17:56:56 -0600 Subject: [PATCH 045/203] Add ability to disable rank/sub_searches in search requests (#101091) This commit adds a global system property for disabling the rank/sub_searches feature of search requests. --- .../search/builder/SearchSourceBuilder.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index 043968e254d1d..2d9d6d1d8d75d 100644 --- a/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -117,6 +117,8 @@ public final class SearchSourceBuilder implements Writeable, ToXContentObject, R public static final ParseField POINT_IN_TIME = new ParseField("pit"); public static final ParseField RUNTIME_MAPPINGS_FIELD = new ParseField("runtime_mappings"); + private static final boolean RANK_SUPPORTED = Booleans.parseBoolean(System.getProperty("es.search.rank_supported"), true); + /** * A static factory method to construct a new search source. */ @@ -1351,6 +1353,9 @@ private SearchSourceBuilder parseXContent(XContentParser parser, boolean checkTr knnSearch = List.of(KnnSearchBuilder.fromXContent(parser)); searchUsage.trackSectionUsage(KNN_FIELD.getPreferredName()); } else if (RANK_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + if (RANK_SUPPORTED == false) { + throwUnknownKey(parser, token, currentFieldName); + } if (parser.nextToken() != XContentParser.Token.FIELD_NAME) { throw new ParsingException( parser.getTokenLocation(), @@ -1555,6 +1560,9 @@ private SearchSourceBuilder parseXContent(XContentParser parser, boolean checkTr } searchUsage.trackSectionUsage(KNN_FIELD.getPreferredName()); } else if (SUB_SEARCHES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + if (RANK_SUPPORTED == false) { + throwUnknownKey(parser, token, currentFieldName); + } if (subSearchSourceBuilders.isEmpty() == false) { throw new IllegalArgumentException( "cannot specify field [" + currentFieldName + "] and field [" + QUERY_FIELD.getPreferredName() + "]" @@ -1572,18 +1580,10 @@ private SearchSourceBuilder parseXContent(XContentParser parser, boolean checkTr } searchUsage.trackSectionUsage(SUB_SEARCHES_FIELD.getPreferredName()); } else { - throw new ParsingException( - parser.getTokenLocation(), - "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation() - ); + throwUnknownKey(parser, token, currentFieldName); } } else { - throw new ParsingException( - parser.getTokenLocation(), - "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation() - ); + throwUnknownKey(parser, token, currentFieldName); } } if (checkTrailingTokens) { @@ -1596,6 +1596,15 @@ private SearchSourceBuilder parseXContent(XContentParser parser, boolean checkTr return this; } + private static void throwUnknownKey(XContentParser parser, XContentParser.Token token, String currentFieldName) + throws ParsingException { + throw new ParsingException( + parser.getTokenLocation(), + "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation() + ); + } + public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { if (from != -1) { builder.field(FROM_FIELD.getPreferredName(), from); From 8a1db8c6c3ffa8028b98c9c1393aee9d9ea4d6f5 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 19 Oct 2023 18:44:51 -0600 Subject: [PATCH 046/203] Move index version constants to IndexVersions (#101094) Similar to the TransportVersions holder class, IndexVersions is the new place to contain all constants for IndexVersion. This commit moves all existing constants to the new class. It is purely mechanical. --- .../analysis/common/CommonAnalysisPlugin.java | 28 +-- .../common/CommonAnalysisPluginTests.java | 41 ++-- .../common/EdgeNGramTokenizerTests.java | 13 +- .../common/SynonymsAnalysisTests.java | 7 +- ...DelimiterGraphTokenFilterFactoryTests.java | 5 +- ...etadataDataStreamRolloverServiceTests.java | 3 +- .../mapper/LegacyGeoShapeFieldMapper.java | 3 +- .../legacygeo/GeoJsonShapeParserTests.java | 3 +- .../legacygeo/GeoWKTShapeParserTests.java | 7 +- .../LegacyGeoShapeFieldMapperTests.java | 3 +- .../mapper/LegacyGeoShapeFieldTypeTests.java | 3 +- .../percolator/PercolateQueryBuilder.java | 3 +- .../percolator/PercolatorFieldMapper.java | 3 +- .../migration/MultiFeatureMigrationIT.java | 4 +- .../repositories/s3/S3Repository.java | 6 +- .../rest/root/MainResponseTests.java | 3 +- .../AnalysisPhoneticFactoryTests.java | 3 +- .../MultiVersionRepositoryAccessIT.java | 3 +- .../elasticsearch/upgrades/RecoveryIT.java | 5 +- .../gateway/GatewayIndexStateIT.java | 4 +- .../mapping/MalformedDynamicTemplateIT.java | 3 +- .../repositories/IndexSnapshotsServiceIT.java | 3 +- .../snapshots/RestoreSnapshotIT.java | 5 +- .../main/java/org/elasticsearch/Build.java | 5 +- ...ransportGetFeatureUpgradeStatusAction.java | 3 +- .../rollover/MetadataRolloverService.java | 4 +- .../coordination/NodeJoinExecutor.java | 3 +- .../cluster/metadata/IndexMetadata.java | 13 +- .../metadata/IndexNameExpressionResolver.java | 3 +- .../metadata/MetadataCreateIndexService.java | 7 +- .../cluster/node/DiscoveryNode.java | 3 +- .../cluster/node/DiscoveryNodes.java | 3 +- .../cluster/node/VersionInformation.java | 3 +- .../allocator/DesiredBalanceReconciler.java | 4 +- .../common/settings/IndexScopedSettings.java | 5 +- .../common/time/DateFormatter.java | 3 +- .../HandshakingTransportAddressConnector.java | 3 +- .../elasticsearch/env/NodeEnvironment.java | 3 +- .../org/elasticsearch/env/NodeMetadata.java | 3 +- .../gateway/GatewayMetaState.java | 4 +- .../gateway/PersistedClusterStateService.java | 7 +- .../elasticsearch/index/IndexSettings.java | 4 +- .../elasticsearch/index/IndexSortConfig.java | 2 +- .../org/elasticsearch/index/IndexVersion.java | 180 ++---------------- .../elasticsearch/index/IndexVersions.java | 170 +++++++++++++++++ .../analysis/ShingleTokenFilterFactory.java | 6 +- .../index/engine/ReadOnlyEngine.java | 3 +- .../index/mapper/CompletionFieldMapper.java | 3 +- .../index/mapper/DateFieldMapper.java | 3 +- .../index/mapper/DocumentMapper.java | 3 +- .../index/mapper/DocumentParser.java | 3 +- .../index/mapper/FieldMapper.java | 11 +- .../index/mapper/FieldNamesFieldMapper.java | 5 +- .../index/mapper/GeoShapeFieldMapper.java | 4 +- .../index/mapper/IpFieldMapper.java | 3 +- .../elasticsearch/index/mapper/Mapper.java | 3 +- .../index/mapper/MapperRegistry.java | 5 +- .../index/mapper/MetadataFieldMapper.java | 4 +- .../index/mapper/NestedObjectMapper.java | 3 +- .../index/mapper/NestedPathFieldMapper.java | 5 +- .../index/mapper/ObjectMapper.java | 3 +- .../index/mapper/RootObjectMapper.java | 3 +- .../index/mapper/SourceFieldMapper.java | 4 +- .../index/mapper/TextFieldMapper.java | 3 +- .../index/mapper/TypeParsers.java | 4 +- .../vectors/DenseVectorFieldMapper.java | 7 +- .../vectors/SparseVectorFieldMapper.java | 7 +- .../index/seqno/ReplicationTracker.java | 8 +- .../elasticsearch/index/shard/IndexShard.java | 3 +- .../index/similarity/SimilarityProviders.java | 7 +- .../org/elasticsearch/index/store/Store.java | 3 +- .../indices/analysis/AnalysisModule.java | 5 +- .../indices/recovery/RecoverySettings.java | 5 +- .../recovery/RecoverySourceHandler.java | 4 +- .../repositories/RepositoriesModule.java | 3 +- .../repositories/RepositoryData.java | 11 +- .../completion/context/GeoContextMapping.java | 5 +- .../snapshots/SnapshotsService.java | 11 +- .../transport/ProxyConnectionStrategy.java | 3 +- .../transport/SniffConnectionStrategy.java | 3 +- .../GetFeatureUpgradeStatusResponseTests.java | 5 +- .../cluster/node/info/NodeInfoTests.java | 3 +- ...portPrevalidateNodeRemovalActionTests.java | 4 +- .../cluster/node/stats/NodeStatsTests.java | 4 +- .../reroute/ClusterRerouteResponseTests.java | 3 +- .../ClusterSearchShardsResponseTests.java | 3 +- .../cluster/stats/VersionStatsTests.java | 11 +- .../action/bulk/TransportBulkActionTests.java | 3 +- .../bulk/TransportBulkActionTookTests.java | 4 +- .../fieldcaps/RequestDispatcherTests.java | 12 +- .../search/SearchShardsResponseTests.java | 5 +- .../cluster/ClusterStateTests.java | 7 +- .../coordination/NodeJoinExecutorTests.java | 15 +- .../PublicationTransportHandlerTests.java | 6 +- .../cluster/metadata/IndexMetadataTests.java | 5 +- .../metadata/IndexMetadataVerifierTests.java | 9 +- .../MetadataCreateIndexServiceTests.java | 5 +- .../cluster/metadata/MetadataTests.java | 23 +-- .../cluster/node/DiscoveryNodeTests.java | 3 +- .../cluster/node/DiscoveryNodesTests.java | 5 +- .../allocation/AllocationCommandsTests.java | 7 +- .../allocation/FailedNodeRoutingTests.java | 3 +- .../allocation/FailedShardsRoutingTests.java | 5 +- .../NodeVersionAllocationDeciderTests.java | 9 +- .../env/NodeEnvironmentTests.java | 5 +- .../elasticsearch/env/NodeMetadataTests.java | 3 +- .../PersistedClusterStateServiceTests.java | 3 +- .../gateway/ReplicaShardAllocatorTests.java | 3 +- .../index/IndexSettingsTests.java | 2 +- .../index/IndexSortSettingsTests.java | 2 +- .../index/IndexVersionTests.java | 12 +- .../index/analysis/PreBuiltAnalyzerTests.java | 3 +- .../index/engine/InternalEngineTests.java | 9 +- .../index/mapper/DateFieldMapperTests.java | 9 +- .../index/mapper/DynamicTemplatesTests.java | 7 +- .../mapper/FieldNamesFieldMapperTests.java | 6 +- .../index/mapper/IpFieldMapperTests.java | 3 +- .../index/mapper/MapperServiceTests.java | 3 +- .../index/mapper/MappingParserTests.java | 3 +- .../index/mapper/NestedObjectMapperTests.java | 3 +- .../mapper/NestedPathFieldMapperTests.java | 3 +- .../index/mapper/ParametrizedMapperTests.java | 9 +- .../index/mapper/TypeParsersTests.java | 5 +- ...BinaryDenseVectorScriptDocValuesTests.java | 3 +- .../vectors/DenseVectorFieldMapperTests.java | 5 +- .../vectors/SparseVectorFieldMapperTests.java | 3 +- .../similarity/SimilarityServiceTests.java | 8 +- .../snapshots/blobstore/FileInfoTests.java | 4 +- .../elasticsearch/index/store/StoreTests.java | 3 +- .../store/VerifyingIndexOutputTests.java | 4 +- .../indices/IndicesModuleTests.java | 11 +- .../indices/analysis/AnalysisModuleTests.java | 5 +- .../indices/recovery/RecoveryStatusTests.java | 4 +- .../recovery/StartRecoveryRequestTests.java | 11 +- .../nodesinfo/NodeInfoStreamingTests.java | 4 +- .../repositories/RepositoryDataTests.java | 9 +- .../script/VectorScoreScriptUtilsTests.java | 9 +- .../field/vectors/DenseVectorTests.java | 3 +- .../ProxyConnectionStrategyTests.java | 3 +- .../SniffConnectionStrategyTests.java | 7 +- .../transport/TransportActionProxyTests.java | 5 +- .../TransportServiceHandshakeTests.java | 19 +- .../cluster/ESAllocationTestCase.java | 3 +- .../index/KnownIndexVersions.java | 2 +- .../index/mapper/MapperTestCase.java | 7 +- .../index/mapper/MetadataMapperTestCase.java | 7 +- .../AbstractSnapshotIntegTestCase.java | 5 +- .../test/index/IndexVersionUtils.java | 5 +- .../AbstractSimpleTransportTestCase.java | 9 +- .../test/index/IndexVersionUtilsTests.java | 5 +- .../action/AutoFollowCoordinatorTests.java | 3 +- .../ilm/SetSingleNodeAllocateStepTests.java | 7 +- .../deprecation/IndexDeprecationChecks.java | 3 +- .../IndexDeprecationChecksTests.java | 3 +- .../ClusterStatsMonitoringDocTests.java | 3 +- .../IndexRecoveryMonitoringDocTests.java | 3 +- .../xpack/profiling/IndexStateResolver.java | 4 +- .../ProfilingDataStreamManagerTests.java | 5 +- .../profiling/ProfilingIndexManagerTests.java | 5 +- ...archableSnapshotIndexMetadataUpgrader.java | 10 +- ...bleSnapshotIndexMetadataUpgraderTests.java | 9 +- .../xpack/security/SecurityTests.java | 5 +- .../filter/SecurityActionFilterTests.java | 3 +- .../SnapshotsRecoveryPlannerServiceTests.java | 5 +- ...BoxQueryLegacyGeoShapeWithDocValuesIT.java | 3 +- .../search/GeoShapeWithDocValuesIT.java | 3 +- .../search/LegacyGeoShapeWithDocValuesIT.java | 3 +- .../GeoShapeWithDocValuesFieldMapper.java | 7 +- .../index/mapper/ShapeFieldMapper.java | 3 +- .../index/query/ShapeQueryProcessor.java | 4 +- ...GeoShapeWithDocValuesFieldMapperTests.java | 5 +- .../index/mapper/ShapeFieldMapperTests.java | 3 +- .../GeoShapeQueryBuilderGeoShapeTests.java | 4 +- ...LegacyGeoShapeWithDocValuesQueryTests.java | 9 +- .../ShapeQueryBuilderOverShapeTests.java | 4 +- .../wildcard/mapper/WildcardFieldMapper.java | 3 +- .../mapper/WildcardFieldMapperTests.java | 3 +- 177 files changed, 730 insertions(+), 569 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/index/IndexVersions.java diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index dd2fbe8a19bab..42bd1296f0b69 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -111,7 +111,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerProvider; import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.PreBuiltAnalyzerProviderFactory; @@ -260,7 +260,7 @@ public Map> getTokenFilters() { return new EdgeNGramTokenFilterFactory(indexSettings, environment, name, settings) { @Override public TokenStream create(TokenStream tokenStream) { - if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_8_0_0)) { + if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "The [edgeNGram] token filter name was deprecated in 6.4 and cannot be used in new indices. " + "Please change the filter name to [edge_ngram] instead." @@ -301,7 +301,7 @@ public TokenStream create(TokenStream tokenStream) { return new NGramTokenFilterFactory(indexSettings, environment, name, settings) { @Override public TokenStream create(TokenStream tokenStream) { - if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_8_0_0)) { + if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "The [nGram] token filter name was deprecated in 6.4 and cannot be used in new indices. " + "Please change the filter name to [ngram] instead." @@ -371,12 +371,12 @@ public Map> getTokenizers() { tokenizers.put("simple_pattern_split", SimplePatternSplitTokenizerFactory::new); tokenizers.put("thai", ThaiTokenizerFactory::new); tokenizers.put("nGram", (IndexSettings indexSettings, Environment environment, String name, Settings settings) -> { - if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_8_0_0)) { + if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "The [nGram] tokenizer name was deprecated in 7.6. " + "Please use the tokenizer name to [ngram] for indices created in versions 8 or higher instead." ); - } else if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_7_6_0)) { + } else if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_7_6_0)) { deprecationLogger.warn( DeprecationCategory.ANALYSIS, "nGram_tokenizer_deprecation", @@ -388,12 +388,12 @@ public Map> getTokenizers() { }); tokenizers.put("ngram", NGramTokenizerFactory::new); tokenizers.put("edgeNGram", (IndexSettings indexSettings, Environment environment, String name, Settings settings) -> { - if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_8_0_0)) { + if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "The [edgeNGram] tokenizer name was deprecated in 7.6. " + "Please use the tokenizer name to [edge_nGram] for indices created in versions 8 or higher instead." ); - } else if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_7_6_0)) { + } else if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_7_6_0)) { deprecationLogger.warn( DeprecationCategory.ANALYSIS, "edgeNGram_tokenizer_deprecation", @@ -588,7 +588,7 @@ public List getPreConfiguredTokenFilters() { ) ); filters.add(PreConfiguredTokenFilter.indexVersion("word_delimiter_graph", false, false, (input, version) -> { - boolean adjustOffsets = version.onOrAfter(IndexVersion.V_7_3_0); + boolean adjustOffsets = version.onOrAfter(IndexVersions.V_7_3_0); return new WordDelimiterGraphFilter( input, adjustOffsets, @@ -613,7 +613,7 @@ public List getPreConfiguredTokenizers() { tokenizers.add(PreConfiguredTokenizer.singleton("whitespace", WhitespaceTokenizer::new)); tokenizers.add(PreConfiguredTokenizer.singleton("ngram", NGramTokenizer::new)); tokenizers.add(PreConfiguredTokenizer.indexVersion("edge_ngram", (version) -> { - if (version.onOrAfter(IndexVersion.V_7_3_0)) { + if (version.onOrAfter(IndexVersions.V_7_3_0)) { return new EdgeNGramTokenizer(NGramTokenizer.DEFAULT_MIN_NGRAM_SIZE, NGramTokenizer.DEFAULT_MAX_NGRAM_SIZE); } return new EdgeNGramTokenizer(EdgeNGramTokenizer.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenizer.DEFAULT_MAX_GRAM_SIZE); @@ -626,12 +626,12 @@ public List getPreConfiguredTokenizers() { // Temporary shim for aliases. TODO deprecate after they are moved tokenizers.add(PreConfiguredTokenizer.indexVersion("nGram", (version) -> { - if (version.onOrAfter(IndexVersion.V_8_0_0)) { + if (version.onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "The [nGram] tokenizer name was deprecated in 7.6. " + "Please use the tokenizer name to [ngram] for indices created in versions 8 or higher instead." ); - } else if (version.onOrAfter(IndexVersion.V_7_6_0)) { + } else if (version.onOrAfter(IndexVersions.V_7_6_0)) { deprecationLogger.warn( DeprecationCategory.ANALYSIS, "nGram_tokenizer_deprecation", @@ -642,12 +642,12 @@ public List getPreConfiguredTokenizers() { return new NGramTokenizer(); })); tokenizers.add(PreConfiguredTokenizer.indexVersion("edgeNGram", (version) -> { - if (version.onOrAfter(IndexVersion.V_8_0_0)) { + if (version.onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "The [edgeNGram] tokenizer name was deprecated in 7.6. " + "Please use the tokenizer name to [edge_ngram] for indices created in versions 8 or higher instead." ); - } else if (version.onOrAfter(IndexVersion.V_7_6_0)) { + } else if (version.onOrAfter(IndexVersions.V_7_6_0)) { deprecationLogger.warn( DeprecationCategory.ANALYSIS, "edgeNGram_tokenizer_deprecation", @@ -655,7 +655,7 @@ public List getPreConfiguredTokenizers() { + "Please change the tokenizer name to [edge_ngram] instead." ); } - if (version.onOrAfter(IndexVersion.V_7_3_0)) { + if (version.onOrAfter(IndexVersions.V_7_3_0)) { return new EdgeNGramTokenizer(NGramTokenizer.DEFAULT_MIN_NGRAM_SIZE, NGramTokenizer.DEFAULT_MAX_NGRAM_SIZE); } return new EdgeNGramTokenizer(EdgeNGramTokenizer.DEFAULT_MIN_GRAM_SIZE, EdgeNGramTokenizer.DEFAULT_MAX_GRAM_SIZE); diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisPluginTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisPluginTests.java index 38ce33dde9a01..c18cb3dddf0ae 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisPluginTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/CommonAnalysisPluginTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.TokenizerFactory; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; @@ -32,7 +33,7 @@ public void testNGramFilterInCustomAnalyzerDeprecationError() throws IOException .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()) ) .put("index.analysis.analyzer.custom_analyzer.type", "custom") .put("index.analysis.analyzer.custom_analyzer.tokenizer", "standard") @@ -56,7 +57,7 @@ public void testNGramFilterInCustomAnalyzerDeprecationError() throws IOException .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_6_0) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_6_0) ) .put("index.analysis.analyzer.custom_analyzer.type", "custom") .put("index.analysis.analyzer.custom_analyzer.tokenizer", "standard") @@ -81,7 +82,7 @@ public void testEdgeNGramFilterInCustomAnalyzerDeprecationError() throws IOExcep .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()) ) .put("index.analysis.analyzer.custom_analyzer.type", "custom") .put("index.analysis.analyzer.custom_analyzer.tokenizer", "standard") @@ -105,7 +106,7 @@ public void testEdgeNGramFilterInCustomAnalyzerDeprecationError() throws IOExcep .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir()) .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_6_0) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_6_0) ) .put("index.analysis.analyzer.custom_analyzer.type", "custom") .put("index.analysis.analyzer.custom_analyzer.tokenizer", "standard") @@ -131,13 +132,13 @@ public void testNGramTokenizerDeprecation() throws IOException { doTestPrebuiltTokenizerDeprecation( "nGram", "ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_5_2), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_5_2), false ); doTestPrebuiltTokenizerDeprecation( "edgeNGram", "edge_ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_5_2), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_5_2), false ); doTestPrebuiltTokenizerDeprecation( @@ -145,8 +146,8 @@ public void testNGramTokenizerDeprecation() throws IOException { "ngram", IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_6_0, - IndexVersion.max(IndexVersion.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0)) + IndexVersions.V_7_6_0, + IndexVersion.max(IndexVersions.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0)) ), true ); @@ -155,8 +156,8 @@ public void testNGramTokenizerDeprecation() throws IOException { "edge_ngram", IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_6_0, - IndexVersion.max(IndexVersion.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0)) + IndexVersions.V_7_6_0, + IndexVersion.max(IndexVersions.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0)) ), true ); @@ -165,7 +166,7 @@ public void testNGramTokenizerDeprecation() throws IOException { () -> doTestPrebuiltTokenizerDeprecation( "nGram", "ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()), true ) ); @@ -174,7 +175,7 @@ public void testNGramTokenizerDeprecation() throws IOException { () -> doTestPrebuiltTokenizerDeprecation( "edgeNGram", "edge_ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()), true ) ); @@ -183,13 +184,13 @@ public void testNGramTokenizerDeprecation() throws IOException { doTestCustomTokenizerDeprecation( "nGram", "ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_5_2), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_5_2), false ); doTestCustomTokenizerDeprecation( "edgeNGram", "edge_ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_5_2), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_5_2), false ); doTestCustomTokenizerDeprecation( @@ -197,8 +198,8 @@ public void testNGramTokenizerDeprecation() throws IOException { "ngram", IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_6_0, - IndexVersion.max(IndexVersion.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0)) + IndexVersions.V_7_6_0, + IndexVersion.max(IndexVersions.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0)) ), true ); @@ -207,8 +208,8 @@ public void testNGramTokenizerDeprecation() throws IOException { "edge_ngram", IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_6_0, - IndexVersion.max(IndexVersion.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0)) + IndexVersions.V_7_6_0, + IndexVersion.max(IndexVersions.V_7_6_0, IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0)) ), true ); @@ -217,7 +218,7 @@ public void testNGramTokenizerDeprecation() throws IOException { () -> doTestCustomTokenizerDeprecation( "nGram", "ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()), true ) ); @@ -226,7 +227,7 @@ public void testNGramTokenizerDeprecation() throws IOException { () -> doTestCustomTokenizerDeprecation( "edgeNGram", "edge_ngram", - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()), true ) ); diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/EdgeNGramTokenizerTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/EdgeNGramTokenizerTests.java index 7f522d55addc7..412e3ba3e380a 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/EdgeNGramTokenizerTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/EdgeNGramTokenizerTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.index.IndexService.IndexCreationContext; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.indices.analysis.AnalysisModule; @@ -51,8 +52,8 @@ public void testPreConfiguredTokenizer() throws IOException { { IndexVersion version = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_7_3_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_7_3_0) ); try (IndexAnalyzers indexAnalyzers = buildAnalyzers(version, "edge_ngram")) { NamedAnalyzer analyzer = indexAnalyzers.get("my_analyzer"); @@ -65,8 +66,8 @@ public void testPreConfiguredTokenizer() throws IOException { { IndexVersion version = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_7_3_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_7_3_0) ); try (IndexAnalyzers indexAnalyzers = buildAnalyzers(version, "edgeNGram")) { NamedAnalyzer analyzer = indexAnalyzers.get("my_analyzer"); @@ -90,8 +91,8 @@ public void testPreConfiguredTokenizer() throws IOException { IndexAnalyzers indexAnalyzers = buildAnalyzers( IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_3_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_3_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ), "edgeNGram" ) diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java index 8ab1b17b6e732..8758c2478e873 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java @@ -18,6 +18,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; import org.elasticsearch.index.analysis.TokenFilterFactory; @@ -238,7 +239,7 @@ public void testShingleFilters() { Settings settings = Settings.builder() .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersion.current()) ) .put("path.home", createTempDir().toString()) .put("index.analysis.filter.synonyms.type", "synonym") @@ -293,7 +294,7 @@ public void testPreconfiguredTokenFilters() throws IOException { Settings settings = Settings.builder() .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersion.current()) ) .put("path.home", createTempDir().toString()) .build(); @@ -325,7 +326,7 @@ public void testDisallowedTokenFilters() throws IOException { Settings settings = Settings.builder() .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersion.current()) ) .put("path.home", createTempDir().toString()) .putList("common_words", "a", "b") diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java index 1ae499ba7e634..68e6d6661f944 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.index.IndexService.IndexCreationContext; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalysisTestsHelper; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; @@ -187,8 +188,8 @@ public void testPreconfiguredFilter() throws IOException { IndexMetadata.SETTING_VERSION_CREATED, IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_7_3_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_7_3_0) ) ) .put("index.analysis.analyzer.my_analyzer.tokenizer", "standard") diff --git a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/MetadataDataStreamRolloverServiceTests.java b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/MetadataDataStreamRolloverServiceTests.java index c0cb1e5452c3d..0391f91a35fb3 100644 --- a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/MetadataDataStreamRolloverServiceTests.java +++ b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/MetadataDataStreamRolloverServiceTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.index.IndexMode; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.MapperTestUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; @@ -491,7 +492,7 @@ private static ClusterState createClusterState(String dataStreamName, int number .put("index.mode", "time_series") .put("index.routing_path", "uid"); if (includeVersion) { - settings.put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.V_8_9_0); + settings.put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.V_8_9_0); } builder.put(IndexMetadata.builder(backingIndex.getName()).settings(settings).numberOfShards(1).numberOfReplicas(0)); } diff --git a/modules/legacy-geo/src/main/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapper.java b/modules/legacy-geo/src/main/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapper.java index 51cc7541a9a4d..3ae6e29802962 100644 --- a/modules/legacy-geo/src/main/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapper.java +++ b/modules/legacy-geo/src/main/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapper.java @@ -29,6 +29,7 @@ import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.elasticsearch.index.mapper.DocumentParserContext; @@ -243,7 +244,7 @@ public Builder(String name, IndexVersion version, boolean ignoreMalformedByDefau }); // Set up serialization - if (version.onOrAfter(IndexVersion.V_7_0_0)) { + if (version.onOrAfter(IndexVersions.V_7_0_0)) { this.strategy.alwaysSerialize(); } // serialize treeLevels if treeLevels is configured, OR if defaults are requested and precision is not configured diff --git a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoJsonShapeParserTests.java b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoJsonShapeParserTests.java index 98effff65d8ed..5ef5eb6c0b5b8 100644 --- a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoJsonShapeParserTests.java +++ b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoJsonShapeParserTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.geometry.MultiLine; import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperBuilderContext; import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper; import org.elasticsearch.legacygeo.parsers.ShapeParser; @@ -383,7 +384,7 @@ public void testParse3DPolygon() throws IOException, ParseException { LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null); - final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); final LegacyGeoShapeFieldMapper mapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", version, false, true).build( MapperBuilderContext.root(false, false) ); diff --git a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoWKTShapeParserTests.java b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoWKTShapeParserTests.java index 5037e0daff13e..6e8a61277cccf 100644 --- a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoWKTShapeParserTests.java +++ b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/GeoWKTShapeParserTests.java @@ -18,6 +18,7 @@ import org.elasticsearch.geometry.MultiLine; import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperBuilderContext; import org.elasticsearch.legacygeo.builders.CoordinatesBuilder; import org.elasticsearch.legacygeo.builders.EnvelopeBuilder; @@ -323,7 +324,7 @@ public void testParseMixedDimensionPolyWithHoleStoredZ() throws IOException { XContentParser parser = createParser(xContentBuilder); parser.nextToken(); - final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); final LegacyGeoShapeFieldMapper mapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", version, false, true).build( MapperBuilderContext.root(false, false) ); @@ -347,7 +348,7 @@ public void testParsePolyWithStoredZ() throws IOException { XContentParser parser = createParser(xContentBuilder); parser.nextToken(); - final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); final LegacyGeoShapeFieldMapper mapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", version, false, true).build( MapperBuilderContext.root(false, false) ); @@ -363,7 +364,7 @@ public void testParseOpenPolygon() throws IOException { XContentParser parser = createParser(xContentBuilder); parser.nextToken(); - final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + final IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); final LegacyGeoShapeFieldMapper defaultMapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", version, false, true).coerce( false ).build(MapperBuilderContext.root(false, false)); diff --git a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapperTests.java b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapperTests.java index e93eaafd13f58..91a94fe174c21 100644 --- a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapperTests.java +++ b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapperTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.geometry.Point; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper; @@ -115,7 +116,7 @@ protected boolean supportsMeta() { @Override protected IndexVersion getVersion() { - return IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + return IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); } public void testLegacySwitches() throws IOException { diff --git a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldTypeTests.java b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldTypeTests.java index 1e2cc84fd4520..dc74b9cd295ce 100644 --- a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldTypeTests.java +++ b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldTypeTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.geo.SpatialStrategy; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.FieldTypeTestCase; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperBuilderContext; @@ -35,7 +36,7 @@ public void testSetStrategyName() { } public void testFetchSourceValue() throws IOException { - IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); MappedFieldType mapper = new LegacyGeoShapeFieldMapper.Builder("field", version, false, true).build( MapperBuilderContext.root(false, false) ).fieldType(); diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java index eac647cfff634..886a67443e831 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java @@ -47,6 +47,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.RestApiVersion; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldDataCache; @@ -597,7 +598,7 @@ static PercolateQuery.QueryStore createStore(MappedFieldType queryBuilderFieldTy assert valueLength > 0; TransportVersion transportVersion; - if (indexVersion.before(IndexVersion.V_8_8_0)) { + if (indexVersion.before(IndexVersions.V_8_8_0)) { transportVersion = TransportVersion.fromId(indexVersion.id()); } else { transportVersion = TransportVersion.readVersion(input); diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java index 016d9d3f75a21..c00eaa894dd69 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java @@ -39,6 +39,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.BinaryFieldMapper; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; @@ -457,7 +458,7 @@ static void createQueryBuilderField( ByteArrayOutputStream stream = new ByteArrayOutputStream(); OutputStreamStreamOutput out = new OutputStreamStreamOutput(stream) ) { - if (indexVersion.before(IndexVersion.V_8_8_0)) { + if (indexVersion.before(IndexVersions.V_8_8_0)) { // just use the index version directly // there's a direct mapping from IndexVersion to TransportVersion before 8.8.0 out.setTransportVersion(TransportVersion.fromId(indexVersion.id())); diff --git a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java index feeb989959626..8f9c2b7f34105 100644 --- a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java +++ b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/migration/MultiFeatureMigrationIT.java @@ -23,7 +23,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; @@ -264,7 +264,7 @@ public void testMultipleFeatureMigration() throws Exception { .setAliasName(".second-internal-managed-alias") .setPrimaryIndex(".second-int-man-old") .setType(SystemIndexDescriptor.Type.INTERNAL_MANAGED) - .setSettings(createSettings(IndexVersion.V_7_0_0, 0)) + .setSettings(createSettings(IndexVersions.V_7_0_0, 0)) .setMappings(createMapping(true, true)) .setOrigin(ORIGIN) .setVersionMetaKey(VERSION_META_KEY) diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index 02fe197077530..0f15d6802eeb6 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -27,7 +27,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.repositories.FinalizeSnapshotContext; @@ -153,12 +153,12 @@ class S3Repository extends MeteredBlobStoreRepository { /** * Artificial delay to introduce after a snapshot finalization or delete has finished so long as the repository is still using the * backwards compatible snapshot format from before - * {@link org.elasticsearch.snapshots.SnapshotsService#SHARD_GEN_IN_REPO_DATA_VERSION} ({@link IndexVersion#V_7_6_0}). + * {@link org.elasticsearch.snapshots.SnapshotsService#SHARD_GEN_IN_REPO_DATA_VERSION} ({@link IndexVersions#V_7_6_0}). * This delay is necessary so that the eventually consistent nature of AWS S3 does not randomly result in repository corruption when * doing repository operations in rapid succession on a repository in the old metadata format. * This setting should not be adjusted in production when working with an AWS S3 backed repository. Doing so risks the repository * becoming silently corrupted. To get rid of this waiting period, either create a new S3 repository or remove all snapshots older than - * {@link IndexVersion#V_7_6_0} from the repository which will trigger an upgrade of the repository metadata to the new + * {@link IndexVersions#V_7_6_0} from the repository which will trigger an upgrade of the repository metadata to the new * format and disable the cooldown period. */ static final Setting COOLDOWN_PERIOD = Setting.timeSetting( diff --git a/modules/rest-root/src/test/java/org/elasticsearch/rest/root/MainResponseTests.java b/modules/rest-root/src/test/java/org/elasticsearch/rest/root/MainResponseTests.java index dc61abf33e286..e715638ee52bb 100644 --- a/modules/rest-root/src/test/java/org/elasticsearch/rest/root/MainResponseTests.java +++ b/modules/rest-root/src/test/java/org/elasticsearch/rest/root/MainResponseTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; @@ -67,7 +68,7 @@ public void testToXContent() throws IOException { build.isSnapshot(), indexVersion.luceneVersion().toString(), build.minWireCompatVersion(), - Build.minimumCompatString(IndexVersion.MINIMUM_COMPATIBLE) + Build.minimumCompatString(IndexVersions.MINIMUM_COMPATIBLE) ) ), Strings.toString(builder) diff --git a/plugins/analysis-phonetic/src/test/java/org/elasticsearch/plugin/analysis/phonetic/AnalysisPhoneticFactoryTests.java b/plugins/analysis-phonetic/src/test/java/org/elasticsearch/plugin/analysis/phonetic/AnalysisPhoneticFactoryTests.java index d1c4cb5a6e4c0..348e9f5fae7c8 100644 --- a/plugins/analysis-phonetic/src/test/java/org/elasticsearch/plugin/analysis/phonetic/AnalysisPhoneticFactoryTests.java +++ b/plugins/analysis-phonetic/src/test/java/org/elasticsearch/plugin/analysis/phonetic/AnalysisPhoneticFactoryTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.indices.analysis.AnalysisFactoryTestCase; import org.elasticsearch.test.IndexSettingsModule; @@ -42,7 +43,7 @@ public void testDisallowedWithSynonyms() throws IOException { Settings settings = Settings.builder() .put( IndexMetadata.SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersion.current()) ) .put("path.home", createTempDir().toString()) .build(); diff --git a/qa/repository-multi-version/src/test/java/org/elasticsearch/upgrades/MultiVersionRepositoryAccessIT.java b/qa/repository-multi-version/src/test/java/org/elasticsearch/upgrades/MultiVersionRepositoryAccessIT.java index a7dad1ebeec50..c020cc118ca78 100644 --- a/qa/repository-multi-version/src/test/java/org/elasticsearch/upgrades/MultiVersionRepositoryAccessIT.java +++ b/qa/repository-multi-version/src/test/java/org/elasticsearch/upgrades/MultiVersionRepositoryAccessIT.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.test.rest.ObjectPath; @@ -183,7 +184,7 @@ public void testUpgradeMovesRepoToNewMetaVersion() throws IOException { // to check behavior on other operations below. final boolean verify = TEST_STEP != TestStep.STEP3_OLD_CLUSTER || SnapshotsService.includesUUIDs(minNodeVersion) - || minNodeVersion.before(IndexVersion.V_7_12_0); + || minNodeVersion.before(IndexVersions.V_7_12_0); if (verify == false) { expectThrowsAnyOf(EXPECTED_BWC_EXCEPTIONS, () -> createRepository(repoName, false, true)); } diff --git a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java index f08427f8949a5..eb05d331af033 100644 --- a/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java +++ b/qa/rolling-upgrade-legacy/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java @@ -24,6 +24,7 @@ import org.elasticsearch.core.Booleans; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.rest.ObjectPath; import org.hamcrest.Matchers; @@ -430,7 +431,7 @@ public void testRecoveryClosedIndex() throws Exception { } final IndexVersion indexVersionCreated = indexVersionCreated(indexName); - if (indexVersionCreated.onOrAfter(IndexVersion.V_7_2_0)) { + if (indexVersionCreated.onOrAfter(IndexVersions.V_7_2_0)) { // index was created on a version that supports the replication of closed indices, // so we expect the index to be closed and replicated ensureGreen(indexName); @@ -500,7 +501,7 @@ public void testClosedIndexNoopRecovery() throws Exception { closeIndex(indexName); } - if (indexVersionCreated(indexName).onOrAfter(IndexVersion.V_7_2_0)) { + if (indexVersionCreated(indexName).onOrAfter(IndexVersions.V_7_2_0)) { // index was created on a version that supports the replication of closed indices, so we expect it to be closed and replicated assertTrue(minimumNodeVersion().onOrAfter(Version.V_7_2_0)); ensureGreen(indexName); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java index 1817e5c6debe6..ff47be7ee4e01 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java @@ -36,7 +36,7 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.NodeMetadata; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.indices.IndexClosedException; import org.elasticsearch.indices.ShardLimitValidator; @@ -400,7 +400,7 @@ public void testRecoverBrokenIndexMetadata() throws Exception { .settings( Settings.builder() .put(metadata.getSettings()) - .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.MINIMUM_COMPATIBLE) + .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.MINIMUM_COMPATIBLE) // this is invalid but should be archived .put("index.similarity.BM25.type", "boolean") // this one is not validated ahead of time and breaks allocation diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java index 6e2b989c6da8a..ce916541dfaa5 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.index.IndexVersionUtils; @@ -54,7 +55,7 @@ public void testBWCMalformedDynamicTemplate() { Settings.builder() .put(indexSettings()) .put("number_of_shards", 1) - .put("index.version.created", IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0)) + .put("index.version.created", IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0)) ).setMapping(mapping).get() ); client().prepareIndex(indexName).setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/repositories/IndexSnapshotsServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/repositories/IndexSnapshotsServiceIT.java index b77428c6f6396..be8053a1d6866 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/repositories/IndexSnapshotsServiceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/repositories/IndexSnapshotsServiceIT.java @@ -20,6 +20,7 @@ import org.elasticsearch.core.Strings; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.repositories.fs.FsRepository; import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase; @@ -113,7 +114,7 @@ public void testGetShardSnapshotReturnsTheLatestSuccessfulSnapshot() throws Exce final boolean useBwCFormat = randomBoolean(); if (useBwCFormat) { - final IndexVersion version = randomVersionBetween(random(), IndexVersion.V_7_5_0, IndexVersion.current()); + final IndexVersion version = randomVersionBetween(random(), IndexVersions.V_7_5_0, IndexVersion.current()); initWithSnapshotVersion(repoName, repoPath, version); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java index c541d157b3c63..e57731efbcf2b 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.InvalidIndexNameException; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.blobstore.FileRestoreContext; @@ -905,7 +906,7 @@ public void testFailOnAncientVersion() throws Exception { final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); createRepository(repoName, FsRepository.TYPE, repoPath); - final IndexVersion oldVersion = IndexVersion.fromId(IndexVersion.MINIMUM_COMPATIBLE.id() - 1); + final IndexVersion oldVersion = IndexVersion.fromId(IndexVersions.MINIMUM_COMPATIBLE.id() - 1); final String oldSnapshot = initWithSnapshotVersion(repoName, repoPath, oldVersion); final SnapshotRestoreException snapshotRestoreException = expectThrows( SnapshotRestoreException.class, @@ -917,7 +918,7 @@ public void testFailOnAncientVersion() throws Exception { "the snapshot was created with Elasticsearch version [" + oldVersion + "] which is below the current versions minimum index compatibility version [" - + IndexVersion.MINIMUM_COMPATIBLE + + IndexVersions.MINIMUM_COMPATIBLE + "]" ) ); diff --git a/server/src/main/java/org/elasticsearch/Build.java b/server/src/main/java/org/elasticsearch/Build.java index 1320346ce2ee3..24cd82d29614e 100644 --- a/server/src/main/java/org/elasticsearch/Build.java +++ b/server/src/main/java/org/elasticsearch/Build.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.core.Booleans; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.internal.BuildExtension; import org.elasticsearch.plugins.ExtensionLoader; @@ -127,14 +128,14 @@ private static Build findLocalBuild() { final String flavor = "default"; String minWireCompat = Version.CURRENT.minimumCompatibilityVersion().toString(); - String minIndexCompat = minimumCompatString(IndexVersion.MINIMUM_COMPATIBLE); + String minIndexCompat = minimumCompatString(IndexVersions.MINIMUM_COMPATIBLE); String displayString = defaultDisplayString(type, hash, date, qualifiedVersionString(version, qualifier, isSnapshot)); return new Build(flavor, type, hash, date, version, qualifier, isSnapshot, minWireCompat, minIndexCompat, displayString); } public static String minimumCompatString(IndexVersion minimumCompatible) { - if (minimumCompatible.before(IndexVersion.FIRST_DETACHED_INDEX_VERSION)) { + if (minimumCompatible.before(IndexVersions.FIRST_DETACHED_INDEX_VERSION)) { // use Version for compatibility return Version.fromId(minimumCompatible.id()).toString(); } else { diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java index 652f5102769ae..af8637cf1febc 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/migration/TransportGetFeatureUpgradeStatusAction.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import org.elasticsearch.persistent.PersistentTasksService; @@ -54,7 +55,7 @@ public class TransportGetFeatureUpgradeStatusAction extends TransportMasterNodeA * Once all feature migrations for 8.x -> 9.x have been tested, we can bump this to Version.V_8_0_0 */ public static final Version NO_UPGRADE_REQUIRED_VERSION = Version.V_7_0_0; - public static final IndexVersion NO_UPGRADE_REQUIRED_INDEX_VERSION = IndexVersion.V_7_0_0; + public static final IndexVersion NO_UPGRADE_REQUIRED_INDEX_VERSION = IndexVersions.V_7_0_0; private final SystemIndices systemIndices; PersistentTasksService persistentTasksService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java index c2177b757a7a3..1ba249aff8538 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java @@ -34,7 +34,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexMode; import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.SystemDataStreamDescriptor; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.snapshots.SnapshotInProgressException; @@ -327,7 +327,7 @@ private static void downgradeBrokenTsdbBackingIndices(DataStream dataStream, Met for (Index indexName : dataStream.getIndices()) { var index = builder.getSafe(indexName); final Settings originalSettings = index.getSettings(); - if (index.getCreationVersion().before(IndexVersion.FIRST_DETACHED_INDEX_VERSION) + if (index.getCreationVersion().before(IndexVersions.FIRST_DETACHED_INDEX_VERSION) && index.getIndexMode() == IndexMode.TIME_SERIES && originalSettings.keySet().contains(IndexSettings.TIME_SERIES_START_TIME.getKey()) == false && originalSettings.keySet().contains(IndexSettings.TIME_SERIES_END_TIME.getKey()) == false) { diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java index 170648452d141..a9c1332c59915 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.Priority; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import java.util.ArrayList; @@ -328,7 +329,7 @@ private static void blockForbiddenVersions(TransportVersion joiningTransportVers * Ensures that all indices are compatible with the given index version. This will ensure that all indices in the given metadata * will not be created with a newer version of elasticsearch as well as that all indices are newer or equal to the minimum index * compatibility version. - * @see IndexVersion#MINIMUM_COMPATIBLE + * @see IndexVersions#MINIMUM_COMPATIBLE * @throws IllegalStateException if any index is incompatible with the given version */ public static void ensureIndexCompatibility(IndexVersion minSupportedVersion, IndexVersion maxSupportedVersion, Metadata metadata) { diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java index 9709e149b28d1..c19b6b801c3c5 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java @@ -45,6 +45,7 @@ import org.elasticsearch.index.IndexMode; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -349,7 +350,7 @@ public static APIBlock readFrom(StreamInput input) throws IOException { public static final Setting SETTING_INDEX_VERSION_CREATED = Setting.versionIdSetting( SETTING_VERSION_CREATED, - IndexVersion.ZERO, + IndexVersions.ZERO, IndexVersion::fromId, Property.IndexScope, Property.PrivateIndex @@ -1057,7 +1058,7 @@ public IndexVersion getCreationVersion() { /** * Return the {@link IndexVersion} that this index provides compatibility for. - * This is typically compared to the {@link IndexVersion#MINIMUM_COMPATIBLE} to figure out whether the index can be handled + * This is typically compared to the {@link IndexVersions#MINIMUM_COMPATIBLE} to figure out whether the index can be handled * by the cluster. * By default, this is equal to the {@link #getCreationVersion()}, but can also be a newer version if the index has been imported as * a legacy index from an older snapshot, and its metadata has been converted to be handled by newer version nodes. @@ -2190,7 +2191,7 @@ IndexMetadata build(boolean repair) { var aliasesMap = aliases.build(); for (AliasMetadata alias : aliasesMap.values()) { if (alias.alias().equals(index)) { - if (repair && indexCreatedVersion.equals(IndexVersion.V_8_5_0)) { + if (repair && indexCreatedVersion.equals(IndexVersions.V_8_5_0)) { var updatedBuilder = ImmutableOpenMap.builder(aliasesMap); final var brokenAlias = updatedBuilder.remove(index); final var fixedAlias = AliasMetadata.newAliasMetadata(brokenAlias, index + "-alias-corrupted-by-8-5"); @@ -2520,7 +2521,7 @@ public static IndexMetadata fromXContent(XContentParser parser, Map map */ private static IndexVersion indexCreatedVersion(Settings indexSettings) { IndexVersion indexVersion = IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings); - if (indexVersion.equals(IndexVersion.ZERO)) { + if (indexVersion.equals(IndexVersions.ZERO)) { final String message = String.format( Locale.ROOT, "[%s] is not present in the index settings for index with UUID [%s]", @@ -2676,7 +2677,7 @@ private static IndexVersion indexCreatedVersion(Settings indexSettings) { public static Settings addHumanReadableSettings(Settings settings) { Settings.Builder builder = Settings.builder().put(settings); IndexVersion version = SETTING_INDEX_VERSION_CREATED.get(settings); - if (version.equals(IndexVersion.ZERO) == false) { + if (version.equals(IndexVersions.ZERO) == false) { builder.put(SETTING_VERSION_CREATED_STRING, version.toString()); } Long creationDate = settings.getAsLong(SETTING_CREATION_DATE, null); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index 904dc5eb933b4..b50b1e0a74d93 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -30,6 +30,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.IndexClosedException; import org.elasticsearch.indices.InvalidIndexNameException; import org.elasticsearch.indices.SystemIndices; @@ -63,7 +64,7 @@ public class IndexNameExpressionResolver { public static final String EXCLUDED_DATA_STREAMS_KEY = "es.excluded_ds"; public static final Version SYSTEM_INDEX_ENFORCEMENT_VERSION = Version.V_8_0_0; - public static final IndexVersion SYSTEM_INDEX_ENFORCEMENT_INDEX_VERSION = IndexVersion.V_8_0_0; + public static final IndexVersion SYSTEM_INDEX_ENFORCEMENT_INDEX_VERSION = IndexVersions.V_8_0_0; private final ThreadContext threadContext; private final SystemIndices systemIndices; diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java index 3302549a1b860..da24f0b9d0dc5 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java @@ -60,6 +60,7 @@ import org.elasticsearch.index.IndexSettingProviders; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService.MergeReason; @@ -1088,7 +1089,7 @@ static Settings aggregateIndexSettings( private static void validateSoftDeleteSettings(Settings indexSettings) { if (IndexSettings.INDEX_SOFT_DELETES_SETTING.get(indexSettings) == false - && IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings).onOrAfter(IndexVersion.V_8_0_0)) { + && IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings).onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "Creating indices with soft-deletes disabled is no longer supported. " + "Please do not specify a value for setting [index.soft_deletes.enabled]." @@ -1591,7 +1592,7 @@ static void prepareResizeIndexSettings( * the less default split operations are supported */ public static int calculateNumRoutingShards(int numShards, IndexVersion indexVersionCreated) { - if (indexVersionCreated.onOrAfter(IndexVersion.V_7_0_0)) { + if (indexVersionCreated.onOrAfter(IndexVersions.V_7_0_0)) { // only select this automatically for indices that are created on or after 7.0 this will prevent this new behaviour // until we have a fully upgraded cluster. Additionally it will make integrating testing easier since mixed clusters // will always have the behavior of the min node in the cluster. @@ -1609,7 +1610,7 @@ public static int calculateNumRoutingShards(int numShards, IndexVersion indexVer } public static void validateTranslogRetentionSettings(Settings indexSettings) { - if (IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings).onOrAfter(IndexVersion.V_8_0_0) + if (IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings).onOrAfter(IndexVersions.V_8_0_0) && (IndexSettings.INDEX_TRANSLOG_RETENTION_AGE_SETTING.exists(indexSettings) || IndexSettings.INDEX_TRANSLOG_RETENTION_SIZE_SETTING.exists(indexSettings))) { throw new IllegalArgumentException( diff --git a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java index 554bdce72a2ff..60aab68ec65dc 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.util.StringLiteralDeduplicator; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.node.Node; import org.elasticsearch.xcontent.ToXContentFragment; import org.elasticsearch.xcontent.XContentBuilder; @@ -295,7 +296,7 @@ private static VersionInformation inferVersionInformation(Version version) { IndexVersion.fromId(version.id) ); } else { - return new VersionInformation(version, IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()); + return new VersionInformation(version, IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()); } } diff --git a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java index 3ee28437ff81c..cd2c927d87f69 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNodes.java @@ -21,6 +21,7 @@ import org.elasticsearch.core.Booleans; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import java.io.IOException; import java.util.ArrayList; @@ -881,7 +882,7 @@ public DiscoveryNodes build() { Objects.requireNonNullElse(maxNodeVersion, Version.CURRENT), Objects.requireNonNullElse(minNodeVersion, Version.CURRENT.minimumCompatibilityVersion()), Objects.requireNonNullElse(maxDataNodeCompatibleIndexVersion, IndexVersion.current()), - Objects.requireNonNullElse(minSupportedIndexVersion, IndexVersion.MINIMUM_COMPATIBLE), + Objects.requireNonNullElse(minSupportedIndexVersion, IndexVersions.MINIMUM_COMPATIBLE), computeTiersToNodesMap(dataNodes) ); } diff --git a/server/src/main/java/org/elasticsearch/cluster/node/VersionInformation.java b/server/src/main/java/org/elasticsearch/cluster/node/VersionInformation.java index a2e2e801db958..7cb140ee42c03 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/VersionInformation.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/VersionInformation.java @@ -10,6 +10,7 @@ import org.elasticsearch.Version; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import java.util.Objects; @@ -23,7 +24,7 @@ public record VersionInformation(Version nodeVersion, IndexVersion minIndexVersi public static final VersionInformation CURRENT = new VersionInformation( Version.CURRENT, - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceReconciler.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceReconciler.java index 279c774127e04..625591ba8b90b 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceReconciler.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/DesiredBalanceReconciler.java @@ -28,7 +28,7 @@ import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; import org.elasticsearch.gateway.PriorityComparator; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.threadpool.ThreadPool; @@ -145,7 +145,7 @@ private boolean allocateUnassignedInvariant() { final var shardCounts = allocation.metadata().stream().filter(indexMetadata -> // skip any pre-7.2 closed indices which have no routing table entries at all - indexMetadata.getCreationVersion().onOrAfter(IndexVersion.V_7_2_0) + indexMetadata.getCreationVersion().onOrAfter(IndexVersions.V_7_2_0) || indexMetadata.getState() == IndexMetadata.State.OPEN || MetadataIndexStateService.isIndexVerifiedBeforeClosed(indexMetadata)) .flatMap( diff --git a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index 4cd2ff52cf29a..f7c9e72d36326 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -22,6 +22,7 @@ import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSortConfig; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.IndexingSlowLog; import org.elasticsearch.index.MergePolicyConfig; import org.elasticsearch.index.MergeSchedulerConfig; @@ -254,8 +255,8 @@ protected void validateDeprecatedAndRemovedSettingV7(Settings settings, Setting< // IndexMetadata at hand, in which case the setting version will be empty. We don't want to // error out on those validations, we will check with the creation version present at index // creation time, as well as on index update settings. - if (indexVersion.equals(IndexVersion.ZERO) == false - && (indexVersion.before(IndexVersion.V_7_0_0) || indexVersion.onOrAfter(IndexVersion.V_8_0_0))) { + if (indexVersion.equals(IndexVersions.ZERO) == false + && (indexVersion.before(IndexVersions.V_7_0_0) || indexVersion.onOrAfter(IndexVersions.V_8_0_0))) { throw new IllegalArgumentException("unknown setting [" + setting.getKey() + "]"); } } diff --git a/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java index b168513baf427..41f44dfbdedbc 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatter.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import java.time.Instant; import java.time.ZoneId; @@ -119,7 +120,7 @@ static DateFormatter forPattern(String input, IndexVersion supportedVersion) { List formatters = new ArrayList<>(patterns.length); for (String pattern : patterns) { // make sure we still support camel case for indices created before 8.0 - if (supportedVersion.before(IndexVersion.V_8_0_0)) { + if (supportedVersion.before(IndexVersions.V_8_0_0)) { pattern = LegacyFormatNames.camelCaseToSnakeCase(pattern); } formatters.add(DateFormatters.forPattern(pattern)); diff --git a/server/src/main/java/org/elasticsearch/discovery/HandshakingTransportAddressConnector.java b/server/src/main/java/org/elasticsearch/discovery/HandshakingTransportAddressConnector.java index 89fd114e7852a..209faa7207be1 100644 --- a/server/src/main/java/org/elasticsearch/discovery/HandshakingTransportAddressConnector.java +++ b/server/src/main/java/org/elasticsearch/discovery/HandshakingTransportAddressConnector.java @@ -23,6 +23,7 @@ import org.elasticsearch.core.Releasable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.transport.ConnectTransportException; import org.elasticsearch.transport.ConnectionProfile; import org.elasticsearch.transport.TransportRequestOptions.Type; @@ -89,7 +90,7 @@ public void connectToRemoteMasterNode(TransportAddress transportAddress, ActionL emptySet(), new VersionInformation( Version.CURRENT.minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ) ), diff --git a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java index 943cb06e9c6b5..cc685b26ce239 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/server/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -49,6 +49,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardPath; import org.elasticsearch.index.store.FsDirectoryFactory; @@ -537,7 +538,7 @@ static void checkForIndexCompatibility(Logger logger, DataPath... dataPaths) thr + "] is incompatible. Revert this node to version [" + bestDowngradeVersion + "] and delete any indices with versions earlier than [" - + IndexVersion.MINIMUM_COMPATIBLE + + IndexVersions.MINIMUM_COMPATIBLE + "] before upgrading to version [" + Build.current().version() + "]. If all such indices have already been deleted, revert this node to version [" diff --git a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java index 37dd01733e664..a714ee4cf5ec0 100644 --- a/server/src/main/java/org/elasticsearch/env/NodeMetadata.java +++ b/server/src/main/java/org/elasticsearch/env/NodeMetadata.java @@ -12,6 +12,7 @@ import org.elasticsearch.Version; import org.elasticsearch.gateway.MetadataStateFormat; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.xcontent.ObjectParser; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.XContentBuilder; @@ -174,7 +175,7 @@ public NodeMetadata build() { previousNodeVersion = nodeVersion; } if (this.oldestIndexVersion == null) { - oldestIndexVersion = IndexVersion.ZERO; + oldestIndexVersion = IndexVersions.ZERO; } else { oldestIndexVersion = this.oldestIndexVersion; } diff --git a/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java b/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java index 686f03830257b..a7cf7299a8502 100644 --- a/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java +++ b/server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java @@ -35,7 +35,7 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.Tuple; import org.elasticsearch.env.NodeMetadata; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.node.Node; import org.elasticsearch.plugins.ClusterCoordinationPlugin; import org.elasticsearch.plugins.MetadataUpgrader; @@ -298,7 +298,7 @@ static Metadata upgradeMetadata(Metadata metadata, IndexMetadataVerifier indexMe boolean changed = false; final Metadata.Builder upgradedMetadata = Metadata.builder(metadata); for (IndexMetadata indexMetadata : metadata) { - IndexMetadata newMetadata = indexMetadataVerifier.verifyIndexMetadata(indexMetadata, IndexVersion.MINIMUM_COMPATIBLE); + IndexMetadata newMetadata = indexMetadataVerifier.verifyIndexMetadata(indexMetadata, IndexVersions.MINIMUM_COMPATIBLE); changed |= indexMetadata != newMetadata; upgradedMetadata.put(newMetadata, false); } diff --git a/server/src/main/java/org/elasticsearch/gateway/PersistedClusterStateService.java b/server/src/main/java/org/elasticsearch/gateway/PersistedClusterStateService.java index ddc98c1155055..fcf50ba3a8a44 100644 --- a/server/src/main/java/org/elasticsearch/gateway/PersistedClusterStateService.java +++ b/server/src/main/java/org/elasticsearch/gateway/PersistedClusterStateService.java @@ -71,6 +71,7 @@ import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.NodeMetadata; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; @@ -344,7 +345,7 @@ public record OnDiskStateMetadata( public static NodeMetadata nodeMetadata(Path... dataPaths) throws IOException { String nodeId = null; Version version = null; - IndexVersion oldestIndexVersion = IndexVersion.ZERO; + IndexVersion oldestIndexVersion = IndexVersions.ZERO; for (final Path dataPath : dataPaths) { final Path indexPath = dataPath.resolve(METADATA_DIRECTORY_NAME); if (Files.exists(indexPath)) { @@ -364,7 +365,7 @@ public static NodeMetadata nodeMetadata(Path... dataPaths) throws IOException { if (userData.containsKey(OLDEST_INDEX_VERSION_KEY)) { oldestIndexVersion = IndexVersion.fromId(Integer.parseInt(userData.get(OLDEST_INDEX_VERSION_KEY))); } else { - oldestIndexVersion = IndexVersion.ZERO; + oldestIndexVersion = IndexVersions.ZERO; } } } catch (IndexNotFoundException e) { @@ -703,7 +704,7 @@ private static void consumeFromType( if (document.getField(PAGE_FIELD_NAME) == null) { // legacy format: not paginated or compressed - assert IndexVersion.MINIMUM_COMPATIBLE.before(IndexVersion.V_7_16_0); + assert IndexVersions.MINIMUM_COMPATIBLE.before(IndexVersions.V_7_16_0); bytesReferenceConsumer.accept(documentData); continue; } diff --git a/server/src/main/java/org/elasticsearch/index/IndexSettings.java b/server/src/main/java/org/elasticsearch/index/IndexSettings.java index d887ed8d1531d..83a6d9319c75a 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexSettings.java +++ b/server/src/main/java/org/elasticsearch/index/IndexSettings.java @@ -306,7 +306,7 @@ public void validate(final TimeValue value, final Map, Object> settin && fastRefresh == false && value.compareTo(TimeValue.ZERO) > 0 && value.compareTo(STATELESS_MIN_NON_FAST_REFRESH_INTERVAL) < 0 - && indexVersion.after(IndexVersion.V_8_10_0)) { + && indexVersion.after(IndexVersions.V_8_10_0)) { throw new IllegalArgumentException( "index setting [" + IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey() @@ -872,7 +872,7 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti mergeSchedulerConfig = new MergeSchedulerConfig(this); gcDeletesInMillis = scopedSettings.get(INDEX_GC_DELETES_SETTING).getMillis(); softDeleteEnabled = scopedSettings.get(INDEX_SOFT_DELETES_SETTING); - assert softDeleteEnabled || version.before(IndexVersion.V_8_0_0) : "soft deletes must be enabled in version " + version; + assert softDeleteEnabled || version.before(IndexVersions.V_8_0_0) : "soft deletes must be enabled in version " + version; softDeleteRetentionOperations = scopedSettings.get(INDEX_SOFT_DELETES_RETENTION_OPERATIONS_SETTING); retentionLeaseMillis = scopedSettings.get(INDEX_SOFT_DELETES_RETENTION_LEASE_PERIOD_SETTING).millis(); warmerEnabled = scopedSettings.get(INDEX_WARMER_ENABLED_SETTING); diff --git a/server/src/main/java/org/elasticsearch/index/IndexSortConfig.java b/server/src/main/java/org/elasticsearch/index/IndexSortConfig.java index fd1c0c33eb8a6..98c2e31838379 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexSortConfig.java +++ b/server/src/main/java/org/elasticsearch/index/IndexSortConfig.java @@ -224,7 +224,7 @@ public Sort buildIndexSort( throw new IllegalArgumentException(err); } if (Objects.equals(ft.name(), sortSpec.field) == false) { - if (this.indexCreatedVersion.onOrAfter(IndexVersion.V_7_13_0)) { + if (this.indexCreatedVersion.onOrAfter(IndexVersions.V_7_13_0)) { throw new IllegalArgumentException("Cannot use alias [" + sortSpec.field + "] as an index sort field"); } else { DEPRECATION_LOGGER.warn( diff --git a/server/src/main/java/org/elasticsearch/index/IndexVersion.java b/server/src/main/java/org/elasticsearch/index/IndexVersion.java index 0a3b1a1be1f4d..765cc256d84b1 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexVersion.java +++ b/server/src/main/java/org/elasticsearch/index/IndexVersion.java @@ -12,23 +12,13 @@ import org.elasticsearch.common.VersionId; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.core.Assertions; import org.elasticsearch.internal.VersionExtension; import org.elasticsearch.plugins.ExtensionLoader; import org.elasticsearch.xcontent.ToXContentFragment; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.NavigableMap; import java.util.ServiceLoader; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; /** * The index version. @@ -47,10 +37,10 @@ * resulting in the same index version being used across multiple commits, * causing problems when you try to upgrade between those two merged commits. *

Version compatibility

- * The earliest compatible version is hardcoded in the {@link #MINIMUM_COMPATIBLE} field. Previously, this was dynamically calculated - * from the major/minor versions of {@link Version}, but {@code IndexVersion} does not have separate major/minor version numbers. - * So the minimum compatible version is hard-coded as the index version used by the first version of the previous major release. - * {@link #MINIMUM_COMPATIBLE} should be updated appropriately whenever a major release happens. + * The earliest compatible version is hardcoded in the {@link IndexVersions#MINIMUM_COMPATIBLE} field. Previously, this was dynamically + * calculated from the major/minor versions of {@link Version}, but {@code IndexVersion} does not have separate major/minor version + * numbers. So the minimum compatible version is hard-coded as the index version used by the first version of the previous major release. + * {@link IndexVersions#MINIMUM_COMPATIBLE} should be updated appropriately whenever a major release happens. *

Adding a new version

* A new index version should be added every time a change is made to the serialization protocol of one or more classes. * Each index version should only be used in a single merged commit (apart from BwC versions copied from {@link Version}). @@ -61,98 +51,8 @@ * If you revert a commit with an index version change, you must ensure there is a new index version * representing the reverted change. Do not let the index version go backwards, it must always be incremented. */ -@SuppressWarnings({"checkstyle:linelength", "deprecation"}) public record IndexVersion(int id, Version luceneVersion) implements VersionId, ToXContentFragment { - /* - * NOTE: IntelliJ lies! - * This map is used during class construction, referenced by the registerIndexVersion method. - * When all the index version constants have been registered, the map is cleared & never touched again. - */ - @SuppressWarnings("UnusedAssignment") - static TreeSet IDS = new TreeSet<>(); - - private static IndexVersion def(int id, Version luceneVersion) { - if (IDS == null) throw new IllegalStateException("The IDS map needs to be present to call this method"); - - if (IDS.add(id) == false) { - throw new IllegalArgumentException("Version id " + id + " defined twice"); - } - if (id < IDS.last()) { - throw new IllegalArgumentException("Version id " + id + " is not defined in the right location. Keep constants sorted"); - } - return new IndexVersion(id, luceneVersion); - } - - public static final IndexVersion ZERO = def(0, Version.LATEST); - public static final IndexVersion V_7_0_0 = def(7_00_00_99, Version.LUCENE_8_0_0); - public static final IndexVersion V_7_1_0 = def(7_01_00_99, Version.LUCENE_8_0_0); - public static final IndexVersion V_7_2_0 = def(7_02_00_99, Version.LUCENE_8_0_0); - public static final IndexVersion V_7_2_1 = def(7_02_01_99, Version.LUCENE_8_0_0); - public static final IndexVersion V_7_3_0 = def(7_03_00_99, Version.LUCENE_8_1_0); - public static final IndexVersion V_7_4_0 = def(7_04_00_99, Version.LUCENE_8_2_0); - public static final IndexVersion V_7_5_0 = def(7_05_00_99, Version.LUCENE_8_3_0); - public static final IndexVersion V_7_5_2 = def(7_05_02_99, Version.LUCENE_8_3_0); - public static final IndexVersion V_7_6_0 = def(7_06_00_99, Version.LUCENE_8_4_0); - public static final IndexVersion V_7_7_0 = def(7_07_00_99, Version.LUCENE_8_5_1); - public static final IndexVersion V_7_8_0 = def(7_08_00_99, Version.LUCENE_8_5_1); - public static final IndexVersion V_7_9_0 = def(7_09_00_99, Version.LUCENE_8_6_0); - public static final IndexVersion V_7_10_0 = def(7_10_00_99, Version.LUCENE_8_7_0); - public static final IndexVersion V_7_11_0 = def(7_11_00_99, Version.LUCENE_8_7_0); - public static final IndexVersion V_7_12_0 = def(7_12_00_99, Version.LUCENE_8_8_0); - public static final IndexVersion V_7_13_0 = def(7_13_00_99, Version.LUCENE_8_8_2); - public static final IndexVersion V_7_14_0 = def(7_14_00_99, Version.LUCENE_8_9_0); - public static final IndexVersion V_7_15_0 = def(7_15_00_99, Version.LUCENE_8_9_0); - public static final IndexVersion V_7_16_0 = def(7_16_00_99, Version.LUCENE_8_10_1); - public static final IndexVersion V_7_17_0 = def(7_17_00_99, Version.LUCENE_8_11_1); - public static final IndexVersion V_8_0_0 = def(8_00_00_99, Version.LUCENE_9_0_0); - public static final IndexVersion V_8_1_0 = def(8_01_00_99, Version.LUCENE_9_0_0); - public static final IndexVersion V_8_2_0 = def(8_02_00_99, Version.LUCENE_9_1_0); - public static final IndexVersion V_8_3_0 = def(8_03_00_99, Version.LUCENE_9_2_0); - public static final IndexVersion V_8_4_0 = def(8_04_00_99, Version.LUCENE_9_3_0); - public static final IndexVersion V_8_5_0 = def(8_05_00_99, Version.LUCENE_9_4_1); - public static final IndexVersion V_8_6_0 = def(8_06_00_99, Version.LUCENE_9_4_2); - public static final IndexVersion V_8_7_0 = def(8_07_00_99, Version.LUCENE_9_5_0); - public static final IndexVersion V_8_8_0 = def(8_08_00_99, Version.LUCENE_9_6_0); - public static final IndexVersion V_8_8_2 = def(8_08_02_99, Version.LUCENE_9_6_0); - public static final IndexVersion V_8_9_0 = def(8_09_00_99, Version.LUCENE_9_7_0); - public static final IndexVersion V_8_9_1 = def(8_09_01_99, Version.LUCENE_9_7_0); - public static final IndexVersion V_8_10_0 = def(8_10_00_99, Version.LUCENE_9_7_0); - - /* - * READ THE COMMENT BELOW THIS BLOCK OF DECLARATIONS BEFORE ADDING NEW INDEX VERSIONS - * Detached index versions added below here. - */ - public static final IndexVersion FIRST_DETACHED_INDEX_VERSION = def(8_500_000, Version.LUCENE_9_7_0); - public static final IndexVersion NEW_SPARSE_VECTOR = def(8_500_001, Version.LUCENE_9_7_0); - public static final IndexVersion SPARSE_VECTOR_IN_FIELD_NAMES_SUPPORT = def(8_500_002, Version.LUCENE_9_7_0); - public static final IndexVersion UPGRADE_LUCENE_9_8 = def(8_500_003, Version.LUCENE_9_8_0); - - /* - * STOP! READ THIS FIRST! No, really, - * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ - * / ___|_ _/ _ \| _ \| | | _ \| ____| / \ | _ \ |_ _| | | |_ _/ ___| | ___|_ _| _ \/ ___|_ _| | - * \___ \ | || | | | |_) | | | |_) | _| / _ \ | | | | | | | |_| || |\___ \ | |_ | || |_) \___ \ | | | | - * ___) || || |_| | __/|_| | _ <| |___ / ___ \| |_| | | | | _ || | ___) | | _| | || _ < ___) || | |_| - * |____/ |_| \___/|_| (_) |_| \_\_____/_/ \_\____/ |_| |_| |_|___|____/ |_| |___|_| \_\____/ |_| (_) - * - * A new index version should be added EVERY TIME a change is made to index metadata or data storage. - * Each index version should only be used in a single merged commit (apart from the BwC versions copied from o.e.Version, ≤V_8_11_0). - * - * To add a new index version, add a new constant at the bottom of the list, above this comment, which is one greater than the - * current highest version id. Use a descriptive constant name. Don't add other lines, comments, etc. - * - * REVERTING AN INDEX VERSION - * - * If you revert a commit with an index version change, you MUST ensure there is a NEW index version representing the reverted - * change. DO NOT let the index version go backwards, it must ALWAYS be incremented. - * - * DETERMINING TRANSPORT VERSIONS FROM GIT HISTORY - * - * TODO after the release of v8.11.0, copy the instructions about using git to track the history of versions from TransportVersion.java - * (the example commands won't make sense until at least 8.11.0 is released) - */ - private static class CurrentHolder { private static final IndexVersion CURRENT = findCurrent(); @@ -160,11 +60,11 @@ private static class CurrentHolder { private static IndexVersion findCurrent() { var versionExtension = ExtensionLoader.loadSingleton(ServiceLoader.load(VersionExtension.class), () -> null); if (versionExtension == null) { - return LATEST_DEFINED; + return IndexVersions.LATEST_DEFINED; } - var version = versionExtension.getCurrentIndexVersion(LATEST_DEFINED); + var version = versionExtension.getCurrentIndexVersion(IndexVersions.LATEST_DEFINED); - assert version.onOrAfter(LATEST_DEFINED); + assert version.onOrAfter(IndexVersions.LATEST_DEFINED); assert version.luceneVersion.equals(Version.LATEST) : "IndexVersion must be upgraded to [" + Version.LATEST @@ -175,68 +75,12 @@ private static IndexVersion findCurrent() { } } - public static final IndexVersion MINIMUM_COMPATIBLE = V_7_0_0; - - private static final NavigableMap VERSION_IDS = getAllVersionIds(IndexVersion.class); - - static final IndexVersion LATEST_DEFINED; - static { - LATEST_DEFINED = VERSION_IDS.lastEntry().getValue(); - - // see comment on IDS field - // now we're registered the index versions, we can clear the map - IDS = null; - } - - static NavigableMap getAllVersionIds(Class cls) { - Map versionIdFields = new HashMap<>(); - NavigableMap builder = new TreeMap<>(); - - Set ignore = Set.of("ZERO", "CURRENT", "MINIMUM_COMPATIBLE"); - - for (Field declaredField : cls.getFields()) { - if (declaredField.getType().equals(IndexVersion.class)) { - String fieldName = declaredField.getName(); - if (ignore.contains(fieldName)) { - continue; - } - - IndexVersion version; - try { - version = (IndexVersion) declaredField.get(null); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - builder.put(version.id, version); - - if (Assertions.ENABLED) { - // check the version number is unique - var sameVersionNumber = versionIdFields.put(version.id, fieldName); - assert sameVersionNumber == null - : "Versions [" - + sameVersionNumber - + "] and [" - + fieldName - + "] have the same version number [" - + version.id - + "]. Each IndexVersion should have a different version number"; - } - } - } - - return Collections.unmodifiableNavigableMap(builder); - } - - static Collection getAllVersions() { - return VERSION_IDS.values(); - } - public static IndexVersion readVersion(StreamInput in) throws IOException { return fromId(in.readVInt()); } public static IndexVersion fromId(int id) { - IndexVersion known = VERSION_IDS.get(id); + IndexVersion known = IndexVersions.VERSION_IDS.get(id); if (known != null) { return known; } @@ -246,10 +90,10 @@ public static IndexVersion fromId(int id) { // Our best guess is to use the same lucene version as the previous // version in the list, assuming that it didn't change. // if it's older than any known version use the previous major to the oldest known lucene version - var prev = VERSION_IDS.floorEntry(id); + var prev = IndexVersions.VERSION_IDS.floorEntry(id); Version luceneVersion = prev != null ? prev.getValue().luceneVersion - : Version.fromBits(VERSION_IDS.firstEntry().getValue().luceneVersion.major - 1, 0, 0); + : Version.fromBits(IndexVersions.VERSION_IDS.firstEntry().getValue().luceneVersion.major - 1, 0, 0); return new IndexVersion(id, luceneVersion); } @@ -281,14 +125,14 @@ public static IndexVersion current() { } public boolean isLegacyIndexVersion() { - return before(MINIMUM_COMPATIBLE); + return before(IndexVersions.MINIMUM_COMPATIBLE); } public static IndexVersion getMinimumCompatibleIndexVersion(int versionId) { int major = versionId / 1_000_000; if (major == IndexVersion.current().id() / 1_000_000) { // same compatibility version as current - return IndexVersion.MINIMUM_COMPATIBLE; + return IndexVersions.MINIMUM_COMPATIBLE; } else { int compatId = (major-1) * 1_000_000; if (major <= 8) compatId += 99; diff --git a/server/src/main/java/org/elasticsearch/index/IndexVersions.java b/server/src/main/java/org/elasticsearch/index/IndexVersions.java new file mode 100644 index 0000000000000..6327c2ba53f54 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/IndexVersions.java @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index; + +import org.apache.lucene.util.Version; +import org.elasticsearch.core.Assertions; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +@SuppressWarnings("deprecation") +public class IndexVersions { + + /* + * NOTE: IntelliJ lies! + * This map is used during class construction, referenced by the registerIndexVersion method. + * When all the index version constants have been registered, the map is cleared & never touched again. + */ + @SuppressWarnings("UnusedAssignment") + static TreeSet IDS = new TreeSet<>(); + + private static IndexVersion def(int id, Version luceneVersion) { + if (IDS == null) throw new IllegalStateException("The IDS map needs to be present to call this method"); + + if (IDS.add(id) == false) { + throw new IllegalArgumentException("Version id " + id + " defined twice"); + } + if (id < IDS.last()) { + throw new IllegalArgumentException("Version id " + id + " is not defined in the right location. Keep constants sorted"); + } + return new IndexVersion(id, luceneVersion); + } + + public static final IndexVersion ZERO = def(0, Version.LATEST); + public static final IndexVersion V_7_0_0 = def(7_00_00_99, Version.LUCENE_8_0_0); + + public static final IndexVersion V_7_1_0 = def(7_01_00_99, Version.LUCENE_8_0_0); + public static final IndexVersion V_7_2_0 = def(7_02_00_99, Version.LUCENE_8_0_0); + public static final IndexVersion V_7_2_1 = def(7_02_01_99, Version.LUCENE_8_0_0); + public static final IndexVersion V_7_3_0 = def(7_03_00_99, Version.LUCENE_8_1_0); + public static final IndexVersion V_7_4_0 = def(7_04_00_99, Version.LUCENE_8_2_0); + public static final IndexVersion V_7_5_0 = def(7_05_00_99, Version.LUCENE_8_3_0); + public static final IndexVersion V_7_5_2 = def(7_05_02_99, Version.LUCENE_8_3_0); + public static final IndexVersion V_7_6_0 = def(7_06_00_99, Version.LUCENE_8_4_0); + public static final IndexVersion V_7_7_0 = def(7_07_00_99, Version.LUCENE_8_5_1); + public static final IndexVersion V_7_8_0 = def(7_08_00_99, Version.LUCENE_8_5_1); + public static final IndexVersion V_7_9_0 = def(7_09_00_99, Version.LUCENE_8_6_0); + public static final IndexVersion V_7_10_0 = def(7_10_00_99, Version.LUCENE_8_7_0); + public static final IndexVersion V_7_11_0 = def(7_11_00_99, Version.LUCENE_8_7_0); + public static final IndexVersion V_7_12_0 = def(7_12_00_99, Version.LUCENE_8_8_0); + public static final IndexVersion V_7_13_0 = def(7_13_00_99, Version.LUCENE_8_8_2); + public static final IndexVersion V_7_14_0 = def(7_14_00_99, Version.LUCENE_8_9_0); + public static final IndexVersion V_7_15_0 = def(7_15_00_99, Version.LUCENE_8_9_0); + public static final IndexVersion V_7_16_0 = def(7_16_00_99, Version.LUCENE_8_10_1); + public static final IndexVersion V_7_17_0 = def(7_17_00_99, Version.LUCENE_8_11_1); + public static final IndexVersion V_8_0_0 = def(8_00_00_99, Version.LUCENE_9_0_0); + public static final IndexVersion V_8_1_0 = def(8_01_00_99, Version.LUCENE_9_0_0); + public static final IndexVersion V_8_2_0 = def(8_02_00_99, Version.LUCENE_9_1_0); + public static final IndexVersion V_8_3_0 = def(8_03_00_99, Version.LUCENE_9_2_0); + public static final IndexVersion V_8_4_0 = def(8_04_00_99, Version.LUCENE_9_3_0); + public static final IndexVersion V_8_5_0 = def(8_05_00_99, Version.LUCENE_9_4_1); + public static final IndexVersion V_8_6_0 = def(8_06_00_99, Version.LUCENE_9_4_2); + public static final IndexVersion V_8_7_0 = def(8_07_00_99, Version.LUCENE_9_5_0); + public static final IndexVersion V_8_8_0 = def(8_08_00_99, Version.LUCENE_9_6_0); + public static final IndexVersion V_8_8_2 = def(8_08_02_99, Version.LUCENE_9_6_0); + public static final IndexVersion V_8_9_0 = def(8_09_00_99, Version.LUCENE_9_7_0); + public static final IndexVersion V_8_9_1 = def(8_09_01_99, Version.LUCENE_9_7_0); + public static final IndexVersion V_8_10_0 = def(8_10_00_99, Version.LUCENE_9_7_0); + /* + * READ THE COMMENT BELOW THIS BLOCK OF DECLARATIONS BEFORE ADDING NEW INDEX VERSIONS + * Detached index versions added below here. + */ + public static final IndexVersion FIRST_DETACHED_INDEX_VERSION = def(8_500_000, Version.LUCENE_9_7_0); + public static final IndexVersion NEW_SPARSE_VECTOR = def(8_500_001, Version.LUCENE_9_7_0); + public static final IndexVersion SPARSE_VECTOR_IN_FIELD_NAMES_SUPPORT = def(8_500_002, Version.LUCENE_9_7_0); + public static final IndexVersion UPGRADE_LUCENE_9_8 = def(8_500_003, Version.LUCENE_9_8_0); + + /* + * STOP! READ THIS FIRST! No, really, + * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ + * / ___|_ _/ _ \| _ \| | | _ \| ____| / \ | _ \ |_ _| | | |_ _/ ___| | ___|_ _| _ \/ ___|_ _| | + * \___ \ | || | | | |_) | | | |_) | _| / _ \ | | | | | | | |_| || |\___ \ | |_ | || |_) \___ \ | | | | + * ___) || || |_| | __/|_| | _ <| |___ / ___ \| |_| | | | | _ || | ___) | | _| | || _ < ___) || | |_| + * |____/ |_| \___/|_| (_) |_| \_\_____/_/ \_\____/ |_| |_| |_|___|____/ |_| |___|_| \_\____/ |_| (_) + * + * A new index version should be added EVERY TIME a change is made to index metadata or data storage. + * Each index version should only be used in a single merged commit (apart from the BwC versions copied from o.e.Version, ≤V_8_11_0). + * + * To add a new index version, add a new constant at the bottom of the list, above this comment, which is one greater than the + * current highest version id. Use a descriptive constant name. Don't add other lines, comments, etc. + * + * REVERTING AN INDEX VERSION + * + * If you revert a commit with an index version change, you MUST ensure there is a NEW index version representing the reverted + * change. DO NOT let the index version go backwards, it must ALWAYS be incremented. + * + * DETERMINING TRANSPORT VERSIONS FROM GIT HISTORY + * + * TODO after the release of v8.11.0, copy the instructions about using git to track the history of versions from TransportVersion.java + * (the example commands won't make sense until at least 8.11.0 is released) + */ + + public static final IndexVersion MINIMUM_COMPATIBLE = V_7_0_0; + + static final NavigableMap VERSION_IDS = getAllVersionIds(IndexVersions.class); + static final IndexVersion LATEST_DEFINED; + static { + LATEST_DEFINED = VERSION_IDS.lastEntry().getValue(); + + // see comment on IDS field + // now we're registered the index versions, we can clear the map + IDS = null; + } + + static NavigableMap getAllVersionIds(Class cls) { + Map versionIdFields = new HashMap<>(); + NavigableMap builder = new TreeMap<>(); + + Set ignore = Set.of("ZERO", "MINIMUM_COMPATIBLE"); + + for (Field declaredField : cls.getFields()) { + if (declaredField.getType().equals(IndexVersion.class)) { + String fieldName = declaredField.getName(); + if (ignore.contains(fieldName)) { + continue; + } + + IndexVersion version; + try { + version = (IndexVersion) declaredField.get(null); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + builder.put(version.id(), version); + + if (Assertions.ENABLED) { + // check the version number is unique + var sameVersionNumber = versionIdFields.put(version.id(), fieldName); + assert sameVersionNumber == null + : "Versions [" + + sameVersionNumber + + "] and [" + + fieldName + + "] have the same version number [" + + version.id() + + "]. Each IndexVersion should have a different version number"; + } + } + } + + return Collections.unmodifiableNavigableMap(builder); + } + + static Collection getAllVersions() { + return VERSION_IDS.values(); + } +} diff --git a/server/src/main/java/org/elasticsearch/index/analysis/ShingleTokenFilterFactory.java b/server/src/main/java/org/elasticsearch/index/analysis/ShingleTokenFilterFactory.java index 256fc75733cfb..174253252416c 100644 --- a/server/src/main/java/org/elasticsearch/index/analysis/ShingleTokenFilterFactory.java +++ b/server/src/main/java/org/elasticsearch/index/analysis/ShingleTokenFilterFactory.java @@ -15,7 +15,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.lucene.analysis.miscellaneous.DisableGraphAttribute; public class ShingleTokenFilterFactory extends AbstractTokenFilterFactory { @@ -36,7 +36,7 @@ public ShingleTokenFilterFactory(IndexSettings indexSettings, Environment enviro int shingleDiff = maxShingleSize - minShingleSize + (outputUnigrams ? 1 : 0); if (shingleDiff > maxAllowedShingleDiff) { - if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_7_0_0)) { + if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_7_0_0)) { throw new IllegalArgumentException( "In Shingle TokenFilter the difference between max_shingle_size and min_shingle_size (and +1 if outputting unigrams)" + " must be less than or equal to: [" @@ -81,7 +81,7 @@ public TokenStream create(TokenStream tokenStream) { @Override public TokenFilterFactory getSynonymFilter() { - if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_7_0_0)) { + if (indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_7_0_0)) { throw new IllegalArgumentException("Token filter [" + name() + "] cannot be used to parse synonyms"); } else { DEPRECATION_LOGGER.warn( diff --git a/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java b/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java index cae59baf1dfbd..aa9bddf414296 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/ReadOnlyEngine.java @@ -27,6 +27,7 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DocumentParser; import org.elasticsearch.index.mapper.MappingLookup; import org.elasticsearch.index.seqno.SeqNoStats; @@ -176,7 +177,7 @@ protected void ensureMaxSeqNoEqualsToGlobalCheckpoint(final SeqNoStats seqNoStat // created after the refactoring of the Close Index API and its TransportVerifyShardBeforeCloseAction // that guarantee that all operations have been flushed to Lucene. IndexVersion indexVersionCreated = engineConfig.getIndexSettings().getIndexVersionCreated(); - if (indexVersionCreated.onOrAfter(IndexVersion.V_7_2_0) + if (indexVersionCreated.onOrAfter(IndexVersions.V_7_2_0) || (seqNoStats.getGlobalCheckpoint() != SequenceNumbers.UNASSIGNED_SEQ_NO)) { assert assertMaxSeqNoEqualsToGlobalCheckpoint(seqNoStats.getMaxSeqNo(), seqNoStats.getGlobalCheckpoint()); if (seqNoStats.getMaxSeqNo() != seqNoStats.getGlobalCheckpoint()) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java index 581c87d224f2d..2859d8bb29917 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.util.Maps; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerScope; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.query.SearchExecutionContext; @@ -211,7 +212,7 @@ public CompletionFieldMapper build(MapperBuilderContext context) { private void checkCompletionContextsLimit() { if (this.contexts.getValue() != null && this.contexts.getValue().size() > COMPLETION_CONTEXTS_LIMIT) { - if (indexVersionCreated.onOrAfter(IndexVersion.V_8_0_0)) { + if (indexVersionCreated.onOrAfter(IndexVersions.V_8_0_0)) { throw new IllegalArgumentException( "Limit of completion field contexts [" + COMPLETION_CONTEXTS_LIMIT + "] has been exceeded" ); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java index cd71c80cdb8ed..c80df48f56977 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java @@ -34,6 +34,7 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType; @@ -326,7 +327,7 @@ private Long parseNullValue(DateFieldType fieldType) { try { return fieldType.parse(nullValue.getValue()); } catch (Exception e) { - if (indexCreatedVersion.onOrAfter(IndexVersion.V_8_0_0)) { + if (indexCreatedVersion.onOrAfter(IndexVersions.V_8_0_0)) { throw new MapperParsingException("Error parsing [null_value] on field [" + name() + "]: " + e.getMessage(), e); } else { DEPRECATION_LOGGER.warn( diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index 02d4aa7a756e0..f7dc09cdbb370 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import java.util.List; @@ -52,7 +53,7 @@ public static DocumentMapper createEmpty(MapperService mapperService) { boolean isSyntheticSourceMalformed(CompressedXContent source, IndexVersion version) { return sourceMapper().isSynthetic() && source.string().contains("\"_source\":{\"mode\":\"synthetic\"}") == false - && version.onOrBefore(IndexVersion.V_8_10_0); + && version.onOrBefore(IndexVersions.V_8_10_0); } public Mapping mapping() { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index 16bd3967da57e..a7a565d821443 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper; @@ -49,7 +50,7 @@ */ public final class DocumentParser { - public static final IndexVersion DYNAMICALLY_MAP_DENSE_VECTORS_INDEX_VERSION = IndexVersion.FIRST_DETACHED_INDEX_VERSION; + public static final IndexVersion DYNAMICALLY_MAP_DENSE_VECTORS_INDEX_VERSION = IndexVersions.FIRST_DETACHED_INDEX_VERSION; private final XContentParserConfiguration parserConfiguration; private final Supplier documentParsingObserverSupplier; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java index 350ac22c5e216..32c87a856b6d0 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; @@ -1310,7 +1311,7 @@ public final void parse(String name, MappingParserContext parserContext, Map { - if (parserContext.indexVersionCreated().onOrAfter(IndexVersion.V_8_0_0)) { + if (parserContext.indexVersionCreated().onOrAfter(IndexVersions.V_8_0_0)) { throw new MapperParsingException("Unknown parameter [boost] on mapper [" + name + "]"); } deprecationLogger.warn( @@ -1354,7 +1355,7 @@ public final void parse(String name, MappingParserContext parserContext, Map DEPRECATED_PARAMS = Set.of("store", "meta", "index", "doc_values", "index_options", "similarity"); private static boolean isDeprecatedParameter(String propName, IndexVersion indexCreatedVersion) { - if (indexCreatedVersion.onOrAfter(IndexVersion.V_8_0_0)) { + if (indexCreatedVersion.onOrAfter(IndexVersions.V_8_0_0)) { return false; } return DEPRECATED_PARAMS.contains(propName); @@ -1430,7 +1431,7 @@ public static final class TypeParser implements Mapper.TypeParser { * @param builderFunction a function that produces a Builder from a name and parsercontext */ public TypeParser(BiFunction builderFunction) { - this(builderFunction, (n, c) -> {}, IndexVersion.MINIMUM_COMPATIBLE); + this(builderFunction, (n, c) -> {}, IndexVersions.MINIMUM_COMPATIBLE); } /** @@ -1445,7 +1446,7 @@ public TypeParser( BiFunction builderFunction, BiConsumer contextValidator ) { - this(builderFunction, contextValidator, IndexVersion.MINIMUM_COMPATIBLE); + this(builderFunction, contextValidator, IndexVersions.MINIMUM_COMPATIBLE); } private TypeParser( diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java index cefd31efb49dd..57a4885d44d5a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.query.SearchExecutionContext; import java.util.Collections; @@ -64,7 +65,7 @@ static class Builder extends MetadataFieldMapper.Builder { private final boolean createdOnOrAfterV8; Builder(IndexVersion indexVersionCreated) { - this(indexVersionCreated.onOrAfter(IndexVersion.V_8_0_0)); + this(indexVersionCreated.onOrAfter(IndexVersions.V_8_0_0)); } Builder(boolean createdOnOrAfterV8) { @@ -101,7 +102,7 @@ public FieldNamesFieldMapper build() { private static final FieldNamesFieldMapper DEFAULT_OLD = new FieldNamesFieldMapper(Defaults.ENABLED, false); public static final TypeParser PARSER = new ConfigurableTypeParser( - c -> c.indexVersionCreated().onOrAfter(IndexVersion.V_8_0_0) ? DEFAULT : DEFAULT_OLD, + c -> c.indexVersionCreated().onOrAfter(IndexVersions.V_8_0_0) ? DEFAULT : DEFAULT_OLD, c -> new Builder(c.indexVersionCreated()) ); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java index ad287e1c6b005..e39684705e26a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java @@ -18,7 +18,7 @@ import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.geometry.Geometry; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.query.SearchExecutionContext; @@ -131,7 +131,7 @@ public String typeName() { @Override public Query geoShapeQuery(SearchExecutionContext context, String fieldName, ShapeRelation relation, LatLonGeometry... geometries) { // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0) - if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(IndexVersion.V_7_5_0)) { + if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(IndexVersions.V_7_5_0)) { throw new QueryShardException( context, ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "]." diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java index 80810ee0d7ab4..4f7a8d5691c69 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -29,6 +29,7 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; @@ -130,7 +131,7 @@ private InetAddress parseNullValue() { try { return InetAddresses.forString(nullValueAsString); } catch (Exception e) { - if (indexCreatedVersion.onOrAfter(IndexVersion.V_8_0_0)) { + if (indexCreatedVersion.onOrAfter(IndexVersions.V_8_0_0)) { throw new MapperParsingException("Error parsing [null_value] on field [" + name() + "]: " + e.getMessage(), e); } else { DEPRECATION_LOGGER.warn( diff --git a/server/src/main/java/org/elasticsearch/index/mapper/Mapper.java b/server/src/main/java/org/elasticsearch/index/mapper/Mapper.java index 598e8c4d394e8..e977b0aac014a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/Mapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/Mapper.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.util.StringLiteralDeduplicator; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.xcontent.ToXContentFragment; import java.util.Map; @@ -45,7 +46,7 @@ public interface TypeParser { * Whether we can parse this type on indices with the given index created version. */ default boolean supportsVersion(IndexVersion indexCreatedVersion) { - return indexCreatedVersion.onOrAfter(IndexVersion.MINIMUM_COMPATIBLE); + return indexCreatedVersion.onOrAfter(IndexVersions.MINIMUM_COMPATIBLE); } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MapperRegistry.java b/server/src/main/java/org/elasticsearch/index/mapper/MapperRegistry.java index 7fe99de6fea39..dcf24c9a61bbd 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MapperRegistry.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MapperRegistry.java @@ -9,6 +9,7 @@ package org.elasticsearch.index.mapper; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.plugins.MapperPlugin; import java.util.Collections; @@ -71,9 +72,9 @@ public Map getRuntimeFieldParsers() { * returned map uses the name of the field as a key. */ public Map getMetadataMapperParsers(IndexVersion indexCreatedVersion) { - if (indexCreatedVersion.onOrAfter(IndexVersion.V_8_0_0)) { + if (indexCreatedVersion.onOrAfter(IndexVersions.V_8_0_0)) { return metadataMapperParsers; - } else if (indexCreatedVersion.onOrAfter(IndexVersion.V_7_0_0)) { + } else if (indexCreatedVersion.onOrAfter(IndexVersions.V_7_0_0)) { return metadataMapperParsers7x; } else if (indexCreatedVersion.onOrAfter(IndexVersion.fromId(6000099))) { return metadataMapperParsers6x; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java index 0ca3044af64b3..9b5027455c68c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java @@ -12,7 +12,7 @@ import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.xcontent.support.XContentMapValues; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; @@ -149,7 +149,7 @@ public final void parseMetadataField(String name, MappingParserContext parserCon Parameter parameter = paramsMap.get(propName); if (parameter == null) { if (UNSUPPORTED_PARAMETERS_8_6_0.contains(propName)) { - if (parserContext.indexVersionCreated().onOrAfter(IndexVersion.V_8_6_0)) { + if (parserContext.indexVersionCreated().onOrAfter(IndexVersions.V_8_6_0)) { // silently ignore type, and a few other parameters: sadly we've been doing this for a long time deprecationLogger.warn( DeprecationCategory.API, diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java index 3ff77e2192634..de2466dc327c4 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.Explicit; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; @@ -117,7 +118,7 @@ public MapperBuilderContext createChildContext(String name) { NestedObjectMapper(String name, String fullPath, Map mappers, Builder builder) { super(name, fullPath, builder.enabled, Explicit.IMPLICIT_TRUE, builder.dynamic, mappers); - if (builder.indexCreatedVersion.before(IndexVersion.V_8_0_0)) { + if (builder.indexCreatedVersion.before(IndexVersions.V_8_0_0)) { this.nestedTypePath = "__" + fullPath; } else { this.nestedTypePath = fullPath; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java index 721b4ada67590..6a0f4a87eb890 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java @@ -15,6 +15,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.query.SearchExecutionContext; import java.util.Collections; @@ -29,7 +30,7 @@ public class NestedPathFieldMapper extends MetadataFieldMapper { private static final NestedPathFieldMapper INSTANCE_PRE_V8 = new NestedPathFieldMapper(NAME_PRE_V8); public static String name(IndexVersion version) { - if (version.before(IndexVersion.V_8_0_0)) { + if (version.before(IndexVersions.V_8_0_0)) { return NAME_PRE_V8; } return NAME; @@ -44,7 +45,7 @@ public static Field field(IndexVersion version, String path) { } public static final TypeParser PARSER = new FixedTypeParser( - c -> c.indexVersionCreated().before(IndexVersion.V_8_0_0) ? INSTANCE_PRE_V8 : INSTANCE + c -> c.indexVersionCreated().before(IndexVersions.V_8_0_0) ? INSTANCE_PRE_V8 : INSTANCE ); public static final class NestedPathFieldType extends StringFieldType { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java index 6bc82cff20e58..d50b8b0dc088b 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; @@ -361,7 +362,7 @@ private static void validateFieldName(String fieldName, IndexVersion indexCreate if (fieldName.isEmpty()) { throw new IllegalArgumentException("field name cannot be an empty string"); } - if (fieldName.isBlank() & indexCreatedVersion.onOrAfter(IndexVersion.V_8_6_0)) { + if (fieldName.isBlank() & indexCreatedVersion.onOrAfter(IndexVersions.V_8_6_0)) { // blank field names were previously accepted in mappings, but not in documents. throw new IllegalArgumentException("field name cannot contain only whitespaces"); } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java index 55c6a4537f688..b5f9681655481 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DynamicTemplate.XContentFieldType; import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.xcontent.ToXContent; @@ -334,7 +335,7 @@ private static void validateDynamicTemplate(MappingParserContext parserContext, String format = "dynamic template [%s] has invalid content [%s], " + "attempted to validate it with the following match_mapping_type: %s"; String message = String.format(Locale.ROOT, format, template.getName(), Strings.toString(template), Arrays.toString(types)); - final boolean failInvalidDynamicTemplates = parserContext.indexVersionCreated().onOrAfter(IndexVersion.V_8_0_0); + final boolean failInvalidDynamicTemplates = parserContext.indexVersionCreated().onOrAfter(IndexVersions.V_8_0_0); if (failInvalidDynamicTemplates) { throw new IllegalArgumentException(message, lastError); } else { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java index c5d5dbec1ef15..42121147d7f09 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java @@ -20,7 +20,7 @@ import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexMode; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.search.lookup.Source; @@ -186,7 +186,7 @@ private IndexMode getIndexMode() { public static final TypeParser PARSER = new ConfigurableTypeParser( c -> c.getIndexSettings().getMode() == IndexMode.TIME_SERIES - ? c.getIndexSettings().getIndexVersionCreated().onOrAfter(IndexVersion.V_8_7_0) ? TSDB_DEFAULT : TSDB_LEGACY_DEFAULT + ? c.getIndexSettings().getIndexVersionCreated().onOrAfter(IndexVersions.V_8_7_0) ? TSDB_DEFAULT : TSDB_LEGACY_DEFAULT : DEFAULT, c -> new Builder(c.getIndexSettings().getMode()) ); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java index 70f4d2d901b45..7ac524a642401 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java @@ -54,6 +54,7 @@ import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerScope; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; @@ -411,7 +412,7 @@ private SubFieldInfo buildPrefixInfo(MapperBuilderContext context, FieldType fie * or a multi-field). This way search will continue to work on old indices and new indices * will use the expected full name. */ - String fullName = indexCreatedVersion.before(IndexVersion.V_7_2_1) ? name() : context.buildFullName(name); + String fullName = indexCreatedVersion.before(IndexVersions.V_7_2_1) ? name() : context.buildFullName(name); // Copy the index options of the main field to allow phrase queries on // the prefix field. FieldType pft = new FieldType(fieldType); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java b/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java index 40c96b9976317..84c2e11b1beaa 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TypeParsers.java @@ -11,7 +11,7 @@ import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.time.DateFormatter; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.similarity.SimilarityProvider; import java.util.ArrayList; @@ -97,7 +97,7 @@ public static boolean parseMultiField( if (parserContext.isWithinMultiField()) { // For indices created prior to 8.0, we only emit a deprecation warning and do not fail type parsing. This is to // maintain the backwards-compatibility guarantee that we can always load indexes from the previous major version. - if (parserContext.indexVersionCreated().before(IndexVersion.V_8_0_0)) { + if (parserContext.indexVersionCreated().before(IndexVersions.V_8_0_0)) { deprecationLogger.warn( DeprecationCategory.INDICES, "multifield_within_multifield", diff --git a/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java index d22d106b4d368..6aaea1dd32285 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapper.java @@ -36,6 +36,7 @@ import org.apache.lucene.util.VectorUtil; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.mapper.ArraySourceValueFetcher; @@ -76,9 +77,9 @@ */ public class DenseVectorFieldMapper extends FieldMapper { - public static final IndexVersion MAGNITUDE_STORED_INDEX_VERSION = IndexVersion.V_7_5_0; - public static final IndexVersion INDEXED_BY_DEFAULT_INDEX_VERSION = IndexVersion.FIRST_DETACHED_INDEX_VERSION; - public static final IndexVersion LITTLE_ENDIAN_FLOAT_STORED_INDEX_VERSION = IndexVersion.V_8_9_0; + public static final IndexVersion MAGNITUDE_STORED_INDEX_VERSION = IndexVersions.V_7_5_0; + public static final IndexVersion INDEXED_BY_DEFAULT_INDEX_VERSION = IndexVersions.FIRST_DETACHED_INDEX_VERSION; + public static final IndexVersion LITTLE_ENDIAN_FLOAT_STORED_INDEX_VERSION = IndexVersions.V_8_9_0; public static final String CONTENT_TYPE = "dense_vector"; public static short MAX_DIMS_COUNT = 4096; // maximum allowed number of dimensions diff --git a/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java index f10055fd4669b..3b892fc1647b6 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapper.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; @@ -44,10 +45,10 @@ public class SparseVectorFieldMapper extends FieldMapper { static final String ERROR_MESSAGE_7X = "[sparse_vector] field type in old 7.x indices is allowed to " + "contain [sparse_vector] fields, but they cannot be indexed or searched."; static final String ERROR_MESSAGE_8X = "The [sparse_vector] field type is not supported from 8.0 to 8.10 versions."; - static final IndexVersion PREVIOUS_SPARSE_VECTOR_INDEX_VERSION = IndexVersion.V_8_0_0; + static final IndexVersion PREVIOUS_SPARSE_VECTOR_INDEX_VERSION = IndexVersions.V_8_0_0; - static final IndexVersion NEW_SPARSE_VECTOR_INDEX_VERSION = IndexVersion.NEW_SPARSE_VECTOR; - static final IndexVersion SPARSE_VECTOR_IN_FIELD_NAMES_INDEX_VERSION = IndexVersion.SPARSE_VECTOR_IN_FIELD_NAMES_SUPPORT; + static final IndexVersion NEW_SPARSE_VECTOR_INDEX_VERSION = IndexVersions.NEW_SPARSE_VECTOR; + static final IndexVersion SPARSE_VECTOR_IN_FIELD_NAMES_INDEX_VERSION = IndexVersions.SPARSE_VECTOR_IN_FIELD_NAMES_SUPPORT; public static class Builder extends FieldMapper.Builder { diff --git a/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java b/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java index 29537b4ddbdb9..2e39b13b34c78 100644 --- a/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java +++ b/server/src/main/java/org/elasticsearch/index/seqno/ReplicationTracker.java @@ -23,7 +23,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.gateway.WriteStateException; import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.SafeCommitInfo; import org.elasticsearch.index.shard.AbstractIndexShardComponent; import org.elasticsearch.index.shard.IndexShard; @@ -974,15 +974,15 @@ public ReplicationTracker( this.pendingInSync = new HashSet<>(); this.routingTable = null; this.replicationGroup = null; - this.hasAllPeerRecoveryRetentionLeases = indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_7_6_0) + this.hasAllPeerRecoveryRetentionLeases = indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_7_6_0) || (indexSettings.isSoftDeleteEnabled() - && indexSettings.getIndexVersionCreated().onOrAfter(IndexVersion.V_7_4_0) + && indexSettings.getIndexVersionCreated().onOrAfter(IndexVersions.V_7_4_0) && indexSettings.getIndexMetadata().getState() == IndexMetadata.State.OPEN); this.fileBasedRecoveryThreshold = IndexSettings.FILE_BASED_RECOVERY_THRESHOLD_SETTING.get(indexSettings.getSettings()); this.safeCommitInfoSupplier = safeCommitInfoSupplier; this.onReplicationGroupUpdated = onReplicationGroupUpdated; - assert IndexVersion.ZERO.equals(indexSettings.getIndexVersionCreated()) == false; + assert IndexVersions.ZERO.equals(indexSettings.getIndexVersionCreated()) == false; assert invariant(); } diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index ba8fd01ae028e..376724d978768 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -72,6 +72,7 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.bulk.stats.BulkOperationListener; import org.elasticsearch.index.bulk.stats.BulkStats; @@ -3218,7 +3219,7 @@ public RetentionLease addPeerRecoveryRetentionLease( ) { assert assertPrimaryMode(); // only needed for BWC reasons involving rolling upgrades from versions that do not support PRRLs: - assert indexSettings.getIndexVersionCreated().before(IndexVersion.V_7_4_0) || indexSettings.isSoftDeleteEnabled() == false; + assert indexSettings.getIndexVersionCreated().before(IndexVersions.V_7_4_0) || indexSettings.isSoftDeleteEnabled() == false; return replicationTracker.addPeerRecoveryRetentionLease(nodeId, globalCheckpoint, listener); } diff --git a/server/src/main/java/org/elasticsearch/index/similarity/SimilarityProviders.java b/server/src/main/java/org/elasticsearch/index/similarity/SimilarityProviders.java index cc701eef7d063..76764f589c09b 100644 --- a/server/src/main/java/org/elasticsearch/index/similarity/SimilarityProviders.java +++ b/server/src/main/java/org/elasticsearch/index/similarity/SimilarityProviders.java @@ -41,6 +41,7 @@ import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.lucene.similarity.LegacyBM25Similarity; import java.util.Arrays; @@ -100,7 +101,7 @@ private static BasicModel parseBasicModel(IndexVersion indexCreatedVersion, Sett if (model == null) { String replacement = LEGACY_BASIC_MODELS.get(basicModel); if (replacement != null) { - if (indexCreatedVersion.onOrAfter(IndexVersion.V_7_0_0)) { + if (indexCreatedVersion.onOrAfter(IndexVersions.V_7_0_0)) { throw new IllegalArgumentException( "Basic model [" + basicModel + "] isn't supported anymore, " + "please use another model." ); @@ -139,7 +140,7 @@ private static AfterEffect parseAfterEffect(IndexVersion indexCreatedVersion, Se if (effect == null) { String replacement = LEGACY_AFTER_EFFECTS.get(afterEffect); if (replacement != null) { - if (indexCreatedVersion.onOrAfter(IndexVersion.V_7_0_0)) { + if (indexCreatedVersion.onOrAfter(IndexVersions.V_7_0_0)) { throw new IllegalArgumentException( "After effect [" + afterEffect + "] isn't supported anymore, please use another effect." ); @@ -239,7 +240,7 @@ static void assertSettingsIsSubsetOf(String type, IndexVersion version, Settings unknownSettings.removeAll(Arrays.asList(supportedSettings)); unknownSettings.remove("type"); // used to figure out which sim this is if (unknownSettings.isEmpty() == false) { - if (version.onOrAfter(IndexVersion.V_7_0_0)) { + if (version.onOrAfter(IndexVersions.V_7_0_0)) { throw new IllegalArgumentException("Unknown settings for similarity of type [" + type + "]: " + unknownSettings); } else { deprecationLogger.warn( diff --git a/server/src/main/java/org/elasticsearch/index/store/Store.java b/server/src/main/java/org/elasticsearch/index/store/Store.java index 015fbf06e042c..b7bf3a68ade07 100644 --- a/server/src/main/java/org/elasticsearch/index/store/Store.java +++ b/server/src/main/java/org/elasticsearch/index/store/Store.java @@ -59,6 +59,7 @@ import org.elasticsearch.env.ShardLockObtainFailedException; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.CombinedDeletionPolicy; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.seqno.SequenceNumbers; @@ -830,7 +831,7 @@ static MetadataSnapshot loadFromIndexCommit(IndexCommit commit, Directory direct } } if (maxVersion == null) { - maxVersion = IndexVersion.MINIMUM_COMPATIBLE.luceneVersion(); + maxVersion = IndexVersions.MINIMUM_COMPATIBLE.luceneVersion(); } final String segmentsFile = segmentCommitInfos.getSegmentsFileName(); checksumFromLuceneFile( diff --git a/server/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java b/server/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java index 3ba85b9ce4c69..902e080c42328 100644 --- a/server/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java +++ b/server/src/main/java/org/elasticsearch/indices/analysis/AnalysisModule.java @@ -19,6 +19,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AbstractTokenFilterFactory; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.index.analysis.AnalyzerProvider; @@ -136,7 +137,7 @@ private static NamedRegistry> setupTokenFil tokenFilters.register("standard", new AnalysisProvider() { @Override public TokenFilterFactory get(IndexSettings indexSettings, Environment environment, String name, Settings settings) { - if (indexSettings.getIndexVersionCreated().before(IndexVersion.V_7_0_0)) { + if (indexSettings.getIndexVersionCreated().before(IndexVersions.V_7_0_0)) { deprecationLogger.warn( DeprecationCategory.ANALYSIS, "standard_deprecation", @@ -201,7 +202,7 @@ static Map setupPreConfiguredTokenFilters(List // This was originally removed in 7_0_0 but due to a cacheing bug it was still possible // in certain circumstances to create a new index referencing the standard token filter // until version 7_5_2 - if (version.before(IndexVersion.V_7_6_0)) { + if (version.before(IndexVersions.V_7_6_0)) { deprecationLogger.warn( DeprecationCategory.ANALYSIS, "standard_deprecation", diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java index 287521ae60f32..47405e0daa0a7 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java @@ -29,6 +29,7 @@ import org.elasticsearch.core.Releasables; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.monitor.os.OsProbe; import org.elasticsearch.node.NodeRoleSettings; @@ -47,9 +48,9 @@ public class RecoverySettings { public static final Version SNAPSHOT_RECOVERIES_SUPPORTED_VERSION = Version.V_7_15_0; - public static final IndexVersion SNAPSHOT_RECOVERIES_SUPPORTED_INDEX_VERSION = IndexVersion.V_7_15_0; + public static final IndexVersion SNAPSHOT_RECOVERIES_SUPPORTED_INDEX_VERSION = IndexVersions.V_7_15_0; public static final TransportVersion SNAPSHOT_RECOVERIES_SUPPORTED_TRANSPORT_VERSION = TransportVersions.V_7_15_0; - public static final IndexVersion SEQ_NO_SNAPSHOT_RECOVERIES_SUPPORTED_VERSION = IndexVersion.V_7_16_0; + public static final IndexVersion SEQ_NO_SNAPSHOT_RECOVERIES_SUPPORTED_VERSION = IndexVersions.V_7_16_0; public static final TransportVersion SNAPSHOT_FILE_DOWNLOAD_THROTTLING_SUPPORTED_TRANSPORT_VERSION = TransportVersions.V_7_16_0; private static final Logger logger = LogManager.getLogger(RecoverySettings.class); diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java index 81bc226102f62..84385ee04c2dd 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/RecoverySourceHandler.java @@ -38,7 +38,7 @@ import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.RecoveryEngineException; import org.elasticsearch.index.seqno.ReplicationTracker; @@ -976,7 +976,7 @@ void createRetentionLease(final long startingSeqNo, ActionListener IndexVersion.V_7_12_0; - case "7.9.0" -> IndexVersion.V_7_9_0; - case "7.6.0" -> IndexVersion.V_7_6_0; + case "7.12.0" -> IndexVersions.V_7_12_0; + case "7.9.0" -> IndexVersions.V_7_9_0; + case "7.6.0" -> IndexVersions.V_7_6_0; default -> // All (known) versions only ever emit one of the above strings for the format version, so if we see something // else it must be a newer version or else something wholly invalid. Report the raw string rather than trying diff --git a/server/src/main/java/org/elasticsearch/search/suggest/completion/context/GeoContextMapping.java b/server/src/main/java/org/elasticsearch/search/suggest/completion/context/GeoContextMapping.java index e4dffea062721..933d2198a2dae 100644 --- a/server/src/main/java/org/elasticsearch/search/suggest/completion/context/GeoContextMapping.java +++ b/server/src/main/java/org/elasticsearch/search/suggest/completion/context/GeoContextMapping.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.LuceneDocument; @@ -271,7 +272,7 @@ public void validateReferences(IndexVersion indexVersionCreated, Function { return switch (randomIntBetween(1, 4)) { case 1 -> new VersionStats.SingleVersionStats( - IndexVersion.V_7_3_0, + IndexVersions.V_7_3_0, svs.indexCount, svs.primaryShardCount, svs.totalPrimaryByteCount @@ -88,12 +89,12 @@ public void testCreation() { metadata = new Metadata.Builder().put(indexMeta("foo", IndexVersion.current(), 4), true) .put(indexMeta("bar", IndexVersion.current(), 3), true) - .put(indexMeta("baz", IndexVersion.V_7_0_0, 2), true) + .put(indexMeta("baz", IndexVersions.V_7_0_0, 2), true) .build(); stats = VersionStats.of(metadata, Collections.emptyList()); assertThat(stats.versionStats().size(), equalTo(2)); VersionStats.SingleVersionStats s1 = new VersionStats.SingleVersionStats(IndexVersion.current(), 2, 7, 0); - VersionStats.SingleVersionStats s2 = new VersionStats.SingleVersionStats(IndexVersion.V_7_0_0, 1, 2, 0); + VersionStats.SingleVersionStats s2 = new VersionStats.SingleVersionStats(IndexVersions.V_7_0_0, 1, 2, 0); assertThat(stats.versionStats(), containsInAnyOrder(s1, s2)); ShardId shardId = new ShardId("bar", "uuid", 0); @@ -132,7 +133,7 @@ public void testCreation() { stats = VersionStats.of(metadata, Collections.singletonList(nodeResponse)); assertThat(stats.versionStats().size(), equalTo(2)); s1 = new VersionStats.SingleVersionStats(IndexVersion.current(), 2, 7, 100); - s2 = new VersionStats.SingleVersionStats(IndexVersion.V_7_0_0, 1, 2, 0); + s2 = new VersionStats.SingleVersionStats(IndexVersions.V_7_0_0, 1, 2, 0); assertThat(stats.versionStats(), containsInAnyOrder(s1, s2)); } @@ -141,7 +142,7 @@ private static IndexMetadata indexMeta(String name, IndexVersion version, int pr } public static VersionStats randomInstance() { - List versions = List.of(IndexVersion.current(), IndexVersion.V_7_0_0, IndexVersion.V_7_1_0, IndexVersion.V_7_2_0); + List versions = List.of(IndexVersion.current(), IndexVersions.V_7_0_0, IndexVersions.V_7_1_0, IndexVersions.V_7_2_0); List stats = new ArrayList<>(); for (IndexVersion v : versions) { VersionStats.SingleVersionStats s = new VersionStats.SingleVersionStats( diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTests.java index 76db89c138682..e2c71f3b20084 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.IndexingPressure; import org.elasticsearch.index.VersionType; import org.elasticsearch.indices.EmptySystemIndices; @@ -111,7 +112,7 @@ public void setUp() throws Exception { DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("node") .version( VersionUtils.randomCompatibleVersion(random(), Version.CURRENT), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ) .build(); diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java index 7e9556be18f6d..d4c5fc09e821f 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionTookTests.java @@ -29,7 +29,7 @@ import org.elasticsearch.common.util.concurrent.AtomicArray; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.IndexNotFoundException; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.IndexingPressure; import org.elasticsearch.indices.EmptySystemIndices; import org.elasticsearch.tasks.Task; @@ -81,7 +81,7 @@ public void setUp() throws Exception { DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("node") .version( VersionUtils.randomCompatibleVersion(random(), Version.CURRENT), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ) .build(); diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/RequestDispatcherTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/RequestDispatcherTests.java index ef1786892630a..75d5d7fb7c55d 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/RequestDispatcherTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/RequestDispatcherTests.java @@ -42,7 +42,7 @@ import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.index.shard.ShardId; @@ -113,7 +113,7 @@ public void testHappyCluster() throws Exception { Metadata.Builder metadata = Metadata.builder(); for (String index : allIndices) { metadata.put( - IndexMetadata.builder(index).settings(indexSettings(IndexVersion.MINIMUM_COMPATIBLE, between(1, 10), between(0, 2))) + IndexMetadata.builder(index).settings(indexSettings(IndexVersions.MINIMUM_COMPATIBLE, between(1, 10), between(0, 2))) ); } clusterState = newClusterState(metadata.build(), discoNodes.build()); @@ -182,7 +182,7 @@ public void testRetryThenOk() throws Exception { Metadata.Builder metadata = Metadata.builder(); for (String index : allIndices) { metadata.put( - IndexMetadata.builder(index).settings(indexSettings(IndexVersion.MINIMUM_COMPATIBLE, between(1, 10), between(1, 3))) + IndexMetadata.builder(index).settings(indexSettings(IndexVersions.MINIMUM_COMPATIBLE, between(1, 10), between(1, 3))) ); } clusterState = newClusterState(metadata.build(), discoNodes.build()); @@ -302,7 +302,7 @@ public void testRetryButFails() throws Exception { Metadata.Builder metadata = Metadata.builder(); for (String index : allIndices) { metadata.put( - IndexMetadata.builder(index).settings(indexSettings(IndexVersion.MINIMUM_COMPATIBLE, between(1, 10), between(0, 3))) + IndexMetadata.builder(index).settings(indexSettings(IndexVersions.MINIMUM_COMPATIBLE, between(1, 10), between(0, 3))) ); } clusterState = newClusterState(metadata.build(), discoNodes.build()); @@ -424,7 +424,7 @@ public void testSuccessWithAnyMatch() throws Exception { Metadata.Builder metadata = Metadata.builder(); for (String index : allIndices) { metadata.put( - IndexMetadata.builder(index).settings(indexSettings(IndexVersion.MINIMUM_COMPATIBLE, between(2, 10), between(0, 2))) + IndexMetadata.builder(index).settings(indexSettings(IndexVersions.MINIMUM_COMPATIBLE, between(2, 10), between(0, 2))) ); } clusterState = newClusterState(metadata.build(), discoNodes.build()); @@ -520,7 +520,7 @@ public void testStopAfterAllShardsUnmatched() throws Exception { Metadata.Builder metadata = Metadata.builder(); for (String index : allIndices) { metadata.put( - IndexMetadata.builder(index).settings(indexSettings(IndexVersion.MINIMUM_COMPATIBLE, between(1, 10), between(0, 2))) + IndexMetadata.builder(index).settings(indexSettings(IndexVersions.MINIMUM_COMPATIBLE, between(1, 10), between(0, 2))) ); } clusterState = newClusterState(metadata.build(), discoNodes.build()); diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchShardsResponseTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchShardsResponseTests.java index 6d9f3dfaefcf0..8924625aedcdd 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchShardsResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchShardsResponseTests.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.iterable.Iterables; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.query.RandomQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.shard.ShardId; @@ -113,11 +114,11 @@ protected SearchShardsResponse mutateInstance(SearchShardsResponse r) throws IOE public void testLegacyResponse() { DiscoveryNode node1 = DiscoveryNodeUtils.builder("node-1") .address(new TransportAddress(TransportAddress.META_ADDRESS, randomInt(0xFFFF))) - .version(randomCompatibleVersion(random(), Version.CURRENT), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(randomCompatibleVersion(random(), Version.CURRENT), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); DiscoveryNode node2 = DiscoveryNodeUtils.builder("node-2") .address(new TransportAddress(TransportAddress.META_ADDRESS, randomInt(0xFFFF))) - .version(randomCompatibleVersion(random(), Version.CURRENT), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(randomCompatibleVersion(random(), Version.CURRENT), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); final ClusterSearchShardsGroup[] groups = new ClusterSearchShardsGroup[2]; { diff --git a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java index 46c6d1db47a7c..0282e75434562 100644 --- a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java @@ -44,6 +44,7 @@ import org.elasticsearch.health.metadata.HealthMetadata; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.SystemIndices; @@ -373,7 +374,7 @@ public void testToXContent() throws IOException { }""", ephemeralId, Version.CURRENT, - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), TransportVersion.current(), IndexVersion.current(), @@ -630,7 +631,7 @@ public void testToXContent_FlatSettingTrue_ReduceMappingFalse() throws IOExcepti }""", ephemeralId, Version.CURRENT, - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), TransportVersion.current(), IndexVersion.current(), @@ -893,7 +894,7 @@ public void testToXContent_FlatSettingFalse_ReduceMappingTrue() throws IOExcepti }""", ephemeralId, Version.CURRENT, - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), TransportVersion.current(), IndexVersion.current(), diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java index 0d64e6ec20b7f..63142b4bf62f9 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ClusterServiceUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.MockLogAppender; @@ -82,12 +83,12 @@ public void testPreventJoinClusterWithNewerIndices() { .build(); metaBuilder.put(indexMetadata, false); Metadata metadata = metaBuilder.build(); - NodeJoinExecutor.ensureIndexCompatibility(IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current(), metadata); + NodeJoinExecutor.ensureIndexCompatibility(IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), metadata); expectThrows( IllegalStateException.class, () -> NodeJoinExecutor.ensureIndexCompatibility( - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion(IndexVersion.current()), metadata ) @@ -106,7 +107,7 @@ public void testPreventJoinClusterWithUnsupportedIndices() { Metadata metadata = metaBuilder.build(); expectThrows( IllegalStateException.class, - () -> NodeJoinExecutor.ensureIndexCompatibility(IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current(), metadata) + () -> NodeJoinExecutor.ensureIndexCompatibility(IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), metadata) ); } @@ -114,11 +115,13 @@ public void testPreventJoinClusterWithUnsupportedNodeVersions() { DiscoveryNodes.Builder builder = DiscoveryNodes.builder(); final Version version = randomCompatibleVersion(random(), Version.CURRENT); builder.add( - DiscoveryNodeUtils.builder(UUIDs.base64UUID()).version(version, IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()).build() + DiscoveryNodeUtils.builder(UUIDs.base64UUID()) + .version(version, IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) + .build() ); builder.add( DiscoveryNodeUtils.builder(UUIDs.base64UUID()) - .version(randomCompatibleVersion(random(), version), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(randomCompatibleVersion(random(), version), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build() ); DiscoveryNodes nodes = builder.build(); @@ -169,7 +172,7 @@ public void testSuccess() { .build(); metaBuilder.put(indexMetadata, false); Metadata metadata = metaBuilder.build(); - NodeJoinExecutor.ensureIndexCompatibility(IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current(), metadata); + NodeJoinExecutor.ensureIndexCompatibility(IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), metadata); } public static Settings.Builder randomCompatibleVersionSettings() { diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java index d266988f0123d..bc8ddb9c741e2 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java @@ -37,7 +37,7 @@ import org.elasticsearch.common.util.concurrent.DeterministicTaskQueue; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.Nullable; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; @@ -229,7 +229,7 @@ public RecyclerBytesStreamOutput newNetworkBytesStream() { var node = DiscoveryNodeUtils.builder("node-" + allNodes.size()) .version( VersionUtils.randomCompatibleVersion(random(), Version.CURRENT), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ) .build(); @@ -364,7 +364,7 @@ public void testIncludesLastCommittedFieldsInDiffSerialization() { final var otherNode = DiscoveryNodeUtils.builder("otherNode") .version( VersionUtils.randomCompatibleVersion(random(), Version.CURRENT), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ) .build(); diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java index e4a98c5da335b..0680392ffb3f0 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java @@ -27,6 +27,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndicesModule; import org.elasticsearch.test.ESTestCase; @@ -475,7 +476,7 @@ public void testIndexAndAliasWithSameName() { final IllegalArgumentException iae = expectThrows( IllegalArgumentException.class, () -> IndexMetadata.builder("index") - .settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.V_8_5_0)) + .settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.V_8_5_0)) .numberOfShards(1) .numberOfReplicas(0) .putAlias(AliasMetadata.builder("index").build()) @@ -487,7 +488,7 @@ public void testIndexAndAliasWithSameName() { public void testRepairIndexAndAliasWithSameName() { final IndexMetadata indexMetadata = IndexMetadata.builder("index") - .settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.V_8_5_0)) + .settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.V_8_5_0)) .numberOfShards(1) .numberOfReplicas(0) .putAlias(AliasMetadata.builder("index").build()) diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java index 9ae3afe1d9b2a..eb9e26d08ed9c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifierTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperRegistry; import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.test.ESTestCase; @@ -94,12 +95,12 @@ public void testCustomSimilarity() { .put("index.similarity.my_similarity.after_effect", "l") .build() ); - service.verifyIndexMetadata(src, IndexVersion.MINIMUM_COMPATIBLE); + service.verifyIndexMetadata(src, IndexVersions.MINIMUM_COMPATIBLE); } public void testIncompatibleVersion() { IndexMetadataVerifier service = getIndexMetadataVerifier(); - IndexVersion minCompat = IndexVersion.MINIMUM_COMPATIBLE; + IndexVersion minCompat = IndexVersions.MINIMUM_COMPATIBLE; IndexVersion indexCreated = IndexVersion.fromId(randomIntBetween(1000099, minCompat.id() - 1)); final IndexMetadata metadata = newIndexMeta( "foo", @@ -107,7 +108,7 @@ public void testIncompatibleVersion() { ); String message = expectThrows( IllegalStateException.class, - () -> service.verifyIndexMetadata(metadata, IndexVersion.MINIMUM_COMPATIBLE) + () -> service.verifyIndexMetadata(metadata, IndexVersions.MINIMUM_COMPATIBLE) ).getMessage(); assertThat( message, @@ -129,7 +130,7 @@ public void testIncompatibleVersion() { indexCreated = IndexVersionUtils.randomVersionBetween(random(), minCompat, IndexVersion.current()); IndexMetadata goodMeta = newIndexMeta("foo", Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, indexCreated).build()); - service.verifyIndexMetadata(goodMeta, IndexVersion.MINIMUM_COMPATIBLE); + service.verifyIndexMetadata(goodMeta, IndexVersions.MINIMUM_COMPATIBLE); } private IndexMetadataVerifier getIndexMetadataVerifier() { diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexServiceTests.java index 8df51a08b5ecf..43d64522ee6fb 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexServiceTests.java @@ -43,6 +43,7 @@ import org.elasticsearch.index.IndexSettingProviders; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.SearchExecutionContextHelper; @@ -1265,7 +1266,7 @@ public void testRejectTranslogRetentionSettings() { if (randomBoolean()) { settings.put( SETTING_VERSION_CREATED, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()) ); } request.settings(settings.build()); @@ -1300,7 +1301,7 @@ public void testDeprecateTranslogRetentionSettings() { } else { settings.put(IndexSettings.INDEX_TRANSLOG_RETENTION_SIZE_SETTING.getKey(), between(1, 128) + "mb"); } - settings.put(SETTING_VERSION_CREATED, IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0)); + settings.put(SETTING_VERSION_CREATED, IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0)); request.settings(settings.build()); aggregateIndexSettings( ClusterState.EMPTY_STATE, diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java index ad34289d37fed..fe0dd5ea1a5e7 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java @@ -36,6 +36,7 @@ import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.alias.RandomAliasActionsGenerator; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.ingest.IngestMetadata; @@ -774,19 +775,19 @@ public void testFindMappingsWithFilters() throws IOException { public void testOldestIndexComputation() { Metadata metadata = buildIndicesWithVersions( - IndexVersion.V_7_0_0, + IndexVersions.V_7_0_0, IndexVersion.current(), IndexVersion.fromId(IndexVersion.current().id() + 1) ).build(); - assertEquals(IndexVersion.V_7_0_0, metadata.oldestIndexVersion()); + assertEquals(IndexVersions.V_7_0_0, metadata.oldestIndexVersion()); Metadata.Builder b = Metadata.builder(); assertEquals(IndexVersion.current(), b.build().oldestIndexVersion()); Throwable ex = expectThrows( IllegalArgumentException.class, - () -> buildIndicesWithVersions(IndexVersion.V_7_0_0, IndexVersion.ZERO, IndexVersion.fromId(IndexVersion.current().id() + 1)) + () -> buildIndicesWithVersions(IndexVersions.V_7_0_0, IndexVersions.ZERO, IndexVersion.fromId(IndexVersion.current().id() + 1)) .build() ); @@ -1873,8 +1874,8 @@ public void testHiddenAliasValidation() { public void testSystemAliasValidationMixedVersionSystemAndRegularFails() { final IndexVersion random7xVersion = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ); final IndexMetadata currentVersionSystem = buildIndexWithAlias(".system1", SYSTEM_ALIAS_NAME, null, IndexVersion.current(), true); final IndexMetadata oldVersionSystem = buildIndexWithAlias(".oldVersionSystem", SYSTEM_ALIAS_NAME, null, random7xVersion, true); @@ -1923,8 +1924,8 @@ public void testSystemAliasValidationNewSystemAndRegularFails() { public void testSystemAliasOldSystemAndNewRegular() { final IndexVersion random7xVersion = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ); final IndexMetadata oldVersionSystem = buildIndexWithAlias(".oldVersionSystem", SYSTEM_ALIAS_NAME, null, random7xVersion, true); final IndexMetadata regularIndex = buildIndexWithAlias("regular1", SYSTEM_ALIAS_NAME, false, IndexVersion.current(), false); @@ -1936,8 +1937,8 @@ public void testSystemAliasOldSystemAndNewRegular() { public void testSystemIndexValidationAllRegular() { final IndexVersion random7xVersion = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ); final IndexMetadata currentVersionSystem = buildIndexWithAlias(".system1", SYSTEM_ALIAS_NAME, null, IndexVersion.current(), true); final IndexMetadata currentVersionSystem2 = buildIndexWithAlias(".system2", SYSTEM_ALIAS_NAME, null, IndexVersion.current(), true); @@ -1950,8 +1951,8 @@ public void testSystemIndexValidationAllRegular() { public void testSystemAliasValidationAllSystemSomeOld() { final IndexVersion random7xVersion = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ); final IndexMetadata currentVersionSystem = buildIndexWithAlias(".system1", SYSTEM_ALIAS_NAME, null, IndexVersion.current(), true); final IndexMetadata currentVersionSystem2 = buildIndexWithAlias(".system2", SYSTEM_ALIAS_NAME, null, IndexVersion.current(), true); diff --git a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java index cc700451931b5..e9a28f9d44cb2 100644 --- a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodeTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import java.net.InetAddress; @@ -225,7 +226,7 @@ public void testDiscoveryNodeToXContent() { transportAddress, withExternalId ? "test-external-id" : "test-name", Version.CURRENT, - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ) ) diff --git a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java index 8ef158aa277d8..f3df9693dbdf3 100644 --- a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.Version; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; @@ -403,7 +404,7 @@ public void testMinMaxNodeVersions() { assertEquals(Version.CURRENT, DiscoveryNodes.EMPTY_NODES.getMaxNodeVersion()); assertEquals(Version.CURRENT.minimumCompatibilityVersion(), DiscoveryNodes.EMPTY_NODES.getMinNodeVersion()); assertEquals(IndexVersion.current(), DiscoveryNodes.EMPTY_NODES.getMaxDataNodeCompatibleIndexVersion()); - assertEquals(IndexVersion.MINIMUM_COMPATIBLE, DiscoveryNodes.EMPTY_NODES.getMinSupportedIndexVersion()); + assertEquals(IndexVersions.MINIMUM_COMPATIBLE, DiscoveryNodes.EMPTY_NODES.getMinSupportedIndexVersion()); // use a mix of versions with major, minor, and patch numbers List dataVersions = List.of( @@ -481,7 +482,7 @@ public void accept(Consumer update, long expectedGenerat final var node0 = nodeVersionFactory.apply( 0, - new VersionInformation(VersionUtils.randomVersion(random()), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + new VersionInformation(VersionUtils.randomVersion(random()), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) ); testHarness.accept(builder -> builder.add(node0), 0L); diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java index 9e3be76cbce59..c67671d5b240c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java @@ -51,6 +51,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardNotFoundException; import org.elasticsearch.snapshots.SnapshotShardSizeInfo; @@ -713,9 +714,9 @@ public void testCanceledShardIsInitializedRespectingAllocationDeciders() { ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) .nodes( DiscoveryNodes.builder() - .add(newNode("node-0", Version.V_8_10_0, IndexVersion.V_8_10_0)) - .add(newNode("node-1", Version.V_8_9_0, IndexVersion.V_8_9_0)) - .add(newNode("node-2", Version.V_8_9_0, IndexVersion.V_8_9_0)) + .add(newNode("node-0", Version.V_8_10_0, IndexVersions.V_8_10_0)) + .add(newNode("node-1", Version.V_8_9_0, IndexVersions.V_8_9_0)) + .add(newNode("node-2", Version.V_8_9_0, IndexVersions.V_8_9_0)) ) .metadata(Metadata.builder().put(indexMetadata, false)) .routingTable(RoutingTable.builder().add(IndexRoutingTable.builder(shardId.getIndex()).addShard(primary).addShard(replica))) diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedNodeRoutingTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedNodeRoutingTests.java index 1d796c931270b..43b378c88ccd7 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedNodeRoutingTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedNodeRoutingTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Strings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.cluster.ClusterStateChanges; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.index.IndexVersionUtils; @@ -227,7 +228,7 @@ protected DiscoveryNode createNode(DiscoveryNodeRole... mustHaveRoles) { .roles(roles) .version( VersionUtils.randomCompatibleVersion(random(), Version.CURRENT), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ) .build(); diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedShardsRoutingTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedShardsRoutingTests.java index 484a081273314..74926a7556348 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedShardsRoutingTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedShardsRoutingTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.index.IndexVersionUtils; @@ -725,14 +726,14 @@ public void testReplicaOnNewestVersionIsPromoted() { newNode( "node3-old", VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumCompatibilityVersion(), null), - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.MINIMUM_COMPATIBLE, null) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.MINIMUM_COMPATIBLE, null) ) ) .add( newNode( "node4-old", VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumCompatibilityVersion(), null), - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.MINIMUM_COMPATIBLE, null) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.MINIMUM_COMPATIBLE, null) ) ) ) diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/NodeVersionAllocationDeciderTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/NodeVersionAllocationDeciderTests.java index fbf4c8ce3941a..c348cdfe43ecf 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/NodeVersionAllocationDeciderTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/NodeVersionAllocationDeciderTests.java @@ -47,6 +47,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.repositories.IndexId; import org.elasticsearch.snapshots.EmptySnapshotsInfoService; @@ -324,11 +325,11 @@ public void testRebalanceDoesNotAllocatePrimaryAndReplicasOnDifferentVersionNode final DiscoveryNode newNode = DiscoveryNodeUtils.builder("newNode").roles(MASTER_DATA_ROLES).build(); final DiscoveryNode oldNode1 = DiscoveryNodeUtils.builder("oldNode1") .roles(MASTER_DATA_ROLES) - .version(VersionUtils.getPreviousVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) + .version(VersionUtils.getPreviousVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) .build(); final DiscoveryNode oldNode2 = DiscoveryNodeUtils.builder("oldNode2") .roles(MASTER_DATA_ROLES) - .version(VersionUtils.getPreviousVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) + .version(VersionUtils.getPreviousVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) .build(); AllocationId allocationId1P = AllocationId.newInitializing(); AllocationId allocationId1R = AllocationId.newInitializing(); @@ -429,11 +430,11 @@ public void testRestoreDoesNotAllocateSnapshotOnOlderNodes() { final DiscoveryNode newNode = DiscoveryNodeUtils.builder("newNode").roles(MASTER_DATA_ROLES).build(); final DiscoveryNode oldNode1 = DiscoveryNodeUtils.builder("oldNode1") .roles(MASTER_DATA_ROLES) - .version(VersionUtils.getPreviousVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) + .version(VersionUtils.getPreviousVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) .build(); final DiscoveryNode oldNode2 = DiscoveryNodeUtils.builder("oldNode2") .roles(MASTER_DATA_ROLES) - .version(VersionUtils.getPreviousVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) + .version(VersionUtils.getPreviousVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.getPreviousVersion()) .build(); final Snapshot snapshot = new Snapshot("rep1", new SnapshotId("snp1", UUIDs.randomBase64UUID())); diff --git a/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java b/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java index cfcaeb9d9d704..112f96562b7cd 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeEnvironmentTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; @@ -584,7 +585,7 @@ public void testIndexCompatibilityChecks() throws IOException { } Version oldVersion = Version.fromId(between(1, Version.CURRENT.minimumCompatibilityVersion().id - 1)); - IndexVersion oldIndexVersion = IndexVersion.fromId(between(1, IndexVersion.MINIMUM_COMPATIBLE.id() - 1)); + IndexVersion oldIndexVersion = IndexVersion.fromId(between(1, IndexVersions.MINIMUM_COMPATIBLE.id() - 1)); Version previousNodeVersion = Version.fromId(between(Version.CURRENT.minimumCompatibilityVersion().id, Version.CURRENT.id - 1)); overrideOldestIndexVersion(oldIndexVersion, previousNodeVersion, env.nodeDataPaths()); @@ -608,7 +609,7 @@ public void testIndexCompatibilityChecks() throws IOException { ); // This should work - overrideOldestIndexVersion(IndexVersion.MINIMUM_COMPATIBLE, previousNodeVersion, env.nodeDataPaths()); + overrideOldestIndexVersion(IndexVersions.MINIMUM_COMPATIBLE, previousNodeVersion, env.nodeDataPaths()); checkForIndexCompatibility(logger, env.dataPaths()); // Trying to boot with newer version should pass this check diff --git a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java index 6a309767878c4..fc89e4d2176c1 100644 --- a/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java +++ b/server/src/test/java/org/elasticsearch/env/NodeMetadataTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.core.Tuple; import org.elasticsearch.gateway.MetadataStateFormat; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; import org.elasticsearch.test.TransportVersionUtils; @@ -76,7 +77,7 @@ public void testEqualsHashcodeSerialization() { public void testReadsFormatWithoutVersion() throws IOException { // the behaviour tested here is only appropriate if the current version is compatible with versions 7 and earlier - assertTrue(IndexVersion.MINIMUM_COMPATIBLE.onOrBefore(IndexVersion.V_7_0_0)); + assertTrue(IndexVersions.MINIMUM_COMPATIBLE.onOrBefore(IndexVersions.V_7_0_0)); // when the current version is incompatible with version 7, the behaviour should change to reject files like the given resource // which do not have the version field diff --git a/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java b/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java index 4e76b2cad6608..7373e62eba72f 100644 --- a/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java +++ b/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java @@ -58,6 +58,7 @@ import org.elasticsearch.gateway.PersistedClusterStateService.Writer; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.test.CorruptionUtils; import org.elasticsearch.test.ESTestCase; @@ -1489,7 +1490,7 @@ public void testOldestIndexVersionIsCorrectlySerialized() throws IOException { final Path[] dataPaths2 = createDataPaths(); final Path[] combinedPaths = Stream.concat(Arrays.stream(dataPaths1), Arrays.stream(dataPaths2)).toArray(Path[]::new); - IndexVersion oldVersion = IndexVersion.fromId(IndexVersion.MINIMUM_COMPATIBLE.id() - 1); + IndexVersion oldVersion = IndexVersion.fromId(IndexVersions.MINIMUM_COMPATIBLE.id() - 1); final IndexVersion[] indexVersions = new IndexVersion[] { oldVersion, diff --git a/server/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java b/server/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java index f4b1c867b9b21..06e83b8051bb6 100644 --- a/server/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java +++ b/server/src/test/java/org/elasticsearch/gateway/ReplicaShardAllocatorTests.java @@ -37,6 +37,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.seqno.ReplicationTracker; import org.elasticsearch.index.seqno.RetentionLease; @@ -66,7 +67,7 @@ import static org.hamcrest.Matchers.hasSize; public class ReplicaShardAllocatorTests extends ESAllocationTestCase { - private static final String MIN_SUPPORTED_LUCENE_VERSION = IndexVersion.MINIMUM_COMPATIBLE.luceneVersion().toString(); + private static final String MIN_SUPPORTED_LUCENE_VERSION = IndexVersions.MINIMUM_COMPATIBLE.luceneVersion().toString(); private final ShardId shardId = new ShardId("test", "_na_", 0); private final DiscoveryNode node1 = newNode("node1"); diff --git a/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java b/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java index b6700f0cf775f..3d95152c88cc5 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java @@ -385,7 +385,7 @@ public void testStatelessMinRefreshInterval() { .put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()) .put(EXISTING_SHARDS_ALLOCATOR_SETTING.getKey(), "stateless") .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "2s") - .put(SETTING_INDEX_VERSION_CREATED.getKey(), IndexVersion.V_8_10_0.id() + 1) + .put(SETTING_INDEX_VERSION_CREATED.getKey(), IndexVersions.V_8_10_0.id() + 1) .build() ); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new IndexSettings(metadata, Settings.EMPTY)); diff --git a/server/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java b/server/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java index 92e080d85c0c2..d2b2926af7d4c 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexSortSettingsTests.java @@ -161,7 +161,7 @@ public void testSortingAgainstAliases() { public void testSortingAgainstAliasesPre713() { IndexSettings indexSettings = indexSettings( - Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.V_7_12_0).put("index.sort.field", "field").build() + Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersions.V_7_12_0).put("index.sort.field", "field").build() ); MappedFieldType aliased = new KeywordFieldMapper.KeywordFieldType("aliased"); Sort sort = buildIndexSort(indexSettings, Map.of("field", aliased)); diff --git a/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java b/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java index 60eb7313c71d7..e13ce9702cdfd 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexVersionTests.java @@ -32,8 +32,8 @@ public class IndexVersionTests extends ESTestCase { public void testVersionComparison() { - IndexVersion V_7_2_0 = IndexVersion.V_7_2_0; - IndexVersion V_8_0_0 = IndexVersion.V_8_0_0; + IndexVersion V_7_2_0 = IndexVersions.V_7_2_0; + IndexVersion V_8_0_0 = IndexVersions.V_8_0_0; assertThat(V_7_2_0.before(V_8_0_0), is(true)); assertThat(V_7_2_0.before(V_7_2_0), is(false)); assertThat(V_8_0_0.before(V_7_2_0), is(false)); @@ -70,7 +70,7 @@ public static class DuplicatedIdFakeVersion { public void testStaticIndexVersionChecks() { assertThat( - IndexVersion.getAllVersionIds(IndexVersionTests.CorrectFakeVersion.class), + IndexVersions.getAllVersionIds(IndexVersionTests.CorrectFakeVersion.class), equalTo( Map.of( 199, @@ -84,7 +84,7 @@ public void testStaticIndexVersionChecks() { ) ) ); - AssertionError e = expectThrows(AssertionError.class, () -> IndexVersion.getAllVersionIds(DuplicatedIdFakeVersion.class)); + AssertionError e = expectThrows(AssertionError.class, () -> IndexVersions.getAllVersionIds(DuplicatedIdFakeVersion.class)); assertThat(e.getMessage(), containsString("have the same version number")); } @@ -154,7 +154,7 @@ public void testMinimumCompatibleVersion() { } public void testVersionConstantPresent() { - Set ignore = Set.of(IndexVersion.ZERO, IndexVersion.current(), IndexVersion.MINIMUM_COMPATIBLE); + Set ignore = Set.of(IndexVersions.ZERO, IndexVersion.current(), IndexVersions.MINIMUM_COMPATIBLE); assertThat(IndexVersion.current(), sameInstance(IndexVersion.fromId(IndexVersion.current().id()))); assertThat(IndexVersion.current().luceneVersion(), equalTo(org.apache.lucene.util.Version.LATEST)); final int iters = scaledRandomIntBetween(20, 100); @@ -167,7 +167,7 @@ public void testVersionConstantPresent() { } public void testCURRENTIsLatest() { - assertThat(Collections.max(IndexVersion.getAllVersions()), is(IndexVersion.current())); + assertThat(Collections.max(IndexVersions.getAllVersions()), is(IndexVersion.current())); } public void testToString() { diff --git a/server/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java b/server/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java index 0018c9cf1d7da..fa55d24bdbc48 100644 --- a/server/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java +++ b/server/src/test/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.indices.analysis.PreBuiltAnalyzers; @@ -51,7 +52,7 @@ public void testThatDefaultAndStandardAnalyzerAreTheSameInstance() { public void testThatInstancesAreTheSameAlwaysForKeywordAnalyzer() { assertThat( PreBuiltAnalyzers.KEYWORD.getAnalyzer(IndexVersion.current()), - is(PreBuiltAnalyzers.KEYWORD.getAnalyzer(IndexVersion.MINIMUM_COMPATIBLE)) + is(PreBuiltAnalyzers.KEYWORD.getAnalyzer(IndexVersions.MINIMUM_COMPATIBLE)) ); } diff --git a/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index aed83cf8abd95..072851789f2e0 100644 --- a/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -99,6 +99,7 @@ import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.codec.CodecService; import org.elasticsearch.index.fieldvisitor.FieldsVisitor; @@ -6677,7 +6678,7 @@ public long softUpdateDocuments(Term term, Iterable getCommitExtraUserData() { - return Map.of("userkey", "userdata", ES_VERSION, IndexVersion.ZERO.toString()); + return Map.of("userkey", "userdata", ES_VERSION, IndexVersions.ZERO.toString()); } }; engine.skipTranslogRecovery(); @@ -7502,14 +7503,14 @@ public void testTrimUnsafeCommitHasESVersionInUserData() throws IOException { .setIndexDeletionPolicy(NoDeletionPolicy.INSTANCE); try (IndexWriter indexWriter = new IndexWriter(store.directory(), indexWriterConfig)) { Map commitUserDataWithOlderVersion = new HashMap<>(committedSegmentsInfo.userData); - commitUserDataWithOlderVersion.put(ES_VERSION, IndexVersion.V_7_0_0.toString()); + commitUserDataWithOlderVersion.put(ES_VERSION, IndexVersions.V_7_0_0.toString()); indexWriter.setLiveCommitData(commitUserDataWithOlderVersion.entrySet()); indexWriter.commit(); } Map userDataBeforeTrimUnsafeCommits = store.readLastCommittedSegmentsInfo().getUserData(); assertThat(userDataBeforeTrimUnsafeCommits, hasKey(ES_VERSION)); - assertThat(userDataBeforeTrimUnsafeCommits.get(ES_VERSION), is(equalTo(IndexVersion.V_7_0_0.toString()))); + assertThat(userDataBeforeTrimUnsafeCommits.get(ES_VERSION), is(equalTo(IndexVersions.V_7_0_0.toString()))); store.trimUnsafeCommits(config.getTranslogConfig().getTranslogPath()); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java index 091412826b79e..638af8562a098 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.core.Strings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType; import org.elasticsearch.script.DateFieldScript; import org.elasticsearch.script.ScriptService; @@ -230,7 +231,7 @@ public void testBadNullValue() throws IOException { MapperParsingException e = expectThrows( MapperParsingException.class, - () -> createDocumentMapper(IndexVersion.V_8_0_0, fieldMapping(b -> b.field("type", "date").field("null_value", "foo"))) + () -> createDocumentMapper(IndexVersions.V_8_0_0, fieldMapping(b -> b.field("type", "date").field("null_value", "foo"))) ); assertThat( @@ -241,7 +242,7 @@ public void testBadNullValue() throws IOException { ) ); - createDocumentMapper(IndexVersion.V_7_9_0, fieldMapping(b -> b.field("type", "date").field("null_value", "foo"))); + createDocumentMapper(IndexVersions.V_7_9_0, fieldMapping(b -> b.field("type", "date").field("null_value", "foo"))); assertWarnings("Error parsing [foo] as date in [null_value] on field [field]); [null_value] will be ignored"); } @@ -727,8 +728,8 @@ public void testLegacyDateFormatName() { // BWC compatible index, e.g 7.x IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ) ); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DynamicTemplatesTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DynamicTemplatesTests.java index 6c2a02df1db24..54db5832c2726 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DynamicTemplatesTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DynamicTemplatesTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.XContentTestUtils; import org.elasticsearch.test.index.IndexVersionUtils; import org.elasticsearch.xcontent.XContentBuilder; @@ -194,8 +195,8 @@ public void testDynamicMapperWithBadMapping() throws IOException { // in 7.x versions this will issue a deprecation warning IndexVersion version = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ); DocumentMapper mapper = createDocumentMapper(version, topMapping(b -> { b.startArray("dynamic_templates"); @@ -667,7 +668,7 @@ public void testIllegalDynamicTemplate7DotXIndex() throws Exception { mapping.endObject(); } mapping.endObject(); - IndexVersion createdVersion = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_7_0); + IndexVersion createdVersion = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_7_0); MapperService mapperService = createMapperService(createdVersion, mapping); assertThat(mapperService.documentMapper().mappingSource().toString(), containsString("\"type\":\"string\"")); assertWarnings(""" diff --git a/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java index e08bb1bf30a09..0b5fef2b5971c 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java @@ -9,7 +9,7 @@ package org.elasticsearch.index.mapper; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.termvectors.TermVectorsService; import org.elasticsearch.test.index.IndexVersionUtils; import org.elasticsearch.xcontent.XContentFactory; @@ -86,7 +86,7 @@ public void testUsingEnabledSettingThrows() { public void testUsingEnabledBefore8() throws Exception { DocumentMapper docMapper = createDocumentMapper( - IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0), + IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0), topMapping(b -> b.startObject("_field_names").field("enabled", false).endObject()) ); @@ -103,7 +103,7 @@ public void testUsingEnabledBefore8() throws Exception { */ public void testMergingMappingsBefore8() throws Exception { MapperService mapperService = createMapperService( - IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0), + IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0), mapping(b -> {}) ); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java index e53edc118b43f..d2510d3671d23 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java @@ -19,6 +19,7 @@ import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.script.IpFieldScript; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.XContentBuilder; @@ -203,7 +204,7 @@ public void testNullValue() throws IOException { "Failed to parse mapping: Error parsing [null_value] on field [field]: ':1' is not an IP string literal." ); - createDocumentMapper(IndexVersion.V_7_9_0, fieldMapping(b -> { + createDocumentMapper(IndexVersions.V_7_9_0, fieldMapping(b -> { b.field("type", "ip"); b.field("null_value", ":1"); })); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java b/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java index 69b44d383193a..aa3b083f4496f 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.indices.IndicesModule; import org.elasticsearch.test.index.IndexVersionUtils; @@ -271,7 +272,7 @@ public void testIsMetadataField() throws IOException { assertFalse(mapperService.isMetadataField(randomAlphaOfLengthBetween(10, 15))); for (String builtIn : IndicesModule.getBuiltInMetadataFields()) { - if (NestedPathFieldMapper.NAME.equals(builtIn) && version.before(IndexVersion.V_8_0_0)) { + if (NestedPathFieldMapper.NAME.equals(builtIn) && version.before(IndexVersions.V_8_0_0)) { continue; // Nested field does not exist in the 7x line } assertTrue("Expected " + builtIn + " to be a metadata field for version " + version, mapperService.isMetadataField(builtIn)); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java b/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java index ddb81727fd399..abe8e820acae8 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/MappingParserTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.indices.IndicesModule; @@ -317,7 +318,7 @@ public void testBlankFieldName() throws Exception { } public void testBlankFieldNameBefore8_6_0() throws Exception { - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.V_8_5_0); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersions.V_8_5_0); TransportVersion transportVersion = TransportVersionUtils.randomVersionBetween( random(), TransportVersions.MINIMUM_COMPATIBLE, diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java index 82dcf46960008..621f03813b1ff 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.ObjectMapper.Dynamic; import org.elasticsearch.test.index.IndexVersionUtils; @@ -1006,7 +1007,7 @@ public void testReorderParent() throws IOException { assertThat(doc.docs().size(), equalTo(3)); NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper; - if (version.before(IndexVersion.V_8_0_0)) { + if (version.before(IndexVersions.V_8_0_0)) { assertThat(doc.docs().get(0).get("_type"), equalTo(nested1Mapper.nestedTypePath())); } else { assertThat(doc.docs().get(0).get(NestedPathFieldMapper.NAME), equalTo(nested1Mapper.nestedTypePath())); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NestedPathFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NestedPathFieldMapperTests.java index 4338fed4aed08..e652d1e2e85cc 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NestedPathFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NestedPathFieldMapperTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.xcontent.XContentType; import java.io.IOException; @@ -25,7 +26,7 @@ protected String fieldName() { @Override protected boolean isSupportedOn(IndexVersion version) { - return version.onOrAfter(IndexVersion.V_8_0_0); + return version.onOrAfter(IndexVersions.V_8_0_0); } @Override diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java index 191822ad07cfe..9b447d0727152 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java @@ -18,6 +18,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerScope; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; @@ -531,7 +532,7 @@ public void testBWCunknownParametersfromDynamicTemplates() { {"type":"test_mapper","some_unknown_parameter":true,"required":"value"}"""; TestMapper mapper = fromMapping( mapping, - IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0), + IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0), TransportVersionUtils.randomVersionBetween( random(), TransportVersions.V_7_0_0, @@ -549,7 +550,7 @@ public void testBWCunknownParametersfromDynamicTemplates() { MapperParsingException ex = expectThrows( MapperParsingException.class, - () -> fromMapping(mapping, IndexVersion.V_8_0_0, TransportVersions.V_8_0_0, true) + () -> fromMapping(mapping, IndexVersions.V_8_0_0, TransportVersions.V_8_0_0, true) ); assertEquals("unknown parameter [some_unknown_parameter] on mapper [field] of type [test_mapper]", ex.getMessage()); } @@ -586,7 +587,7 @@ public void testDeprecatedParameters() { // 'index' is declared explicitly, 'store' is not, but is one of the previously always-accepted params String mapping = """ {"type":"test_mapper","index":false,"store":true,"required":"value"}"""; - TestMapper mapper = fromMapping(mapping, IndexVersion.V_7_8_0, TransportVersions.V_7_8_0); + TestMapper mapper = fromMapping(mapping, IndexVersions.V_7_8_0, TransportVersions.V_7_8_0); assertWarnings("Parameter [store] has no effect on type [test_mapper] and will be removed in future"); assertFalse(mapper.index); assertEquals(""" @@ -594,7 +595,7 @@ public void testDeprecatedParameters() { MapperParsingException e = expectThrows( MapperParsingException.class, - () -> fromMapping(mapping, IndexVersion.V_8_0_0, TransportVersions.V_8_0_0) + () -> fromMapping(mapping, IndexVersions.V_8_0_0, TransportVersions.V_8_0_0) ); assertEquals("unknown parameter [store] on mapper [field] of type [test_mapper]", e.getMessage()); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/TypeParsersTests.java b/server/src/test/java/org/elasticsearch/index/mapper/TypeParsersTests.java index 5eab2951d9e2d..2b704a25e2232 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/TypeParsersTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/TypeParsersTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerScope; import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; @@ -85,7 +86,7 @@ public void testMultiFieldWithinMultiField() throws IOException { IndexSettings indexSettings = new IndexSettings(metadata, Settings.EMPTY); when(mapperService.getIndexSettings()).thenReturn(indexSettings); - IndexVersion olderVersion = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion olderVersion = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); MappingParserContext olderContext = new MappingParserContext( null, type -> typeParser, @@ -111,7 +112,7 @@ public void testMultiFieldWithinMultiField() throws IOException { // For indices created in 8.0 or later, we should throw an error. Map fieldNodeCopy = XContentHelper.convertToMap(BytesReference.bytes(mapping), true, mapping.contentType()).v2(); - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()); TransportVersion transportVersion = TransportVersionUtils.randomVersionBetween( random(), TransportVersions.V_8_0_0, diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/BinaryDenseVectorScriptDocValuesTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/BinaryDenseVectorScriptDocValuesTests.java index 59ec920e891ee..ff5baf8ba0877 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/BinaryDenseVectorScriptDocValuesTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/BinaryDenseVectorScriptDocValuesTests.java @@ -11,6 +11,7 @@ import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.util.BytesRef; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.ElementType; import org.elasticsearch.script.field.vectors.BinaryDenseVectorDocValuesField; import org.elasticsearch.script.field.vectors.ByteBinaryDenseVectorDocValuesField; @@ -31,7 +32,7 @@ public void testFloatGetVectorValueAndGetMagnitude() throws IOException { float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } }; float[] expectedMagnitudes = { 1.7320f, 2.4495f, 3.3166f }; - for (IndexVersion indexVersion : List.of(IndexVersion.V_7_4_0, IndexVersion.current())) { + for (IndexVersion indexVersion : List.of(IndexVersions.V_7_4_0, IndexVersion.current())) { BinaryDocValues docValues = wrap(vectors, ElementType.FLOAT, indexVersion); DenseVectorDocValuesField field = new BinaryDenseVectorDocValuesField(docValues, "test", ElementType.FLOAT, dims, indexVersion); DenseVectorScriptDocValues scriptDocValues = field.toScriptDocValues(); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java index 6d562f88a0100..b10d756a6e458 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/DenseVectorFieldMapperTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.codec.CodecService; import org.elasticsearch.index.codec.PerFieldMapperCodec; import org.elasticsearch.index.mapper.DocumentMapper; @@ -62,7 +63,7 @@ public class DenseVectorFieldMapperTests extends MapperTestCase { - private static final IndexVersion INDEXED_BY_DEFAULT_PREVIOUS_INDEX_VERSION = IndexVersion.V_8_10_0; + private static final IndexVersion INDEXED_BY_DEFAULT_PREVIOUS_INDEX_VERSION = IndexVersions.V_8_10_0; private final ElementType elementType; private final boolean indexed; private final boolean indexOptionsSet; @@ -591,7 +592,7 @@ public void testDefaultParamsIndexByDefault() throws Exception { } public void testAddDocumentsToIndexBefore_V_7_5_0() throws Exception { - IndexVersion indexVersion = IndexVersion.V_7_4_0; + IndexVersion indexVersion = IndexVersions.V_7_4_0; DocumentMapper mapper = createDocumentMapper( indexVersion, fieldMapping(b -> b.field("index", false).field("type", "dense_vector").field("dims", 3)) diff --git a/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java index 5643d573867bc..79f6768512b85 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/vectors/SparseVectorFieldMapperTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentParsingException; import org.elasticsearch.index.mapper.MappedFieldType; @@ -240,7 +241,7 @@ public void testSparseVectorUnsupportedIndex() throws Exception { IndexVersion version = IndexVersionUtils.randomVersionBetween( random(), PREVIOUS_SPARSE_VECTOR_INDEX_VERSION, - IndexVersion.FIRST_DETACHED_INDEX_VERSION + IndexVersions.FIRST_DETACHED_INDEX_VERSION ); Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(version, fieldMapping(b -> { b.field("type", "sparse_vector"); diff --git a/server/src/test/java/org/elasticsearch/index/similarity/SimilarityServiceTests.java b/server/src/test/java/org/elasticsearch/index/similarity/SimilarityServiceTests.java index fe6a61060d39d..a003436fc0523 100644 --- a/server/src/test/java/org/elasticsearch/index/similarity/SimilarityServiceTests.java +++ b/server/src/test/java/org/elasticsearch/index/similarity/SimilarityServiceTests.java @@ -14,7 +14,7 @@ import org.apache.lucene.search.similarities.Similarity; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexSettings; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.lucene.similarity.LegacyBM25Similarity; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; @@ -73,7 +73,7 @@ public float score(float freq, long norm) { }; IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> SimilarityService.validateSimilarity(IndexVersion.V_7_0_0, negativeScoresSim) + () -> SimilarityService.validateSimilarity(IndexVersions.V_7_0_0, negativeScoresSim) ); assertThat(e.getMessage(), Matchers.containsString("Similarities should not return negative scores")); @@ -98,7 +98,7 @@ public float score(float freq, long norm) { }; e = expectThrows( IllegalArgumentException.class, - () -> SimilarityService.validateSimilarity(IndexVersion.V_7_0_0, decreasingScoresWithFreqSim) + () -> SimilarityService.validateSimilarity(IndexVersions.V_7_0_0, decreasingScoresWithFreqSim) ); assertThat(e.getMessage(), Matchers.containsString("Similarity scores should not decrease when term frequency increases")); @@ -123,7 +123,7 @@ public float score(float freq, long norm) { }; e = expectThrows( IllegalArgumentException.class, - () -> SimilarityService.validateSimilarity(IndexVersion.V_7_0_0, increasingScoresWithNormSim) + () -> SimilarityService.validateSimilarity(IndexVersions.V_7_0_0, increasingScoresWithNormSim) ); assertThat(e.getMessage(), Matchers.containsString("Similarity scores should not increase when norm increases")); } diff --git a/server/src/test/java/org/elasticsearch/index/snapshots/blobstore/FileInfoTests.java b/server/src/test/java/org/elasticsearch/index/snapshots/blobstore/FileInfoTests.java index 1f72c7df8a9aa..b2b5bc7d9444b 100644 --- a/server/src/test/java/org/elasticsearch/index/snapshots/blobstore/FileInfoTests.java +++ b/server/src/test/java/org/elasticsearch/index/snapshots/blobstore/FileInfoTests.java @@ -12,7 +12,7 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.unit.ByteSizeValue; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot.FileInfo; import org.elasticsearch.index.store.StoreFileMetadata; import org.elasticsearch.test.ESTestCase; @@ -32,7 +32,7 @@ import static org.hamcrest.Matchers.is; public class FileInfoTests extends ESTestCase { - private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersion.MINIMUM_COMPATIBLE.luceneVersion(); + private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersions.MINIMUM_COMPATIBLE.luceneVersion(); public void testToFromXContent() throws IOException { final int iters = scaledRandomIntBetween(1, 10); diff --git a/server/src/test/java/org/elasticsearch/index/store/StoreTests.java b/server/src/test/java/org/elasticsearch/index/store/StoreTests.java index 1c6314d60673d..dfbfb737c9ab2 100644 --- a/server/src/test/java/org/elasticsearch/index/store/StoreTests.java +++ b/server/src/test/java/org/elasticsearch/index/store/StoreTests.java @@ -58,6 +58,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.seqno.ReplicationTracker; import org.elasticsearch.index.seqno.RetentionLease; @@ -111,7 +112,7 @@ public class StoreTests extends ESTestCase { "index", Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current()).build() ); - private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersion.MINIMUM_COMPATIBLE.luceneVersion(); + private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersions.MINIMUM_COMPATIBLE.luceneVersion(); public void testRefCount() { final ShardId shardId = new ShardId("index", "_na_", 1); diff --git a/server/src/test/java/org/elasticsearch/index/store/VerifyingIndexOutputTests.java b/server/src/test/java/org/elasticsearch/index/store/VerifyingIndexOutputTests.java index 4cf9c261dc281..d10ee1a7938fc 100644 --- a/server/src/test/java/org/elasticsearch/index/store/VerifyingIndexOutputTests.java +++ b/server/src/test/java/org/elasticsearch/index/store/VerifyingIndexOutputTests.java @@ -14,7 +14,7 @@ import org.apache.lucene.store.IOContext; import org.apache.lucene.util.Version; import org.elasticsearch.common.Numbers; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matcher; @@ -28,7 +28,7 @@ public class VerifyingIndexOutputTests extends ESTestCase { private static final int CHECKSUM_LENGTH = 8; - private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersion.MINIMUM_COMPATIBLE.luceneVersion(); + private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersions.MINIMUM_COMPATIBLE.luceneVersion(); private static final Matcher VERIFICATION_FAILURE = containsString("verification failed (hardware problem?)"); private static final Matcher FOOTER_NOT_CHECKED = allOf(VERIFICATION_FAILURE, containsString("footer=")); private static final Matcher INVALID_LENGTH = allOf(VERIFICATION_FAILURE, containsString("footer=")); diff --git a/server/src/test/java/org/elasticsearch/indices/IndicesModuleTests.java b/server/src/test/java/org/elasticsearch/indices/IndicesModuleTests.java index e081f4ab96252..2ce85a598541e 100644 --- a/server/src/test/java/org/elasticsearch/indices/IndicesModuleTests.java +++ b/server/src/test/java/org/elasticsearch/indices/IndicesModuleTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.indices; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper; import org.elasticsearch.index.mapper.DocCountFieldMapper; import org.elasticsearch.index.mapper.FieldNamesFieldMapper; @@ -89,7 +90,7 @@ public Map getMetadataMappers() { public void testBuiltinMappers() { IndicesModule module = new IndicesModule(Collections.emptyList()); { - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()); assertThat( module.getMapperRegistry().getMapperParser("object", IndexVersion.current()), instanceOf(ObjectMapper.TypeParser.class) @@ -106,8 +107,8 @@ public void testBuiltinMappers() { { IndexVersion version = IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_0_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_0_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ); assertEquals(EXPECTED_METADATA_FIELDS.length - 1, module.getMapperRegistry().getMetadataMapperParsers(version).size()); } @@ -225,14 +226,14 @@ public Map getMetadataMappers() { public void testFieldNamesIsLast() { IndicesModule module = new IndicesModule(Collections.emptyList()); - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()); List fieldNames = new ArrayList<>(module.getMapperRegistry().getMetadataMapperParsers(version).keySet()); assertEquals(FieldNamesFieldMapper.NAME, fieldNames.get(fieldNames.size() - 1)); } public void testFieldNamesIsLastWithPlugins() { IndicesModule module = new IndicesModule(fakePlugins); - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()); List fieldNames = new ArrayList<>(module.getMapperRegistry().getMetadataMapperParsers(version).keySet()); assertEquals(FieldNamesFieldMapper.NAME, fieldNames.get(fieldNames.size() - 1)); } diff --git a/server/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java b/server/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java index 288c186e5f882..c4d6cb6be502d 100644 --- a/server/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java +++ b/server/src/test/java/org/elasticsearch/indices/analysis/AnalysisModuleTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.index.IndexService.IndexCreationContext; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.Analysis; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.index.analysis.CharFilterFactory; @@ -191,7 +192,7 @@ public void testStandardFilterBWC() throws IOException { // cacheing bug meant that it was still possible to create indexes using a standard // filter until 7.6 { - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_6_0, IndexVersion.current()); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_6_0, IndexVersion.current()); final Settings settings = Settings.builder() .put("index.analysis.analyzer.my_standard.tokenizer", "standard") .put("index.analysis.analyzer.my_standard.filter", "standard") @@ -202,7 +203,7 @@ public void testStandardFilterBWC() throws IOException { assertThat(exc.getMessage(), equalTo("The [standard] token filter has been removed.")); } { - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_5_2); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_5_2); final Settings settings = Settings.builder() .put("index.analysis.analyzer.my_standard.tokenizer", "standard") .put("index.analysis.analyzer.my_standard.filter", "standard") diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryStatusTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryStatusTests.java index 998497f991ed5..e53019fd93506 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryStatusTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/RecoveryStatusTests.java @@ -13,7 +13,7 @@ import org.apache.lucene.util.Version; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.index.IndexService; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.store.StoreFileMetadata; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -23,7 +23,7 @@ import java.util.regex.Pattern; public class RecoveryStatusTests extends ESSingleNodeTestCase { - private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersion.MINIMUM_COMPATIBLE.luceneVersion(); + private static final Version MIN_SUPPORTED_LUCENE_VERSION = IndexVersions.MINIMUM_COMPATIBLE.luceneVersion(); public void testRenameTempFiles() throws IOException { IndexService service = createIndex("foo"); diff --git a/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java b/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java index 47d3777573c4f..e2e56d33bbba0 100644 --- a/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java +++ b/server/src/test/java/org/elasticsearch/indices/recovery/StartRecoveryRequestTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.common.UUIDs; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.shard.ShardId; @@ -42,8 +43,14 @@ public void testSerialization() throws Exception { final StartRecoveryRequest outRequest = new StartRecoveryRequest( new ShardId("test", "_na_", 0), UUIDs.randomBase64UUID(), - DiscoveryNodeUtils.builder("a").roles(emptySet()).version(targetNodeVersion, IndexVersion.ZERO, IndexVersion.current()).build(), - DiscoveryNodeUtils.builder("b").roles(emptySet()).version(targetNodeVersion, IndexVersion.ZERO, IndexVersion.current()).build(), + DiscoveryNodeUtils.builder("a") + .roles(emptySet()) + .version(targetNodeVersion, IndexVersions.ZERO, IndexVersion.current()) + .build(), + DiscoveryNodeUtils.builder("b") + .roles(emptySet()) + .version(targetNodeVersion, IndexVersions.ZERO, IndexVersion.current()) + .build(), randomNonNegativeLong(), metadataSnapshot, randomBoolean(), diff --git a/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java b/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java index cd9e23d80cb71..6ea93cc9c5940 100644 --- a/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java +++ b/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java @@ -22,7 +22,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.Processors; import org.elasticsearch.http.HttpInfo; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.ingest.IngestInfo; import org.elasticsearch.ingest.ProcessorInfo; import org.elasticsearch.monitor.jvm.JvmInfo; @@ -115,7 +115,7 @@ private static NodeInfo createNodeInfo() { Build build = Build.current(); DiscoveryNode node = DiscoveryNodeUtils.builder("test_node") .roles(emptySet()) - .version(VersionUtils.randomVersion(random()), IndexVersion.ZERO, IndexVersionUtils.randomVersion()) + .version(VersionUtils.randomVersion(random()), IndexVersions.ZERO, IndexVersionUtils.randomVersion()) .build(); Settings settings = randomBoolean() ? null : Settings.builder().put("test", "setting").build(); OsInfo osInfo = null; diff --git a/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java b/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java index bb4d4dac31a5a..44aff1850a869 100644 --- a/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java +++ b/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.util.Maps; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.test.ESTestCase; @@ -108,7 +109,7 @@ public void testAddSnapshots() { newSnapshot, new RepositoryData.SnapshotDetails( randomFrom(SnapshotState.SUCCESS, SnapshotState.PARTIAL, SnapshotState.FAILED), - randomFrom(IndexVersion.current(), IndexVersion.MINIMUM_COMPATIBLE), + randomFrom(IndexVersion.current(), IndexVersions.MINIMUM_COMPATIBLE), randomNonNegativeLong(), randomNonNegativeLong(), randomAlphaOfLength(10) @@ -140,7 +141,7 @@ public void testInitIndices() { snapshotId.getUUID(), new RepositoryData.SnapshotDetails( randomFrom(SnapshotState.values()), - randomFrom(IndexVersion.current(), IndexVersion.MINIMUM_COMPATIBLE), + randomFrom(IndexVersion.current(), IndexVersions.MINIMUM_COMPATIBLE), randomNonNegativeLong(), randomNonNegativeLong(), randomAlphaOfLength(10) @@ -208,7 +209,7 @@ public void testGetSnapshotState() { snapshotId, new RepositoryData.SnapshotDetails( state, - randomFrom(IndexVersion.current(), IndexVersion.MINIMUM_COMPATIBLE), + randomFrom(IndexVersion.current(), IndexVersions.MINIMUM_COMPATIBLE), randomNonNegativeLong(), randomNonNegativeLong(), randomAlphaOfLength(10) @@ -453,7 +454,7 @@ public static RepositoryData generateRandomRepoData() { snapshotId, new RepositoryData.SnapshotDetails( randomFrom(SnapshotState.values()), - randomFrom(IndexVersion.current(), IndexVersion.MINIMUM_COMPATIBLE), + randomFrom(IndexVersion.current(), IndexVersions.MINIMUM_COMPATIBLE), randomNonNegativeLong(), randomNonNegativeLong(), randomAlphaOfLength(10) diff --git a/server/src/test/java/org/elasticsearch/script/VectorScoreScriptUtilsTests.java b/server/src/test/java/org/elasticsearch/script/VectorScoreScriptUtilsTests.java index e57b5039eb119..df9f4384719e3 100644 --- a/server/src/test/java/org/elasticsearch/script/VectorScoreScriptUtilsTests.java +++ b/server/src/test/java/org/elasticsearch/script/VectorScoreScriptUtilsTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.script; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.vectors.BinaryDenseVectorScriptDocValuesTests; import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.ElementType; import org.elasticsearch.index.mapper.vectors.KnnDenseVectorScriptDocValuesTests; @@ -42,11 +43,11 @@ public void testFloatVectorClassBindings() throws IOException { List fields = List.of( new BinaryDenseVectorDocValuesField( - BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, ElementType.FLOAT, IndexVersion.V_7_4_0), + BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, ElementType.FLOAT, IndexVersions.V_7_4_0), "test", ElementType.FLOAT, dims, - IndexVersion.V_7_4_0 + IndexVersions.V_7_4_0 ), new BinaryDenseVectorDocValuesField( BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, ElementType.FLOAT, IndexVersion.current()), @@ -210,11 +211,11 @@ public void testByteVsFloatSimilarity() throws IOException { List fields = List.of( new BinaryDenseVectorDocValuesField( - BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, ElementType.FLOAT, IndexVersion.V_7_4_0), + BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, ElementType.FLOAT, IndexVersions.V_7_4_0), "field0", ElementType.FLOAT, dims, - IndexVersion.V_7_4_0 + IndexVersions.V_7_4_0 ), new BinaryDenseVectorDocValuesField( BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, ElementType.FLOAT, IndexVersion.current()), diff --git a/server/src/test/java/org/elasticsearch/script/field/vectors/DenseVectorTests.java b/server/src/test/java/org/elasticsearch/script/field/vectors/DenseVectorTests.java index a21f3d3c7e0da..2be338efd7174 100644 --- a/server/src/test/java/org/elasticsearch/script/field/vectors/DenseVectorTests.java +++ b/server/src/test/java/org/elasticsearch/script/field/vectors/DenseVectorTests.java @@ -10,6 +10,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.vectors.BinaryDenseVectorScriptDocValuesTests; import org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.ElementType; import org.elasticsearch.test.ESTestCase; @@ -67,7 +68,7 @@ public void testFloatVsListQueryVector() { assertEquals(knn.cosineSimilarity(arrayQV), knn.cosineSimilarity(listQV), 0.001f); assertEquals(knn.cosineSimilarity((Object) listQV), knn.cosineSimilarity((Object) arrayQV), 0.001f); - for (IndexVersion indexVersion : List.of(IndexVersion.V_7_4_0, IndexVersion.current())) { + for (IndexVersion indexVersion : List.of(IndexVersions.V_7_4_0, IndexVersion.current())) { BytesRef value = BinaryDenseVectorScriptDocValuesTests.mockEncodeDenseVector(docVector, ElementType.FLOAT, indexVersion); BinaryDenseVector bdv = new BinaryDenseVector(docVector, value, dims, indexVersion); diff --git a/server/src/test/java/org/elasticsearch/transport/ProxyConnectionStrategyTests.java b/server/src/test/java/org/elasticsearch/transport/ProxyConnectionStrategyTests.java index 965288a989870..49e0ab1653432 100644 --- a/server/src/test/java/org/elasticsearch/transport/ProxyConnectionStrategyTests.java +++ b/server/src/test/java/org/elasticsearch/transport/ProxyConnectionStrategyTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.core.Strings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.TransportVersionUtils; import org.elasticsearch.test.junit.annotations.TestLogging; @@ -237,7 +238,7 @@ public void testProxyStrategyWillOpenNewConnectionsOnDisconnect() throws Excepti public void testConnectFailsWithIncompatibleNodes() { VersionInformation incompatibleVersion = new VersionInformation( Version.CURRENT.minimumCompatibilityVersion().minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); TransportVersion incompatibleTransportVersion = TransportVersionUtils.getPreviousVersion(TransportVersions.MINIMUM_COMPATIBLE); diff --git a/server/src/test/java/org/elasticsearch/transport/SniffConnectionStrategyTests.java b/server/src/test/java/org/elasticsearch/transport/SniffConnectionStrategyTests.java index 3f67c0c65d559..31096a53e67c0 100644 --- a/server/src/test/java/org/elasticsearch/transport/SniffConnectionStrategyTests.java +++ b/server/src/test/java/org/elasticsearch/transport/SniffConnectionStrategyTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.TransportVersionUtils; import org.elasticsearch.test.VersionUtils; @@ -374,7 +375,7 @@ public void testDiscoverWithSingleIncompatibleSeedNode() { List knownNodes = new CopyOnWriteArrayList<>(); VersionInformation incompatibleVersion = new VersionInformation( Version.CURRENT.minimumCompatibilityVersion().minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); TransportVersion incompatibleTransportVersion = TransportVersionUtils.getPreviousVersion(TransportVersions.MINIMUM_COMPATIBLE); @@ -453,7 +454,7 @@ public void testConnectFailsWithIncompatibleNodes() { List knownNodes = new CopyOnWriteArrayList<>(); VersionInformation incompatibleVersion = new VersionInformation( Version.CURRENT.minimumCompatibilityVersion().minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); TransportVersion incompatibleTransportVersion = TransportVersionUtils.getPreviousVersion(TransportVersions.MINIMUM_COMPATIBLE); @@ -1086,7 +1087,7 @@ public void testGetNodePredicateNodeVersion() { Version version = VersionUtils.randomVersion(random()); DiscoveryNode node = DiscoveryNodeUtils.builder("id") .address(address) - .version(version, IndexVersion.ZERO, IndexVersion.current()) + .version(version, IndexVersions.ZERO, IndexVersion.current()) .build(); assertThat(nodePredicate.test(node), equalTo(Version.CURRENT.isCompatible(version))); } diff --git a/server/src/test/java/org/elasticsearch/transport/TransportActionProxyTests.java b/server/src/test/java/org/elasticsearch/transport/TransportActionProxyTests.java index 32a5b5dec9597..fb0eb314a1e33 100644 --- a/server/src/test/java/org/elasticsearch/transport/TransportActionProxyTests.java +++ b/server/src/test/java/org/elasticsearch/transport/TransportActionProxyTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.RefCounted; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskCancellationService; @@ -53,7 +54,7 @@ public class TransportActionProxyTests extends ESTestCase { private static final Version CURRENT_VERSION = Version.fromString(String.valueOf(Version.CURRENT.major) + ".0.0"); protected static final VersionInformation version0 = new VersionInformation( CURRENT_VERSION.minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); protected static final TransportVersion transportVersion0 = TransportVersions.MINIMUM_COMPATIBLE; @@ -63,7 +64,7 @@ public class TransportActionProxyTests extends ESTestCase { protected static final VersionInformation version1 = new VersionInformation( Version.fromId(CURRENT_VERSION.id + 1), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); protected static final TransportVersion transportVersion1 = TransportVersion.fromId(TransportVersion.current().id() + 1); diff --git a/server/src/test/java/org/elasticsearch/transport/TransportServiceHandshakeTests.java b/server/src/test/java/org/elasticsearch/transport/TransportServiceHandshakeTests.java index fe77b161a9223..25ce8254a59c2 100644 --- a/server/src/test/java/org/elasticsearch/transport/TransportServiceHandshakeTests.java +++ b/server/src/test/java/org/elasticsearch/transport/TransportServiceHandshakeTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.TransportVersionUtils; @@ -129,7 +130,7 @@ public void testConnectToNodeLight() { TransportVersionUtils.randomCompatibleVersion(random()), new VersionInformation( VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumCompatibilityVersion(), Version.CURRENT), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ), TransportService.NOOP_TRANSPORT_INTERCEPTOR @@ -137,7 +138,7 @@ public void testConnectToNodeLight() { DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("") .address(transportServiceB.getLocalNode().getAddress()) .roles(emptySet()) - .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); try ( Transport.Connection connection = AbstractSimpleTransportTestCase.openConnection( @@ -174,7 +175,7 @@ public void testMismatchedClusterName() { DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("") .address(transportServiceB.getLocalNode().getAddress()) .roles(emptySet()) - .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); IllegalStateException ex = expectThrows(IllegalStateException.class, () -> { try ( @@ -209,7 +210,7 @@ public void testIncompatibleNodeVersions() { TransportVersions.MINIMUM_COMPATIBLE, new VersionInformation( VersionUtils.getPreviousVersion(Version.CURRENT.minimumCompatibilityVersion()), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ), TransportService.NOOP_TRANSPORT_INTERCEPTOR @@ -217,7 +218,7 @@ public void testIncompatibleNodeVersions() { DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("") .address(transportServiceB.getLocalNode().getAddress()) .roles(emptySet()) - .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); IllegalStateException ex = expectThrows(IllegalStateException.class, () -> { try ( @@ -258,13 +259,13 @@ public void testIncompatibleTransportVersions() { "TS_B", settings, TransportVersionUtils.getPreviousVersion(TransportVersions.MINIMUM_COMPATIBLE), - new VersionInformation(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()), + new VersionInformation(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()), TransportService.NOOP_TRANSPORT_INTERCEPTOR ); DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("") .address(transportServiceB.getLocalNode().getAddress()) .roles(emptySet()) - .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); expectThrows(ConnectTransportException.class, () -> { try ( @@ -336,7 +337,7 @@ public void testRejectsMismatchedBuildHash() { final DiscoveryNode discoveryNode = DiscoveryNodeUtils.builder("") .address(transportServiceB.getLocalNode().getAddress()) .roles(emptySet()) - .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build(); TransportSerializationException ex = expectThrows(TransportSerializationException.class, () -> { try ( @@ -407,7 +408,7 @@ public void testAcceptsMismatchedBuildHashFromDifferentVersion() { "TS_B", Settings.builder().put("cluster.name", "a").build(), TransportVersions.MINIMUM_COMPATIBLE, - new VersionInformation(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()), + new VersionInformation(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()), transportInterceptorB ); AbstractSimpleTransportTestCase.connectToNode(transportServiceA, transportServiceB.getLocalNode(), TestProfiles.LIGHT_PROFILE); diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java index 9ae8b2dfbe0c6..138ab77035b43 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/ESAllocationTestCase.java @@ -40,6 +40,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.gateway.GatewayAllocator; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.snapshots.SnapshotShardSizeInfo; import org.elasticsearch.snapshots.SnapshotsInfoService; import org.elasticsearch.test.ClusterServiceUtils; @@ -215,7 +216,7 @@ protected static DiscoveryNode newNode(String nodeId, Version version) { protected static DiscoveryNode newNode(String nodeId, Version version, IndexVersion indexVersion) { return DiscoveryNodeUtils.builder(nodeId) .roles(MASTER_DATA_ROLES) - .version(version, IndexVersion.MINIMUM_COMPATIBLE, indexVersion) + .version(version, IndexVersions.MINIMUM_COMPATIBLE, indexVersion) .build(); } diff --git a/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java b/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java index e51116898a7ea..628ba1c8fda3b 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java +++ b/test/framework/src/main/java/org/elasticsearch/index/KnownIndexVersions.java @@ -17,5 +17,5 @@ public class KnownIndexVersions { /** * A sorted list of all known transport versions */ - public static final List ALL_VERSIONS = List.copyOf(IndexVersion.getAllVersions()); + public static final List ALL_VERSIONS = List.copyOf(IndexVersions.getAllVersions()); } diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java index 692f7c6810254..9c9d1d763fbd1 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java @@ -33,6 +33,7 @@ import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldDataCache; @@ -87,7 +88,7 @@ */ public abstract class MapperTestCase extends MapperServiceTestCase { - public static final IndexVersion DEPRECATED_BOOST_INDEX_VERSION = IndexVersion.V_7_10_0; + public static final IndexVersion DEPRECATED_BOOST_INDEX_VERSION = IndexVersions.V_7_10_0; protected abstract void minimalMapping(XContentBuilder b) throws IOException; @@ -391,7 +392,7 @@ public final void testEmptyName() { public final void testBlankName() { IndexVersion version = getVersion(); - assumeTrue("blank field names are rejected from 8.6.0 onwards", version.onOrAfter(IndexVersion.V_8_6_0)); + assumeTrue("blank field names are rejected from 8.6.0 onwards", version.onOrAfter(IndexVersions.V_8_6_0)); MapperParsingException e = expectThrows(MapperParsingException.class, () -> createMapperService(version, mapping(b -> { b.startObject(" "); minimalMapping(b); @@ -528,7 +529,7 @@ public void testBoostNotAllowed() throws IOException { } protected IndexVersion boostNotAllowedIndexVersion() { - return IndexVersion.V_8_0_0; + return IndexVersions.V_8_0_0; } /** diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java index d8cf644e87105..77391aadaa554 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.index.IndexVersionUtils; import org.elasticsearch.xcontent.XContentBuilder; @@ -142,8 +143,8 @@ public final void testFixedMetaFieldsAreNotConfigurable() throws IOException { public void testTypeAndFriendsAreAcceptedBefore_8_6_0() throws IOException { assumeTrue("Metadata field " + fieldName() + " isn't configurable", isConfigurable()); - IndexVersion previousVersion = IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_6_0); - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, previousVersion); + IndexVersion previousVersion = IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_6_0); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, previousVersion); assumeTrue("Metadata field " + fieldName() + " is not supported on version " + version, isSupportedOn(version)); MapperService mapperService = createMapperService(version, mapping(b -> {})); // these parameters were previously silently ignored, they will still be ignored in existing indices @@ -166,7 +167,7 @@ public void testTypeAndFriendsAreAcceptedBefore_8_6_0() throws IOException { public void testTypeAndFriendsAreDeprecatedFrom_8_6_0() throws IOException { assumeTrue("Metadata field " + fieldName() + " isn't configurable", isConfigurable()); - IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_6_0, IndexVersion.current()); + IndexVersion version = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_6_0, IndexVersion.current()); assumeTrue("Metadata field " + fieldName() + " is not supported on version " + version, isSupportedOn(version)); MapperService mapperService = createMapperService(version, mapping(b -> {})); // these parameters were previously silently ignored, they are now deprecated in new indices diff --git a/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java index fc4b3ce93e11c..0ac67a4c54830 100644 --- a/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java @@ -36,6 +36,7 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.FinalizeSnapshotContext; import org.elasticsearch.repositories.RepositoriesService; @@ -360,7 +361,7 @@ protected void maybeInitWithOldSnapshotVersion(String repoName, Path repoPath) t initWithSnapshotVersion( repoName, repoPath, - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_8_9_0) + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_8_9_0) ); } } @@ -385,7 +386,7 @@ protected String initWithSnapshotVersion(String repoName, Path repoPath, IndexVe repositoryData.snapshotsToXContent(jsonBuilder, version); final var currentVersionString = Strings.toString(jsonBuilder); final String oldVersionString; - if (version.onOrAfter(IndexVersion.FIRST_DETACHED_INDEX_VERSION)) { + if (version.onOrAfter(IndexVersions.FIRST_DETACHED_INDEX_VERSION)) { oldVersionString = currentVersionString.replace( ",\"index_version\":" + IndexVersion.current(), ",\"index_version\":" + version diff --git a/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java b/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java index 576f7468e0819..51736f6e533ca 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/index/IndexVersionUtils.java @@ -10,6 +10,7 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.KnownIndexVersions; import org.elasticsearch.test.ESTestCase; @@ -114,11 +115,11 @@ public static IndexVersion getNextVersion(IndexVersion version) { /** Returns a random {@code IndexVersion} that is compatible with {@link IndexVersion#current()} */ public static IndexVersion randomCompatibleVersion(Random random) { - return randomVersionBetween(random, IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()); + return randomVersionBetween(random, IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()); } /** Returns a random {@code IndexVersion} that is compatible with the previous version to {@code version} */ public static IndexVersion randomPreviousCompatibleVersion(Random random, IndexVersion version) { - return randomVersionBetween(random, IndexVersion.MINIMUM_COMPATIBLE, getPreviousVersion(version)); + return randomVersionBetween(random, IndexVersions.MINIMUM_COMPATIBLE, getPreviousVersion(version)); } } diff --git a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java index 3b42181216bcb..f9085ec258627 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java @@ -48,6 +48,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.mocksocket.MockServerSocket; import org.elasticsearch.node.Node; import org.elasticsearch.tasks.Task; @@ -122,7 +123,7 @@ public abstract class AbstractSimpleTransportTestCase extends ESTestCase { // we use always a non-alpha or beta version here otherwise minimumCompatibilityVersion will be different for the two used versions protected static final VersionInformation version0 = new VersionInformation( Version.fromString(String.valueOf(Version.CURRENT.major) + ".0.0"), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); protected static final TransportVersion transportVersion0 = TransportVersion.current(); @@ -133,7 +134,7 @@ public abstract class AbstractSimpleTransportTestCase extends ESTestCase { protected static final VersionInformation version1 = new VersionInformation( Version.fromId(version0.nodeVersion().id + 1), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ); protected static final TransportVersion transportVersion1 = TransportVersion.fromId(transportVersion0.id() + 1); @@ -2322,7 +2323,7 @@ public void testHandshakeWithIncompatVersion() { "TS_C", new VersionInformation( Version.CURRENT.minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ), transportVersion, @@ -2361,7 +2362,7 @@ public void testHandshakeUpdatesVersion() throws IOException { "TS_C", new VersionInformation( Version.CURRENT.minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ), transportVersion, diff --git a/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java b/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java index 46b877a7eb4e3..d12966d4239a6 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/index/IndexVersionUtilsTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.test.index; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; @@ -19,7 +20,7 @@ public class IndexVersionUtilsTests extends ESTestCase { /** - * Tests that {@link IndexVersion#MINIMUM_COMPATIBLE} and {@link IndexVersionUtils#allReleasedVersions()} + * Tests that {@link IndexVersions#MINIMUM_COMPATIBLE} and {@link IndexVersionUtils#allReleasedVersions()} * agree with the list of index compatible versions we build in gradle. */ @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/98054") @@ -29,7 +30,7 @@ public void testGradleVersionsMatchVersionUtils() { .stream() /* Java lists all versions from the 5.x series onwards, but we only want to consider * ones that we're supposed to be compatible with. */ - .filter(v -> v.onOrAfter(IndexVersion.MINIMUM_COMPATIBLE)) + .filter(v -> v.onOrAfter(IndexVersions.MINIMUM_COMPATIBLE)) .toList(); List releasedIndexCompatible = released.stream() diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java index e004f9b8f81e8..d1dad46c5515c 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java @@ -35,6 +35,7 @@ import org.elasticsearch.index.IndexMode; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.index.IndexVersionUtils; @@ -2494,7 +2495,7 @@ private static ClusterState createRemoteClusterState( ) { Settings.Builder indexSettings; if (enableSoftDeletes == false) { - indexSettings = settings(IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0)).put( + indexSettings = settings(IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0)).put( IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), false ); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/SetSingleNodeAllocateStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/SetSingleNodeAllocateStepTests.java index 0592be1652c88..78dcaa1be92af 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/SetSingleNodeAllocateStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/SetSingleNodeAllocateStepTests.java @@ -31,6 +31,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.node.Node; import org.elasticsearch.test.VersionUtils; @@ -402,7 +403,7 @@ public void testPerformActionSomeShardsOnlyOnNewNodes() throws Exception { Version.fromId(Version.CURRENT.major * 1_000_000 + 99), VersionUtils.getPreviousVersion() ), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ); final int numNodes = randomIntBetween(2, 20); // Need at least 2 nodes to have some nodes on a new version @@ -471,7 +472,7 @@ public void testPerformActionSomeShardsOnlyOnNewNodesButNewNodesInvalidAttrs() { Version.fromId(Version.CURRENT.major * 1_000_000 + 99), VersionUtils.getPreviousVersion() ), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ); final int numNodes = randomIntBetween(2, 20); // Need at least 2 nodes to have some nodes on a new version @@ -548,7 +549,7 @@ public void testPerformActionNewShardsExistButWithInvalidAttributes() throws Exc Version.fromId(Version.CURRENT.major * 1_000_000 + 99), VersionUtils.getPreviousVersion() ), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersionUtils.randomCompatibleVersion(random()) ); final int numNodes = randomIntBetween(2, 20); // Need at least 2 nodes to have some nodes on a new version diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java index 316f29dde7999..49bdfd58eafd8 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java @@ -13,6 +13,7 @@ import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.frozen.FrozenEngine; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; @@ -32,7 +33,7 @@ public class IndexDeprecationChecks { static DeprecationIssue oldIndicesCheck(IndexMetadata indexMetadata) { // TODO: this check needs to be revised. It's trivially true right now. IndexVersion currentCompatibilityVersion = indexMetadata.getCompatibilityVersion(); - if (currentCompatibilityVersion.before(IndexVersion.V_7_0_0)) { + if (currentCompatibilityVersion.before(IndexVersions.V_7_0_0)) { return new DeprecationIssue( DeprecationIssue.Level.CRITICAL, "Old index with a compatibility version < 7.0", diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java index f64dd18361d2a..abff1499fceb7 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.engine.frozen.FrozenEngine; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; @@ -159,7 +160,7 @@ public void testCamelCaseDeprecation() throws IOException { + "} }"; IndexMetadata simpleIndex = IndexMetadata.builder(randomAlphaOfLengthBetween(5, 10)) - .settings(settings(IndexVersion.V_7_0_0)) + .settings(settings(IndexVersions.V_7_0_0)) .numberOfShards(1) .numberOfReplicas(1) .putMapping(simpleMapping) diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java index 015a8c87824f5..dd85aa6bf7bf7 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java @@ -44,6 +44,7 @@ import org.elasticsearch.core.TimeValue; import org.elasticsearch.discovery.DiscoveryModule; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataStats; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.license.License; @@ -457,7 +458,7 @@ public void testToXContent() throws IOException { mockNodeVersion, pluginEsBuildVersion, Version.CURRENT, - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current(), apmIndicesExist }; final String expectedJson = Strings.format(""" diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexRecoveryMonitoringDocTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexRecoveryMonitoringDocTests.java index 6d16c7b84a99b..35b5578602621 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexRecoveryMonitoringDocTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexRecoveryMonitoringDocTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.Strings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.recovery.RecoveryState; import org.elasticsearch.transport.NodeDisconnectedException; @@ -100,7 +101,7 @@ public void testToXContent() throws IOException { .version( new VersionInformation( Version.CURRENT.minimumCompatibilityVersion(), - IndexVersion.MINIMUM_COMPATIBLE, + IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current() ) ) diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/IndexStateResolver.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/IndexStateResolver.java index 748424386457f..a09d162c32967 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/IndexStateResolver.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/IndexStateResolver.java @@ -16,7 +16,7 @@ import org.elasticsearch.cluster.metadata.MappingMetadata; import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.index.Index; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import java.util.List; import java.util.Map; @@ -52,7 +52,7 @@ public IndexState getIndexState(Cluster logger.trace("Index [{}] health status is RED, any pending mapping upgrades will wait until this changes", metadata.getIndex()); return new IndexState<>(index, metadata.getIndex(), IndexStatus.UNHEALTHY); } - if (checkOutdatedIndices && metadata.getCreationVersion().before(IndexVersion.V_8_9_1)) { + if (checkOutdatedIndices && metadata.getCreationVersion().before(IndexVersions.V_8_9_1)) { logger.trace( "Index [{}] has been created before version 8.9.1 and must be deleted before proceeding with the upgrade.", metadata.getIndex() diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingDataStreamManagerTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingDataStreamManagerTests.java index 0b762b5eb45da..e23d003e2f209 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingDataStreamManagerTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingDataStreamManagerTests.java @@ -42,6 +42,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexMode; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.test.ClusterServiceUtils; @@ -191,7 +192,7 @@ public void testThatOutdatedDataStreamIsDetectedIfCheckEnabled() throws Exceptio nodes, IndexMetadata.State.OPEN, // This is an outdated version that requires indices to be deleted upon migration - IndexVersion.V_8_8_2, + IndexVersions.V_8_8_2, true ); @@ -223,7 +224,7 @@ public void testThatOutdatedDataStreamIsIgnoredIfCheckDisabled() throws Exceptio List.of(existingDataStream), nodes, IndexMetadata.State.OPEN, - IndexVersion.V_8_8_2, + IndexVersions.V_8_8_2, true ); diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexManagerTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexManagerTests.java index 3efd1d4c041f5..923269646d4d1 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexManagerTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ProfilingIndexManagerTests.java @@ -43,6 +43,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.test.ClusterServiceUtils; @@ -191,7 +192,7 @@ public void testThatOutdatedIndexIsDetectedIfCheckEnabled() throws Exception { nodes, IndexMetadata.State.OPEN, // This is an outdated version that requires indices to be deleted upon migration - IndexVersion.V_8_8_2, + IndexVersions.V_8_8_2, true ); @@ -223,7 +224,7 @@ public void testThatOutdatedIndexIsIgnoredIfCheckDisabled() throws Exception { List.of(existingIndex), nodes, IndexMetadata.State.OPEN, - IndexVersion.V_8_8_2, + IndexVersions.V_8_8_2, true ); diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java index 3d9aefba65913..8a7bcc565acfa 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgrader.java @@ -19,7 +19,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.ShardLimitValidator; import org.elasticsearch.threadpool.ThreadPool; @@ -99,8 +99,8 @@ static boolean needsUpgrade(ClusterState state) { return state.metadata() .stream() .filter( - imd -> imd.getCompatibilityVersion().onOrAfter(IndexVersion.V_7_12_0) - && imd.getCompatibilityVersion().before(IndexVersion.V_8_0_0) + imd -> imd.getCompatibilityVersion().onOrAfter(IndexVersions.V_7_12_0) + && imd.getCompatibilityVersion().before(IndexVersions.V_8_0_0) ) .filter(IndexMetadata::isPartialSearchableSnapshot) .map(IndexMetadata::getSettings) @@ -115,8 +115,8 @@ static ClusterState upgradeIndices(ClusterState currentState) { currentState.metadata() .stream() .filter( - imd -> imd.getCompatibilityVersion().onOrAfter(IndexVersion.V_7_12_0) - && imd.getCompatibilityVersion().before(IndexVersion.V_8_0_0) + imd -> imd.getCompatibilityVersion().onOrAfter(IndexVersions.V_7_12_0) + && imd.getCompatibilityVersion().before(IndexVersions.V_8_0_0) ) .filter(imd -> imd.isPartialSearchableSnapshot() && notFrozenShardLimitGroup(imd.getSettings())) .map(SearchableSnapshotIndexMetadataUpgrader::setShardLimitGroupFrozen) diff --git a/x-pack/plugin/searchable-snapshots/src/test/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgraderTests.java b/x-pack/plugin/searchable-snapshots/src/test/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgraderTests.java index 7f1dc6f51d73e..594d356becf87 100644 --- a/x-pack/plugin/searchable-snapshots/src/test/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgraderTests.java +++ b/x-pack/plugin/searchable-snapshots/src/test/java/org/elasticsearch/xpack/searchablesnapshots/upgrade/SearchableSnapshotIndexMetadataUpgraderTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.indices.ShardLimitValidator; import org.elasticsearch.snapshots.SearchableSnapshotsSettings; import org.elasticsearch.test.ESTestCase; @@ -118,8 +119,8 @@ private Settings partialNeedsUpgrade() { return searchableSnapshotSettings( IndexVersionUtils.randomVersionBetween( random(), - IndexVersion.V_7_12_0, - IndexVersionUtils.getPreviousVersion(IndexVersion.V_8_0_0) + IndexVersions.V_7_12_0, + IndexVersionUtils.getPreviousVersion(IndexVersions.V_8_0_0) ), true ); @@ -131,7 +132,7 @@ private Settings partialNeedsUpgrade() { private Settings partial_7_13plus() { return shardLimitGroupFrozen( searchableSnapshotSettings( - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_13_0, IndexVersion.current()), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_13_0, IndexVersion.current()), true ) ); @@ -142,7 +143,7 @@ private Settings partial_7_13plus() { */ private Settings partial_8plusNoShardLimit() { return searchableSnapshotSettings( - IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_8_0_0, IndexVersion.current()), + IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_8_0_0, IndexVersion.current()), true ); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java index 88725e015e511..66790c9898230 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java @@ -36,6 +36,7 @@ import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.index.engine.InternalEngineFactory; import org.elasticsearch.indices.TestIndexNameExpressionResolver; @@ -402,7 +403,7 @@ public void testJoinValidatorOnDisabledSecurity() throws Exception { public void testJoinValidatorForFIPSOnAllowedLicense() throws Exception { DiscoveryNode node = DiscoveryNodeUtils.builder("foo") - .version(VersionUtils.randomVersion(random()), IndexVersion.ZERO, IndexVersionUtils.randomVersion()) + .version(VersionUtils.randomVersion(random()), IndexVersions.ZERO, IndexVersionUtils.randomVersion()) .build(); Metadata.Builder builder = Metadata.builder(); License license = TestUtils.generateSignedLicense( @@ -427,7 +428,7 @@ public void testJoinValidatorForFIPSOnAllowedLicense() throws Exception { public void testJoinValidatorForFIPSOnForbiddenLicense() throws Exception { DiscoveryNode node = DiscoveryNodeUtils.builder("foo") - .version(VersionUtils.randomVersion(random()), IndexVersion.ZERO, IndexVersionUtils.randomVersion()) + .version(VersionUtils.randomVersion(random()), IndexVersions.ZERO, IndexVersionUtils.randomVersion()) .build(); Metadata.Builder builder = Metadata.builder(); final String forbiddenLicenseType = randomFrom( diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java index 18192e29869e3..fc56061a98883 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilterTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; @@ -99,7 +100,7 @@ public void init() throws Exception { .add(DiscoveryNodeUtils.create("id1")) .add( DiscoveryNodeUtils.builder("id2") - .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersion.MINIMUM_COMPATIBLE, IndexVersion.current()) + .version(Version.CURRENT.minimumCompatibilityVersion(), IndexVersions.MINIMUM_COMPATIBLE, IndexVersion.current()) .build() ) .build(); diff --git a/x-pack/plugin/snapshot-based-recoveries/src/test/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/plan/SnapshotsRecoveryPlannerServiceTests.java b/x-pack/plugin/snapshot-based-recoveries/src/test/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/plan/SnapshotsRecoveryPlannerServiceTests.java index 8a00c453382fb..45c7eb1b997b8 100644 --- a/x-pack/plugin/snapshot-based-recoveries/src/test/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/plan/SnapshotsRecoveryPlannerServiceTests.java +++ b/x-pack/plugin/snapshot-based-recoveries/src/test/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/plan/SnapshotsRecoveryPlannerServiceTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot; import org.elasticsearch.index.store.Store; @@ -219,7 +220,7 @@ public void testLogicallyEquivalentSnapshotIsUsedEvenIfFilesAreDifferent() throw if (snapshotVersion == null) { luceneVersion = randomVersionBetween( random(), - IndexVersion.V_7_0_0, + IndexVersions.V_7_0_0, RecoverySettings.SNAPSHOT_RECOVERIES_SUPPORTED_INDEX_VERSION ).luceneVersion(); } else { @@ -410,7 +411,7 @@ public void fetchLatestSnapshotsForShard(ShardId shardId, ActionListener indicesAdmin().preparePutMapping("test").setSource(update, XContentType.JSON).get() diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java index 4a5b46a2d1ed3..d3ee69bf41eae 100644 --- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java +++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.geometry.Circle; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.geo.GeoShapeIntegTestCase; import org.elasticsearch.test.index.IndexVersionUtils; @@ -43,7 +44,7 @@ protected void getGeoShapeMapping(XContentBuilder b) throws IOException { @Override protected IndexVersion randomSupportedVersion() { // legacy shapes can only be created in version lower than 8.x - return IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + return IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); } @Override diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java index 3e904f59ad44e..b07b6da96833f 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java @@ -29,6 +29,7 @@ import org.elasticsearch.geometry.utils.GeometryValidator; import org.elasticsearch.geometry.utils.WellKnownBinary; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.ScriptDocValues; @@ -138,7 +139,7 @@ public Builder( this.geoFormatterFactory = geoFormatterFactory; this.ignoreMalformed = ignoreMalformedParam(m -> builder(m).ignoreMalformed.get(), ignoreMalformedByDefault); this.coerce = coerceParam(m -> builder(m).coerce.get(), coerceByDefault); - this.hasDocValues = Parameter.docValuesParam(m -> builder(m).hasDocValues.get(), IndexVersion.V_7_8_0.onOrBefore(version)); + this.hasDocValues = Parameter.docValuesParam(m -> builder(m).hasDocValues.get(), IndexVersions.V_7_8_0.onOrBefore(version)); addScriptValidation(script, indexed, hasDocValues); } @@ -266,7 +267,7 @@ public String typeName() { public Query geoShapeQuery(SearchExecutionContext context, String fieldName, ShapeRelation relation, LatLonGeometry... geometries) { failIfNotIndexedNorDocValuesFallback(context); // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0) - if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(IndexVersion.V_7_5_0)) { + if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(IndexVersions.V_7_5_0)) { throw new QueryShardException( context, ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "]." @@ -335,7 +336,7 @@ public Mapper.Builder parse(String name, Map node, MappingParser boolean ignoreMalformedByDefault = IGNORE_MALFORMED_SETTING.get(parserContext.getSettings()); boolean coerceByDefault = COERCE_SETTING.get(parserContext.getSettings()); if (LegacyGeoShapeFieldMapper.containsDeprecatedParameter(node.keySet())) { - if (parserContext.indexVersionCreated().onOrAfter(IndexVersion.V_8_0_0)) { + if (parserContext.indexVersionCreated().onOrAfter(IndexVersions.V_8_0_0)) { Set deprecatedParams = LegacyGeoShapeFieldMapper.getDeprecatedParameters(node.keySet()); throw new IllegalArgumentException( "using deprecated parameters " diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapper.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapper.java index 838fd56cfc11a..21c4a1f97c3ef 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapper.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapper.java @@ -18,6 +18,7 @@ import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.ScriptDocValues; @@ -91,7 +92,7 @@ public Builder(String name, IndexVersion version, boolean ignoreMalformedByDefau this.version = version; this.ignoreMalformed = ignoreMalformedParam(m -> builder(m).ignoreMalformed.get(), ignoreMalformedByDefault); this.coerce = coerceParam(m -> builder(m).coerce.get(), coerceByDefault); - this.hasDocValues = Parameter.docValuesParam(m -> builder(m).hasDocValues.get(), IndexVersion.V_8_4_0.onOrBefore(version)); + this.hasDocValues = Parameter.docValuesParam(m -> builder(m).hasDocValues.get(), IndexVersions.V_8_4_0.onOrBefore(version)); } @Override diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryProcessor.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryProcessor.java index 7b28a57759c3d..ac526e6016b23 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryProcessor.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryProcessor.java @@ -24,7 +24,7 @@ import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Polygon; import org.elasticsearch.geometry.Rectangle; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.query.SearchExecutionContext; @@ -46,7 +46,7 @@ public Query shapeQuery( ) { validateIsShapeFieldType(fieldName, context); // CONTAINS queries are not supported by VECTOR strategy for indices created before version 7.5.0 (Lucene 8.3.0); - if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(IndexVersion.V_7_5_0)) { + if (relation == ShapeRelation.CONTAINS && context.indexVersionCreated().before(IndexVersions.V_7_5_0)) { throw new QueryShardException(context, ShapeRelation.CONTAINS + " query relation not supported for Field [" + fieldName + "]."); } if (shape == null) { diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapperTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapperTests.java index a6958f889e78e..0ddb38ea500f1 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapperTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapperTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType; @@ -85,7 +86,7 @@ public void testDefaultConfiguration() throws IOException { } public void testDefaultDocValueConfigurationOnPre7_8() throws IOException { - IndexVersion oldVersion = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_7_7_0); + IndexVersion oldVersion = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_7_7_0); DocumentMapper defaultMapper = createDocumentMapper(oldVersion, fieldMapping(this::minimalMapping)); Mapper fieldMapper = defaultMapper.mappers().getMapper(FIELD_NAME); assertThat(fieldMapper, instanceOf(fieldMapperClass())); @@ -283,7 +284,7 @@ public void testInvalidCurrentVersion() { } public void testGeoShapeLegacyMerge() throws Exception { - IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); MapperService m = createMapperService(version, fieldMapping(b -> b.field("type", getFieldName()))); Exception e = expectThrows( IllegalArgumentException.class, diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapperTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapperTests.java index 5554ff7152b82..26d349a7ee5a6 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapperTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/ShapeFieldMapperTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType; @@ -107,7 +108,7 @@ public void testDefaultConfiguration() throws IOException { public void testDefaultDocValueConfigurationOnPre8_4() throws IOException { // TODO verify which version this test is actually valid for (when PR is actually merged) - IndexVersion oldVersion = IndexVersionUtils.randomVersionBetween(random(), IndexVersion.V_7_0_0, IndexVersion.V_8_3_0); + IndexVersion oldVersion = IndexVersionUtils.randomVersionBetween(random(), IndexVersions.V_7_0_0, IndexVersions.V_8_3_0); DocumentMapper defaultMapper = createDocumentMapper(oldVersion, fieldMapping(this::minimalMapping)); Mapper fieldMapper = defaultMapper.mappers().getMapper(FIELD_NAME); assertThat(fieldMapper, instanceOf(fieldMapperClass())); diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/GeoShapeQueryBuilderGeoShapeTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/GeoShapeQueryBuilderGeoShapeTests.java index 173ddced0817c..593656411eb41 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/GeoShapeQueryBuilderGeoShapeTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/GeoShapeQueryBuilderGeoShapeTests.java @@ -13,7 +13,7 @@ import org.elasticsearch.geo.GeometryTestUtils; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.ShapeType; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.SearchExecutionContext; @@ -88,7 +88,7 @@ protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) { } if (ESTestCase.randomBoolean()) { SearchExecutionContext context = AbstractBuilderTestCase.createSearchExecutionContext(); - if (context.indexVersionCreated().onOrAfter(IndexVersion.V_7_5_0)) { // CONTAINS is only supported from version 7.5 + if (context.indexVersionCreated().onOrAfter(IndexVersions.V_7_5_0)) { // CONTAINS is only supported from version 7.5 if (shapeType == ShapeType.LINESTRING || shapeType == ShapeType.MULTILINESTRING) { builder.relation(ESTestCase.randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.CONTAINS)); } else { diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java index 5dffac26be460..227df1c993d41 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.geometry.Point; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.DocumentParsingException; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper; @@ -70,7 +71,7 @@ protected void createMapping(String indexName, String fieldName, Settings settin ex.getMessage(), containsString("using deprecated parameters [tree] in mapper [" + fieldName + "] of type [geo_shape] is no longer allowed") ); - IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); finalSetting = settings(version).put(settings).build(); indicesAdmin().prepareCreate(indexName).setMapping(xcb).setSettings(finalSetting).get(); } @@ -108,7 +109,7 @@ public void testPointsOnlyExplicit() throws Exception { ) ); - IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); Settings settings = settings(version).build(); indicesAdmin().prepareCreate("geo_points_only").setMapping(mapping).setSettings(settings).get(); ensureGreen(); @@ -163,7 +164,7 @@ public void testPointsOnly() throws Exception { ) ); - IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); Settings settings = settings(version).build(); indicesAdmin().prepareCreate("geo_points_only").setMapping(mapping).setSettings(settings).get(); ensureGreen(); @@ -214,7 +215,7 @@ public void testFieldAlias() throws IOException { containsString("using deprecated parameters [tree] in mapper [geo] of type [geo_shape] is no longer allowed") ); - IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersion.V_8_0_0); + IndexVersion version = IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0); Settings settings = settings(version).build(); indicesAdmin().prepareCreate(defaultIndexName).setMapping(mapping).setSettings(settings).get(); ensureGreen(); diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryBuilderOverShapeTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryBuilderOverShapeTests.java index c3fd1288318d4..f890947698a97 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryBuilderOverShapeTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/ShapeQueryBuilderOverShapeTests.java @@ -12,7 +12,7 @@ import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.ShapeType; -import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.xpack.spatial.util.ShapeTestUtils; @@ -33,7 +33,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws @Override protected ShapeRelation getShapeRelation(ShapeType type) { SearchExecutionContext context = createSearchExecutionContext(); - if (context.indexVersionCreated().onOrAfter(IndexVersion.V_7_5_0)) { // CONTAINS is only supported from version 7.5 + if (context.indexVersionCreated().onOrAfter(IndexVersions.V_7_5_0)) { // CONTAINS is only supported from version 7.5 if (type == ShapeType.LINESTRING || type == ShapeType.MULTILINESTRING) { return randomFrom(ShapeRelation.DISJOINT, ShapeRelation.INTERSECTS, ShapeRelation.CONTAINS); } else { diff --git a/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java b/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java index 54adb26f7ba69..4b4dea8829791 100644 --- a/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java +++ b/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java @@ -52,6 +52,7 @@ import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerScope; import org.elasticsearch.index.analysis.LowercaseNormalizer; import org.elasticsearch.index.analysis.NamedAnalyzer; @@ -266,7 +267,7 @@ public static final class WildcardFieldType extends MappedFieldType { private WildcardFieldType(String name, String nullValue, int ignoreAbove, IndexVersion version, Map meta) { super(name, true, false, true, Defaults.TEXT_SEARCH_INFO, meta); - if (version.onOrAfter(IndexVersion.V_7_10_0)) { + if (version.onOrAfter(IndexVersions.V_7_10_0)) { this.analyzer = WILDCARD_ANALYZER_7_10; } else { this.analyzer = WILDCARD_ANALYZER_7_9; diff --git a/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java b/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java index 79727b9279a98..1fade663cbe2d 100644 --- a/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java +++ b/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java @@ -50,6 +50,7 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.cache.bitset.BitsetFilterCache; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; @@ -125,7 +126,7 @@ public void setUp() throws Exception { builder.ignoreAbove(MAX_FIELD_LENGTH); wildcardFieldType = builder.build(MapperBuilderContext.root(false, false)); - Builder builder79 = new WildcardFieldMapper.Builder(WILDCARD_FIELD_NAME, IndexVersion.V_7_9_0); + Builder builder79 = new WildcardFieldMapper.Builder(WILDCARD_FIELD_NAME, IndexVersions.V_7_9_0); wildcardFieldType79 = builder79.build(MapperBuilderContext.root(false, false)); org.elasticsearch.index.mapper.KeywordFieldMapper.Builder kwBuilder = new KeywordFieldMapper.Builder( From 1eda6ac74b50133b6c1f027b59b476ff9220dfb3 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 20 Oct 2023 11:18:58 +0100 Subject: [PATCH 047/203] Extract ESIntegTestCase#prepareSearch (#101175) Relates #101172 --- .../bucket/AdjacencyMatrixIT.java | 51 +- .../bucket/SearchCancellationIT.java | 3 +- .../bucket/TimeSeriesAggregationsIT.java | 37 +- .../TimeSeriesNestedAggregationsIT.java | 12 +- .../pipeline/DateDerivativeIT.java | 120 ++- .../aggregations/pipeline/SerialDiffIT.java | 34 +- .../common/QueryStringWithAnalyzersIT.java | 3 +- .../common/ReloadSynonymAnalyzerIT.java | 8 +- .../common/HighlighterWithAnalyzersTests.java | 3 +- .../datastreams/DataStreamIT.java | 16 +- .../ingest/geoip/GeoIpDownloaderIT.java | 3 +- .../script/expression/MoreExpressionIT.java | 38 +- .../script/expression/StoredExpressionIT.java | 10 +- .../legacygeo/search/LegacyGeoShapeIT.java | 2 +- .../index/mapper/MatchOnlyTextMapperIT.java | 16 +- .../RankFeaturesMapperIntegrationIT.java | 24 +- .../TokenCountFieldMapperIntegrationIT.java | 8 +- .../join/aggregations/ChildrenIT.java | 26 +- .../join/aggregations/ParentIT.java | 14 +- .../join/query/ChildQuerySearchIT.java | 496 +++++----- .../elasticsearch/join/query/InnerHitsIT.java | 201 ++--- .../percolator/PercolatorQuerySearchIT.java | 155 ++-- .../documentation/ReindexDocumentationIT.java | 2 +- ...rollDocumentsAfterConflictsIntegTests.java | 3 +- .../elasticsearch/reindex/CancelTests.java | 14 +- .../reindex/DeleteByQueryBasicTests.java | 38 +- .../reindex/DeleteByQueryConcurrentTests.java | 6 +- .../reindex/ReindexBasicTests.java | 30 +- .../reindex/ReindexFailureTests.java | 2 +- .../reindex/UpdateByQueryBasicTests.java | 6 +- .../s3/S3BlobStoreRepositoryTests.java | 6 +- .../index/mapper/size/SizeMappingIT.java | 18 +- .../store/smb/AbstractAzureFsTestCase.java | 2 +- .../action/RejectionActionIT.java | 3 +- .../admin/cluster/node/tasks/TasksIT.java | 12 +- .../admin/indices/create/CloneIndexIT.java | 8 +- .../admin/indices/create/CreateIndexIT.java | 5 +- .../admin/indices/create/ShrinkIndexIT.java | 26 +- .../admin/indices/create/SplitIndexIT.java | 42 +- .../action/bulk/BulkProcessor2RetryIT.java | 2 +- .../action/bulk/BulkProcessorRetryIT.java | 2 +- .../action/bulk/BulkWithUpdatesIT.java | 2 +- .../action/bulk/WriteAckDelayIT.java | 2 +- .../action/search/LookupRuntimeFieldIT.java | 26 +- .../action/search/PointInTimeIT.java | 4 +- .../action/search/TransportSearchIT.java | 29 +- .../master/IndexingMasterFailoverIT.java | 2 +- .../elasticsearch/aliases/IndexAliasesIT.java | 44 +- .../elasticsearch/blocks/SimpleBlocksIT.java | 4 +- .../broadcast/BroadcastActionsIT.java | 2 +- .../coordination/RareClusterStateIT.java | 2 +- .../cluster/routing/ShardRoutingRoleIT.java | 2 +- .../allocation/decider/MockDiskUsagesIT.java | 6 +- .../document/DocumentActionsIT.java | 4 +- .../elasticsearch/index/FinalPipelineIT.java | 8 +- .../elasticsearch/index/HiddenIndexIT.java | 15 +- .../index/IndexRequestBuilderIT.java | 2 +- .../index/WaitUntilRefreshIT.java | 22 +- .../index/engine/MaxDocsLimitIT.java | 12 +- .../mapper/CopyToMapperIntegrationIT.java | 5 +- .../index/mapper/DynamicMappingIT.java | 30 +- .../mapper/MultiFieldsIntegrationIT.java | 15 +- .../query/plugin/CustomQueryParserIT.java | 4 +- .../index/search/MatchPhraseQueryIT.java | 4 +- .../RemoveCorruptedShardDataCommandIT.java | 10 +- .../index/store/CorruptedTranslogIT.java | 3 +- .../index/store/ExceptionRetryIT.java | 9 +- .../elasticsearch/indexing/IndexActionIT.java | 4 +- .../indices/IndicesOptionsIntegrationIT.java | 8 +- .../mapping/ConcurrentDynamicTemplateIT.java | 4 +- .../mapping/MalformedDynamicTemplateIT.java | 2 +- .../mapping/UpdateMappingIntegrationIT.java | 2 +- .../recovery/IndexPrimaryRelocationIT.java | 5 +- .../indices/recovery/IndexRecoveryIT.java | 6 +- .../recovery/ReplicaToPrimaryPromotionIT.java | 4 +- .../indices/state/CloseIndexIT.java | 9 +- .../state/CloseWhileRelocatingShardsIT.java | 2 +- .../indices/stats/IndexStatsIT.java | 29 +- .../template/SimpleIndexTemplateIT.java | 32 +- .../elasticsearch/recovery/RelocationIT.java | 15 +- .../elasticsearch/routing/AliasRoutingIT.java | 100 +-- .../search/SearchCancellationIT.java | 34 +- .../SearchServiceCleanupOnLostMasterIT.java | 2 +- .../elasticsearch/search/SearchTimeoutIT.java | 9 +- .../search/StressSearchServiceReaperIT.java | 2 +- .../AggregationsIntegrationIT.java | 3 +- .../search/aggregations/CombiIT.java | 11 +- .../search/aggregations/EquivalenceIT.java | 92 +- .../search/aggregations/MetadataIT.java | 9 +- .../search/aggregations/MissingValueIT.java | 62 +- .../aggregations/bucket/BooleanTermsIT.java | 28 +- .../aggregations/bucket/DateHistogramIT.java | 408 ++++----- .../bucket/DateHistogramOffsetIT.java | 9 +- .../aggregations/bucket/DateRangeIT.java | 153 ++-- .../bucket/DiversifiedSamplerIT.java | 19 +- .../aggregations/bucket/DoubleTermsIT.java | 299 +++--- .../search/aggregations/bucket/FilterIT.java | 21 +- .../search/aggregations/bucket/FiltersIT.java | 111 +-- .../aggregations/bucket/GeoDistanceIT.java | 75 +- .../aggregations/bucket/GeoHashGridIT.java | 39 +- .../search/aggregations/bucket/GlobalIT.java | 6 +- .../aggregations/bucket/HistogramIT.java | 279 +++--- .../search/aggregations/bucket/IpRangeIT.java | 87 +- .../search/aggregations/bucket/IpTermsIT.java | 18 +- .../aggregations/bucket/LongTermsIT.java | 273 +++--- .../aggregations/bucket/MinDocCountIT.java | 18 +- .../aggregations/bucket/NaNSortingIT.java | 28 +- .../search/aggregations/bucket/NestedIT.java | 151 ++-- .../aggregations/bucket/RandomSamplerIT.java | 49 +- .../search/aggregations/bucket/RangeIT.java | 133 ++- .../aggregations/bucket/ReverseNestedIT.java | 152 ++-- .../search/aggregations/bucket/SamplerIT.java | 12 +- .../aggregations/bucket/ShardReduceIT.java | 45 +- .../aggregations/bucket/ShardSizeTermsIT.java | 45 +- .../SignificantTermsSignificanceScoreIT.java | 184 ++-- .../bucket/TermsDocCountErrorIT.java | 849 ++++++++---------- .../bucket/TermsShardMinDocCountIT.java | 76 +- .../bucket/terms/StringTermsIT.java | 405 ++++----- .../CardinalityWithRequestBreakerIT.java | 14 +- .../aggregations/metrics/ExtendedStatsIT.java | 62 +- .../aggregations/metrics/GeoBoundsIT.java | 12 +- .../aggregations/metrics/GeoCentroidIT.java | 9 +- .../metrics/HDRPercentileRanksIT.java | 66 +- .../metrics/HDRPercentilesIT.java | 57 +- .../metrics/MedianAbsoluteDeviationIT.java | 55 +- .../metrics/ScriptedMetricIT.java | 60 +- .../search/aggregations/metrics/StatsIT.java | 30 +- .../search/aggregations/metrics/SumIT.java | 30 +- .../metrics/TDigestPercentileRanksIT.java | 68 +- .../metrics/TDigestPercentilesIT.java | 56 +- .../aggregations/metrics/TopHitsIT.java | 255 +++--- .../aggregations/metrics/ValueCountIT.java | 39 +- ...ketMetricsPipeLineAggregationTestCase.java | 124 ++- .../aggregations/pipeline/BucketScriptIT.java | 386 ++++---- .../pipeline/ExtendedStatsBucketIT.java | 27 +- .../pipeline/PercentilesBucketIT.java | 64 +- .../basic/SearchWhileCreatingIndexIT.java | 2 +- .../basic/TransportTwoNodesSearchIT.java | 33 +- .../search/fetch/subphase/InnerHitsIT.java | 246 +++-- .../highlight/CustomHighlighterSearchIT.java | 14 +- .../highlight/HighlighterSearchIT.java | 194 ++-- .../search/fields/SearchFieldsIT.java | 14 +- .../FunctionScoreFieldValueIT.java | 21 +- .../functionscore/RandomScoreFunctionIT.java | 95 +- .../search/geo/GeoDistanceIT.java | 2 +- .../search/geo/GeoPolygonIT.java | 6 +- .../search/morelikethis/MoreLikeThisIT.java | 34 +- .../search/msearch/MultiSearchIT.java | 14 +- .../search/nested/NestedWithMinScoreIT.java | 2 +- .../search/nested/SimpleNestedIT.java | 203 ++--- .../search/nested/VectorNestedIT.java | 7 +- .../aggregation/AggregationProfilerIT.java | 24 +- .../search/profile/query/QueryProfilerIT.java | 6 +- .../elasticsearch/search/query/ExistsIT.java | 12 +- .../search/query/IntervalQueriesIT.java | 8 +- .../search/query/MultiMatchQueryIT.java | 444 ++++----- .../search/query/QueryStringIT.java | 77 +- .../search/query/ScriptScoreQueryIT.java | 16 +- .../search/query/SearchQueryIT.java | 427 ++++----- .../search/query/SimpleQueryStringIT.java | 62 +- .../search/routing/SearchPreferenceIT.java | 5 +- .../scriptfilter/ScriptQuerySearchIT.java | 6 +- .../search/scroll/DuelScrollIT.java | 12 +- .../search/scroll/SearchScrollIT.java | 18 +- .../search/searchafter/SearchAfterIT.java | 40 +- .../search/simple/SimpleSearchIT.java | 121 +-- .../search/slice/SearchSliceIT.java | 25 +- .../search/sort/FieldSortIT.java | 123 +-- .../search/sort/GeoDistanceIT.java | 54 +- .../search/source/MetadataFetchingIT.java | 19 +- .../search/source/SourceFetchingIT.java | 20 +- .../suggest/CompletionSuggestSearchIT.java | 220 ++--- .../ContextCompletionSuggestSearchIT.java | 7 +- .../snapshots/ConcurrentSnapshotsIT.java | 2 +- .../SharedClusterSnapshotRestoreIT.java | 4 +- .../threadpool/SimpleThreadPoolIT.java | 4 +- .../bucket/ShardSizeTestCase.java | 4 +- .../SharedSignificantTermsTestMethods.java | 8 +- .../test/seektracker/SeekTrackerPluginIT.java | 2 +- .../AbstractIndexRecoveryIntegTestCase.java | 8 +- .../ESBlobStoreRepositoryIntegTestCase.java | 10 +- .../ESFsBasedRepositoryIntegTestCase.java | 2 +- ...ESMockAPIBasedRepositoryIntegTestCase.java | 8 +- .../bucket/AbstractTermsTestCase.java | 30 +- .../metrics/AbstractGeoTestCase.java | 3 +- .../metrics/CentroidAggregationTestBase.java | 15 +- .../SpatialBoundsAggregationTestBase.java | 24 +- .../search/geo/BaseShapeIntegTestCase.java | 11 +- .../search/geo/GeoShapeIntegTestCase.java | 8 +- .../elasticsearch/test/ESIntegTestCase.java | 8 +- .../MultiTermsWithRequestBreakerIT.java | 14 +- .../sourceonly/SourceOnlySnapshotIT.java | 26 +- .../xpack/unsignedlong/UnsignedLongTests.java | 46 +- .../ml/integration/CategorizationIT.java | 19 +- .../ClassificationHousePricingIT.java | 2 +- .../ml/integration/ClassificationIT.java | 16 +- .../DataFrameAnalysisCustomFeatureIT.java | 2 +- .../ml/integration/DatafeedWithAggsIT.java | 23 +- .../ml/integration/DeleteExpiredDataIT.java | 15 +- .../ml/integration/DetectionRulesIT.java | 3 +- ...erimResultsDeletedAfterReopeningJobIT.java | 2 +- .../MlNativeAutodetectIntegTestCase.java | 29 +- ...NativeDataFrameAnalyticsIntegTestCase.java | 35 +- .../xpack/ml/integration/ModelPlotsIT.java | 7 +- .../OutlierDetectionWithMissingFieldsIT.java | 2 +- .../xpack/ml/integration/PersistJobIT.java | 16 +- .../xpack/ml/integration/RegressionIT.java | 18 +- .../ml/integration/RevertModelSnapshotIT.java | 3 +- .../integration/RunDataFrameAnalyticsIT.java | 35 +- .../ml/integration/ScheduledEventsIT.java | 9 +- .../CategorizeTextAggregationIT.java | 9 +- .../CategorizeTextDistributedIT.java | 8 +- .../ml/integration/NetworkDisruptionIT.java | 18 +- .../ml/job/persistence/MockClientBuilder.java | 20 - .../monitoring/MultiNodesStatsTests.java | 8 +- .../local/LocalExporterIntegTests.java | 25 +- .../LocalExporterResourceIntegTests.java | 4 +- .../rrf/RRFRankCoordinatorCanMatchIT.java | 18 +- .../xpack/rank/rrf/RRFRankMultiShardIT.java | 39 +- .../rank/rrf/RRFRankShardCanMatchIT.java | 15 +- .../BaseSearchableSnapshotsIntegTestCase.java | 9 +- .../SearchableSnapshotsIntegTests.java | 2 +- ...napshotsRecoverFromSnapshotIntegTests.java | 2 +- ...archableSnapshotsRepositoryIntegTests.java | 8 +- ...ableSnapshotsBlobStoreCacheIntegTests.java | 6 +- .../shared/NodesCachesStatsIntegTests.java | 13 +- .../DocumentLevelSecurityRandomTests.java | 2 +- .../DocumentLevelSecurityTests.java | 52 +- .../FieldLevelSecurityRandomTests.java | 9 +- .../integration/FieldLevelSecurityTests.java | 32 +- .../integration/KibanaUserRoleIntegTests.java | 6 +- .../SecurityCachePermissionTests.java | 7 +- .../integration/SecurityClearScrollTests.java | 2 +- .../ShrinkIndexWithSecurityTests.java | 5 +- .../security/authz/ReadActionsTests.java | 4 +- .../security/authz/SecurityScrollTests.java | 6 +- .../profile/SecurityDomainIntegTests.java | 2 +- .../slm/SLMSnapshotBlockingIntegTests.java | 6 +- .../SnapshotBasedIndexRecoveryIT.java | 4 +- .../GeoGridAggAndQueryConsistencyIT.java | 14 +- .../search/GeoShapeWithDocValuesIT.java | 2 +- .../search/LegacyGeoShapeWithDocValuesIT.java | 2 +- .../spatial/search/SpatialQueryStringIT.java | 22 +- .../watcher/WatcherConcreteIndexTests.java | 2 +- .../actions/TimeThrottleIntegrationTests.java | 6 +- .../actions/email/EmailAttachmentTests.java | 7 +- .../ArrayCompareConditionSearchTests.java | 9 +- .../CompareConditionSearchTests.java | 28 +- .../HistoryTemplateEmailMappingsTests.java | 16 +- .../HistoryTemplateHttpMappingsTests.java | 17 +- ...storyTemplateIndexActionMappingsTests.java | 6 +- ...storyTemplateSearchInputMappingsTests.java | 12 +- .../input/chain/ChainIntegrationTests.java | 2 +- .../AbstractWatcherIntegrationTestCase.java | 30 +- .../test/integration/BasicWatcherTests.java | 2 +- .../test/integration/BootStrapTests.java | 8 +- .../integration/HistoryIntegrationTests.java | 8 +- .../integration/RejectedExecutionTests.java | 2 +- .../test/integration/SingleNodeTests.java | 2 +- .../test/integration/WatchAckTests.java | 2 +- .../test/integration/WatchMetadataTests.java | 4 +- .../transform/TransformIntegrationTests.java | 12 +- .../action/delete/DeleteWatchTests.java | 4 +- 263 files changed, 4721 insertions(+), 6552 deletions(-) diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java index 2f3b87d73a223..3ff0dc99bdc71 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java @@ -98,9 +98,9 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSimple() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))) + ).get(); assertNoFailures(response); @@ -130,9 +130,9 @@ public void testSimple() throws Exception { } public void testCustomSeparator() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(adjacencyMatrix("tags", "\t", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", "\t", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))) + ).get(); assertNoFailures(response); @@ -153,9 +153,9 @@ public void testCustomSeparator() throws Exception { // https://github.com/elastic/elasticsearch/issues/8438 public void testEmptyFilterDeclarations() throws Exception { QueryBuilder emptyFilter = new BoolQueryBuilder(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(adjacencyMatrix("tags", newMap("all", emptyFilter).add("tag1", termQuery("tag", "tag1")))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", newMap("all", emptyFilter).add("tag1", termQuery("tag", "tag1"))) + ).get(); assertNoFailures(response); @@ -173,12 +173,10 @@ public void testWithSubAggregation() throws Exception { BoolQueryBuilder boolQ = new BoolQueryBuilder(); boolQ.must(termQuery("tag", "tag1")); boolQ.must(termQuery("tag", "tag2")); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")).add("both", boolQ)) - .subAggregation(avg("avg_value").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")).add("both", boolQ)) + .subAggregation(avg("avg_value").field("value")) + ).get(); assertNoFailures(response); @@ -291,7 +289,7 @@ public void testTooLargeMatrix() { } try { - client().prepareSearch("idx").addAggregation(adjacencyMatrix("tags", "\t", filtersMap)).get(); + prepareSearch("idx").addAggregation(adjacencyMatrix("tags", "\t", filtersMap)).get(); fail("SearchPhaseExecutionException should have been thrown"); } catch (SearchPhaseExecutionException ex) { assertThat(ex.getCause().getMessage(), containsString("Number of filters is too large")); @@ -303,11 +301,9 @@ public void testTooLargeMatrix() { } public void testAsSubAggregation() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field("value").interval(2L).subAggregation(adjacencyMatrix("matrix", newMap("all", matchAllQuery()))) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field("value").interval(2L).subAggregation(adjacencyMatrix("matrix", newMap("all", matchAllQuery()))) + ).get(); assertNoFailures(response); @@ -327,13 +323,11 @@ public void testAsSubAggregation() { public void testWithContextBasedSubAggregation() throws Exception { try { - client().prepareSearch("idx") - .addAggregation( - adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))).subAggregation( - avg("avg_value") - ) + prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))).subAggregation( + avg("avg_value") ) - .get(); + ).get(); fail( "expected execution to fail - an attempt to have a context based numeric sub-aggregation, but there is not value source" @@ -346,8 +340,7 @@ public void testWithContextBasedSubAggregation() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/SearchCancellationIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/SearchCancellationIT.java index 8363aa310a451..4d64ad1030136 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/SearchCancellationIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/SearchCancellationIT.java @@ -97,8 +97,7 @@ public void testCancellationDuringTimeSeriesAggregation() throws Exception { logger.info("Executing search"); TimeSeriesAggregationBuilder timeSeriesAggregationBuilder = new TimeSeriesAggregationBuilder("test_agg"); - ActionFuture searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + ActionFuture searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addAggregation( timeSeriesAggregationBuilder.subAggregation( new ScriptedMetricAggregationBuilder("sub_agg").initScript( diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java index 381c8d23a1764..78479a9f1d811 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java @@ -176,7 +176,7 @@ public void setupSuiteScopeCluster() throws Exception { } public void testStandAloneTimeSeriesAgg() { - SearchResponse response = client().prepareSearch("index").setSize(0).addAggregation(timeSeries("by_ts")).get(); + SearchResponse response = prepareSearch("index").setSize(0).addAggregation(timeSeries("by_ts")).get(); assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); @@ -194,8 +194,7 @@ public void testStandAloneTimeSeriesAgg() { public void testTimeSeriesGroupedByADimension() { String groupBy = "dim_" + randomIntBetween(0, numberOfDimensions - 1); - SearchResponse response = client().prepareSearch("index") - .setSize(0) + SearchResponse response = prepareSearch("index").setSize(0) .addAggregation( terms("by_dim").field(groupBy) .size(data.size()) @@ -223,8 +222,7 @@ public void testTimeSeriesGroupedByADimension() { public void testTimeSeriesGroupedByDateHistogram() { DateHistogramInterval fixedInterval = DateHistogramInterval.days(randomIntBetween(10, 100)); - SearchResponse response = client().prepareSearch("index") - .setSize(0) + SearchResponse response = prepareSearch("index").setSize(0) .addAggregation( dateHistogram("by_time").field("@timestamp") .fixedInterval(fixedInterval) @@ -266,11 +264,7 @@ public void testStandAloneTimeSeriesAggWithDimFilter() { if (include == false) { queryBuilder = QueryBuilders.boolQuery().mustNot(queryBuilder); } - SearchResponse response = client().prepareSearch("index") - .setQuery(queryBuilder) - .setSize(0) - .addAggregation(timeSeries("by_ts")) - .get(); + SearchResponse response = prepareSearch("index").setQuery(queryBuilder).setSize(0).addAggregation(timeSeries("by_ts")).get(); assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); @@ -296,8 +290,7 @@ public void testStandAloneTimeSeriesAggWithGlobalAggregation() { if (include == false) { queryBuilder = QueryBuilders.boolQuery().mustNot(queryBuilder); } - SearchResponse response = client().prepareSearch("index") - .setQuery(queryBuilder) + SearchResponse response = prepareSearch("index").setQuery(queryBuilder) .setSize(0) .addAggregation(timeSeries("by_ts").subAggregation(sum("filter_sum").field("metric_" + metric))) .addAggregation(global("everything").subAggregation(sum("all_sum").field("metric_" + metric))) @@ -326,8 +319,7 @@ public void testStandAloneTimeSeriesAggWithGlobalAggregation() { ElasticsearchException e = expectThrows( ElasticsearchException.class, - () -> client().prepareSearch("index") - .setQuery(QueryBuilders.termQuery("dim_" + dim, val)) + () -> prepareSearch("index").setQuery(QueryBuilders.termQuery("dim_" + dim, val)) .setSize(0) .addAggregation(global("everything").subAggregation(timeSeries("by_ts"))) .get() @@ -345,11 +337,7 @@ public void testStandAloneTimeSeriesAggWithMetricFilter() { } else { queryBuilder.lte(val); } - SearchResponse response = client().prepareSearch("index") - .setQuery(queryBuilder) - .setSize(0) - .addAggregation(timeSeries("by_ts")) - .get(); + SearchResponse response = prepareSearch("index").setQuery(queryBuilder).setSize(0).addAggregation(timeSeries("by_ts")).get(); assertNoFailures(response); Aggregations aggregations = response.getAggregations(); assertNotNull(aggregations); @@ -380,8 +368,7 @@ public void testRetrievingHits() { int expectedSize = count(filteredData); ElasticsearchException e = expectThrows( ElasticsearchException.class, - () -> client().prepareSearch("index") - .setQuery(queryBuilder) + () -> prepareSearch("index").setQuery(queryBuilder) .setSize(expectedSize * 2) .addAggregation(timeSeries("by_ts").subAggregation(topHits("hits").size(100))) .addAggregation(topHits("top_hits").size(100)) // top level top hits @@ -515,13 +502,9 @@ public void testGetHitsFailure() throws Exception { QueryBuilder queryBuilder = QueryBuilders.rangeQuery("@timestamp").lte("2021-01-01T00:10:00Z"); assertNoFailures( - client().prepareSearch("test") - .setQuery(queryBuilder) - .setSize(10) - .addSort("key", SortOrder.ASC) - .addSort("@timestamp", SortOrder.ASC) + prepareSearch("test").setQuery(queryBuilder).setSize(10).addSort("key", SortOrder.ASC).addSort("@timestamp", SortOrder.ASC) ); - assertNoFailures(client().prepareSearch("test").setQuery(queryBuilder).setSize(10).addAggregation(timeSeries("by_ts"))); + assertNoFailures(prepareSearch("test").setQuery(queryBuilder).setSize(10).addAggregation(timeSeries("by_ts"))); assertAcked(indicesAdmin().delete(new DeleteIndexRequest("test")).actionGet()); } diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java index 0eba8cd5ace14..b282b54088123 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java @@ -155,7 +155,7 @@ private static String formatDim(int dimId) { public void testTimeSeriesAggregation() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts"); - final SearchResponse aggregationResponse = client().prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); + final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); assertTimeSeriesAggregation(ts); } @@ -164,9 +164,9 @@ public void testSumByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new SumAggregationBuilder("sum").field("gauge_metric") ); - final SearchResponse searchResponse = client().prepareSearch("index").setQuery(new MatchAllQueryBuilder()).get(); + final SearchResponse searchResponse = prepareSearch("index").setQuery(new MatchAllQueryBuilder()).get(); assertNotEquals(numberOfDocuments, searchResponse.getHits().getHits().length); - final SearchResponse aggregationResponse = client().prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); + final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); assertTimeSeriesAggregation(ts); } @@ -175,7 +175,7 @@ public void testTermsByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new TermsAggregationBuilder("terms").field("dim_0") ); - final SearchResponse aggregationResponse = client().prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); + final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); assertTimeSeriesAggregation(ts); } @@ -184,7 +184,7 @@ public void testDateHistogramByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new DateHistogramAggregationBuilder("date_histogram").field("@timestamp").calendarInterval(DateHistogramInterval.HOUR) ); - final SearchResponse aggregationResponse = client().prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); + final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); assertTimeSeriesAggregation(ts); } @@ -193,7 +193,7 @@ public void testCardinalityByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new CardinalityAggregationBuilder("dim_n_cardinality").field(formatDim(numberOfDimensions - 1)) ); - final SearchResponse aggregationResponse = client().prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); + final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); assertTimeSeriesAggregation(ts); ts.getBuckets().forEach(bucket -> { assertCardinality(bucket.getAggregations().get("dim_n_cardinality"), 1); }); diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java index 40bde4b2fea21..5c4a7d5ef5875 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java @@ -116,14 +116,12 @@ public void afterEachTest() throws IOException { } public void testSingleValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ).get(); assertNoFailures(response); @@ -161,14 +159,12 @@ public void testSingleValuedField() throws Exception { } public void testSingleValuedFieldNormalised() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.DAY)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.DAY)) + ).get(); assertNoFailures(response); @@ -225,15 +221,13 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstStart() throws Excep indexRandom(true, builders); ensureSearchable(); - SearchResponse response = client().prepareSearch(IDX_DST_START) - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .timeZone(timezone) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) - ) - .get(); + SearchResponse response = prepareSearch(IDX_DST_START).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.DAY) + .timeZone(timezone) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) + ).get(); assertNoFailures(response); @@ -283,15 +277,13 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstEnd() throws Excepti indexRandom(true, builders); ensureSearchable(); - SearchResponse response = client().prepareSearch(IDX_DST_END) - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .timeZone(timezone) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) - ) - .get(); + SearchResponse response = prepareSearch(IDX_DST_END).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.DAY) + .timeZone(timezone) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) + ).get(); assertNoFailures(response); @@ -343,15 +335,13 @@ public void testSingleValuedFieldNormalised_timeZone_AsiaKathmandu() throws Exce indexRandom(true, builders); ensureSearchable(); - SearchResponse response = client().prepareSearch(IDX_DST_KATHMANDU) - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.HOUR) - .timeZone(timezone) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.MINUTE)) - ) - .get(); + SearchResponse response = prepareSearch(IDX_DST_KATHMANDU).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.HOUR) + .timeZone(timezone) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.MINUTE)) + ).get(); assertNoFailures(response); @@ -411,15 +401,13 @@ private static void assertBucket( } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(sum("sum").field("value")) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "sum")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(sum("sum").field("value")) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "sum")) + ).get(); assertNoFailures(response); @@ -495,14 +483,12 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { } public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("dates") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("dates") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ).get(); assertNoFailures(response); @@ -553,14 +539,12 @@ public void testMultiValuedField() throws Exception { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ).get(); assertNoFailures(response); diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java index b7b93ed5b681d..c918220d19e51 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java @@ -220,16 +220,14 @@ private void setupExpected(MetricTarget target) { } public void testBasicDiff() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(INTERVAL_FIELD) - .interval(interval) - .extendedBounds(0L, (long) (interval * (numBuckets - 1))) - .subAggregation(metric) - .subAggregation(diff("diff_counts", "_count").lag(lag).gapPolicy(gapPolicy)) - .subAggregation(diff("diff_values", "the_metric").lag(lag).gapPolicy(gapPolicy)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(INTERVAL_FIELD) + .interval(interval) + .extendedBounds(0L, (long) (interval * (numBuckets - 1))) + .subAggregation(metric) + .subAggregation(diff("diff_counts", "_count").lag(lag).gapPolicy(gapPolicy)) + .subAggregation(diff("diff_values", "the_metric").lag(lag).gapPolicy(gapPolicy)) + ).get(); assertNoFailures(response); @@ -264,15 +262,13 @@ public void testBasicDiff() { public void testInvalidLagSize() { try { - client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(INTERVAL_FIELD) - .interval(interval) - .extendedBounds(0L, (long) (interval * (numBuckets - 1))) - .subAggregation(metric) - .subAggregation(diff("diff_counts", "_count").lag(-1).gapPolicy(gapPolicy)) - ) - .get(); + prepareSearch("idx").addAggregation( + histogram("histo").field(INTERVAL_FIELD) + .interval(interval) + .extendedBounds(0L, (long) (interval * (numBuckets - 1))) + .subAggregation(metric) + .subAggregation(diff("diff_counts", "_count").lag(-1).gapPolicy(gapPolicy)) + ).get(); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), is("[lag] must be a positive integer: [diff_counts]")); } diff --git a/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/QueryStringWithAnalyzersIT.java b/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/QueryStringWithAnalyzersIT.java index fa6f08337233e..32e20aea3c2e1 100644 --- a/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/QueryStringWithAnalyzersIT.java +++ b/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/QueryStringWithAnalyzersIT.java @@ -53,8 +53,7 @@ public void testCustomWordDelimiterQueryString() { refresh(); assertHitCount( - client().prepareSearch("test") - .setQuery(queryStringQuery("foo.baz").defaultOperator(Operator.AND).field("field1").field("field2")), + prepareSearch("test").setQuery(queryStringQuery("foo.baz").defaultOperator(Operator.AND).field("field1").field("field2")), 1L ); } diff --git a/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/ReloadSynonymAnalyzerIT.java b/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/ReloadSynonymAnalyzerIT.java index 857d16b8cdc89..a9ffdb60419f9 100644 --- a/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/ReloadSynonymAnalyzerIT.java +++ b/modules/analysis-common/src/internalClusterTest/java/org/elasticsearch/analysis/common/ReloadSynonymAnalyzerIT.java @@ -83,8 +83,8 @@ private void testSynonymsUpdate(boolean preview) throws FileNotFoundException, I client().prepareIndex("test").setId("1").setSource("field", "foo").get(); assertNoFailures(indicesAdmin().prepareRefresh("test").execute().actionGet()); - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "baz")), 1L); - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "buzz")), 0L); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "baz")), 1L); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "buzz")), 0L); Response analyzeResponse = indicesAdmin().prepareAnalyze("test", "foo").setAnalyzer("my_synonym_analyzer").get(); assertEquals(2, analyzeResponse.getTokens().size()); assertEquals("foo", analyzeResponse.getTokens().get(0).getTerm()); @@ -124,9 +124,9 @@ private void testSynonymsUpdate(boolean preview) throws FileNotFoundException, I assertTrue(tokens.contains(testTerm)); } - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "baz")), 1L); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "baz")), 1L); long expectedHitCount = preview ? 0L : 1L; - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", testTerm)), expectedHitCount); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", testTerm)), expectedHitCount); } } } diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java index d7e4dbf4c0c01..6798aba477f97 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java @@ -114,8 +114,7 @@ public void testNgramHighlightingWithBrokenPositions() throws IOException { ); client().prepareIndex("test").setId("1").setSource("name", "ARCOTEL Hotels Deutschland").get(); refresh(); - SearchResponse search = client().prepareSearch("test") - .setQuery(matchQuery("name.autocomplete", "deut tel").operator(Operator.OR)) + SearchResponse search = prepareSearch("test").setQuery(matchQuery("name.autocomplete", "deut tel").operator(Operator.OR)) .highlighter(new HighlightBuilder().field("name.autocomplete")) .get(); assertHighlight(search, 0, "name.autocomplete", 0, equalTo("ARCOTEL Hotels Deutschland")); diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java index 6cef9280707b8..896be7b172d6e 100644 --- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java +++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java @@ -560,10 +560,10 @@ public void testResolvabilityOfDataStreamsInAPIs() throws Exception { false ); verifyResolvability(dataStreamName, indicesAdmin().prepareRefresh(dataStreamName), false); - verifyResolvability(dataStreamName, client().prepareSearch(dataStreamName), false, 1); + verifyResolvability(dataStreamName, prepareSearch(dataStreamName), false, 1); verifyResolvability( dataStreamName, - client().prepareMultiSearch().add(client().prepareSearch(dataStreamName).setQuery(matchAllQuery())), + client().prepareMultiSearch().add(prepareSearch(dataStreamName).setQuery(matchAllQuery())), false ); verifyResolvability(dataStreamName, indicesAdmin().prepareClearCache(dataStreamName), false); @@ -606,10 +606,10 @@ public void testResolvabilityOfDataStreamsInAPIs() throws Exception { String wildcardExpression = "logs*"; verifyResolvability(wildcardExpression, indicesAdmin().prepareRefresh(wildcardExpression), false); - verifyResolvability(wildcardExpression, client().prepareSearch(wildcardExpression), false, 2); + verifyResolvability(wildcardExpression, prepareSearch(wildcardExpression), false, 2); verifyResolvability( wildcardExpression, - client().prepareMultiSearch().add(client().prepareSearch(wildcardExpression).setQuery(matchAllQuery())), + client().prepareMultiSearch().add(prepareSearch(wildcardExpression).setQuery(matchAllQuery())), false ); verifyResolvability(wildcardExpression, indicesAdmin().prepareClearCache(wildcardExpression), false); @@ -754,9 +754,9 @@ public void testDataSteamAliasWithFilter() throws Exception { ); // Searching the data stream directly should return all hits: - assertSearchHits(client().prepareSearch("logs-foobar"), "1", "2"); + assertSearchHits(prepareSearch("logs-foobar"), "1", "2"); // Search the alias should only return document 2, because it matches with the defined filter in the alias: - assertSearchHits(client().prepareSearch("foo"), "2"); + assertSearchHits(prepareSearch("foo"), "2"); // Update alias: addAction = new AliasActions(AliasActions.Type.ADD).index(dataStreamName) @@ -784,9 +784,9 @@ public void testDataSteamAliasWithFilter() throws Exception { ); // Searching the data stream directly should return all hits: - assertSearchHits(client().prepareSearch("logs-foobar"), "1", "2"); + assertSearchHits(prepareSearch("logs-foobar"), "1", "2"); // Search the alias should only return document 1, because it matches with the defined filter in the alias: - assertSearchHits(client().prepareSearch("foo"), "1"); + assertSearchHits(prepareSearch("foo"), "1"); } public void testSearchFilteredAndUnfilteredAlias() throws Exception { diff --git a/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java b/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java index 12b5d4630373f..6d360cda59b09 100644 --- a/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java +++ b/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java @@ -251,8 +251,7 @@ public void testGeoIpDatabasesDownload() throws Exception { BoolQueryBuilder queryBuilder = new BoolQueryBuilder().filter(new MatchQueryBuilder("name", id)) .filter(new RangeQueryBuilder("chunk").from(metadata.firstChunk()).to(metadata.lastChunk(), true)); int size = metadata.lastChunk() - metadata.firstChunk() + 1; - SearchResponse res = client().prepareSearch(GeoIpDownloader.DATABASES_INDEX) - .setSize(size) + SearchResponse res = prepareSearch(GeoIpDownloader.DATABASES_INDEX).setSize(size) .setQuery(queryBuilder) .addSort("chunk", SortOrder.ASC) .get(); diff --git a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java index 218b0c7c5157d..6c428556c7344 100644 --- a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java +++ b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java @@ -565,30 +565,22 @@ public void testPipelineAggregationScript() throws Exception { client().prepareIndex("agg_index").setId("4").setSource("one", 4.0, "two", 2.0, "three", 3.0, "four", 4.0), client().prepareIndex("agg_index").setId("5").setSource("one", 5.0, "two", 2.0, "three", 3.0, "four", 4.0) ); - SearchResponse response = client().prepareSearch("agg_index") - .addAggregation( - histogram("histogram").field("one") - .interval(2) - .subAggregation(sum("twoSum").field("two")) - .subAggregation(sum("threeSum").field("three")) - .subAggregation(sum("fourSum").field("four")) - .subAggregation( - bucketScript( - "totalSum", - new Script( - ScriptType.INLINE, - ExpressionScriptEngine.NAME, - "_value0 + _value1 + _value2", - Collections.emptyMap() - ), - "twoSum", - "threeSum", - "fourSum" - ) + SearchResponse response = prepareSearch("agg_index").addAggregation( + histogram("histogram").field("one") + .interval(2) + .subAggregation(sum("twoSum").field("two")) + .subAggregation(sum("threeSum").field("three")) + .subAggregation(sum("fourSum").field("four")) + .subAggregation( + bucketScript( + "totalSum", + new Script(ScriptType.INLINE, ExpressionScriptEngine.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "twoSum", + "threeSum", + "fourSum" ) - ) - .execute() - .actionGet(); + ) + ).execute().actionGet(); Histogram histogram = response.getAggregations().get("histogram"); assertThat(histogram, notNullValue()); diff --git a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java index 6fde54668cf51..77e90d42ad94c 100644 --- a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java +++ b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java @@ -61,13 +61,11 @@ public void testAllOpsDisabledIndexedScripts() throws IOException { assertThat(e.toString(), containsString("cannot execute scripts using [field] context")); } try { - client().prepareSearch("test") - .setSource( - new SearchSourceBuilder().aggregation( - AggregationBuilders.terms("test").script(new Script(ScriptType.STORED, null, "script1", Collections.emptyMap())) - ) + prepareSearch("test").setSource( + new SearchSourceBuilder().aggregation( + AggregationBuilders.terms("test").script(new Script(ScriptType.STORED, null, "script1", Collections.emptyMap())) ) - .get(); + ).get(); } catch (Exception e) { assertThat(e.toString(), containsString("cannot execute scripts using [aggs] context")); } diff --git a/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java b/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java index ba441bad0815f..14a7c873ec6e3 100644 --- a/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java +++ b/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java @@ -74,7 +74,7 @@ public void testLegacyCircle() throws Exception { })); // test self crossing of circles - SearchResponse searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); } } diff --git a/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/MatchOnlyTextMapperIT.java b/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/MatchOnlyTextMapperIT.java index 3a6d7f5a52e8c..9e5ca7a3cdc05 100644 --- a/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/MatchOnlyTextMapperIT.java +++ b/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/MatchOnlyTextMapperIT.java @@ -66,11 +66,9 @@ public void testHighlightingWithMatchOnlyTextFieldMatchPhrase() throws IOExcepti BulkResponse bulkItemResponses = bulk.get(); assertNoFailures(bulkItemResponses); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.matchPhraseQuery("message", "marking and sending shard")) - .setSize(500) - .highlighter(new HighlightBuilder().field("message")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + QueryBuilders.matchPhraseQuery("message", "marking and sending shard") + ).setSize(500).highlighter(new HighlightBuilder().field("message")).get(); assertNoFailures(searchResponse); for (SearchHit searchHit : searchResponse.getHits()) { assertThat( @@ -114,11 +112,9 @@ public void testHighlightingWithMatchOnlyTextFieldSyntheticSource() throws IOExc BulkResponse bulkItemResponses = bulk.get(); assertNoFailures(bulkItemResponses); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.matchPhraseQuery("message", "marking and sending shard")) - .setSize(500) - .highlighter(new HighlightBuilder().field("message")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + QueryBuilders.matchPhraseQuery("message", "marking and sending shard") + ).setSize(500).highlighter(new HighlightBuilder().field("message")).get(); assertNoFailures(searchResponse); for (SearchHit searchHit : searchResponse.getHits()) { assertThat( diff --git a/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/RankFeaturesMapperIntegrationIT.java b/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/RankFeaturesMapperIntegrationIT.java index badc2dd568f57..87699f285063f 100644 --- a/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/RankFeaturesMapperIntegrationIT.java +++ b/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/RankFeaturesMapperIntegrationIT.java @@ -39,30 +39,24 @@ protected Collection> nodePlugins() { public void testRankFeaturesTermQuery() throws IOException { init(); - SearchResponse response = client().prepareSearch(INDEX_NAME) - .setQuery(QueryBuilders.termQuery(FIELD_NAME, HIGHER_RANKED_FEATURE)) - .get(); + SearchResponse response = prepareSearch(INDEX_NAME).setQuery(QueryBuilders.termQuery(FIELD_NAME, HIGHER_RANKED_FEATURE)).get(); assertThat(response.getHits().getTotalHits().value, equalTo(2L)); for (SearchHit hit : response.getHits().getHits()) { assertThat(hit.getScore(), equalTo(20f)); } - response = client().prepareSearch(INDEX_NAME) - .setQuery(QueryBuilders.termQuery(FIELD_NAME, HIGHER_RANKED_FEATURE).boost(100f)) - .get(); + response = prepareSearch(INDEX_NAME).setQuery(QueryBuilders.termQuery(FIELD_NAME, HIGHER_RANKED_FEATURE).boost(100f)).get(); assertThat(response.getHits().getTotalHits().value, equalTo(2L)); for (SearchHit hit : response.getHits().getHits()) { assertThat(hit.getScore(), equalTo(2000f)); } - response = client().prepareSearch(INDEX_NAME) - .setQuery( - QueryBuilders.boolQuery() - .should(QueryBuilders.termQuery(FIELD_NAME, HIGHER_RANKED_FEATURE)) - .should(QueryBuilders.termQuery(FIELD_NAME, LOWER_RANKED_FEATURE).boost(3f)) - .minimumShouldMatch(1) - ) - .get(); + response = prepareSearch(INDEX_NAME).setQuery( + QueryBuilders.boolQuery() + .should(QueryBuilders.termQuery(FIELD_NAME, HIGHER_RANKED_FEATURE)) + .should(QueryBuilders.termQuery(FIELD_NAME, LOWER_RANKED_FEATURE).boost(3f)) + .minimumShouldMatch(1) + ).get(); assertThat(response.getHits().getTotalHits().value, equalTo(3L)); for (SearchHit hit : response.getHits().getHits()) { if (hit.getId().equals("all")) { @@ -76,7 +70,7 @@ public void testRankFeaturesTermQuery() throws IOException { } } - response = client().prepareSearch(INDEX_NAME).setQuery(QueryBuilders.termQuery(FIELD_NAME, "missing_feature")).get(); + response = prepareSearch(INDEX_NAME).setQuery(QueryBuilders.termQuery(FIELD_NAME, "missing_feature")).get(); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); } diff --git a/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/TokenCountFieldMapperIntegrationIT.java b/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/TokenCountFieldMapperIntegrationIT.java index b2a7560985165..ed5d89ad1df8c 100644 --- a/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/TokenCountFieldMapperIntegrationIT.java +++ b/modules/mapper-extras/src/internalClusterTest/java/org/elasticsearch/index/mapper/TokenCountFieldMapperIntegrationIT.java @@ -175,19 +175,19 @@ private IndexRequestBuilder prepareIndex(String id, String... texts) throws IOEx } private SearchResponse searchById(String id) { - return prepareSearch().setQuery(QueryBuilders.termQuery("_id", id)).get(); + return prepareTokenCountFieldMapperSearch().setQuery(QueryBuilders.termQuery("_id", id)).get(); } private SearchRequestBuilder searchByNumericRange(int low, int high) { - return prepareSearch().setQuery( + return prepareTokenCountFieldMapperSearch().setQuery( QueryBuilders.rangeQuery( randomFrom(Arrays.asList("foo.token_count", "foo.token_count_unstored", "foo.token_count_with_doc_values")) ).gte(low).lte(high) ); } - private SearchRequestBuilder prepareSearch() { - SearchRequestBuilder request = client().prepareSearch("test"); + private SearchRequestBuilder prepareTokenCountFieldMapperSearch() { + SearchRequestBuilder request = prepareSearch("test"); request.addStoredField("foo.token_count"); request.addStoredField("foo.token_count_without_position_increments"); if (loadCountedFields) { diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java index 521a2d31b4156..cafcf11a1438d 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -44,8 +44,7 @@ public class ChildrenIT extends AbstractParentChildTestCase { public void testSimpleChildrenAgg() { - final SearchRequestBuilder searchRequest = client().prepareSearch("test") - .setQuery(matchQuery("randomized", true)) + final SearchRequestBuilder searchRequest = prepareSearch("test").setQuery(matchQuery("randomized", true)) .addAggregation(children("to_comment", "comment")); final SearchResponse searchResponse = searchRequest.get(); long count = categoryToControl.values().stream().mapToLong(control -> control.commentIds.size()).sum(); @@ -55,8 +54,7 @@ public void testSimpleChildrenAgg() { } public void testChildrenAggs() { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchQuery("randomized", true)) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchQuery("randomized", true)) .addAggregation( terms("category").field("category") .size(10000) @@ -98,8 +96,7 @@ public void testChildrenAggs() { } public void testParentWithMultipleBuckets() { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchQuery("randomized", false)) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchQuery("randomized", false)) .addAggregation( terms("category").field("category") .size(10000) @@ -173,9 +170,9 @@ public void testWithDeletes() throws Exception { indexRandom(true, requests); for (int i = 0; i < 10; i++) { - SearchResponse searchResponse = client().prepareSearch(indexName) - .addAggregation(children("children", "child").subAggregation(sum("counts").field("count"))) - .get(); + SearchResponse searchResponse = prepareSearch(indexName).addAggregation( + children("children", "child").subAggregation(sum("counts").field("count")) + ).get(); assertNoFailures(searchResponse); Children children = searchResponse.getAggregations().get("children"); @@ -202,7 +199,7 @@ public void testWithDeletes() throws Exception { } public void testNonExistingChildType() throws Exception { - SearchResponse searchResponse = client().prepareSearch("test").addAggregation(children("non-existing", "xyz")).get(); + SearchResponse searchResponse = prepareSearch("test").addAggregation(children("non-existing", "xyz")).get(); assertNoFailures(searchResponse); Children children = searchResponse.getAggregations().get("non-existing"); @@ -254,8 +251,7 @@ public void testPostCollection() throws Exception { requests.add(createIndexRequest(indexName, childType, "16", "2", "color", "green", "size", "44")); indexRandom(true, requests); - SearchResponse response = client().prepareSearch(indexName) - .setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None)) + SearchResponse response = prepareSearch(indexName).setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None)) .addAggregation( children("my-refinements", childType).subAggregation(terms("my-colors").field("color")) .subAggregation(terms("my-sizes").field("size")) @@ -304,8 +300,7 @@ public void testHierarchicalChildrenAggs() { createIndexRequest(indexName, childType, "3", "2", "name", "brussels").setRouting("1").get(); refresh(); - SearchResponse response = client().prepareSearch(indexName) - .setQuery(matchQuery("name", "europe")) + SearchResponse response = prepareSearch(indexName).setQuery(matchQuery("name", "europe")) .addAggregation( children(parentType, parentType).subAggregation(children(childType, childType).subAggregation(terms("name").field("name"))) ) @@ -355,8 +350,7 @@ public void testPostCollectAllLeafReaders() throws Exception { requests.add(createIndexRequest("index", "childType", "8", "3", "name", "Dan", "age", 1)); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("index") - .setSize(0) + SearchResponse response = prepareSearch("index").setSize(0) .addAggregation( AggregationBuilders.terms("towns") .field("town") diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java index 7f780090cbd3a..23043650d589c 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java @@ -33,8 +33,7 @@ public class ParentIT extends AbstractParentChildTestCase { public void testSimpleParentAgg() { - final SearchRequestBuilder searchRequest = client().prepareSearch("test") - .setSize(0) + final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(0) .setQuery(matchQuery("randomized", true)) .addAggregation(parent("to_article", "comment")); SearchResponse searchResponse = searchRequest.get(); @@ -53,8 +52,7 @@ public void testSimpleParentAgg() { } public void testSimpleParentAggWithSubAgg() { - final SearchRequestBuilder searchRequest = client().prepareSearch("test") - .setSize(10000) + final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(10000) .setQuery(matchQuery("randomized", true)) .addAggregation(parent("to_article", "comment").subAggregation(terms("category").field("category").size(10000))); SearchResponse searchResponse = searchRequest.get(); @@ -108,8 +106,7 @@ public void testSimpleParentAggWithSubAgg() { } public void testParentAggs() throws Exception { - final SearchRequestBuilder searchRequest = client().prepareSearch("test") - .setSize(10000) + final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(10000) .setQuery(matchQuery("randomized", true)) .addAggregation( terms("to_commenter").field("commenter") @@ -200,7 +197,7 @@ private Map> getCommenterToComments() { } public void testNonExistingParentType() throws Exception { - SearchResponse searchResponse = client().prepareSearch("test").addAggregation(parent("non-existing", "xyz")).get(); + SearchResponse searchResponse = prepareSearch("test").addAggregation(parent("non-existing", "xyz")).get(); assertNoFailures(searchResponse); Parent parent = searchResponse.getAggregations().get("non-existing"); @@ -209,8 +206,7 @@ public void testNonExistingParentType() throws Exception { } public void testTermsParentAggTerms() throws Exception { - final SearchRequestBuilder searchRequest = client().prepareSearch("test") - .setSize(10000) + final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(10000) .setQuery(matchQuery("randomized", true)) .addAggregation( terms("to_commenter").field("commenter") diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java index baa4a4459f408..a5b9e5532a960 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java @@ -89,49 +89,43 @@ public void testMultiLevelChild() throws Exception { createIndexRequest("test", "grandchild", "gc1", "c1", "gc_field", "gc_value1").setRouting("p1").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - boolQuery().must(matchAllQuery()) - .filter( - hasChildQuery( - "child", - boolQuery().must(termQuery("c_field", "c_value1")) - .filter(hasChildQuery("grandchild", termQuery("gc_field", "gc_value1"), ScoreMode.None)), - ScoreMode.None - ) + SearchResponse searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()) + .filter( + hasChildQuery( + "child", + boolQuery().must(termQuery("c_field", "c_value1")) + .filter(hasChildQuery("grandchild", termQuery("gc_field", "gc_value1"), ScoreMode.None)), + ScoreMode.None ) - ) - .get(); + ) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false))) - .execute() - .actionGet(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)) + ).execute().actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false))) - .execute() - .actionGet(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false)) + ).execute().actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("gc1")); - searchResponse = client().prepareSearch("test") - .setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)) + searchResponse = prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)) .execute() .actionGet(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - searchResponse = client().prepareSearch("test") - .setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)) + searchResponse = prepareSearch("test").setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)) .execute() .actionGet(); assertNoFailures(searchResponse); @@ -148,9 +142,7 @@ public void test2744() throws IOException { createIndexRequest("test", "foo", "1", null, "foo", 1).get(); createIndexRequest("test", "test", "2", "1", "foo", 1).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("test", matchQuery("foo", 1), ScoreMode.None)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(hasChildQuery("test", matchQuery("foo", 1), ScoreMode.None)).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -172,7 +164,7 @@ public void testSimpleChildQuery() throws Exception { // TEST FETCHING _parent from child SearchResponse searchResponse; - searchResponse = client().prepareSearch("test").setQuery(idsQuery().addIds("c1")).get(); + searchResponse = prepareSearch("test").setQuery(idsQuery().addIds("c1")).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); @@ -180,9 +172,9 @@ public void testSimpleChildQuery() throws Exception { assertThat(extractValue("join_field.parent", searchResponse.getHits().getAt(0).getSourceAsMap()), equalTo("p1")); // TEST matching on parent - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child")) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); @@ -193,28 +185,28 @@ public void testSimpleChildQuery() throws Exception { assertThat(extractValue("join_field.parent", searchResponse.getHits().getAt(1).getSourceAsMap()), equalTo("p1")); // HAS CHILD - searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")).get(); + searchResponse = prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "blue")).execute().actionGet(); + searchResponse = prepareSearch("test").setQuery(randomHasChild("child", "c_field", "blue")).execute().actionGet(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); - searchResponse = client().prepareSearch("test").setQuery(randomHasChild("child", "c_field", "red")).get(); + searchResponse = prepareSearch("test").setQuery(randomHasChild("child", "c_field", "red")).get(); assertHitCount(searchResponse, 2L); assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); // HAS PARENT - searchResponse = client().prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value2")).get(); + searchResponse = prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value2")).get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, 2L); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c3")); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("c4")); - searchResponse = client().prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value1")).get(); + searchResponse = prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value1")).get(); assertHitCount(searchResponse, 2L); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("c2")); @@ -247,10 +239,8 @@ public void testCachingBugWithFqueryFilter() throws Exception { for (int i = 1; i <= 10; i++) { logger.info("Round {}", i); - assertNoFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Max))) - ); - assertNoFailures(client().prepareSearch("test").setQuery(constantScoreQuery(hasParentQuery("parent", matchAllQuery(), true)))); + assertNoFailures(prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Max)))); + assertNoFailures(prepareSearch("test").setQuery(constantScoreQuery(hasParentQuery("parent", matchAllQuery(), true)))); } } @@ -286,10 +276,9 @@ public void testHasParentFilter() throws Exception { assertThat(parentToChildren.isEmpty(), equalTo(false)); for (Map.Entry> parentToChildrenEntry : parentToChildren.entrySet()) { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", parentToChildrenEntry.getKey()), false))) - .setSize(numChildDocsPerParent) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasParentQuery("parent", termQuery("p_field", parentToChildrenEntry.getKey()), false)) + ).setSize(numChildDocsPerParent).get(); assertNoFailures(searchResponse); Set childIds = parentToChildrenEntry.getValue(); @@ -323,44 +312,42 @@ public void testSimpleChildQueryWithFlush() throws Exception { // HAS CHILD QUERY - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) - .get(); + searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); - searchResponse = client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None)).get(); + searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None)).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); // HAS CHILD FILTER - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); @@ -385,14 +372,9 @@ public void testScopedFacet() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - hasChildQuery( - "child", - boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), - ScoreMode.None - ) - ) + SearchResponse searchResponse = prepareSearch("test").setQuery( + hasChildQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), ScoreMode.None) + ) .addAggregation( AggregationBuilders.global("global") .subAggregation( @@ -431,9 +413,9 @@ public void testDeletedParent() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); @@ -444,9 +426,9 @@ public void testDeletedParent() throws Exception { createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1_updated").get(); indicesAdmin().prepareRefresh().get(); - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); @@ -468,14 +450,12 @@ public void testDfsSearchType() throws Exception { refresh(); assertNoFailures( - client().prepareSearch("test") - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(boolQuery().mustNot(hasChildQuery("child", boolQuery().should(queryStringQuery("c_field:*")), ScoreMode.None))) ); assertNoFailures( - client().prepareSearch("test") - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(boolQuery().mustNot(hasParentQuery("parent", boolQuery().should(queryStringQuery("p_field:*")), false))) ); } @@ -490,15 +470,15 @@ public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrCh client().prepareIndex("test").setId("3").setSource("p_field", 1).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchAllQuery(), ScoreMode.None))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchAllQuery(), false))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchAllQuery(), false)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); } @@ -512,24 +492,18 @@ public void testCountApiUsage() throws Exception { createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); - assertHitCount( - client().prepareSearch("test").setSize(0).setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)), - 1L - ); + assertHitCount(prepareSearch("test").setSize(0).setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)), 1L); - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)), 1L); + assertHitCount(prepareSearch("test").setSize(0).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)), 1L); assertHitCount( - client().prepareSearch("test") - .setSize(0) + prepareSearch("test").setSize(0) .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None))), 1L ); assertHitCount( - client().prepareSearch("test") - .setSize(0) - .setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false))), + prepareSearch("test").setSize(0).setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false))), 1L ); } @@ -543,17 +517,13 @@ public void testExplainUsage() throws Exception { createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setExplain(true) + SearchResponse searchResponse = prepareSearch("test").setExplain(true) .setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)) .get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); - searchResponse = client().prepareSearch("test") - .setExplain(true) - .setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)) - .get(); + searchResponse = prepareSearch("test").setExplain(true).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); @@ -617,16 +587,14 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { ensureGreen(); indexRandom(true, createDocBuilders().toArray(new IndexRequestBuilder[0])); - SearchResponse response = client().prepareSearch("test") - .setQuery( - hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), - ScoreMode.Total - ) + SearchResponse response = prepareSearch("test").setQuery( + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), + ScoreMode.Total ) - .get(); + ).get(); assertThat(response.getHits().getTotalHits().value, equalTo(3L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("1")); @@ -636,16 +604,14 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); assertThat(response.getHits().getHits()[2].getScore(), equalTo(3f)); - response = client().prepareSearch("test") - .setQuery( - hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), - ScoreMode.Max - ) + response = prepareSearch("test").setQuery( + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), + ScoreMode.Max ) - .get(); + ).get(); assertThat(response.getHits().getTotalHits().value, equalTo(3L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); @@ -655,16 +621,14 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { assertThat(response.getHits().getHits()[2].getId(), equalTo("1")); assertThat(response.getHits().getHits()[2].getScore(), equalTo(2f)); - response = client().prepareSearch("test") - .setQuery( - hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), - ScoreMode.Avg - ) + response = prepareSearch("test").setQuery( + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), + ScoreMode.Avg ) - .get(); + ).get(); assertThat(response.getHits().getTotalHits().value, equalTo(3L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); @@ -674,18 +638,14 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { assertThat(response.getHits().getHits()[2].getId(), equalTo("1")); assertThat(response.getHits().getHits()[2].getScore(), equalTo(1.5f)); - response = client().prepareSearch("test") - .setQuery( - hasParentQuery( - "parent", - QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), fieldValueFactorFunction("p_field2")) - .boostMode(CombineFunction.REPLACE), - true - ) + response = prepareSearch("test").setQuery( + hasParentQuery( + "parent", + QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), fieldValueFactorFunction("p_field2")) + .boostMode(CombineFunction.REPLACE), + true ) - .addSort(SortBuilders.fieldSort("c_field3")) - .addSort(SortBuilders.scoreSort()) - .get(); + ).addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get(); assertThat(response.getHits().getTotalHits().value, equalTo(7L)); assertThat(response.getHits().getHits()[0].getId(), equalTo("16")); @@ -709,9 +669,7 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep assertAcked(prepareCreate("test").setMapping(buildParentJoinFieldMappingFromSimplifiedDef("join_field", true, "parent", "child"))); ensureGreen(); - SearchResponse response = client().prepareSearch("test") - .setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)) - .get(); + SearchResponse response = prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); @@ -720,19 +678,19 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep .setRefreshPolicy(RefreshPolicy.IMMEDIATE) .get(); - response = client().prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); + response = prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - response = client().prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)).get(); + response = prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - response = client().prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), false)).get(); + response = prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), false)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - response = client().prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), true)).get(); + response = prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), true)).get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, equalTo(0L)); } @@ -748,16 +706,16 @@ public void testHasChildAndHasParentFilter_withFilter() throws Exception { client().prepareIndex("test").setId("3").setSource("p_field", 2).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", termQuery("c_field", 1), ScoreMode.None))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", termQuery("c_field", 1), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", 1), false))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", 1), false)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2")); @@ -771,15 +729,13 @@ public void testHasChildInnerHitsHighlighting() throws Exception { createIndexRequest("test", "child", "2", "1", "c_field", "foo bar").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - hasChildQuery("child", matchQuery("c_field", "foo"), ScoreMode.None).innerHit( - new InnerHitBuilder().setHighlightBuilder( - new HighlightBuilder().field(new Field("c_field").highlightQuery(QueryBuilders.matchQuery("c_field", "bar"))) - ) + SearchResponse searchResponse = prepareSearch("test").setQuery( + hasChildQuery("child", matchQuery("c_field", "foo"), ScoreMode.None).innerHit( + new InnerHitBuilder().setHighlightBuilder( + new HighlightBuilder().field(new Field("c_field").highlightQuery(QueryBuilders.matchQuery("c_field", "bar"))) ) ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1")); @@ -799,26 +755,24 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { createIndexRequest("test", "child", "2", "1", "c_field", 1).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)) + ).get(); assertSearchHit(searchResponse, 1, hasId("1")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchQuery("p_field", 1), false))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchQuery("p_field", 1), false)) + ).get(); assertSearchHit(searchResponse, 1, hasId("2")); - searchResponse = client().prepareSearch("test") - .setQuery( - boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None))) - ) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None))) + ).get(); assertSearchHit(searchResponse, 1, hasId("1")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false)))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false))) + ).get(); assertSearchHit(searchResponse, 1, hasId("2")); } @@ -852,8 +806,7 @@ public void testSimpleQueryRewrite() throws Exception { SearchType[] searchTypes = new SearchType[] { SearchType.QUERY_THEN_FETCH, SearchType.DFS_QUERY_THEN_FETCH }; for (SearchType searchType : searchTypes) { - SearchResponse searchResponse = client().prepareSearch("test") - .setSearchType(searchType) + SearchResponse searchResponse = prepareSearch("test").setSearchType(searchType) .setQuery(hasChildQuery("child", prefixQuery("c_field", "c"), ScoreMode.Max)) .addSort("p_field", SortOrder.ASC) .setSize(5) @@ -866,8 +819,7 @@ public void testSimpleQueryRewrite() throws Exception { assertThat(searchResponse.getHits().getHits()[3].getId(), equalTo("p003")); assertThat(searchResponse.getHits().getHits()[4].getId(), equalTo("p004")); - searchResponse = client().prepareSearch("test") - .setSearchType(searchType) + searchResponse = prepareSearch("test").setSearchType(searchType) .setQuery(hasParentQuery("parent", prefixQuery("p_field", "p"), true)) .addSort("c_field", SortOrder.ASC) .setSize(5) @@ -897,17 +849,17 @@ public void testReIndexingParentAndChildDocuments() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c3")); @@ -922,17 +874,15 @@ public void testReIndexingParentAndChildDocuments() throws Exception { indicesAdmin().prepareRefresh("test").get(); } - searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)) - .get(); + searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true))) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(searchResponse.getHits().getAt(0).getId(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); @@ -953,8 +903,7 @@ public void testHasChildQueryWithMinimumScore() throws Exception { createIndexRequest("test", "child", "c5", "p2", "c_field", "x").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) + SearchResponse searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) .setMinScore(3) // Score needs to be 3 or above! .get(); assertNoFailures(searchResponse); @@ -971,8 +920,9 @@ public void testParentFieldQuery() throws Exception { ensureGreen(); assertHitCount( - client().prepareSearch("test") - .setQuery(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))), + prepareSearch("test").setQuery( + boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child")) + ), 0L ); @@ -980,19 +930,19 @@ public void testParentFieldQuery() throws Exception { refresh(); assertHitCount( - client().prepareSearch("test") - .setQuery(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))), + prepareSearch("test").setQuery( + boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child")) + ), 1L ); createIndexRequest("test", "child", "c2", "p2").get(); refresh(); assertHitCount( - client().prepareSearch("test") - .setQuery( - boolQuery().should(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) - .should(boolQuery().filter(termQuery("join_field#parent", "p2")).filter(termQuery("join_field", "child"))) - ), + prepareSearch("test").setQuery( + boolQuery().should(boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child"))) + .should(boolQuery().filter(termQuery("join_field#parent", "p2")).filter(termQuery("join_field", "child"))) + ), 2L ); } @@ -1007,15 +957,12 @@ public void testParentIdQuery() throws Exception { createIndexRequest("test", "child", "c1", "p1").get(); refresh(); - assertHitCount(client().prepareSearch("test").setQuery(parentId("child", "p1")), 1L); + assertHitCount(prepareSearch("test").setQuery(parentId("child", "p1")), 1L); createIndexRequest("test", "child", "c2", "p2").get(); refresh(); - assertHitCount( - client().prepareSearch("test").setQuery(boolQuery().should(parentId("child", "p1")).should(parentId("child", "p2"))), - 2L - ); + assertHitCount(prepareSearch("test").setQuery(boolQuery().should(parentId("child", "p1")).should(parentId("child", "p2"))), 2L); } public void testHasChildNotBeingCached() throws IOException { @@ -1037,18 +984,18 @@ public void testHasChildNotBeingCached() throws IOException { indicesAdmin().prepareFlush("test").get(); indicesAdmin().prepareRefresh("test").get(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); createIndexRequest("test", "child", "c2", "p2", "c_field", "blue").get(); indicesAdmin().prepareRefresh("test").get(); - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); } @@ -1104,34 +1051,32 @@ public void testHasChildQueryOnlyReturnsSingleChildType() throws Exception { refresh(); assertHitCount( - client().prepareSearch("grandissue") - .setQuery( - boolQuery().must( - hasChildQuery( - "parent", - boolQuery().must( - hasChildQuery("child_type_one", boolQuery().must(queryStringQuery("name:William*")), ScoreMode.None) - ), - ScoreMode.None - ) + prepareSearch("grandissue").setQuery( + boolQuery().must( + hasChildQuery( + "parent", + boolQuery().must( + hasChildQuery("child_type_one", boolQuery().must(queryStringQuery("name:William*")), ScoreMode.None) + ), + ScoreMode.None ) - ), + ) + ), 1L ); assertHitCount( - client().prepareSearch("grandissue") - .setQuery( - boolQuery().must( - hasChildQuery( - "parent", - boolQuery().must( - hasChildQuery("child_type_two", boolQuery().must(queryStringQuery("name:William*")), ScoreMode.None) - ), - ScoreMode.None - ) + prepareSearch("grandissue").setQuery( + boolQuery().must( + hasChildQuery( + "parent", + boolQuery().must( + hasChildQuery("child_type_two", boolQuery().must(queryStringQuery("name:William*")), ScoreMode.None) + ), + ScoreMode.None ) - ), + ) + ), 0L ); } @@ -1196,21 +1141,17 @@ public void testHasChildQueryWithNestedInnerObjects() throws Exception { refresh(); ScoreMode scoreMode = randomFrom(ScoreMode.values()); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - boolQuery().must(hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)) - .filter(boolQuery().mustNot(termQuery("p_field", "3"))) - ) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + boolQuery().must(hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3"))) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery( - boolQuery().must(hasChildQuery("child", termQuery("c_field", "red"), scoreMode)) - .filter(boolQuery().mustNot(termQuery("p_field", "3"))) - ) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(hasChildQuery("child", termQuery("c_field", "red"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3"))) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); } @@ -1224,30 +1165,28 @@ public void testNamedFilters() throws Exception { createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max).queryName("test")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max).queryName("test") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test") - .setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true).queryName("test")) - .get(); + searchResponse = prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true).queryName("test")).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None).queryName("test"))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None).queryName("test")) + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); - searchResponse = client().prepareSearch("test") - .setQuery(constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false).queryName("test"))) - .get(); + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false).queryName("test")) + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); @@ -1262,35 +1201,35 @@ public void testParentChildQueriesNoParentType() throws Exception { refresh(); try { - client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None)).get(); + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None)).get(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); } try { - client().prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)).get(); + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)).get(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); } try { - client().prepareSearch("test").setPostFilter(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None)).get(); + prepareSearch("test").setPostFilter(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None)).get(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); } try { - client().prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)).get(); + prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)).get(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); } try { - client().prepareSearch("test").setPostFilter(hasParentQuery("parent", termQuery("p_field", "1"), false)).get(); + prepareSearch("test").setPostFilter(hasParentQuery("parent", termQuery("p_field", "1"), false)).get(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); @@ -1361,8 +1300,7 @@ public void testParentChildQueriesViaScrollApi() throws Exception { boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchAllQuery(), false)) }; for (QueryBuilder query : queries) { - SearchResponse scrollResponse = client().prepareSearch("test") - .setScroll(TimeValue.timeValueSeconds(30)) + SearchResponse scrollResponse = prepareSearch("test").setScroll(TimeValue.timeValueSeconds(30)) .setSize(1) .addStoredField("_id") .setQuery(query) @@ -1422,7 +1360,7 @@ private SearchResponse minMaxQuery(ScoreMode scoreMode, int minChildren, Integer scoreMode ).minMaxChildren(minChildren, maxChildren != null ? maxChildren : HasChildQueryBuilder.DEFAULT_MAX_CHILDREN); - return client().prepareSearch("test").setQuery(hasChildQuery).addSort("_score", SortOrder.DESC).addSort("id", SortOrder.ASC).get(); + return prepareSearch("test").setQuery(hasChildQuery).addSort("_score", SortOrder.DESC).addSort("id", SortOrder.ASC).get(); } public void testMinMaxChildren() throws Exception { @@ -1707,12 +1645,12 @@ public void testHasParentInnerQueryType() { // make sure that when we explicitly set a type, the inner query is executed in the context of the child type instead assertSearchHits( - client().prepareSearch("test").setQuery(hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)), + prepareSearch("test").setQuery(hasChildQuery("child-type", new IdsQueryBuilder().addIds("child-id"), ScoreMode.None)), "parent-id" ); // make sure that when we explicitly set a type, the inner query is executed in the context of the parent type instead assertSearchHits( - client().prepareSearch("test").setQuery(hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)), + prepareSearch("test").setQuery(hasParentQuery("parent-type", new IdsQueryBuilder().addIds("parent-id"), false)), "child-id" ); } @@ -1744,25 +1682,19 @@ public void testHighlightersIgnoreParentChild() throws IOException { String[] highlightTypes = new String[] { "plain", "fvh", "unified" }; for (String highlightType : highlightTypes) { logger.info("Testing with highlight type [{}]", highlightType); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) - .must(new HasChildQueryBuilder("child-type", new MatchAllQueryBuilder(), ScoreMode.None)) - ) - .highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) + .must(new HasChildQueryBuilder("child-type", new MatchAllQueryBuilder(), ScoreMode.None)) + ).highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))).get(); assertHitCount(searchResponse, 1); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("parent-id")); HighlightField highlightField = searchResponse.getHits().getAt(0).getHighlightFields().get("searchText"); assertThat(highlightField.getFragments()[0].string(), equalTo("quick brown fox")); - searchResponse = client().prepareSearch("test") - .setQuery( - new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) - .must(new HasParentQueryBuilder("parent-type", new MatchAllQueryBuilder(), false)) - ) - .highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))) - .get(); + searchResponse = prepareSearch("test").setQuery( + new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) + .must(new HasParentQueryBuilder("parent-type", new MatchAllQueryBuilder(), false)) + ).highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))).get(); assertHitCount(searchResponse, 1); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("child-id")); highlightField = searchResponse.getHits().getAt(0).getHighlightFields().get("searchText"); @@ -1783,10 +1715,10 @@ public void testAliasesFilterWithHasChildQuery() throws Exception { ); assertAcked(indicesAdmin().prepareAliases().addAlias("my-index", "filter2", hasParentQuery("parent", matchAllQuery(), false))); - SearchResponse response = client().prepareSearch("filter1").get(); + SearchResponse response = prepareSearch("filter1").get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - response = client().prepareSearch("filter2").get(); + response = prepareSearch("filter2").get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("2")); } diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java index 23c7802507ae4..e64d5594fa77b 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java @@ -114,9 +114,9 @@ public void testSimpleParentChild() throws Exception { requests.add(createIndexRequest("articles", "comment", "c6", "p2", "message", "elephant scared by mice x y")); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("articles") - .setQuery(hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder())) - .get(); + SearchResponse response = prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("p1")); @@ -130,13 +130,11 @@ public void testSimpleParentChild() throws Exception { assertThat(innerHits.getAt(1).getId(), equalTo("c2")); final boolean seqNoAndTerm = randomBoolean(); - response = client().prepareSearch("articles") - .setQuery( - hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit( - new InnerHitBuilder().setSeqNoAndPrimaryTerm(seqNoAndTerm) - ) + response = prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit( + new InnerHitBuilder().setSeqNoAndPrimaryTerm(seqNoAndTerm) ) - .get(); + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("p2")); @@ -165,17 +163,15 @@ public void testSimpleParentChild() throws Exception { assertThat(innerHits.getAt(2).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); } - response = client().prepareSearch("articles") - .setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder().addFetchField("message") - .setHighlightBuilder(new HighlightBuilder().field("message")) - .setExplain(true) - .setSize(1) - .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) - ) + response = prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder().addFetchField("message") + .setHighlightBuilder(new HighlightBuilder().field("message")) + .setExplain(true) + .setSize(1) + .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) ) - .get(); + ).get(); assertNoFailures(response); innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); assertThat(innerHits.getHits().length, equalTo(1)); @@ -184,13 +180,11 @@ public void testSimpleParentChild() throws Exception { assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("fox eat quick")); assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); - response = client().prepareSearch("articles") - .setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder().addDocValueField("message").setSize(1) - ) + response = prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder().addDocValueField("message").setSize(1) ) - .get(); + ).get(); assertNoFailures(response); innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); assertThat(innerHits.getHits().length, equalTo(1)); @@ -257,11 +251,7 @@ public void testRandomParentChild() throws Exception { ) ) ); - SearchResponse searchResponse = client().prepareSearch("idx") - .setSize(numDocs) - .addSort("id", SortOrder.ASC) - .setQuery(boolQuery) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setSize(numDocs).addSort("id", SortOrder.ASC).setQuery(boolQuery).get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, numDocs); @@ -330,8 +320,7 @@ public void testInnerHitsOnHasParent() throws Exception { ); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("stack") - .addSort("id", SortOrder.ASC) + SearchResponse response = prepareSearch("stack").addSort("id", SortOrder.ASC) .setQuery( boolQuery().must(matchQuery("body", "fail2ban")) .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder())) @@ -373,15 +362,13 @@ public void testParentChildMultipleLayers() throws Exception { requests.add(createIndexRequest("articles", "remark", "6", "4", "message", "bad").setRouting("2")); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("articles") - .setQuery( - hasChildQuery( - "comment", - hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder()), - ScoreMode.None - ).innerHit(new InnerHitBuilder()) - ) - .get(); + SearchResponse response = prepareSearch("articles").setQuery( + hasChildQuery( + "comment", + hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None + ).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); @@ -396,15 +383,13 @@ public void testParentChildMultipleLayers() throws Exception { assertThat(innerHits.getTotalHits().value, equalTo(1L)); assertThat(innerHits.getAt(0).getId(), equalTo("5")); - response = client().prepareSearch("articles") - .setQuery( - hasChildQuery( - "comment", - hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder()), - ScoreMode.None - ).innerHit(new InnerHitBuilder()) - ) - .get(); + response = prepareSearch("articles").setQuery( + hasChildQuery( + "comment", + hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None + ).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); @@ -452,26 +437,22 @@ public void testRoyals() throws Exception { requests.add(createIndexRequest("royals", "baron", "baron4", "earl4").setRouting("king")); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("royals") - .setQuery( - boolQuery().filter( - hasParentQuery( - "prince", - hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings")), - false - ).innerHit(new InnerHitBuilder().setName("princes")) - ) - .filter( - hasChildQuery( - "earl", - hasChildQuery("baron", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("barons")), - ScoreMode.None - ).innerHit( - new InnerHitBuilder().addSort(SortBuilders.fieldSort("id").order(SortOrder.ASC)).setName("earls").setSize(4) - ) - ) + SearchResponse response = prepareSearch("royals").setQuery( + boolQuery().filter( + hasParentQuery( + "prince", + hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings")), + false + ).innerHit(new InnerHitBuilder().setName("princes")) ) - .get(); + .filter( + hasChildQuery( + "earl", + hasChildQuery("baron", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("barons")), + ScoreMode.None + ).innerHit(new InnerHitBuilder().addSort(SortBuilders.fieldSort("id").order(SortOrder.ASC)).setName("earls").setSize(4)) + ) + ).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); @@ -517,12 +498,9 @@ public void testMatchesQueriesParentChildInnerHits() throws Exception { requests.add(createIndexRequest("index", "child", "5", "2", "field", "value1")); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("index") - .setQuery( - hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None).innerHit(new InnerHitBuilder()) - ) - .addSort("id", SortOrder.ASC) - .get(); + SearchResponse response = prepareSearch("index").setQuery( + hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None).innerHit(new InnerHitBuilder()) + ).addSort("id", SortOrder.ASC).get(); assertHitCount(response, 2); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); @@ -537,7 +515,7 @@ public void testMatchesQueriesParentChildInnerHits() throws Exception { QueryBuilder query = hasChildQuery("child", matchQuery("field", "value2").queryName("_name2"), ScoreMode.None).innerHit( new InnerHitBuilder() ); - response = client().prepareSearch("index").setQuery(query).addSort("id", SortOrder.ASC).get(); + response = prepareSearch("index").setQuery(query).addSort("id", SortOrder.ASC).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); @@ -561,7 +539,7 @@ public void testUseMaxDocInsteadOfSize() throws Exception { QueryBuilder query = hasChildQuery("child", matchQuery("field", "value1"), ScoreMode.None).innerHit( new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1) ); - SearchResponse response = client().prepareSearch("index1").setQuery(query).get(); + SearchResponse response = prepareSearch("index1").setQuery(query).get(); assertNoFailures(response); assertHitCount(response, 1); } @@ -579,18 +557,16 @@ public void testNestedInnerHitWrappedInParentChildInnerhit() { createIndexRequest("test", "parent_type", "1", null, "key", "value").get(); createIndexRequest("test", "child_type", "2", "1", "nested_type", Collections.singletonMap("key", "value")).get(); refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery( - boolQuery().must(matchQuery("key", "value")) - .should( - hasChildQuery( - "child_type", - nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder()), - ScoreMode.None - ).innerHit(new InnerHitBuilder()) - ) - ) - .get(); + SearchResponse response = prepareSearch("test").setQuery( + boolQuery().must(matchQuery("key", "value")) + .should( + hasChildQuery( + "child_type", + nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None + ).innerHit(new InnerHitBuilder()) + ) + ).get(); assertHitCount(response, 1); SearchHit hit = response.getHits().getAt(0); String parentId = (String) extractValue("join_field.parent", hit.getInnerHits().get("child_type").getAt(0).getSourceAsMap()); @@ -640,22 +616,19 @@ public void testTooHighResultWindow() { createIndexRequest("index1", "child_type", "2", "1").get(); refresh(); assertHitCountAndNoFailures( - client().prepareSearch("index1") - .setQuery( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setFrom(50).setSize(10).setName("_name")) - ), + prepareSearch("index1").setQuery( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setFrom(50).setSize(10).setName("_name")) + ), 1 ); Exception e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("index1") - .setQuery( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setFrom(100).setSize(10).setName("_name")) - ) - .get() + () -> prepareSearch("index1").setQuery( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setFrom(100).setSize(10).setName("_name")) + ).get() ); assertThat( e.getCause().getMessage(), @@ -663,12 +636,10 @@ public void testTooHighResultWindow() { ); e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("index1") - .setQuery( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setFrom(10).setSize(100).setName("_name")) - ) - .get() + () -> prepareSearch("index1").setQuery( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setFrom(10).setSize(100).setName("_name")) + ).get() ); assertThat( e.getCause().getMessage(), @@ -676,18 +647,16 @@ public void testTooHighResultWindow() { ); updateIndexSettings(Settings.builder().put(IndexSettings.MAX_INNER_RESULT_WINDOW_SETTING.getKey(), 110), "index1"); assertNoFailures( - client().prepareSearch("index1") - .setQuery( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setFrom(100).setSize(10).setName("_name")) - ) + prepareSearch("index1").setQuery( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setFrom(100).setSize(10).setName("_name")) + ) ); assertNoFailures( - client().prepareSearch("index1") - .setQuery( - hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) - .innerHit(new InnerHitBuilder().setFrom(10).setSize(100).setName("_name")) - ) + prepareSearch("index1").setQuery( + hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true) + .innerHit(new InnerHitBuilder().setFrom(10).setSize(100).setName("_name")) + ) ); } } diff --git a/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java index 5d6d51e548eea..39b42b25ca65d 100644 --- a/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java +++ b/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java @@ -1101,48 +1101,44 @@ public void testPercolatorQueryViaMultiSearch() throws Exception { MultiSearchResponse response = client().prepareMultiSearch() .add( - client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "query", - BytesReference.bytes(jsonBuilder().startObject().field("field1", "b").endObject()), - XContentType.JSON - ) + prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "query", + BytesReference.bytes(jsonBuilder().startObject().field("field1", "b").endObject()), + XContentType.JSON ) + ) ) .add( - client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "query", - BytesReference.bytes(yamlBuilder().startObject().field("field1", "c").endObject()), - XContentType.YAML - ) + prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "query", + BytesReference.bytes(yamlBuilder().startObject().field("field1", "c").endObject()), + XContentType.YAML ) + ) ) .add( - client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "query", - BytesReference.bytes(jsonBuilder().startObject().field("field1", "b c").endObject()), - XContentType.JSON - ) + prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "query", + BytesReference.bytes(jsonBuilder().startObject().field("field1", "b c").endObject()), + XContentType.JSON ) + ) ) .add( - client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "query", - BytesReference.bytes(jsonBuilder().startObject().field("field1", "d").endObject()), - XContentType.JSON - ) + prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "query", + BytesReference.bytes(jsonBuilder().startObject().field("field1", "d").endObject()), + XContentType.JSON ) + ) ) - .add(client().prepareSearch("test").setQuery(new PercolateQueryBuilder("query", "test", "5", null, null, null))) + .add(prepareSearch("test").setQuery(new PercolateQueryBuilder("query", "test", "5", null, null, null))) .add( - client().prepareSearch("test") // non existing doc, so error element + prepareSearch("test") // non existing doc, so error element .setQuery(new PercolateQueryBuilder("query", "test", "6", null, null, null)) ) .get(); @@ -1241,40 +1237,33 @@ public void testWrappedWithConstantScore() throws Exception { indicesAdmin().prepareRefresh().get(); - SearchResponse response = client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "q", - BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), - XContentType.JSON - ) + SearchResponse response = prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "q", + BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), + XContentType.JSON ) - .get(); + ).get(); assertEquals(1, response.getHits().getTotalHits().value); - response = client().prepareSearch("test") - .setQuery( + response = prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "q", + BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), + XContentType.JSON + ) + ).addSort("_doc", SortOrder.ASC).get(); + assertEquals(1, response.getHits().getTotalHits().value); + + response = prepareSearch("test").setQuery( + constantScoreQuery( new PercolateQueryBuilder( "q", BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), XContentType.JSON ) ) - .addSort("_doc", SortOrder.ASC) - .get(); - assertEquals(1, response.getHits().getTotalHits().value); - - response = client().prepareSearch("test") - .setQuery( - constantScoreQuery( - new PercolateQueryBuilder( - "q", - BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), - XContentType.JSON - ) - ) - ) - .get(); + ).get(); assertEquals(1, response.getHits().getTotalHits().value); } @@ -1310,48 +1299,40 @@ public void testWithWildcardFieldNames() throws Exception { .execute() .actionGet(); - SearchResponse response = client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "q_simple", - BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), - XContentType.JSON - ) + SearchResponse response = prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "q_simple", + BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), + XContentType.JSON ) - .get(); + ).get(); assertEquals(1, response.getHits().getTotalHits().value); - response = client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "q_string", - BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), - XContentType.JSON - ) + response = prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "q_string", + BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), + XContentType.JSON ) - .get(); + ).get(); assertEquals(1, response.getHits().getTotalHits().value); - response = client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "q_match", - BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), - XContentType.JSON - ) + response = prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "q_match", + BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), + XContentType.JSON ) - .get(); + ).get(); assertEquals(1, response.getHits().getTotalHits().value); - response = client().prepareSearch("test") - .setQuery( - new PercolateQueryBuilder( - "q_combo", - BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), - XContentType.JSON - ) + response = prepareSearch("test").setQuery( + new PercolateQueryBuilder( + "q_combo", + BytesReference.bytes(jsonBuilder().startObject().field("text_1", "yada").endObject()), + XContentType.JSON ) - .get(); + ).get(); assertEquals(1, response.getHits().getTotalHits().value); } } diff --git a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java index dd9bde8035ef4..9947d8a727d28 100644 --- a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java +++ b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java @@ -282,7 +282,7 @@ private ReindexRequestBuilder reindexAndPartiallyBlock() throws Exception { ); // Checks that the all documents have been indexed and correctly counted - assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0), numDocs); + assertHitCount(prepareSearch(INDEX_NAME).setSize(0), numDocs); assertThat(ALLOWED_OPERATIONS.drainPermits(), equalTo(0)); ReindexRequestBuilder builder = new ReindexRequestBuilder(client, ReindexAction.INSTANCE).source(INDEX_NAME) diff --git a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/index/reindex/BulkByScrollUsesAllScrollDocumentsAfterConflictsIntegTests.java b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/index/reindex/BulkByScrollUsesAllScrollDocumentsAfterConflictsIntegTests.java index 148189152b30a..d7f71fcc510ab 100644 --- a/modules/reindex/src/internalClusterTest/java/org/elasticsearch/index/reindex/BulkByScrollUsesAllScrollDocumentsAfterConflictsIntegTests.java +++ b/modules/reindex/src/internalClusterTest/java/org/elasticsearch/index/reindex/BulkByScrollUsesAllScrollDocumentsAfterConflictsIntegTests.java @@ -201,8 +201,7 @@ public void testDeleteByQuery() throws Exception { // Ensure that the write thread blocking task is currently executing barrier.await(); - final SearchResponse searchResponse = client().prepareSearch(sourceIndex) - .setSize(numDocs) // Get all indexed docs + final SearchResponse searchResponse = prepareSearch(sourceIndex).setSize(numDocs) // Get all indexed docs .addSort(SORTING_FIELD, SortOrder.DESC) .execute() .actionGet(); diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/CancelTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/CancelTests.java index 803264f6b73be..35ad5fe9532cd 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/CancelTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/CancelTests.java @@ -102,7 +102,7 @@ private void testCancel( ); // Checks that the all documents have been indexed and correctly counted - assertHitCount(client().prepareSearch(INDEX).setSize(0), numDocs); + assertHitCount(prepareSearch(INDEX).setSize(0), numDocs); assertThat(ALLOWED_OPERATIONS.drainPermits(), equalTo(0)); // Scroll by 1 so that cancellation is easier to control @@ -225,7 +225,7 @@ public void testReindexCancel() throws Exception { assertThat(response, matcher().created(modified).reasonCancelled(equalTo("by user request"))); refresh("dest"); - assertHitCount(client().prepareSearch("dest").setSize(0), modified); + assertHitCount(prepareSearch("dest").setSize(0), modified); }, equalTo("reindex from [" + INDEX + "] to [dest]")); } @@ -241,7 +241,7 @@ public void testUpdateByQueryCancel() throws Exception { testCancel(UpdateByQueryAction.NAME, updateByQuery().setPipeline("set-processed").source(INDEX), (response, total, modified) -> { assertThat(response, matcher().updated(modified).reasonCancelled(equalTo("by user request"))); - assertHitCount(client().prepareSearch(INDEX).setSize(0).setQuery(termQuery("processed", true)), modified); + assertHitCount(prepareSearch(INDEX).setSize(0).setQuery(termQuery("processed", true)), modified); }, equalTo("update-by-query [" + INDEX + "]")); assertAcked(clusterAdmin().deletePipeline(new DeletePipelineRequest("set-processed")).get()); @@ -253,7 +253,7 @@ public void testDeleteByQueryCancel() throws Exception { deleteByQuery().source(INDEX).filter(QueryBuilders.matchAllQuery()), (response, total, modified) -> { assertThat(response, matcher().deleted(modified).reasonCancelled(equalTo("by user request"))); - assertHitCount(client().prepareSearch(INDEX).setSize(0), total - modified); + assertHitCount(prepareSearch(INDEX).setSize(0), total - modified); }, equalTo("delete-by-query [" + INDEX + "]") ); @@ -266,7 +266,7 @@ public void testReindexCancelWithWorkers() throws Exception { (response, total, modified) -> { assertThat(response, matcher().created(modified).reasonCancelled(equalTo("by user request")).slices(hasSize(5))); refresh("dest"); - assertHitCount(client().prepareSearch("dest").setSize(0), modified); + assertHitCount(prepareSearch("dest").setSize(0), modified); }, equalTo("reindex from [" + INDEX + "] to [dest]") ); @@ -287,7 +287,7 @@ public void testUpdateByQueryCancelWithWorkers() throws Exception { updateByQuery().setPipeline("set-processed").source(INDEX).setSlices(5), (response, total, modified) -> { assertThat(response, matcher().updated(modified).reasonCancelled(equalTo("by user request")).slices(hasSize(5))); - assertHitCount(client().prepareSearch(INDEX).setSize(0).setQuery(termQuery("processed", true)), modified); + assertHitCount(prepareSearch(INDEX).setSize(0).setQuery(termQuery("processed", true)), modified); }, equalTo("update-by-query [" + INDEX + "]") ); @@ -301,7 +301,7 @@ public void testDeleteByQueryCancelWithWorkers() throws Exception { deleteByQuery().source(INDEX).filter(QueryBuilders.matchAllQuery()).setSlices(5), (response, total, modified) -> { assertThat(response, matcher().deleted(modified).reasonCancelled(equalTo("by user request")).slices(hasSize(5))); - assertHitCount(client().prepareSearch(INDEX).setSize(0), total - modified); + assertHitCount(prepareSearch(INDEX).setSize(0), total - modified); }, equalTo("delete-by-query [" + INDEX + "]") ); diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java index cfcd78374bca0..d3b401ab3541d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java @@ -61,25 +61,25 @@ public void testBasics() throws Exception { client().prepareIndex("test").setId("7").setSource("foo", "f") ); - assertHitCount(client().prepareSearch("test").setSize(0), 7); + assertHitCount(prepareSearch("test").setSize(0), 7); // Deletes two docs that matches "foo:a" assertThat(deleteByQuery().source("test").filter(termQuery("foo", "a")).refresh(true).get(), matcher().deleted(2)); - assertHitCount(client().prepareSearch("test").setSize(0), 5); + assertHitCount(prepareSearch("test").setSize(0), 5); // Deletes the two first docs with limit by size DeleteByQueryRequestBuilder request = deleteByQuery().source("test").filter(QueryBuilders.matchAllQuery()).size(2).refresh(true); request.source().addSort("foo.keyword", SortOrder.ASC); assertThat(request.get(), matcher().deleted(2)); - assertHitCount(client().prepareSearch("test").setSize(0), 3); + assertHitCount(prepareSearch("test").setSize(0), 3); // Deletes but match no docs assertThat(deleteByQuery().source("test").filter(termQuery("foo", "no_match")).refresh(true).get(), matcher().deleted(0)); - assertHitCount(client().prepareSearch("test").setSize(0), 3); + assertHitCount(prepareSearch("test").setSize(0), 3); // Deletes all remaining docs assertThat(deleteByQuery().source("test").filter(QueryBuilders.matchAllQuery()).refresh(true).get(), matcher().deleted(3)); - assertHitCount(client().prepareSearch("test").setSize(0), 0); + assertHitCount(prepareSearch("test").setSize(0), 0); } public void testDeleteByQueryWithOneIndex() throws Exception { @@ -92,7 +92,7 @@ public void testDeleteByQueryWithOneIndex() throws Exception { indexRandom(true, true, true, builders); assertThat(deleteByQuery().source("t*").filter(QueryBuilders.matchAllQuery()).refresh(true).get(), matcher().deleted(docs)); - assertHitCount(client().prepareSearch("test").setSize(0), 0); + assertHitCount(prepareSearch("test").setSize(0), 0); } public void testDeleteByQueryWithMultipleIndices() throws Exception { @@ -122,7 +122,7 @@ public void testDeleteByQueryWithMultipleIndices() throws Exception { for (int i = 0; i < indices; i++) { long remaining = docs - candidates[i]; - assertHitCount(client().prepareSearch("test-" + i).setSize(0), remaining); + assertHitCount(prepareSearch("test-" + i).setSize(0), remaining); } assertHitCount(client().prepareSearch().setSize(0), (indices * docs) - deletions); @@ -186,13 +186,13 @@ public void testDeleteByMatchQuery() throws Exception { indexRandom(true, true, true, builders); int n = between(0, docs - 1); - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(matchQuery("_id", Integer.toString(n))), 1); - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(QueryBuilders.matchAllQuery()), docs); + assertHitCount(prepareSearch("test").setSize(0).setQuery(matchQuery("_id", Integer.toString(n))), 1); + assertHitCount(prepareSearch("test").setSize(0).setQuery(QueryBuilders.matchAllQuery()), docs); DeleteByQueryRequestBuilder delete = deleteByQuery().source("alias").filter(matchQuery("_id", Integer.toString(n))); assertThat(delete.refresh(true).get(), matcher().deleted(1L)); - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(QueryBuilders.matchAllQuery()), docs - 1); + assertHitCount(prepareSearch("test").setSize(0).setQuery(QueryBuilders.matchAllQuery()), docs - 1); } public void testDeleteByQueryWithDateMath() throws Exception { @@ -201,7 +201,7 @@ public void testDeleteByQueryWithDateMath() throws Exception { DeleteByQueryRequestBuilder delete = deleteByQuery().source("test").filter(rangeQuery("d").to("now-1h")); assertThat(delete.refresh(true).get(), matcher().deleted(1L)); - assertHitCount(client().prepareSearch("test").setSize(0), 0); + assertHitCount(prepareSearch("test").setSize(0), 0); } public void testDeleteByQueryOnReadOnlyIndex() throws Exception { @@ -224,7 +224,7 @@ public void testDeleteByQueryOnReadOnlyIndex() throws Exception { disableIndexBlock("test", SETTING_READ_ONLY); } - assertHitCount(client().prepareSearch("test").setSize(0), docs); + assertHitCount(prepareSearch("test").setSize(0), docs); } public void testDeleteByQueryOnReadOnlyAllowDeleteIndex() throws Exception { @@ -280,9 +280,9 @@ public void testDeleteByQueryOnReadOnlyAllowDeleteIndex() throws Exception { } } if (diskAllocationDeciderEnabled) { - assertHitCount(client().prepareSearch("test").setSize(0), 0); + assertHitCount(prepareSearch("test").setSize(0), 0); } else { - assertHitCount(client().prepareSearch("test").setSize(0), docs); + assertHitCount(prepareSearch("test").setSize(0), docs); } } @@ -297,7 +297,7 @@ public void testSlices() throws Exception { client().prepareIndex("test").setId("6").setSource("foo", "e"), client().prepareIndex("test").setId("7").setSource("foo", "f") ); - assertHitCount(client().prepareSearch("test").setSize(0), 7); + assertHitCount(prepareSearch("test").setSize(0), 7); int slices = randomSlices(); int expectedSlices = expectedSliceStatuses(slices, "test"); @@ -307,14 +307,14 @@ public void testSlices() throws Exception { deleteByQuery().source("test").filter(termQuery("foo", "a")).refresh(true).setSlices(slices).get(), matcher().deleted(2).slices(hasSize(expectedSlices)) ); - assertHitCount(client().prepareSearch("test").setSize(0), 5); + assertHitCount(prepareSearch("test").setSize(0), 5); // Delete remaining docs assertThat( deleteByQuery().source("test").filter(QueryBuilders.matchAllQuery()).refresh(true).setSlices(slices).get(), matcher().deleted(5).slices(hasSize(expectedSlices)) ); - assertHitCount(client().prepareSearch("test").setSize(0), 0); + assertHitCount(prepareSearch("test").setSize(0), 0); } public void testMultipleSources() throws Exception { @@ -333,7 +333,7 @@ public void testMultipleSources() throws Exception { List allDocs = docs.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); indexRandom(true, allDocs); for (Map.Entry> entry : docs.entrySet()) { - assertHitCount(client().prepareSearch(entry.getKey()).setSize(0), entry.getValue().size()); + assertHitCount(prepareSearch(entry.getKey()).setSize(0), entry.getValue().size()); } int slices = randomSlices(1, 10); @@ -347,7 +347,7 @@ public void testMultipleSources() throws Exception { ); for (String index : docs.keySet()) { - assertHitCount(client().prepareSearch(index).setSize(0), 0); + assertHitCount(prepareSearch(index).setSize(0), 0); } } diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryConcurrentTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryConcurrentTests.java index 2e5fd4803fbe4..81d00d98b1fec 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryConcurrentTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryConcurrentTests.java @@ -40,7 +40,7 @@ public void testConcurrentDeleteByQueriesOnDifferentDocs() throws Throwable { final CountDownLatch start = new CountDownLatch(1); for (int t = 0; t < threads.length; t++) { final int threadNum = t; - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(QueryBuilders.termQuery("field", threadNum)), docs); + assertHitCount(prepareSearch("test").setSize(0).setQuery(QueryBuilders.termQuery("field", threadNum)), docs); Runnable r = () -> { try { @@ -64,7 +64,7 @@ public void testConcurrentDeleteByQueriesOnDifferentDocs() throws Throwable { } for (int t = 0; t < threads.length; t++) { - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(QueryBuilders.termQuery("field", t)), 0); + assertHitCount(prepareSearch("test").setSize(0).setQuery(QueryBuilders.termQuery("field", t)), 0); } } @@ -105,7 +105,7 @@ public void testConcurrentDeleteByQueriesOnSameDocs() throws Throwable { thread.join(); } - assertHitCount(client().prepareSearch("test").setSize(0), 0L); + assertHitCount(prepareSearch("test").setSize(0), 0L); assertThat(deleted.get(), equalTo(docs)); } } diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexBasicTests.java index 3bc029058e705..45ca5a536f34f 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexBasicTests.java @@ -35,28 +35,28 @@ public void testFiltering() throws Exception { client().prepareIndex("source").setId("3").setSource("foo", "b"), client().prepareIndex("source").setId("4").setSource("foo", "c") ); - assertHitCount(client().prepareSearch("source").setSize(0), 4); + assertHitCount(prepareSearch("source").setSize(0), 4); // Copy all the docs ReindexRequestBuilder copy = reindex().source("source").destination("dest").refresh(true); assertThat(copy.get(), matcher().created(4)); - assertHitCount(client().prepareSearch("dest").setSize(0), 4); + assertHitCount(prepareSearch("dest").setSize(0), 4); // Now none of them createIndex("none"); copy = reindex().source("source").destination("none").filter(termQuery("foo", "no_match")).refresh(true); assertThat(copy.get(), matcher().created(0)); - assertHitCount(client().prepareSearch("none").setSize(0), 0); + assertHitCount(prepareSearch("none").setSize(0), 0); // Now half of them copy = reindex().source("source").destination("dest_half").filter(termQuery("foo", "a")).refresh(true); assertThat(copy.get(), matcher().created(2)); - assertHitCount(client().prepareSearch("dest_half").setSize(0), 2); + assertHitCount(prepareSearch("dest_half").setSize(0), 2); // Limit with maxDocs copy = reindex().source("source").destination("dest_size_one").maxDocs(1).refresh(true); assertThat(copy.get(), matcher().created(1)); - assertHitCount(client().prepareSearch("dest_size_one").setSize(0), 1); + assertHitCount(prepareSearch("dest_size_one").setSize(0), 1); } public void testCopyMany() throws Exception { @@ -67,14 +67,14 @@ public void testCopyMany() throws Exception { } indexRandom(true, docs); - assertHitCount(client().prepareSearch("source").setSize(0), max); + assertHitCount(prepareSearch("source").setSize(0), max); // Copy all the docs ReindexRequestBuilder copy = reindex().source("source").destination("dest").refresh(true); // Use a small batch size so we have to use more than one batch copy.source().setSize(5); assertThat(copy.get(), matcher().created(max).batches(max, 5)); - assertHitCount(client().prepareSearch("dest").setSize(0), max); + assertHitCount(prepareSearch("dest").setSize(0), max); // Copy some of the docs int half = max / 2; @@ -83,7 +83,7 @@ public void testCopyMany() throws Exception { copy.source().setSize(5); copy.maxDocs(half); assertThat(copy.get(), matcher().created(half).batches(half, 5)); - assertHitCount(client().prepareSearch("dest_half").setSize(0), half); + assertHitCount(prepareSearch("dest_half").setSize(0), half); } public void testCopyManyWithSlices() throws Exception { @@ -94,7 +94,7 @@ public void testCopyManyWithSlices() throws Exception { } indexRandom(true, docs); - assertHitCount(client().prepareSearch("source").setSize(0), max); + assertHitCount(prepareSearch("source").setSize(0), max); int slices = randomSlices(); int expectedSlices = expectedSliceStatuses(slices, "source"); @@ -104,7 +104,7 @@ public void testCopyManyWithSlices() throws Exception { // Use a small batch size so we have to use more than one batch copy.source().setSize(5); assertThat(copy.get(), matcher().created(max).batches(greaterThanOrEqualTo(max / 5)).slices(hasSize(expectedSlices))); - assertHitCount(client().prepareSearch("dest").setSize(0), max); + assertHitCount(prepareSearch("dest").setSize(0), max); // Copy some of the docs int half = max / 2; @@ -114,7 +114,7 @@ public void testCopyManyWithSlices() throws Exception { copy.maxDocs(half); BulkByScrollResponse response = copy.get(); assertThat(response, matcher().created(lessThanOrEqualTo((long) half)).slices(hasSize(expectedSlices))); - assertHitCount(client().prepareSearch("dest_half").setSize(0), response.getCreated()); + assertHitCount(prepareSearch("dest_half").setSize(0), response.getCreated()); } public void testMultipleSources() throws Exception { @@ -134,7 +134,7 @@ public void testMultipleSources() throws Exception { List allDocs = docs.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); indexRandom(true, allDocs); for (Map.Entry> entry : docs.entrySet()) { - assertHitCount(client().prepareSearch(entry.getKey()).setSize(0), entry.getValue().size()); + assertHitCount(prepareSearch(entry.getKey()).setSize(0), entry.getValue().size()); } int slices = randomSlices(1, 10); @@ -145,7 +145,7 @@ public void testMultipleSources() throws Exception { BulkByScrollResponse response = request.get(); assertThat(response, matcher().created(allDocs.size()).slices(hasSize(expectedSlices))); - assertHitCount(client().prepareSearch("dest").setSize(0), allDocs.size()); + assertHitCount(prepareSearch("dest").setSize(0), allDocs.size()); } public void testMissingSources() { @@ -166,12 +166,12 @@ public void testReindexFromComplexDateMathIndexName() throws Exception { client().prepareIndex(sourceIndexName).setId("3").setSource("foo", "b"), client().prepareIndex(sourceIndexName).setId("4").setSource("foo", "c") ); - assertHitCount(client().prepareSearch(sourceIndexName).setSize(0), 4); + assertHitCount(prepareSearch(sourceIndexName).setSize(0), 4); // Copy all the docs ReindexRequestBuilder copy = reindex().source(sourceIndexName).destination(destIndexName).refresh(true); assertThat(copy.get(), matcher().created(4)); - assertHitCount(client().prepareSearch(destIndexName).setSize(0), 4); + assertHitCount(prepareSearch(destIndexName).setSize(0), 4); } } diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFailureTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFailureTests.java index 8a3066ebac83d..5e868598d165e 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFailureTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFailureTests.java @@ -128,7 +128,7 @@ public void testDateMathResolvesSameIndexName() throws Exception { client().prepareIndex(sourceIndexName).setId("3").setSource("foo", "b"), client().prepareIndex(sourceIndexName).setId("4").setSource("foo", "c") ); - assertHitCount(client().prepareSearch(sourceIndexName).setSize(0), 4); + assertHitCount(prepareSearch(sourceIndexName).setSize(0), 4); ActionRequestValidationException e = expectThrows( ActionRequestValidationException.class, diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryBasicTests.java index fb02abdab0f6a..f37c9b5891416 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryBasicTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/UpdateByQueryBasicTests.java @@ -34,7 +34,7 @@ public void testBasics() throws Exception { client().prepareIndex("test").setId("3").setSource("foo", "b"), client().prepareIndex("test").setId("4").setSource("foo", "c") ); - assertHitCount(client().prepareSearch("test").setSize(0), 4); + assertHitCount(prepareSearch("test").setSize(0), 4); assertEquals(1, client().prepareGet("test", "1").get().getVersion()); assertEquals(1, client().prepareGet("test", "4").get().getVersion()); @@ -74,7 +74,7 @@ public void testSlices() throws Exception { client().prepareIndex("test").setId("3").setSource("foo", "b"), client().prepareIndex("test").setId("4").setSource("foo", "c") ); - assertHitCount(client().prepareSearch("test").setSize(0), 4); + assertHitCount(prepareSearch("test").setSize(0), 4); assertEquals(1, client().prepareGet("test", "1").get().getVersion()); assertEquals(1, client().prepareGet("test", "4").get().getVersion()); @@ -124,7 +124,7 @@ public void testMultipleSources() throws Exception { List allDocs = docs.values().stream().flatMap(Collection::stream).collect(Collectors.toList()); indexRandom(true, allDocs); for (Map.Entry> entry : docs.entrySet()) { - assertHitCount(client().prepareSearch(entry.getKey()).setSize(0), entry.getValue().size()); + assertHitCount(prepareSearch(entry.getKey()).setSize(0), entry.getValue().size()); } int slices = randomSlices(1, 10); diff --git a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java index 5790fdb785bb6..b23e32b651698 100644 --- a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java +++ b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java @@ -198,7 +198,7 @@ public void testAbortRequestStats() throws Exception { flushAndRefresh(index); ForceMergeResponse forceMerge = client().admin().indices().prepareForceMerge(index).setFlush(true).setMaxNumSegments(1).get(); assertThat(forceMerge.getSuccessfulShards(), equalTo(1)); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); // Intentionally fail snapshot to trigger abortMultipartUpload requests shouldFailCompleteMultipartUploadRequest.set(true); @@ -240,14 +240,14 @@ public void testMetrics() throws Exception { flushAndRefresh(index); ForceMergeResponse forceMerge = client().admin().indices().prepareForceMerge(index).setFlush(true).setMaxNumSegments(1).get(); assertThat(forceMerge.getSuccessfulShards(), equalTo(1)); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); final String snapshot = "snapshot"; assertSuccessfulSnapshot(clusterAdmin().prepareCreateSnapshot(repository, snapshot).setWaitForCompletion(true).setIndices(index)); assertAcked(client().admin().indices().prepareDelete(index)); assertSuccessfulRestore(clusterAdmin().prepareRestoreSnapshot(repository, snapshot).setWaitForCompletion(true)); ensureGreen(index); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); assertAcked(clusterAdmin().prepareDeleteSnapshot(repository, snapshot).get()); final Map aggregatedMetrics = new HashMap<>(); diff --git a/plugins/mapper-size/src/internalClusterTest/java/org/elasticsearch/index/mapper/size/SizeMappingIT.java b/plugins/mapper-size/src/internalClusterTest/java/org/elasticsearch/index/mapper/size/SizeMappingIT.java index ead12a7b2246f..e92c7ca4bdebb 100644 --- a/plugins/mapper-size/src/internalClusterTest/java/org/elasticsearch/index/mapper/size/SizeMappingIT.java +++ b/plugins/mapper-size/src/internalClusterTest/java/org/elasticsearch/index/mapper/size/SizeMappingIT.java @@ -110,15 +110,15 @@ public void testGetWithFields() throws Exception { assertAcked(prepareCreate("test").setMapping("_size", "enabled=true")); final String source = "{\"f\":\"" + randomAlphaOfLengthBetween(1, 100) + "\"}"; indexRandom(true, client().prepareIndex("test").setId("1").setSource(source, XContentType.JSON)); - SearchResponse searchResponse = client().prepareSearch("test").addFetchField("_size").get(); + SearchResponse searchResponse = prepareSearch("test").addFetchField("_size").get(); assertEquals(source.length(), ((Long) searchResponse.getHits().getHits()[0].getFields().get("_size").getValue()).intValue()); // this should not work when requesting fields via wildcard expression - searchResponse = client().prepareSearch("test").addFetchField("*").get(); + searchResponse = prepareSearch("test").addFetchField("*").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); // This should STILL work - searchResponse = client().prepareSearch("test").addStoredField("*").get(); + searchResponse = prepareSearch("test").addStoredField("*").get(); assertNotNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); } @@ -126,13 +126,13 @@ public void testWildCardWithFieldsWhenDisabled() throws Exception { assertAcked(prepareCreate("test").setMapping("_size", "enabled=false")); final String source = "{\"f\":\"" + randomAlphaOfLengthBetween(1, 100) + "\"}"; indexRandom(true, client().prepareIndex("test").setId("1").setSource(source, XContentType.JSON)); - SearchResponse searchResponse = client().prepareSearch("test").addFetchField("_size").get(); + SearchResponse searchResponse = prepareSearch("test").addFetchField("_size").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); - searchResponse = client().prepareSearch("test").addFetchField("*").get(); + searchResponse = prepareSearch("test").addFetchField("*").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); - searchResponse = client().prepareSearch("test").addStoredField("*").get(); + searchResponse = prepareSearch("test").addStoredField("*").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); } @@ -140,13 +140,13 @@ public void testWildCardWithFieldsWhenNotProvided() throws Exception { assertAcked(prepareCreate("test")); final String source = "{\"f\":\"" + randomAlphaOfLengthBetween(1, 100) + "\"}"; indexRandom(true, client().prepareIndex("test").setId("1").setSource(source, XContentType.JSON)); - SearchResponse searchResponse = client().prepareSearch("test").addFetchField("_size").get(); + SearchResponse searchResponse = prepareSearch("test").addFetchField("_size").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); - searchResponse = client().prepareSearch("test").addFetchField("*").get(); + searchResponse = prepareSearch("test").addFetchField("*").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); - searchResponse = client().prepareSearch("test").addStoredField("*").get(); + searchResponse = prepareSearch("test").addStoredField("*").get(); assertNull(searchResponse.getHits().getHits()[0].getFields().get("_size")); } } diff --git a/plugins/store-smb/src/internalClusterTest/java/org/elasticsearch/index/store/smb/AbstractAzureFsTestCase.java b/plugins/store-smb/src/internalClusterTest/java/org/elasticsearch/index/store/smb/AbstractAzureFsTestCase.java index d4763af7a505a..4d1f6426821c4 100644 --- a/plugins/store-smb/src/internalClusterTest/java/org/elasticsearch/index/store/smb/AbstractAzureFsTestCase.java +++ b/plugins/store-smb/src/internalClusterTest/java/org/elasticsearch/index/store/smb/AbstractAzureFsTestCase.java @@ -32,7 +32,7 @@ public void testAzureFs() { indexDoc("test", "" + i, "foo", "bar"); } refresh(); - SearchResponse response = client().prepareSearch("test").get(); + SearchResponse response = prepareSearch("test").get(); assertThat(response.getHits().getTotalHits().value, is(nbDocs)); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/RejectionActionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/RejectionActionIT.java index a98f98ded28b4..c7082f7979ed9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/RejectionActionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/RejectionActionIT.java @@ -50,8 +50,7 @@ public void testSimulatedSearchRejectionLoad() throws Throwable { final CountDownLatch latch = new CountDownLatch(numberOfAsyncOps); final CopyOnWriteArrayList responses = new CopyOnWriteArrayList<>(); for (int i = 0; i < numberOfAsyncOps; i++) { - client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(QueryBuilders.matchQuery("field", "1")) .execute(new LatchedActionListener<>(new ActionListener() { @Override diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java index c0a54ef874de5..4d488ede6f0de 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java @@ -782,15 +782,15 @@ public void testTaskStoringSuccessfulResult() throws Exception { assertNoFailures(indicesAdmin().prepareRefresh(TaskResultsService.TASK_INDEX).get()); - SearchResponse searchResponse = client().prepareSearch(TaskResultsService.TASK_INDEX) - .setSource(SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("task.action", taskInfo.action()))) - .get(); + SearchResponse searchResponse = prepareSearch(TaskResultsService.TASK_INDEX).setSource( + SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("task.action", taskInfo.action())) + ).get(); assertEquals(1L, searchResponse.getHits().getTotalHits().value); - searchResponse = client().prepareSearch(TaskResultsService.TASK_INDEX) - .setSource(SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("task.node", taskInfo.taskId().getNodeId()))) - .get(); + searchResponse = prepareSearch(TaskResultsService.TASK_INDEX).setSource( + SearchSourceBuilder.searchSource().query(QueryBuilders.termQuery("task.node", taskInfo.taskId().getNodeId())) + ).get(); assertEquals(1L, searchResponse.getHits().getTotalHits().value); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java index 4f84509086a5e..b18564beb6557 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java @@ -80,21 +80,21 @@ public void testCreateCloneIndex() { } final int size = docs > 0 ? 2 * docs : 1; - assertHitCount(client().prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); if (createWithReplicas == false) { // bump replicas setReplicaCount(1, "target"); ensureGreen(); - assertHitCount(client().prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); } for (int i = docs; i < 2 * docs; i++) { client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } flushAndRefresh(); - assertHitCount(client().prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")), 2 * docs); - assertHitCount(client().prepareSearch("source").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")), 2 * docs); + assertHitCount(prepareSearch("source").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); GetSettingsResponse target = indicesAdmin().prepareGetSettings("target").get(); assertThat( target.getIndexToSettings().get("target").getAsVersionId("index.version.created", IndexVersion::fromId), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java index 27154c883d270..e3ea54f382c0a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java @@ -270,11 +270,10 @@ public void onFailure(Exception e) { // we only really assert that we never reuse segments of old indices or anything like this here and that nothing fails with // crazy exceptions - SearchResponse expected = client().prepareSearch("test") - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) + SearchResponse expected = prepareSearch("test").setIndicesOptions(IndicesOptions.lenientExpandOpen()) .setQuery(new RangeQueryBuilder("index_version").from(indexVersion.get(), true)) .get(); - SearchResponse all = client().prepareSearch("test").setIndicesOptions(IndicesOptions.lenientExpandOpen()).get(); + SearchResponse all = prepareSearch("test").setIndicesOptions(IndicesOptions.lenientExpandOpen()).get(); assertEquals(expected + " vs. " + all, expected.getHits().getTotalHits().value, all.getHits().getTotalHits().value); logger.info("total: {}", expected.getHits().getTotalHits().value); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java index 173719a8dc92e..ac206a0ed060a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java @@ -107,7 +107,7 @@ public void testCreateShrinkIndexToN() { .get() ); ensureGreen(); - assertHitCount(client().prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); for (int i = 0; i < 20; i++) { // now update client().prepareIndex("first_shrink") @@ -116,8 +116,8 @@ public void testCreateShrinkIndexToN() { .get(); } flushAndRefresh(); - assertHitCount(client().prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); - assertHitCount(client().prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); // relocate all shards to one node such that we can merge it. updateIndexSettings( @@ -134,14 +134,14 @@ public void testCreateShrinkIndexToN() { .get() ); ensureGreen(); - assertHitCount(client().prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); // let it be allocated anywhere and bump replicas updateIndexSettings( Settings.builder().putNull("index.routing.allocation.include._id").put("index.number_of_replicas", 1), "second_shrink" ); ensureGreen(); - assertHitCount(client().prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); for (int i = 0; i < 20; i++) { // now update client().prepareIndex("second_shrink") @@ -150,9 +150,9 @@ public void testCreateShrinkIndexToN() { .get(); } flushAndRefresh(); - assertHitCount(client().prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); - assertHitCount(client().prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); - assertHitCount(client().prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); assertNoResizeSourceIndexSettings("first_shrink"); assertNoResizeSourceIndexSettings("second_shrink"); @@ -311,21 +311,21 @@ public void testCreateShrinkIndex() { } final int size = docs > 0 ? 2 * docs : 1; - assertHitCount(client().prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); if (createWithReplicas == false) { // bump replicas setReplicaCount(1, "target"); ensureGreen(); - assertHitCount(client().prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); } for (int i = docs; i < 2 * docs; i++) { client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } flushAndRefresh(); - assertHitCount(client().prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")), 2 * docs); - assertHitCount(client().prepareSearch("source").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")), 2 * docs); + assertHitCount(prepareSearch("source").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); GetSettingsResponse target = indicesAdmin().prepareGetSettings("target").get(); assertThat( target.getIndexToSettings().get("target").getAsVersionId("index.version.created", IndexVersion::fromId), @@ -409,7 +409,7 @@ public void testCreateShrinkIndexFails() throws Exception { // we support the expected shard size in the allocator to sum up over the source index shards assertTrue("expected shard size must be set but wasn't: " + expectedShardSize, expectedShardSize > 0); ensureGreen(); - assertHitCount(client().prepareSearch("target").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); + assertHitCount(prepareSearch("target").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20); assertNoResizeSourceIndexSettings("target"); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java index 84fe73ead7d8a..68b75fc04770c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java @@ -189,7 +189,7 @@ private void splitToN(int sourceShards, int firstSplitShards, int secondSplitSha .get() ); ensureGreen(); - assertHitCount(client().prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); assertNoResizeSourceIndexSettings("first_split"); for (int i = 0; i < numDocs; i++) { // now update @@ -200,8 +200,8 @@ private void splitToN(int sourceShards, int firstSplitShards, int secondSplitSha builder.get(); } flushAndRefresh(); - assertHitCount(client().prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); - assertHitCount(client().prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); for (int i = 0; i < numDocs; i++) { GetResponse getResponse = client().prepareGet("first_split", Integer.toString(i)).setRouting(routingValue[i]).get(); assertTrue(getResponse.isExists()); @@ -217,13 +217,13 @@ private void splitToN(int sourceShards, int firstSplitShards, int secondSplitSha .get() ); ensureGreen(); - assertHitCount(client().prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); assertNoResizeSourceIndexSettings("second_split"); // let it be allocated anywhere and bump replicas setReplicaCount(1, "second_split"); ensureGreen(); - assertHitCount(client().prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); for (int i = 0; i < numDocs; i++) { // now update IndexRequestBuilder builder = indexFunc.apply("second_split", i); @@ -237,30 +237,24 @@ private void splitToN(int sourceShards, int firstSplitShards, int secondSplitSha GetResponse getResponse = client().prepareGet("second_split", Integer.toString(i)).setRouting(routingValue[i]).get(); assertTrue(getResponse.isExists()); } - assertHitCount(client().prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); - assertHitCount(client().prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); - assertHitCount(client().prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); + assertHitCount(prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs); if (useNested) { assertNested("source", numDocs); assertNested("first_split", numDocs); assertNested("second_split", numDocs); } - assertAllUniqueDocs( - client().prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), - numDocs - ); - assertAllUniqueDocs( - client().prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), - numDocs - ); - assertAllUniqueDocs(client().prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), numDocs); + assertAllUniqueDocs(prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), numDocs); + assertAllUniqueDocs(prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), numDocs); + assertAllUniqueDocs(prepareSearch("source").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")).get(), numDocs); } public void assertNested(String index, int numDocs) { // now, do a nested query - SearchResponse searchResponse = client().prepareSearch(index) - .setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) - .get(); + SearchResponse searchResponse = prepareSearch(index).setQuery( + nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) numDocs)); } @@ -410,21 +404,21 @@ public void testCreateSplitIndex() throws Exception { } final int size = docs > 0 ? 2 * docs : 1; - assertHitCount(client().prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); if (createWithReplicas == false) { // bump replicas setReplicaCount(1, "target"); ensureGreen(); - assertHitCount(client().prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); } for (int i = docs; i < 2 * docs; i++) { client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); } flushAndRefresh(); - assertHitCount(client().prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")), 2 * docs); - assertHitCount(client().prepareSearch("source").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); + assertHitCount(prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")), 2 * docs); + assertHitCount(prepareSearch("source").setSize(size).setQuery(new TermsQueryBuilder("foo", "bar")), docs); GetSettingsResponse target = indicesAdmin().prepareGetSettings("target").get(); assertThat( target.getIndexToSettings().get("target").getAsVersionId("index.version.created", IndexVersion::fromId), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessor2RetryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessor2RetryIT.java index 33fcaaea39853..18a8ae2dd2800 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessor2RetryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessor2RetryIT.java @@ -136,7 +136,7 @@ public void afterBulk(long executionId, BulkRequest request, Exception failure) indicesAdmin().refresh(new RefreshRequest()).get(); - SearchResponse results = client().prepareSearch(INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).setSize(0).get(); + SearchResponse results = prepareSearch(INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).setSize(0).get(); assertThat(bulkProcessor.getTotalBytesInFlight(), equalTo(0L)); if (rejectedExecutionExpected) { assertThat((int) results.getHits().getTotalHits().value, lessThanOrEqualTo(numberOfAsyncOps)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessorRetryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessorRetryIT.java index 44b7040ba3267..e664f6e6bb42f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessorRetryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkProcessorRetryIT.java @@ -131,7 +131,7 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) indicesAdmin().refresh(new RefreshRequest()).get(); - SearchResponse results = client().prepareSearch(INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).setSize(0).get(); + SearchResponse results = prepareSearch(INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).setSize(0).get(); if (rejectedExecutionExpected) { assertThat((int) results.getHits().getTotalHits().value, lessThanOrEqualTo(numberOfAsyncOps)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java index 7009ba7858dd7..0f12959519322 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java @@ -634,7 +634,7 @@ public void testThatMissingIndexDoesNotAbortFullBulkRequest() throws Exception { .setRefreshPolicy(RefreshPolicy.IMMEDIATE); client().bulk(bulkRequest).get(); - assertHitCount(client().prepareSearch("bulkindex*"), 3); + assertHitCount(prepareSearch("bulkindex*"), 3); assertBusy(() -> assertAcked(indicesAdmin().prepareClose("bulkindex2"))); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/WriteAckDelayIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/WriteAckDelayIT.java index f0c6359876aab..6ec01c3be5626 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/WriteAckDelayIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/WriteAckDelayIT.java @@ -42,7 +42,7 @@ public void testIndexWithWriteDelayEnabled() throws Exception { for (int j = 0; j < numOfChecks; j++) { try { logger.debug("running search"); - SearchResponse response = client().prepareSearch("test").get(); + SearchResponse response = prepareSearch("test").get(); if (response.getHits().getTotalHits().value != numOfDocs) { final String message = "Count is " + response.getHits().getTotalHits().value diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/LookupRuntimeFieldIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/LookupRuntimeFieldIT.java index 78fd60579d5d2..7871a14264944 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/LookupRuntimeFieldIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/LookupRuntimeFieldIT.java @@ -132,8 +132,7 @@ public void populateIndex() throws Exception { } public void testBasic() { - SearchResponse searchResponse = client().prepareSearch("books") - .addFetchField("author") + SearchResponse searchResponse = prepareSearch("books").addFetchField("author") .addFetchField("title") .addSort("published_date", SortOrder.DESC) .setSize(3) @@ -169,18 +168,17 @@ public void testBasic() { } public void testLookupMultipleIndices() throws IOException { - SearchResponse searchResponse = client().prepareSearch("books") - .setRuntimeMappings(parseMapping(""" - { - "publisher": { - "type": "lookup", - "target_index": "publishers", - "input_field": "publisher_id", - "target_field": "_id", - "fetch_fields": ["name", "city"] - } + SearchResponse searchResponse = prepareSearch("books").setRuntimeMappings(parseMapping(""" + { + "publisher": { + "type": "lookup", + "target_index": "publishers", + "input_field": "publisher_id", + "target_field": "_id", + "fetch_fields": ["name", "city"] } - """)) + } + """)) .setFetchSource(false) .addFetchField("title") .addFetchField("author") @@ -217,7 +215,7 @@ public void testLookupMultipleIndices() throws IOException { } public void testFetchField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("books").setRuntimeMappings(parseMapping(""" + SearchResponse searchResponse = prepareSearch("books").setRuntimeMappings(parseMapping(""" { "author": { "type": "lookup", diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java index 09e0c064685a4..2f81992973142 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java @@ -96,7 +96,7 @@ public void testBasic() { } refresh("test"); if (randomBoolean()) { - SearchResponse resp2 = client().prepareSearch("test").setPreference(null).setQuery(new MatchAllQueryBuilder()).get(); + SearchResponse resp2 = prepareSearch("test").setPreference(null).setQuery(new MatchAllQueryBuilder()).get(); assertNoFailures(resp2); assertHitCount(resp2, numDocs - deletedDocs); } @@ -259,7 +259,7 @@ public void testIndexNotFound() { assertHitCount(resp, index1 + index2); indicesAdmin().prepareDelete("index-1").get(); if (randomBoolean()) { - resp = client().prepareSearch("index-*").get(); + resp = prepareSearch("index-*").get(); assertNoFailures(resp); assertHitCount(resp, index2); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java index e44a5a6a48181..1d320571864fe 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java @@ -310,12 +310,11 @@ public void testWaitForRefreshIndexValidation() throws Exception { Arrays.fill(validCheckpoints, SequenceNumbers.UNASSIGNED_SEQ_NO); // no exception - client().prepareSearch("testAlias").setWaitForCheckpoints(Collections.singletonMap("testAlias", validCheckpoints)).get(); + prepareSearch("testAlias").setWaitForCheckpoints(Collections.singletonMap("testAlias", validCheckpoints)).get(); IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("testFailedAlias") - .setWaitForCheckpoints(Collections.singletonMap("testFailedAlias", validCheckpoints)) + () -> prepareSearch("testFailedAlias").setWaitForCheckpoints(Collections.singletonMap("testFailedAlias", validCheckpoints)) .get() ); assertThat( @@ -328,7 +327,7 @@ public void testWaitForRefreshIndexValidation() throws Exception { IllegalArgumentException e2 = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("test1").setWaitForCheckpoints(Collections.singletonMap("test1", new long[2])).get() + () -> prepareSearch("test1").setWaitForCheckpoints(Collections.singletonMap("test1", new long[2])).get() ); assertThat( e2.getMessage(), @@ -342,7 +341,7 @@ public void testWaitForRefreshIndexValidation() throws Exception { IllegalArgumentException e3 = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("testAlias").setWaitForCheckpoints(Collections.singletonMap("testAlias", new long[2])).get() + () -> prepareSearch("testAlias").setWaitForCheckpoints(Collections.singletonMap("testAlias", new long[2])).get() ); assertThat( e3.getMessage(), @@ -356,7 +355,7 @@ public void testWaitForRefreshIndexValidation() throws Exception { IllegalArgumentException e4 = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("testAlias").setWaitForCheckpoints(Collections.singletonMap("test2", validCheckpoints)).get() + () -> prepareSearch("testAlias").setWaitForCheckpoints(Collections.singletonMap("test2", validCheckpoints)).get() ); assertThat( e4.getMessage(), @@ -375,11 +374,11 @@ public void testShardCountLimit() throws Exception { assertAcked(prepareCreate("test2").setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numPrimaries2))); // no exception - client().prepareSearch("test1").get(); + prepareSearch("test1").get(); updateClusterSettings(Settings.builder().put(TransportSearchAction.SHARD_COUNT_LIMIT_SETTING.getKey(), numPrimaries1 - 1)); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> client().prepareSearch("test1").get()); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> prepareSearch("test1").get()); assertThat( e.getMessage(), containsString("Trying to query " + numPrimaries1 + " shards, which is over the limit of " + (numPrimaries1 - 1)) @@ -388,7 +387,7 @@ public void testShardCountLimit() throws Exception { updateClusterSettings(Settings.builder().put(TransportSearchAction.SHARD_COUNT_LIMIT_SETTING.getKey(), numPrimaries1)); // no exception - client().prepareSearch("test1").get(); + prepareSearch("test1").get(); e = expectThrows(IllegalArgumentException.class, () -> client().prepareSearch("test1", "test2").get()); assertThat( @@ -425,8 +424,7 @@ public void testSearchIdle() throws Exception { client().prepareIndex("test").setId("2").setSource("created_date", "2020-01-02").get(); client().prepareIndex("test").setId("3").setSource("created_date", "2020-01-03").get(); assertBusy(() -> { - SearchResponse resp = client().prepareSearch("test") - .setQuery(new RangeQueryBuilder("created_date").gte("2020-01-02").lte("2020-01-03")) + SearchResponse resp = prepareSearch("test").setQuery(new RangeQueryBuilder("created_date").gte("2020-01-02").lte("2020-01-03")) .setPreFilterShardSize(randomIntBetween(1, 3)) .get(); assertThat(resp.getHits().getTotalHits().value, equalTo(2L)); @@ -442,8 +440,7 @@ public void testCircuitBreakerReduceFail() throws Exception { final CountDownLatch latch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { int batchReduceSize = randomIntBetween(2, Math.max(numShards + 1, 3)); - SearchRequest request = client().prepareSearch("test") - .addAggregation(new TestAggregationBuilder("test")) + SearchRequest request = prepareSearch("test").addAggregation(new TestAggregationBuilder("test")) .setBatchedReduceSize(batchReduceSize) .request(); final int index = i; @@ -484,8 +481,7 @@ public void onFailure(Exception e) { final CountDownLatch latch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { int batchReduceSize = randomIntBetween(2, Math.max(numShards + 1, 3)); - SearchRequest request = client().prepareSearch("test") - .addAggregation(new TestAggregationBuilder("test")) + SearchRequest request = prepareSearch("test").addAggregation(new TestAggregationBuilder("test")) .setBatchedReduceSize(batchReduceSize) .request(); final int index = i; @@ -522,8 +518,7 @@ public void testCircuitBreakerFetchFail() throws Exception { final CountDownLatch latch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { int batchReduceSize = randomIntBetween(2, Math.max(numShards + 1, 3)); - SearchRequest request = client().prepareSearch("boom") - .setBatchedReduceSize(batchReduceSize) + SearchRequest request = prepareSearch("boom").setBatchedReduceSize(batchReduceSize) .setAllowPartialSearchResults(false) .request(); final int index = i; diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/support/master/IndexingMasterFailoverIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/support/master/IndexingMasterFailoverIT.java index 9e50d57f5eb99..837c55e81b471 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/support/master/IndexingMasterFailoverIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/support/master/IndexingMasterFailoverIT.java @@ -97,7 +97,7 @@ public void run() { ensureGreen("myindex"); refresh(); - assertThat(client().prepareSearch("myindex").get().getHits().getTotalHits().value, equalTo(10L)); + assertThat(prepareSearch("myindex").get().getHits().getTotalHits().value, equalTo(10L)); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java index 9aa78f44881e2..978b9dda888f0 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java @@ -269,23 +269,22 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { ).actionGet(); logger.info("--> checking single filtering alias search"); - SearchResponse searchResponse = client().prepareSearch("foos").setQuery(QueryBuilders.matchAllQuery()).get(); + SearchResponse searchResponse = prepareSearch("foos").setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "1"); logger.info("--> checking single filtering alias wildcard search"); - searchResponse = client().prepareSearch("fo*").setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch("fo*").setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "1"); - searchResponse = client().prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "1", "2", "3"); logger.info("--> checking single filtering alias search with sort"); - searchResponse = client().prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).addSort("_index", SortOrder.ASC).get(); + searchResponse = prepareSearch("tests").setQuery(QueryBuilders.matchAllQuery()).addSort("_index", SortOrder.ASC).get(); assertHits(searchResponse.getHits(), "1", "2", "3"); logger.info("--> checking single filtering alias search with global facets"); - searchResponse = client().prepareSearch("tests") - .setQuery(QueryBuilders.matchQuery("name", "bar")) + searchResponse = prepareSearch("tests").setQuery(QueryBuilders.matchQuery("name", "bar")) .addAggregation(AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("test").field("name"))) .get(); assertNoFailures(searchResponse); @@ -294,8 +293,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { assertThat(terms.getBuckets().size(), equalTo(4)); logger.info("--> checking single filtering alias search with global facets and sort"); - searchResponse = client().prepareSearch("tests") - .setQuery(QueryBuilders.matchQuery("name", "bar")) + searchResponse = prepareSearch("tests").setQuery(QueryBuilders.matchQuery("name", "bar")) .addAggregation(AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("test").field("name"))) .addSort("_index", SortOrder.ASC) .get(); @@ -305,8 +303,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { assertThat(terms.getBuckets().size(), equalTo(4)); logger.info("--> checking single filtering alias search with non-global facets"); - searchResponse = client().prepareSearch("tests") - .setQuery(QueryBuilders.matchQuery("name", "bar")) + searchResponse = prepareSearch("tests").setQuery(QueryBuilders.matchQuery("name", "bar")) .addAggregation(AggregationBuilders.terms("test").field("name")) .addSort("_index", SortOrder.ASC) .get(); @@ -318,7 +315,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { assertHits(searchResponse.getHits(), "1", "2"); logger.info("--> checking single non-filtering alias search"); - searchResponse = client().prepareSearch("alias1").setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch("alias1").setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "1", "2", "3", "4"); logger.info("--> checking non-filtering alias and filtering alias search"); @@ -376,18 +373,18 @@ public void testSearchingFilteringAliasesTwoIndices() throws Exception { refresh(); logger.info("--> checking filtering alias for two indices"); - SearchResponse searchResponse = client().prepareSearch("foos").setQuery(QueryBuilders.matchAllQuery()).get(); + SearchResponse searchResponse = prepareSearch("foos").setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "1", "5"); assertThat( - client().prepareSearch("foos").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value, + prepareSearch("foos").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value, equalTo(2L) ); logger.info("--> checking filtering alias for one index"); - searchResponse = client().prepareSearch("bars").setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch("bars").setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "2"); assertThat( - client().prepareSearch("bars").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value, + prepareSearch("bars").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value, equalTo(1L) ); @@ -614,7 +611,7 @@ public void testDeletingByQueryFilteringAliases() throws Exception { logger.info("--> checking counts before delete"); assertThat( - client().prepareSearch("bars").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value, + prepareSearch("bars").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value, equalTo(1L) ); } @@ -1141,8 +1138,8 @@ public void testAliasFilterWithNowInRangeFilterAndQuery() { client().prepareIndex("my-index").setSource("timestamp", "2016-12-12").get(); if (i % 2 == 0) { refresh(); - assertHitCount(client().prepareSearch("filter1"), i); - assertHitCount(client().prepareSearch("filter2"), i); + assertHitCount(prepareSearch("filter1"), i); + assertHitCount(prepareSearch("filter2"), i); } } } @@ -1239,7 +1236,7 @@ public void testRemoveIndexAndReplaceWithAlias() throws InterruptedException, Ex "test_2", () -> assertAcked(indicesAdmin().prepareAliases().addAlias("test_2", "test").removeIndex("test")) ); - assertHitCount(client().prepareSearch("test"), 1); + assertHitCount(prepareSearch("test"), 1); } public void testHiddenAliasesMustBeConsistent() { @@ -1331,22 +1328,21 @@ public void testIndexingAndQueryingHiddenAliases() throws Exception { refresh(writeIndex, nonWriteIndex); // Make sure that the doc written to the alias made it - SearchResponse searchResponse = client().prepareSearch(writeIndex).setQuery(QueryBuilders.matchAllQuery()).get(); + SearchResponse searchResponse = prepareSearch(writeIndex).setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "2", "3"); // Ensure that all docs can be gotten through the alias - searchResponse = client().prepareSearch(alias).setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch(alias).setQuery(QueryBuilders.matchAllQuery()).get(); assertHits(searchResponse.getHits(), "1", "2", "3"); // And querying using a wildcard with indices options set to expand hidden - searchResponse = client().prepareSearch("alias*") - .setQuery(QueryBuilders.matchAllQuery()) + searchResponse = prepareSearch("alias*").setQuery(QueryBuilders.matchAllQuery()) .setIndicesOptions(IndicesOptions.fromOptions(false, false, true, false, true, true, true, false, false)) .get(); assertHits(searchResponse.getHits(), "1", "2", "3"); // And that querying the alias with a wildcard and no expand options fails - searchResponse = client().prepareSearch("alias*").setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch("alias*").setQuery(QueryBuilders.matchAllQuery()).get(); assertThat(searchResponse.getHits().getHits(), emptyArray()); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/blocks/SimpleBlocksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/blocks/SimpleBlocksIT.java index 91253b57f8526..9b78bb9369fd7 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/blocks/SimpleBlocksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/blocks/SimpleBlocksIT.java @@ -265,7 +265,7 @@ public void testAddIndexBlock() throws Exception { } indicesAdmin().prepareRefresh(indexName).get(); - assertHitCount(client().prepareSearch(indexName).setSize(0), nbDocs); + assertHitCount(prepareSearch(indexName).setSize(0), nbDocs); } public void testSameBlockTwice() throws Exception { @@ -390,7 +390,7 @@ public void testAddBlockWhileIndexingDocuments() throws Exception { disableIndexBlock(indexName, block); } refresh(indexName); - assertHitCount(client().prepareSearch(indexName).setSize(0).setTrackTotalHitsUpTo(TRACK_TOTAL_HITS_ACCURATE), nbDocs); + assertHitCount(prepareSearch(indexName).setSize(0).setTrackTotalHitsUpTo(TRACK_TOTAL_HITS_ACCURATE), nbDocs); } public void testAddBlockWhileDeletingIndices() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/broadcast/BroadcastActionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/broadcast/BroadcastActionsIT.java index 1542c2880683b..5e8e6c634fa47 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/broadcast/BroadcastActionsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/broadcast/BroadcastActionsIT.java @@ -42,7 +42,7 @@ public void testBroadcastOperations() throws IOException { // check count for (int i = 0; i < 5; i++) { // test successful - SearchResponse countResponse = client().prepareSearch("test").setSize(0).setQuery(matchAllQuery()).get(); + SearchResponse countResponse = prepareSearch("test").setSize(0).setQuery(matchAllQuery()).get(); assertThat(countResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(countResponse.getTotalShards(), equalTo(numShards.numPrimaries)); assertThat(countResponse.getSuccessfulShards(), equalTo(numShards.numPrimaries)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RareClusterStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RareClusterStateIT.java index df6e6dada3934..3a2c6b5ebd0f7 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RareClusterStateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/RareClusterStateIT.java @@ -213,7 +213,7 @@ public void testDeleteCreateInOneBulk() throws Exception { long dataClusterStateVersion = internalCluster().clusterService(dataNode).state().version(); assertThat(masterClusterStateVersion, equalTo(dataClusterStateVersion)); }); - assertHitCount(client().prepareSearch("test"), 0); + assertHitCount(prepareSearch("test"), 0); } public void testDelayedMappingPropagationOnPrimary() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java index 0bcf49bdd2ae2..0f42b5999920a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java @@ -498,7 +498,7 @@ public void testSearchRouting() throws Exception { } // Regular search for (int i = 0; i < 10; i++) { - final var search = client().prepareSearch(INDEX_NAME).setProfile(true); + final var search = prepareSearch(INDEX_NAME).setProfile(true); switch (randomIntBetween(0, 2)) { case 0 -> search.setRouting(randomAlphaOfLength(10)); case 1 -> search.setPreference(randomSearchPreference(routingTableWatcher.numShards, internalCluster().getNodeNames())); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java index 8b4a82cc36c20..965674b772998 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java @@ -195,7 +195,7 @@ public void testAutomaticReleaseOfIndexBlock() throws Exception { } client().prepareIndex("test").setId("1").setSource("foo", "bar").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); - assertSearchHits(client().prepareSearch("test"), "1"); + assertSearchHits(prepareSearch("test"), "1"); // Move all nodes above the low watermark so no shard movement can occur, and at least one node above the flood stage watermark so // the index is blocked @@ -221,7 +221,7 @@ public void testAutomaticReleaseOfIndexBlock() throws Exception { client().prepareIndex().setIndex("test").setId("2").setSource("foo", "bar"), IndexMetadata.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK ); - assertSearchHits(client().prepareSearch("test"), "1"); + assertSearchHits(prepareSearch("test"), "1"); logger.info("--> index is confirmed read-only, releasing disk space"); @@ -240,7 +240,7 @@ public void testAutomaticReleaseOfIndexBlock() throws Exception { throw new AssertionError("retrying", e); } }); - assertSearchHits(client().prepareSearch("test"), "1", "3"); + assertSearchHits(prepareSearch("test"), "1", "3"); } public void testOnlyMovesEnoughShardsToDropBelowHighWatermark() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/document/DocumentActionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/document/DocumentActionsIT.java index 828ae839cdd7d..42bc0f19bf757 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/document/DocumentActionsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/document/DocumentActionsIT.java @@ -154,14 +154,14 @@ public void testIndexActions() throws Exception { // check count for (int i = 0; i < 5; i++) { // test successful - SearchResponse countResponse = client().prepareSearch("test").setSize(0).setQuery(matchAllQuery()).execute().actionGet(); + SearchResponse countResponse = prepareSearch("test").setSize(0).setQuery(matchAllQuery()).execute().actionGet(); assertNoFailures(countResponse); assertThat(countResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(countResponse.getSuccessfulShards(), equalTo(numShards.numPrimaries)); assertThat(countResponse.getFailedShards(), equalTo(0)); // count with no query is a match all one - countResponse = client().prepareSearch("test").setSize(0).execute().actionGet(); + countResponse = prepareSearch("test").setSize(0).execute().actionGet(); assertThat( "Failures " + countResponse.getShardFailures(), countResponse.getShardFailures() == null ? 0 : countResponse.getShardFailures().length, diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java index ffa6799601b14..ab4706c865be9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java @@ -139,7 +139,7 @@ public void testFinalPipelineOfOldDestinationIsNotInvoked() { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); assertEquals(RestStatus.CREATED, indexResponse.status()); - SearchResponse target = client().prepareSearch("target").get(); + SearchResponse target = prepareSearch("target").get(); assertEquals(1, target.getHits().getTotalHits().value); assertFalse(target.getHits().getAt(0).getSourceAsMap().containsKey("final")); } @@ -165,7 +165,7 @@ public void testFinalPipelineOfNewDestinationIsInvoked() { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); assertEquals(RestStatus.CREATED, indexResponse.status()); - SearchResponse target = client().prepareSearch("target").get(); + SearchResponse target = prepareSearch("target").get(); assertEquals(1, target.getHits().getTotalHits().value); assertEquals(true, target.getHits().getAt(0).getSourceAsMap().get("final")); } @@ -191,7 +191,7 @@ public void testDefaultPipelineOfNewDestinationIsNotInvoked() { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); assertEquals(RestStatus.CREATED, indexResponse.status()); - SearchResponse target = client().prepareSearch("target").get(); + SearchResponse target = prepareSearch("target").get(); assertEquals(1, target.getHits().getTotalHits().value); assertFalse(target.getHits().getAt(0).getSourceAsMap().containsKey("final")); } @@ -217,7 +217,7 @@ public void testDefaultPipelineOfRerouteDestinationIsInvoked() { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); assertEquals(RestStatus.CREATED, indexResponse.status()); - SearchResponse target = client().prepareSearch("target").get(); + SearchResponse target = prepareSearch("target").get(); assertEquals(1, target.getHits().getTotalHits().value); assertTrue(target.getHits().getAt(0).getSourceAsMap().containsKey("final")); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java index ae1b68913460c..63e5c7acc67ba 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java @@ -40,21 +40,19 @@ public void testHiddenIndexSearch() { client().prepareIndex("hidden-index").setSource("foo", "bar").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); // default not visible to wildcard expansion - SearchResponse searchResponse = client().prepareSearch(randomFrom("*", "_all", "h*", "*index")) - .setSize(1000) + SearchResponse searchResponse = prepareSearch(randomFrom("*", "_all", "h*", "*index")).setSize(1000) .setQuery(QueryBuilders.matchAllQuery()) .get(); boolean matchedHidden = Arrays.stream(searchResponse.getHits().getHits()).anyMatch(hit -> "hidden-index".equals(hit.getIndex())); assertFalse(matchedHidden); // direct access allowed - searchResponse = client().prepareSearch("hidden-index").setSize(1000).setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch("hidden-index").setSize(1000).setQuery(QueryBuilders.matchAllQuery()).get(); matchedHidden = Arrays.stream(searchResponse.getHits().getHits()).anyMatch(hit -> "hidden-index".equals(hit.getIndex())); assertTrue(matchedHidden); // with indices option to include hidden - searchResponse = client().prepareSearch(randomFrom("*", "_all", "h*", "*index")) - .setSize(1000) + searchResponse = prepareSearch(randomFrom("*", "_all", "h*", "*index")).setSize(1000) .setQuery(QueryBuilders.matchAllQuery()) .setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_HIDDEN) .get(); @@ -64,16 +62,13 @@ public void testHiddenIndexSearch() { // implicit based on use of pattern starting with . and a wildcard assertAcked(indicesAdmin().prepareCreate(".hidden-index").setSettings(Settings.builder().put("index.hidden", true).build()).get()); client().prepareIndex(".hidden-index").setSource("foo", "bar").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); - searchResponse = client().prepareSearch(randomFrom(".*", ".hidden-*")).setSize(1000).setQuery(QueryBuilders.matchAllQuery()).get(); + searchResponse = prepareSearch(randomFrom(".*", ".hidden-*")).setSize(1000).setQuery(QueryBuilders.matchAllQuery()).get(); matchedHidden = Arrays.stream(searchResponse.getHits().getHits()).anyMatch(hit -> ".hidden-index".equals(hit.getIndex())); assertTrue(matchedHidden); // make index not hidden updateIndexSettings(Settings.builder().put("index.hidden", false), "hidden-index"); - searchResponse = client().prepareSearch(randomFrom("*", "_all", "h*", "*index")) - .setSize(1000) - .setQuery(QueryBuilders.matchAllQuery()) - .get(); + searchResponse = prepareSearch(randomFrom("*", "_all", "h*", "*index")).setSize(1000).setQuery(QueryBuilders.matchAllQuery()).get(); matchedHidden = Arrays.stream(searchResponse.getHits().getHits()).anyMatch(hit -> "hidden-index".equals(hit.getIndex())); assertTrue(matchedHidden); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/IndexRequestBuilderIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/IndexRequestBuilderIT.java index b69bb7b42fa19..31368a3cfb8fd 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/IndexRequestBuilderIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/IndexRequestBuilderIT.java @@ -37,7 +37,7 @@ public void testSetSource() throws InterruptedException, ExecutionException { client().prepareIndex("test").setSource(map) }; indexRandom(true, builders); ElasticsearchAssertions.assertHitCount( - client().prepareSearch("test").setQuery(QueryBuilders.termQuery("test_field", "foobar")), + prepareSearch("test").setQuery(QueryBuilders.termQuery("test_field", "foobar")), builders.length ); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java index 09290f40d6c29..ec03a740f8ade 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/WaitUntilRefreshIT.java @@ -63,25 +63,25 @@ public void testIndex() { .get(); assertEquals(RestStatus.CREATED, index.status()); assertFalse("request shouldn't have forced a refresh", index.forcedRefresh()); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); } public void testDelete() throws InterruptedException, ExecutionException { // Index normally indexRandom(true, client().prepareIndex("test").setId("1").setSource("foo", "bar")); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); // Now delete with blockUntilRefresh DeleteResponse delete = client().prepareDelete("test", "1").setRefreshPolicy(RefreshPolicy.WAIT_UNTIL).get(); assertEquals(DocWriteResponse.Result.DELETED, delete.getResult()); assertFalse("request shouldn't have forced a refresh", delete.forcedRefresh()); - assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar"))); + assertNoSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar"))); } public void testUpdate() throws InterruptedException, ExecutionException { // Index normally indexRandom(true, client().prepareIndex("test").setId("1").setSource("foo", "bar")); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); // Update with RefreshPolicy.WAIT_UNTIL UpdateResponse update = client().prepareUpdate("test", "1") @@ -90,7 +90,7 @@ public void testUpdate() throws InterruptedException, ExecutionException { .get(); assertEquals(2, update.getVersion()); assertFalse("request shouldn't have forced a refresh", update.forcedRefresh()); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "baz")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "baz")), "1"); // Upsert with RefreshPolicy.WAIT_UNTIL update = client().prepareUpdate("test", "2") @@ -100,7 +100,7 @@ public void testUpdate() throws InterruptedException, ExecutionException { .get(); assertEquals(1, update.getVersion()); assertFalse("request shouldn't have forced a refresh", update.forcedRefresh()); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "cat")), "2"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "cat")), "2"); // Update-becomes-delete with RefreshPolicy.WAIT_UNTIL update = client().prepareUpdate("test", "2") @@ -109,7 +109,7 @@ public void testUpdate() throws InterruptedException, ExecutionException { .get(); assertEquals(2, update.getVersion()); assertFalse("request shouldn't have forced a refresh", update.forcedRefresh()); - assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "cat"))); + assertNoSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "cat"))); } public void testBulk() { @@ -117,19 +117,19 @@ public void testBulk() { BulkRequestBuilder bulk = client().prepareBulk().setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); bulk.add(client().prepareIndex("test").setId("1").setSource("foo", "bar")); assertBulkSuccess(bulk.get()); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); // Update by bulk with RefreshPolicy.WAIT_UNTIL bulk = client().prepareBulk().setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); bulk.add(client().prepareUpdate("test", "1").setDoc(Requests.INDEX_CONTENT_TYPE, "foo", "baz")); assertBulkSuccess(bulk.get()); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "baz")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "baz")), "1"); // Delete by bulk with RefreshPolicy.WAIT_UNTIL bulk = client().prepareBulk().setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); bulk.add(client().prepareDelete("test", "1")); assertBulkSuccess(bulk.get()); - assertNoSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar"))); + assertNoSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar"))); // Update makes a noop bulk = client().prepareBulk().setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); @@ -153,7 +153,7 @@ public void testNoRefreshInterval() throws InterruptedException, ExecutionExcept } assertEquals(RestStatus.CREATED, index.get().status()); assertFalse("request shouldn't have forced a refresh", index.get().forcedRefresh()); - assertSearchHits(client().prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); + assertSearchHits(prepareSearch("test").setQuery(matchQuery("foo", "bar")), "1"); } private void assertBulkSuccess(BulkResponse response) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/engine/MaxDocsLimitIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/engine/MaxDocsLimitIT.java index 56e1598bd7a15..f297b61e7087d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/engine/MaxDocsLimitIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/engine/MaxDocsLimitIT.java @@ -108,8 +108,7 @@ public void testMaxDocsLimit() throws Exception { ); assertThat(deleteError.getMessage(), containsString("Number of documents in the index can't exceed [" + maxDocs.get() + "]")); indicesAdmin().prepareRefresh("test").get(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(new MatchAllQueryBuilder()) + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchAllQueryBuilder()) .setTrackTotalHitsUpTo(Integer.MAX_VALUE) .setSize(0) .get(); @@ -121,8 +120,7 @@ public void testMaxDocsLimit() throws Exception { internalCluster().fullRestart(); internalCluster().ensureAtLeastNumDataNodes(2); ensureGreen("test"); - searchResponse = client().prepareSearch("test") - .setQuery(new MatchAllQueryBuilder()) + searchResponse = prepareSearch("test").setQuery(new MatchAllQueryBuilder()) .setTrackTotalHitsUpTo(Integer.MAX_VALUE) .setSize(0) .get(); @@ -137,8 +135,7 @@ public void testMaxDocsLimitConcurrently() throws Exception { assertThat(indexingResult.numFailures, greaterThan(0)); assertThat(indexingResult.numSuccess, both(greaterThan(0)).and(lessThanOrEqualTo(maxDocs.get()))); indicesAdmin().prepareRefresh("test").get(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(new MatchAllQueryBuilder()) + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchAllQueryBuilder()) .setTrackTotalHitsUpTo(Integer.MAX_VALUE) .setSize(0) .get(); @@ -155,8 +152,7 @@ public void testMaxDocsLimitConcurrently() throws Exception { assertThat(indexingResult.numSuccess, equalTo(0)); } indicesAdmin().prepareRefresh("test").get(); - searchResponse = client().prepareSearch("test") - .setQuery(new MatchAllQueryBuilder()) + searchResponse = prepareSearch("test").setQuery(new MatchAllQueryBuilder()) .setTrackTotalHitsUpTo(Integer.MAX_VALUE) .setSize(0) .get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java index c6055a295eabd..7a93c315a84be 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java @@ -37,8 +37,7 @@ public void testDynamicTemplateCopyTo() throws Exception { SubAggCollectionMode aggCollectionMode = randomFrom(SubAggCollectionMode.values()); - SearchResponse response = client().prepareSearch("test-idx") - .setQuery(QueryBuilders.termQuery("even", true)) + SearchResponse response = prepareSearch("test-idx").setQuery(QueryBuilders.termQuery("even", true)) .addAggregation(AggregationBuilders.terms("test").field("test_field").size(recordCount * 2).collectMode(aggCollectionMode)) .addAggregation( AggregationBuilders.terms("test_raw").field("test_field_raw").size(recordCount * 2).collectMode(aggCollectionMode) @@ -67,7 +66,7 @@ public void testDynamicObjectCopyTo() throws Exception { assertAcked(indicesAdmin().prepareCreate("test-idx").setMapping(mapping)); client().prepareIndex("test-idx").setId("1").setSource("foo", "bar").get(); indicesAdmin().prepareRefresh("test-idx").execute().actionGet(); - SearchResponse response = client().prepareSearch("test-idx").setQuery(QueryBuilders.termQuery("root.top.child", "bar")).get(); + SearchResponse response = prepareSearch("test-idx").setQuery(QueryBuilders.termQuery("root.top.child", "bar")).get(); assertThat(response.getHits().getTotalHits().value, equalTo(1L)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java index be2cd1eeb3581..5760b8bebf7c9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java @@ -372,15 +372,17 @@ public void testBulkRequestWithDynamicTemplates() throws Exception { assertFalse(bulkResponse.hasFailures()); assertSearchHits( - client().prepareSearch("test") - .setQuery(new GeoBoundingBoxQueryBuilder("location").setCorners(new GeoPoint(42, -72), new GeoPoint(40, -74))), + prepareSearch("test").setQuery( + new GeoBoundingBoxQueryBuilder("location").setCorners(new GeoPoint(42, -72), new GeoPoint(40, -74)) + ), "1", "2", "4" ); assertSearchHits( - client().prepareSearch("test") - .setQuery(new GeoBoundingBoxQueryBuilder("address.location").setCorners(new GeoPoint(42, -72), new GeoPoint(40, -74))), + prepareSearch("test").setQuery( + new GeoBoundingBoxQueryBuilder("address.location").setCorners(new GeoPoint(42, -72), new GeoPoint(40, -74)) + ), "3" ); } @@ -460,15 +462,15 @@ public void testDynamicRuntimeNoConflicts() { assertFalse(bulkItemResponses.buildFailureMessage(), bulkItemResponses.hasFailures()); { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(new MatchQueryBuilder("one", "one")).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("one", "one")).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(new MatchQueryBuilder("one.two", 3.5)).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("one.two", 3.5)).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(new MatchQueryBuilder("one.two.three", "1")).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("one.two.three", "1")).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } } @@ -508,21 +510,19 @@ public void testDynamicRuntimeObjectFields() { assertFalse(bulkItemResponses.buildFailureMessage(), bulkItemResponses.hasFailures()); { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(new MatchQueryBuilder("obj.one", 1)).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.one", 1)).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(new MatchQueryBuilder("anything", "anything")).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("anything", "anything")).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one", "one")).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one", "one")).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(new MatchQueryBuilder("obj.runtime.one.two", "1")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one.two", "1")).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } @@ -569,9 +569,7 @@ public void testDynamicRuntimeObjectFields() { ); { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(new MatchQueryBuilder("obj.runtime.dynamic.number", 1)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.dynamic.number", 1)).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java index d6eeee2206585..64a4f8d30e600 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java @@ -47,9 +47,9 @@ public void testMultiFields() throws Exception { client().prepareIndex("my-index").setId("1").setSource("title", "Multi fields").setRefreshPolicy(IMMEDIATE).get(); - SearchResponse searchResponse = client().prepareSearch("my-index").setQuery(matchQuery("title", "multi")).get(); + SearchResponse searchResponse = prepareSearch("my-index").setQuery(matchQuery("title", "multi")).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("my-index").setQuery(matchQuery("title.not_analyzed", "Multi fields")).get(); + searchResponse = prepareSearch("my-index").setQuery(matchQuery("title.not_analyzed", "Multi fields")).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertAcked(indicesAdmin().preparePutMapping("my-index").setSource(createPutMappingSource())); @@ -68,7 +68,7 @@ public void testMultiFields() throws Exception { client().prepareIndex("my-index").setId("1").setSource("title", "Multi fields").setRefreshPolicy(IMMEDIATE).get(); - searchResponse = client().prepareSearch("my-index").setQuery(matchQuery("title.uncased", "Multi")).get(); + searchResponse = prepareSearch("my-index").setQuery(matchQuery("title.uncased", "Multi")).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); } @@ -92,12 +92,11 @@ public void testGeoPointMultiField() throws Exception { GeoPoint point = new GeoPoint(51, 19); client().prepareIndex("my-index").setId("1").setSource("a", point.toString()).setRefreshPolicy(IMMEDIATE).get(); - SearchResponse countResponse = client().prepareSearch("my-index") - .setSize(0) + SearchResponse countResponse = prepareSearch("my-index").setSize(0) .setQuery(constantScoreQuery(geoDistanceQuery("a").point(51, 19).distance(50, DistanceUnit.KILOMETERS))) .get(); assertThat(countResponse.getHits().getTotalHits().value, equalTo(1L)); - countResponse = client().prepareSearch("my-index").setSize(0).setQuery(matchQuery("a.b", point.geohash())).get(); + countResponse = prepareSearch("my-index").setSize(0).setQuery(matchQuery("a.b", point.geohash())).get(); assertThat(countResponse.getHits().getTotalHits().value, equalTo(1L)); } @@ -119,7 +118,7 @@ public void testCompletionMultiField() throws Exception { assertThat(bField.get("type").toString(), equalTo("keyword")); client().prepareIndex("my-index").setId("1").setSource("a", "complete me").setRefreshPolicy(IMMEDIATE).get(); - SearchResponse countResponse = client().prepareSearch("my-index").setSize(0).setQuery(matchQuery("a.b", "complete me")).get(); + SearchResponse countResponse = prepareSearch("my-index").setSize(0).setQuery(matchQuery("a.b", "complete me")).get(); assertThat(countResponse.getHits().getTotalHits().value, equalTo(1L)); } @@ -141,7 +140,7 @@ public void testIpMultiField() throws Exception { assertThat(bField.get("type").toString(), equalTo("keyword")); client().prepareIndex("my-index").setId("1").setSource("a", "127.0.0.1").setRefreshPolicy(IMMEDIATE).get(); - SearchResponse countResponse = client().prepareSearch("my-index").setSize(0).setQuery(matchQuery("a.b", "127.0.0.1")).get(); + SearchResponse countResponse = prepareSearch("my-index").setSize(0).setQuery(matchQuery("a.b", "127.0.0.1")).get(); assertThat(countResponse.getHits().getTotalHits().value, equalTo(1L)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/query/plugin/CustomQueryParserIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/query/plugin/CustomQueryParserIT.java index 5526343619e16..bb20ddd321d7c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/query/plugin/CustomQueryParserIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/query/plugin/CustomQueryParserIT.java @@ -42,10 +42,10 @@ protected int numberOfShards() { } public void testCustomDummyQuery() { - assertHitCount(client().prepareSearch("index").setQuery(new DummyQueryBuilder()), 1L); + assertHitCount(prepareSearch("index").setQuery(new DummyQueryBuilder()), 1L); } public void testCustomDummyQueryWithinBooleanQuery() { - assertHitCount(client().prepareSearch("index").setQuery(new BoolQueryBuilder().must(new DummyQueryBuilder())), 1L); + assertHitCount(prepareSearch("index").setQuery(new BoolQueryBuilder().must(new DummyQueryBuilder())), 1L); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/search/MatchPhraseQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/search/MatchPhraseQueryIT.java index 369e6cef28970..7751d5e7783b9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/search/MatchPhraseQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/search/MatchPhraseQueryIT.java @@ -47,10 +47,10 @@ public void testZeroTermsQuery() throws ExecutionException, InterruptedException MatchPhraseQueryBuilder baseQuery = matchPhraseQuery("name", "the who").analyzer("standard_stopwords"); MatchPhraseQueryBuilder matchNoneQuery = baseQuery.zeroTermsQuery(ZeroTermsQueryOption.NONE); - assertHitCount(client().prepareSearch(INDEX).setQuery(matchNoneQuery), 0L); + assertHitCount(prepareSearch(INDEX).setQuery(matchNoneQuery), 0L); MatchPhraseQueryBuilder matchAllQuery = baseQuery.zeroTermsQuery(ZeroTermsQueryOption.ALL); - assertHitCount(client().prepareSearch(INDEX).setQuery(matchAllQuery), 2L); + assertHitCount(prepareSearch(INDEX).setQuery(matchAllQuery), 2L); } private List getIndexRequests() { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java index 345f2a82d82c4..d57cbe50074ac 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/RemoveCorruptedShardDataCommandIT.java @@ -255,7 +255,7 @@ public Settings onNodeStopped(String nodeName) throws Exception { ensureGreen(indexName); - assertHitCount(client().prepareSearch(indexName).setQuery(matchAllQuery()), expectedNumDocs); + assertHitCount(prepareSearch(indexName).setQuery(matchAllQuery()), expectedNumDocs); } public void testCorruptTranslogTruncation() throws Exception { @@ -424,13 +424,13 @@ public Settings onNodeStopped(String nodeName) throws Exception { ensureYellow(indexName); // Run a search and make sure it succeeds - assertHitCount(client().prepareSearch(indexName).setQuery(matchAllQuery()), numDocsToKeep); + assertHitCount(prepareSearch(indexName).setQuery(matchAllQuery()), numDocsToKeep); logger.info("--> starting the replica node to test recovery"); internalCluster().startNode(node2PathSettings); ensureGreen(indexName); for (String node : internalCluster().nodesInclude(indexName)) { - SearchRequestBuilder q = client().prepareSearch(indexName).setPreference("_only_nodes:" + node).setQuery(matchAllQuery()); + SearchRequestBuilder q = prepareSearch(indexName).setPreference("_only_nodes:" + node).setQuery(matchAllQuery()); assertHitCount(q, numDocsToKeep); } final RecoveryResponse recoveryResponse = indicesAdmin().prepareRecoveries(indexName).setActiveOnly(false).get(); @@ -513,7 +513,7 @@ public void testCorruptTranslogTruncationOfReplica() throws Exception { ensureYellow(); // Run a search and make sure it succeeds - assertHitCount(client().prepareSearch(indexName).setQuery(matchAllQuery()), totalDocs); + assertHitCount(prepareSearch(indexName).setQuery(matchAllQuery()), totalDocs); // check replica corruption final RemoveCorruptedShardDataCommand command = new RemoveCorruptedShardDataCommand(); @@ -534,7 +534,7 @@ public void testCorruptTranslogTruncationOfReplica() throws Exception { internalCluster().startNode(node2PathSettings); ensureGreen(indexName); for (String node : internalCluster().nodesInclude(indexName)) { - assertHitCount(client().prepareSearch(indexName).setPreference("_only_nodes:" + node).setQuery(matchAllQuery()), totalDocs); + assertHitCount(prepareSearch(indexName).setPreference("_only_nodes:" + node).setQuery(matchAllQuery()), totalDocs); } final RecoveryResponse recoveryResponse = indicesAdmin().prepareRecoveries(indexName).setActiveOnly(false).get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedTranslogIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedTranslogIT.java index 8731c319043a8..b8ecbc2e750af 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedTranslogIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedTranslogIT.java @@ -97,8 +97,7 @@ public void onAllNodesStopped() throws Exception { }); assertThat( - expectThrows(SearchPhaseExecutionException.class, () -> client().prepareSearch("test").setQuery(matchAllQuery()).get()) - .getMessage(), + expectThrows(SearchPhaseExecutionException.class, () -> prepareSearch("test").setQuery(matchAllQuery()).get()).getMessage(), containsString("all shards failed") ); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java index b1b3a6850c061..2ea5be52cb51d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java @@ -107,7 +107,7 @@ public void testRetryDueToExceptionOnNetworkLayer() throws ExecutionException, I } refresh(); - SearchResponse searchResponse = client().prepareSearch("index").setSize(numDocs * 2).addStoredField("_id").get(); + SearchResponse searchResponse = prepareSearch("index").setSize(numDocs * 2).addStoredField("_id").get(); Set uniqueIds = new HashSet<>(); long dupCounter = 0; @@ -115,10 +115,9 @@ public void testRetryDueToExceptionOnNetworkLayer() throws ExecutionException, I for (int i = 0; i < searchResponse.getHits().getHits().length; i++) { if (uniqueIds.add(searchResponse.getHits().getHits()[i].getId()) == false) { if (found_duplicate_already == false) { - SearchResponse dupIdResponse = client().prepareSearch("index") - .setQuery(termQuery("_id", searchResponse.getHits().getHits()[i].getId())) - .setExplain(true) - .get(); + SearchResponse dupIdResponse = prepareSearch("index").setQuery( + termQuery("_id", searchResponse.getHits().getHits()[i].getId()) + ).setExplain(true).get(); assertThat(dupIdResponse.getHits().getTotalHits().value, greaterThan(1L)); logger.info("found a duplicate id:"); for (SearchHit hit : dupIdResponse.getHits()) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indexing/IndexActionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indexing/IndexActionIT.java index 6c1b9a56f04cf..e3c66f3dabfdf 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indexing/IndexActionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indexing/IndexActionIT.java @@ -55,7 +55,7 @@ public void testAutoGenerateIdNoDuplicates() throws Exception { for (int j = 0; j < numOfChecks; j++) { try { logger.debug("running search with all types"); - SearchResponse response = client().prepareSearch("test").get(); + SearchResponse response = prepareSearch("test").get(); if (response.getHits().getTotalHits().value != numOfDocs) { final String message = "Count is " + response.getHits().getTotalHits().value @@ -74,7 +74,7 @@ public void testAutoGenerateIdNoDuplicates() throws Exception { } try { logger.debug("running search with a specific type"); - SearchResponse response = client().prepareSearch("test").get(); + SearchResponse response = prepareSearch("test").get(); if (response.getHits().getTotalHits().value != numOfDocs) { final String message = "Count is " + response.getHits().getTotalHits().value diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java index 9f5138a1a0d3c..e465643143265 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java @@ -395,7 +395,7 @@ public void testWildcardBehaviourSnapshotRestore() throws Exception { public void testAllMissingLenient() throws Exception { createIndex("test1"); client().prepareIndex("test1").setId("1").setSource("k", "v").setRefreshPolicy(IMMEDIATE).get(); - assertHitCount(client().prepareSearch("test2").setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(matchAllQuery()), 0L); + assertHitCount(prepareSearch("test2").setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(matchAllQuery()), 0L); assertHitCount( client().prepareSearch("test2", "test3").setQuery(matchAllQuery()).setIndicesOptions(IndicesOptions.lenientExpandOpen()), 0L @@ -406,7 +406,7 @@ public void testAllMissingLenient() throws Exception { public void testAllMissingStrict() throws Exception { createIndex("test1"); - expectThrows(IndexNotFoundException.class, () -> client().prepareSearch("test2").setQuery(matchAllQuery()).execute().actionGet()); + expectThrows(IndexNotFoundException.class, () -> prepareSearch("test2").setQuery(matchAllQuery()).execute().actionGet()); expectThrows( IndexNotFoundException.class, @@ -606,7 +606,7 @@ public void testUpdateSettings() throws Exception { } static SearchRequestBuilder search(String... indices) { - return client().prepareSearch(indices).setQuery(matchAllQuery()); + return prepareSearch(indices).setQuery(matchAllQuery()); } static MultiSearchRequestBuilder msearch(IndicesOptions options, String... indices) { @@ -614,7 +614,7 @@ static MultiSearchRequestBuilder msearch(IndicesOptions options, String... indic if (options != null) { multiSearchRequestBuilder.setIndicesOptions(options); } - return multiSearchRequestBuilder.add(client().prepareSearch(indices).setQuery(matchAllQuery())); + return multiSearchRequestBuilder.add(prepareSearch(indices).setQuery(matchAllQuery())); } static ClearIndicesCacheRequestBuilder clearCache(String... indices) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/ConcurrentDynamicTemplateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/ConcurrentDynamicTemplateIT.java index ce38fed6a7069..7541bce29fbe9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/ConcurrentDynamicTemplateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/ConcurrentDynamicTemplateIT.java @@ -77,8 +77,8 @@ public void onFailure(Exception e) { latch.await(); assertThat(throwable, emptyIterable()); refresh(); - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.matchQuery(fieldName, "test-user")), numDocs); - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.matchQuery(fieldName, "test user")), 0); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.matchQuery(fieldName, "test-user")), numDocs); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.matchQuery(fieldName, "test user")), 0); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java index ce916541dfaa5..c3f7fbd86f1bf 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java @@ -60,7 +60,7 @@ public void testBWCMalformedDynamicTemplate() { ); client().prepareIndex(indexName).setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); assertNoFailures((indicesAdmin().prepareRefresh(indexName)).get()); - assertHitCount(client().prepareSearch(indexName), 1); + assertHitCount(prepareSearch(indexName), 1); MapperParsingException ex = expectThrows( MapperParsingException.class, diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java index 5e1e81945c177..0eca3d689903e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/UpdateMappingIntegrationIT.java @@ -86,7 +86,7 @@ public void testDynamicUpdates() throws Exception { logger.info("checking all the documents are there"); RefreshResponse refreshResponse = indicesAdmin().prepareRefresh().execute().actionGet(); assertThat(refreshResponse.getFailedShards(), equalTo(0)); - SearchResponse response = client().prepareSearch("test").setSize(0).execute().actionGet(); + SearchResponse response = prepareSearch("test").setSize(0).execute().actionGet(); assertThat(response.getHits().getTotalHits().value, equalTo((long) recCount)); logger.info("checking all the fields are in the mappings"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexPrimaryRelocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexPrimaryRelocationIT.java index f82ea1d893393..cdd77d5864a7b 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexPrimaryRelocationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexPrimaryRelocationIT.java @@ -105,10 +105,9 @@ public void run() { finished.set(true); indexingThread.join(); refresh("test"); - ElasticsearchAssertions.assertHitCount(client().prepareSearch("test").setTrackTotalHits(true), numAutoGenDocs.get()); + ElasticsearchAssertions.assertHitCount(prepareSearch("test").setTrackTotalHits(true), numAutoGenDocs.get()); ElasticsearchAssertions.assertHitCount( - client().prepareSearch("test") - .setTrackTotalHits(true)// extra paranoia ;) + prepareSearch("test").setTrackTotalHits(true)// extra paranoia ;) .setQuery(QueryBuilders.termQuery("auto", true)), numAutoGenDocs.get() ); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index e44db1d86619a..7ab3cecb95d86 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -394,7 +394,7 @@ public void testReplicaRecovery() throws Exception { } refresh(INDEX_NAME); - assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0), numOfDocs); + assertHitCount(prepareSearch(INDEX_NAME).setSize(0), numOfDocs); final boolean closedIndex = randomBoolean(); if (closedIndex) { @@ -439,7 +439,7 @@ public void testReplicaRecovery() throws Exception { if (closedIndex) { assertAcked(indicesAdmin().prepareOpen(INDEX_NAME)); } - assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0), numOfDocs); + assertHitCount(prepareSearch(INDEX_NAME).setSize(0), numOfDocs); } public void testCancelNewShardRecoveryAndUsesExistingShardCopy() throws Exception { @@ -925,7 +925,7 @@ private IndicesStatsResponse createAndPopulateIndex(String name, int nodeCount, indexRandom(true, docs); flush(); - assertThat(client().prepareSearch(name).setSize(0).get().getHits().getTotalHits().value, equalTo((long) numDocs)); + assertThat(prepareSearch(name).setSize(0).get().getHits().getTotalHits().value, equalTo((long) numDocs)); return indicesAdmin().prepareStats(name).execute().actionGet(); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/ReplicaToPrimaryPromotionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/ReplicaToPrimaryPromotionIT.java index 67b885e315ae9..8595f11bae428 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/ReplicaToPrimaryPromotionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/ReplicaToPrimaryPromotionIT.java @@ -43,7 +43,7 @@ public void testPromoteReplicaToPrimary() throws Exception { refresh(indexName); } - assertHitCount(client().prepareSearch(indexName).setSize(0), numOfDocs); + assertHitCount(prepareSearch(indexName).setSize(0), numOfDocs); ensureGreen(indexName); // sometimes test with a closed index @@ -76,6 +76,6 @@ public void testPromoteReplicaToPrimary() throws Exception { assertAcked(indicesAdmin().prepareOpen(indexName)); ensureYellowAndNoInitializingShards(indexName); } - assertHitCount(client().prepareSearch(indexName).setSize(0), numOfDocs); + assertHitCount(prepareSearch(indexName).setSize(0), numOfDocs); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseIndexIT.java index fd9bfdd9e0138..91425067bd817 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseIndexIT.java @@ -132,7 +132,7 @@ public void testCloseIndex() throws Exception { assertIndexIsClosed(indexName); assertAcked(indicesAdmin().prepareOpen(indexName)); - assertHitCount(client().prepareSearch(indexName).setSize(0), nbDocs); + assertHitCount(prepareSearch(indexName).setSize(0), nbDocs); } public void testCloseAlreadyClosedIndex() throws Exception { @@ -244,7 +244,7 @@ public void testCloseWhileIndexingDocuments() throws Exception { assertIndexIsClosed(indexName); assertAcked(indicesAdmin().prepareOpen(indexName)); - assertHitCount(client().prepareSearch(indexName).setSize(0).setTrackTotalHitsUpTo(TRACK_TOTAL_HITS_ACCURATE), nbDocs); + assertHitCount(prepareSearch(indexName).setSize(0).setTrackTotalHitsUpTo(TRACK_TOTAL_HITS_ACCURATE), nbDocs); } public void testCloseWhileDeletingIndices() throws Exception { @@ -348,10 +348,7 @@ public void testConcurrentClosesAndOpens() throws Exception { } refresh(indexName); assertIndexIsOpened(indexName); - assertHitCount( - client().prepareSearch(indexName).setSize(0).setTrackTotalHitsUpTo(TRACK_TOTAL_HITS_ACCURATE), - indexer.totalIndexedDocs() - ); + assertHitCount(prepareSearch(indexName).setSize(0).setTrackTotalHitsUpTo(TRACK_TOTAL_HITS_ACCURATE), indexer.totalIndexedDocs()); } public void testCloseIndexWaitForActiveShards() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java index b67098c8f37a7..5fa512eafc45f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java @@ -246,7 +246,7 @@ public void testCloseWhileRelocatingShards() throws Exception { ensureGreen(indices); for (String index : acknowledgedCloses) { - long docsCount = client().prepareSearch(index).setSize(0).setTrackTotalHits(true).get().getHits().getTotalHits().value; + long docsCount = prepareSearch(index).setSize(0).setTrackTotalHits(true).get().getHits().getTotalHits().value; assertEquals( "Expected " + docsPerIndex.get(index) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java index 494d9c4b0a6f3..d4d3596349b93 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java @@ -374,7 +374,7 @@ public void testQueryCache() throws Exception { assertThat(indicesAdmin().prepareStats("idx").setRequestCache(true).get().getTotal().getRequestCache().getMissCount(), equalTo(0L)); for (int i = 0; i < 10; i++) { assertThat( - client().prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH).setSize(0).get().getHits().getTotalHits().value, + prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH).setSize(0).get().getHits().getTotalHits().value, equalTo((long) numDocs) ); assertThat( @@ -409,7 +409,7 @@ public void testQueryCache() throws Exception { for (int i = 0; i < 10; i++) { assertThat( - client().prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH).setSize(0).get().getHits().getTotalHits().value, + prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH).setSize(0).get().getHits().getTotalHits().value, equalTo((long) numDocs) ); assertThat( @@ -427,8 +427,7 @@ public void testQueryCache() throws Exception { // test explicit request parameter assertThat( - client().prepareSearch("idx") - .setSearchType(SearchType.QUERY_THEN_FETCH) + prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH) .setSize(0) .setRequestCache(false) .get() @@ -442,8 +441,7 @@ public void testQueryCache() throws Exception { ); assertThat( - client().prepareSearch("idx") - .setSearchType(SearchType.QUERY_THEN_FETCH) + prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH) .setSize(0) .setRequestCache(true) .get() @@ -462,7 +460,7 @@ public void testQueryCache() throws Exception { updateIndexSettings(Settings.builder().put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), false), "idx"); assertThat( - client().prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH).setSize(0).get().getHits().getTotalHits().value, + prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH).setSize(0).get().getHits().getTotalHits().value, equalTo((long) numDocs) ); assertThat( @@ -471,8 +469,7 @@ public void testQueryCache() throws Exception { ); assertThat( - client().prepareSearch("idx") - .setSearchType(SearchType.QUERY_THEN_FETCH) + prepareSearch("idx").setSearchType(SearchType.QUERY_THEN_FETCH) .setSize(0) .setRequestCache(true) .get() @@ -1013,7 +1010,7 @@ public void testGroupsParam() throws Exception { client().prepareIndex("test1").setId(Integer.toString(1)).setSource("foo", "bar").execute().actionGet(); refresh(); - client().prepareSearch("_all").setStats("bar", "baz").execute().actionGet(); + prepareSearch("_all").setStats("bar", "baz").execute().actionGet(); IndicesStatsRequestBuilder builder = indicesAdmin().prepareStats(); IndicesStatsResponse stats = builder.execute().actionGet(); @@ -1163,9 +1160,7 @@ public void testFilterCacheStats() throws Exception { // the query cache has an optimization that disables it automatically if there is contention, // so we run it in an assertBusy block which should eventually succeed assertBusy(() -> { - assertNoFailures( - client().prepareSearch("index").setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("foo", "baz"))) - ); + assertNoFailures(prepareSearch("index").setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("foo", "baz")))); IndicesStatsResponse stats = indicesAdmin().prepareStats("index").setQueryCache(true).get(); assertCumulativeQueryCacheStats(stats); assertThat(stats.getTotal().queryCache.getHitCount(), equalTo(0L)); @@ -1174,9 +1169,7 @@ public void testFilterCacheStats() throws Exception { }); assertBusy(() -> { - assertNoFailures( - client().prepareSearch("index").setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("foo", "baz"))) - ); + assertNoFailures(prepareSearch("index").setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("foo", "baz")))); IndicesStatsResponse stats = indicesAdmin().prepareStats("index").setQueryCache(true).get(); assertCumulativeQueryCacheStats(stats); assertThat(stats.getTotal().queryCache.getHitCount(), greaterThan(0L)); @@ -1224,9 +1217,7 @@ public void testFilterCacheStats() throws Exception { ); assertBusy(() -> { - assertNoFailures( - client().prepareSearch("index").setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("foo", "baz"))) - ); + assertNoFailures(prepareSearch("index").setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.matchQuery("foo", "baz")))); IndicesStatsResponse stats = indicesAdmin().prepareStats("index").setQueryCache(true).get(); assertCumulativeQueryCacheStats(stats); assertThat(stats.getTotal().queryCache.getHitCount(), greaterThan(0L)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java index 76e3ad8ef74d4..17f64a689d1a2 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java @@ -152,8 +152,7 @@ public void testSimpleIndexTemplateTests() throws Exception { client().prepareIndex("test_index").setId("1").setSource("field1", "value1", "field2", "value 2").setRefreshPolicy(IMMEDIATE).get(); ensureGreen(); - SearchResponse searchResponse = client().prepareSearch("test_index") - .setQuery(termQuery("field1", "value1")) + SearchResponse searchResponse = prepareSearch("test_index").setQuery(termQuery("field1", "value1")) .addStoredField("field1") .addStoredField("field2") .execute() @@ -168,8 +167,7 @@ public void testSimpleIndexTemplateTests() throws Exception { ensureGreen(); // now only match on one template (template_1) - searchResponse = client().prepareSearch("text_index") - .setQuery(termQuery("field1", "value1")) + searchResponse = prepareSearch("text_index").setQuery(termQuery("field1", "value1")) .addStoredField("field1") .addStoredField("field2") .execute() @@ -512,16 +510,16 @@ public void testIndexTemplateWithAliases() throws Exception { refresh(); - assertHitCount(client().prepareSearch("test_index"), 5L); - assertHitCount(client().prepareSearch("simple_alias"), 5L); - assertHitCount(client().prepareSearch("templated_alias-test_index"), 5L); + assertHitCount(prepareSearch("test_index"), 5L); + assertHitCount(prepareSearch("simple_alias"), 5L); + assertHitCount(prepareSearch("templated_alias-test_index"), 5L); - SearchResponse searchResponse = client().prepareSearch("filtered_alias").get(); + SearchResponse searchResponse = prepareSearch("filtered_alias").get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("type"), equalTo("type2")); // Search the complex filter alias - searchResponse = client().prepareSearch("complex_filtered_alias").get(); + searchResponse = prepareSearch("complex_filtered_alias").get(); assertHitCount(searchResponse, 3L); Set types = new HashSet<>(); @@ -558,9 +556,9 @@ public void testIndexTemplateWithAliasesInSource() { client().prepareIndex("test_index").setId("2").setSource("field", "value2").get(); refresh(); - assertHitCount(client().prepareSearch("test_index"), 2L); + assertHitCount(prepareSearch("test_index"), 2L); - SearchResponse searchResponse = client().prepareSearch("my_alias").get(); + SearchResponse searchResponse = prepareSearch("my_alias").get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("field"), equalTo("value2")); } @@ -593,10 +591,10 @@ public void testIndexTemplateWithAliasesSource() { client().prepareIndex("test_index").setId("2").setSource("field", "value2").get(); refresh(); - assertHitCount(client().prepareSearch("test_index"), 2L); - assertHitCount(client().prepareSearch("alias1"), 2L); + assertHitCount(prepareSearch("test_index"), 2L); + assertHitCount(prepareSearch("alias1"), 2L); - SearchResponse searchResponse = client().prepareSearch("alias2").get(); + SearchResponse searchResponse = prepareSearch("alias2").get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("field"), equalTo("value2")); } @@ -852,8 +850,7 @@ public void testMultipleTemplate() throws IOException { ensureGreen(); // ax -> matches template - SearchResponse searchResponse = client().prepareSearch("ax") - .setQuery(termQuery("field1", "value1")) + SearchResponse searchResponse = prepareSearch("ax").setQuery(termQuery("field1", "value1")) .addStoredField("field1") .addStoredField("field2") .execute() @@ -864,8 +861,7 @@ public void testMultipleTemplate() throws IOException { assertNull(searchResponse.getHits().getAt(0).field("field2")); // bx -> matches template - searchResponse = client().prepareSearch("bx") - .setQuery(termQuery("field1", "value1")) + searchResponse = prepareSearch("bx").setQuery(termQuery("field1", "value1")) .addStoredField("field1") .addStoredField("field2") .execute() diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java index 6a8de644b00cf..c221fbf78ccca 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java @@ -133,7 +133,7 @@ public void testSimpleRelocationNoIndexing() { logger.info("--> verifying count"); indicesAdmin().prepareRefresh().execute().actionGet(); - assertThat(client().prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(20L)); + assertThat(prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(20L)); logger.info("--> start another node"); final String node_2 = internalCluster().startNode(); @@ -157,7 +157,7 @@ public void testSimpleRelocationNoIndexing() { logger.info("--> verifying count again..."); indicesAdmin().prepareRefresh().execute().actionGet(); - assertThat(client().prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(20L)); + assertThat(prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(20L)); } public void testRelocationWhileIndexingRandom() throws Exception { @@ -236,8 +236,7 @@ public void testRelocationWhileIndexingRandom() throws Exception { boolean ranOnce = false; for (int i = 0; i < 10; i++) { logger.info("--> START search test round {}", i + 1); - SearchHits hits = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchHits hits = prepareSearch("test").setQuery(matchAllQuery()) .setSize((int) indexer.totalIndexedDocs()) .storedFields() .execute() @@ -486,7 +485,7 @@ public void testIndexSearchAndRelocateConcurrently() throws Exception { for (int i = 0; i < searchThreads.length; i++) { searchThreads[i] = new Thread(() -> { while (stopped.get() == false) { - assertNoFailures(client().prepareSearch("test").setRequestCache(false)); + assertNoFailures(prepareSearch("test").setRequestCache(false)); } }); searchThreads[i].start(); @@ -501,7 +500,7 @@ public void testIndexSearchAndRelocateConcurrently() throws Exception { docs[i] = client().prepareIndex("test").setId(id).setSource("field1", English.intToEnglish(i)); } indexRandom(true, docs); - assertHitCount(client().prepareSearch("test"), numDocs); + assertHitCount(prepareSearch("test"), numDocs); logger.info(" --> moving index to new nodes"); updateIndexSettings( @@ -580,7 +579,7 @@ public void testRelocateWhileWaitingForRefresh() { logger.info("--> verifying count"); indicesAdmin().prepareRefresh().execute().actionGet(); - assertThat(client().prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(20L)); + assertThat(prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(20L)); } public void testRelocateWhileContinuouslyIndexingAndWaitingForRefresh() throws Exception { @@ -650,7 +649,7 @@ public void testRelocateWhileContinuouslyIndexingAndWaitingForRefresh() throws E assertTrue(pendingIndexResponses.stream().allMatch(ActionFuture::isDone)); }, 1, TimeUnit.MINUTES); - assertThat(client().prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(120L)); + assertThat(prepareSearch("test").setSize(0).execute().actionGet().getHits().getTotalHits().value, equalTo(120L)); } public void testRelocationEstablishedPeerRecoveryRetentionLeases() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java index f6abb5939e54b..7acd0e8b62ff9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java @@ -152,18 +152,12 @@ public void testAliasSearchRouting() throws Exception { ); assertThat( - client().prepareSearch("alias1") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(0L) ); assertThat( - client().prepareSearch("alias1") - .setSize(0) + prepareSearch("alias1").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -198,17 +192,11 @@ public void testAliasSearchRouting() throws Exception { equalTo(1L) ); assertThat( - client().prepareSearch("alias0") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias0").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(1L) ); assertThat( - client().prepareSearch("alias0") - .setSize(0) + prepareSearch("alias0").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -263,17 +251,11 @@ public void testAliasSearchRouting() throws Exception { equalTo(1L) ); assertThat( - client().prepareSearch("alias0") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias0").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(1L) ); assertThat( - client().prepareSearch("alias0") - .setSize(0) + prepareSearch("alias0").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -307,17 +289,11 @@ public void testAliasSearchRouting() throws Exception { equalTo(1L) ); assertThat( - client().prepareSearch("alias1") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias1").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(1L) ); assertThat( - client().prepareSearch("alias1") - .setSize(0) + prepareSearch("alias1").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -351,17 +327,11 @@ public void testAliasSearchRouting() throws Exception { equalTo(2L) ); assertThat( - client().prepareSearch("alias01") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias01").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(2L) ); assertThat( - client().prepareSearch("alias01") - .setSize(0) + prepareSearch("alias01").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -511,17 +481,11 @@ public void testAliasSearchRoutingWithTwoIndices() throws Exception { logger.info("--> search with alias-ab, should find two"); for (int i = 0; i < 5; i++) { assertThat( - client().prepareSearch("alias-ab") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias-ab").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(2L) ); assertThat( - client().prepareSearch("alias-ab") - .setSize(0) + prepareSearch("alias-ab").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -574,12 +538,7 @@ public void testAliasSearchRoutingWithConcreteAndAliasedIndices_issue2682() thro logger.info("--> search all on index_* should find two"); for (int i = 0; i < 5; i++) { assertThat( - client().prepareSearch("index_*") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("index_*").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(2L) ); } @@ -602,8 +561,7 @@ public void testAliasSearchRoutingWithConcreteAndAliasedIndices_issue3268() thro logger.info("--> indexing on index_2 which is a concrete index"); client().prepareIndex("index_2").setId("2").setSource("field", "value2").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); - SearchResponse searchResponse = client().prepareSearch("index_*") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse searchResponse = prepareSearch("index_*").setSearchType(SearchType.QUERY_THEN_FETCH) .setSize(1) .setQuery(QueryBuilders.matchAllQuery()) .execute() @@ -630,17 +588,11 @@ public void testIndexingAliasesOverTime() throws Exception { for (int i = 0; i < 5; i++) { assertThat(client().prepareGet("test", "0").setRouting("3").execute().actionGet().isExists(), equalTo(true)); assertThat( - client().prepareSearch("alias") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(1L) ); assertThat( - client().prepareSearch("alias") - .setSize(0) + prepareSearch("alias").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -656,17 +608,11 @@ public void testIndexingAliasesOverTime() throws Exception { logger.info("--> verifying search with wrong routing should not find"); for (int i = 0; i < 5; i++) { assertThat( - client().prepareSearch("alias") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(0L) ); assertThat( - client().prepareSearch("alias") - .setSize(0) + prepareSearch("alias").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() @@ -691,17 +637,11 @@ public void testIndexingAliasesOverTime() throws Exception { assertThat(client().prepareGet("test", "0").setRouting("3").execute().actionGet().isExists(), equalTo(true)); assertThat(client().prepareGet("test", "1").setRouting("4").execute().actionGet().isExists(), equalTo(true)); assertThat( - client().prepareSearch("alias") - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, + prepareSearch("alias").setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value, equalTo(2L) ); assertThat( - client().prepareSearch("alias") - .setSize(0) + prepareSearch("alias").setSize(0) .setQuery(QueryBuilders.matchAllQuery()) .execute() .actionGet() diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java index 233c492e54b7e..05fa9dc66928c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchCancellationIT.java @@ -65,9 +65,9 @@ public void testCancellationDuringQueryPhase() throws Exception { indexTestData(); logger.info("Executing search"); - ActionFuture searchResponse = client().prepareSearch("test") - .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()))) - .execute(); + ActionFuture searchResponse = prepareSearch("test").setQuery( + scriptQuery(new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap())) + ).execute(); awaitForBlock(plugins); cancelSearch(SearchAction.NAME); @@ -82,9 +82,10 @@ public void testCancellationDuringFetchPhase() throws Exception { indexTestData(); logger.info("Executing search"); - ActionFuture searchResponse = client().prepareSearch("test") - .addScriptField("test_field", new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap())) - .execute(); + ActionFuture searchResponse = prepareSearch("test").addScriptField( + "test_field", + new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()) + ).execute(); awaitForBlock(plugins); cancelSearch(SearchAction.NAME); @@ -110,8 +111,7 @@ public void testCancellationDuringAggregation() throws Exception { termsAggregationBuilder.field("field.keyword"); } - ActionFuture searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + ActionFuture searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addAggregation( termsAggregationBuilder.subAggregation( new ScriptedMetricAggregationBuilder("sub_agg").initScript( @@ -144,8 +144,7 @@ public void testCancellationOfScrollSearches() throws Exception { indexTestData(); logger.info("Executing search"); - ActionFuture searchResponse = client().prepareSearch("test") - .setScroll(TimeValue.timeValueSeconds(10)) + ActionFuture searchResponse = prepareSearch("test").setScroll(TimeValue.timeValueSeconds(10)) .setSize(5) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()))) .execute(); @@ -171,8 +170,7 @@ public void testCancellationOfScrollSearchesOnFollowupRequests() throws Exceptio logger.info("Executing search"); TimeValue keepAlive = TimeValue.timeValueSeconds(5); - SearchResponse searchResponse = client().prepareSearch("test") - .setScroll(keepAlive) + SearchResponse searchResponse = prepareSearch("test").setScroll(keepAlive) .setSize(2) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()))) .get(); @@ -209,11 +207,10 @@ public void testCancelMultiSearch() throws Exception { indexTestData(); ActionFuture msearchResponse = client().prepareMultiSearch() .add( - client().prepareSearch("test") - .addScriptField( - "test_field", - new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()) - ) + prepareSearch("test").addScriptField( + "test_field", + new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()) + ) ) .execute(); awaitForBlock(plugins); @@ -244,8 +241,7 @@ public void testCancelFailedSearchWhenPartialResultDisallowed() throws Exception Thread searchThread = new Thread(() -> { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + () -> prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SEARCH_BLOCK_SCRIPT_NAME, Collections.emptyMap()))) .setAllowPartialSearchResults(false) .setSize(1000) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java index 732f61ad9d151..e84afecb96f3f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java @@ -73,7 +73,7 @@ private void testLostMaster(CheckedBiConsumer loseMas index("test", "test", "{}"); - assertThat(client().prepareSearch("test").setScroll("30m").get().getScrollId(), is(notNullValue())); + assertThat(prepareSearch("test").setScroll("30m").get().getScrollId(), is(notNullValue())); loseMaster.accept(master, dataNode); // in the past, this failed because the search context for the scroll would prevent the shard lock from being released. diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchTimeoutIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchTimeoutIT.java index 008e164dd265d..1bcf2d8fb327f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchTimeoutIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchTimeoutIT.java @@ -55,8 +55,7 @@ private void indexDocs() { @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/98369") public void testTopHitsTimeout() { indexDocs(); - SearchResponse searchResponse = client().prepareSearch("test") - .setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) + SearchResponse searchResponse = prepareSearch("test").setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) .get(); assertThat(searchResponse.isTimedOut(), equalTo(true)); @@ -71,8 +70,7 @@ public void testTopHitsTimeout() { @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/98053") public void testAggsTimeout() { indexDocs(); - SearchResponse searchResponse = client().prepareSearch("test") - .setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) + SearchResponse searchResponse = prepareSearch("test").setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) .setSize(0) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) .addAggregation(new TermsAggregationBuilder("terms").field("field.keyword")) @@ -96,8 +94,7 @@ public void testPartialResultsIntolerantTimeout() throws Exception { ElasticsearchException ex = expectThrows( ElasticsearchException.class, - () -> client().prepareSearch("test") - .setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) + () -> prepareSearch("test").setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) .setAllowPartialSearchResults(false) // this line causes timeouts to report failures .get() diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java index c194674d1d99f..eec815d6957aa 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/StressSearchServiceReaperIT.java @@ -43,7 +43,7 @@ public void testStressReaper() throws ExecutionException, InterruptedException { indexRandom(true, builders); final int iterations = scaledRandomIntBetween(500, 1000); for (int i = 0; i < iterations; i++) { - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(matchAllQuery()).setSize(num), num); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(matchAllQuery()).setSize(num), num); } } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java index 0c940ad1f3154..cc74dcc3d0d28 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java @@ -39,8 +39,7 @@ public void setupSuiteScopeCluster() throws Exception { public void testScroll() { final int size = randomIntBetween(1, 4); - SearchResponse response = client().prepareSearch("index") - .setSize(size) + SearchResponse response = prepareSearch("index").setSize(size) .setScroll(TimeValue.timeValueMinutes(1)) .addAggregation(terms("f").field("f")) .get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java index 9c8786206264f..a0144d30a4728 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/CombiIT.java @@ -61,8 +61,7 @@ public void testMultipleAggsOnSameField_WithDifferentRequiredValueSourceType() t ensureSearchable(); SubAggCollectionMode aggCollectionMode = randomFrom(SubAggCollectionMode.values()); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(missing("missing_values").field("value")) + SearchResponse response = prepareSearch("idx").addAggregation(missing("missing_values").field("value")) .addAggregation(terms("values").field("value").collectMode(aggCollectionMode)) .get(); @@ -109,11 +108,9 @@ public void testSubAggregationForTopAggregationOnUnmappedField() throws Exceptio ensureSearchable("idx"); SubAggCollectionMode aggCollectionMode = randomFrom(SubAggCollectionMode.values()); - SearchResponse searchResponse = client().prepareSearch("idx") - .addAggregation( - histogram("values").field("value1").interval(1).subAggregation(terms("names").field("name").collectMode(aggCollectionMode)) - ) - .get(); + SearchResponse searchResponse = prepareSearch("idx").addAggregation( + histogram("values").field("value1").interval(1).subAggregation(terms("names").field("name").collectMode(aggCollectionMode)) + ).get(); assertThat(searchResponse.getHits().getTotalHits().value, Matchers.equalTo(0L)); Histogram values = searchResponse.getAggregations().get("values"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java index f8e6ed5193e9c..ea896c73f8882 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/EquivalenceIT.java @@ -152,7 +152,7 @@ public void testRandomRanges() throws Exception { } } - SearchRequestBuilder reqBuilder = client().prepareSearch("idx").addAggregation(query); + SearchRequestBuilder reqBuilder = prepareSearch("idx").addAggregation(query); for (int i = 0; i < ranges.length; ++i) { RangeQueryBuilder filter = QueryBuilders.rangeQuery("values"); if (ranges[i][0] != Double.NEGATIVE_INFINITY) { @@ -254,13 +254,12 @@ public void testDuelTerms() throws Exception { assertNoFailures(indicesAdmin().prepareRefresh("idx").setIndicesOptions(IndicesOptions.lenientExpandOpen()).execute().get()); - SearchResponse resp = client().prepareSearch("idx") - .addAggregation( - terms("long").field("long_values") - .size(maxNumTerms) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(min("min").field("num")) - ) + SearchResponse resp = prepareSearch("idx").addAggregation( + terms("long").field("long_values") + .size(maxNumTerms) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(min("min").field("num")) + ) .addAggregation( terms("double").field("double_values") .size(maxNumTerms) @@ -356,15 +355,12 @@ public void testDuelTermsHistogram() throws Exception { Map params = new HashMap<>(); params.put("interval", interval); - SearchResponse resp = client().prepareSearch("idx") - .addAggregation( - terms("terms").field("values") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "floor(_value / interval)", params)) - .size(maxNumTerms) - ) - .addAggregation(histogram("histo").field("values").interval(interval).minDocCount(1)) - .get(); + SearchResponse resp = prepareSearch("idx").addAggregation( + terms("terms").field("values") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "floor(_value / interval)", params)) + .size(maxNumTerms) + ).addAggregation(histogram("histo").field("values").interval(interval).minDocCount(1)).get(); assertNoFailures(resp); @@ -402,13 +398,11 @@ public void testLargeNumbersOfPercentileBuckets() throws Exception { } indexRandom(true, indexingRequests); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").field("double_value") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(percentiles("pcts").field("double_value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").field("double_value") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(percentiles("pcts").field("double_value")) + ).get(); assertAllSuccessful(response); assertEquals(numDocs, response.getHits().getTotalHits().value); } @@ -418,13 +412,11 @@ public void testReduce() throws Exception { createIndex("idx"); final int value = randomIntBetween(0, 10); indexRandom(true, client().prepareIndex("idx").setSource("f", value)); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filter("filter", QueryBuilders.matchAllQuery()).subAggregation( - range("range").field("f").addUnboundedTo(6).addUnboundedFrom(6).subAggregation(sum("sum").field("f")) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + filter("filter", QueryBuilders.matchAllQuery()).subAggregation( + range("range").field("f").addUnboundedTo(6).addUnboundedFrom(6).subAggregation(sum("sum").field("f")) ) - .get(); + ).get(); assertNoFailures(response); @@ -481,29 +473,25 @@ public void testDuelDepthBreadthFirst() throws Exception { } indexRandom(true, reqs); - final SearchResponse r1 = client().prepareSearch("idx") - .addAggregation( - terms("f1").field("f1") - .collectMode(SubAggCollectionMode.DEPTH_FIRST) - .subAggregation( - terms("f2").field("f2") - .collectMode(SubAggCollectionMode.DEPTH_FIRST) - .subAggregation(terms("f3").field("f3").collectMode(SubAggCollectionMode.DEPTH_FIRST)) - ) - ) - .get(); + final SearchResponse r1 = prepareSearch("idx").addAggregation( + terms("f1").field("f1") + .collectMode(SubAggCollectionMode.DEPTH_FIRST) + .subAggregation( + terms("f2").field("f2") + .collectMode(SubAggCollectionMode.DEPTH_FIRST) + .subAggregation(terms("f3").field("f3").collectMode(SubAggCollectionMode.DEPTH_FIRST)) + ) + ).get(); assertNoFailures(r1); - final SearchResponse r2 = client().prepareSearch("idx") - .addAggregation( - terms("f1").field("f1") - .collectMode(SubAggCollectionMode.BREADTH_FIRST) - .subAggregation( - terms("f2").field("f2") - .collectMode(SubAggCollectionMode.BREADTH_FIRST) - .subAggregation(terms("f3").field("f3").collectMode(SubAggCollectionMode.BREADTH_FIRST)) - ) - ) - .get(); + final SearchResponse r2 = prepareSearch("idx").addAggregation( + terms("f1").field("f1") + .collectMode(SubAggCollectionMode.BREADTH_FIRST) + .subAggregation( + terms("f2").field("f2") + .collectMode(SubAggCollectionMode.BREADTH_FIRST) + .subAggregation(terms("f3").field("f3").collectMode(SubAggCollectionMode.BREADTH_FIRST)) + ) + ).get(); assertNoFailures(r2); final Terms t1 = r1.getAggregations().get("f1"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java index 0cfd27f03c6e1..b255a7b5f9bb6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MetadataIT.java @@ -39,12 +39,9 @@ public void testMetadataSetOnAggregationResult() throws Exception { final var nestedMetadata = Map.of("nested", "value"); var metadata = Map.of("key", "value", "numeric", 1.2, "bool", true, "complex", nestedMetadata); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("the_terms").setMetadata(metadata).field("name").subAggregation(sum("the_sum").setMetadata(metadata).field("value")) - ) - .addAggregation(maxBucket("the_max_bucket", "the_terms>the_sum").setMetadata(metadata)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("the_terms").setMetadata(metadata).field("name").subAggregation(sum("the_sum").setMetadata(metadata).field("value")) + ).addAggregation(maxBucket("the_max_bucket", "the_terms>the_sum").setMetadata(metadata)).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java index 6ad46d7a77ee8..8b7f566750042 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/MissingValueIT.java @@ -54,9 +54,7 @@ protected void setupSuiteScopeCluster() throws Exception { } public void testUnmappedTerms() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(terms("my_terms").field("non_existing_field").missing("bar")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation(terms("my_terms").field("non_existing_field").missing("bar")).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); @@ -65,16 +63,16 @@ public void testUnmappedTerms() { public void testStringTerms() { for (ExecutionMode mode : ExecutionMode.values()) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(terms("my_terms").field("str").executionHint(mode.toString()).missing("bar")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("my_terms").field("str").executionHint(mode.toString()).missing("bar") + ).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); assertEquals(1, terms.getBucketByKey("foo").getDocCount()); assertEquals(1, terms.getBucketByKey("bar").getDocCount()); - response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("str").missing("foo")).get(); + response = prepareSearch("idx").addAggregation(terms("my_terms").field("str").missing("foo")).get(); assertNoFailures(response); terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); @@ -83,14 +81,14 @@ public void testStringTerms() { } public void testLongTerms() { - SearchResponse response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("long").missing(4)).get(); + SearchResponse response = prepareSearch("idx").addAggregation(terms("my_terms").field("long").missing(4)).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); assertEquals(1, terms.getBucketByKey("3").getDocCount()); assertEquals(1, terms.getBucketByKey("4").getDocCount()); - response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("long").missing(3)).get(); + response = prepareSearch("idx").addAggregation(terms("my_terms").field("long").missing(3)).get(); assertNoFailures(response); terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); @@ -98,14 +96,14 @@ public void testLongTerms() { } public void testDoubleTerms() { - SearchResponse response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("double").missing(4.5)).get(); + SearchResponse response = prepareSearch("idx").addAggregation(terms("my_terms").field("double").missing(4.5)).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); assertEquals(1, terms.getBucketByKey("4.5").getDocCount()); assertEquals(1, terms.getBucketByKey("5.5").getDocCount()); - response = client().prepareSearch("idx").addAggregation(terms("my_terms").field("double").missing(5.5)).get(); + response = prepareSearch("idx").addAggregation(terms("my_terms").field("double").missing(5.5)).get(); assertNoFailures(response); terms = response.getAggregations().get("my_terms"); assertEquals(1, terms.getBuckets().size()); @@ -113,9 +111,9 @@ public void testDoubleTerms() { } public void testUnmappedHistogram() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("my_histogram").field("non-existing_field").interval(5).missing(12)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("my_histogram").field("non-existing_field").interval(5).missing(12) + ).get(); assertNoFailures(response); Histogram histogram = response.getAggregations().get("my_histogram"); assertEquals(1, histogram.getBuckets().size()); @@ -124,9 +122,7 @@ public void testUnmappedHistogram() { } public void testHistogram() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("my_histogram").field("long").interval(5).missing(7)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation(histogram("my_histogram").field("long").interval(5).missing(7)).get(); assertNoFailures(response); Histogram histogram = response.getAggregations().get("my_histogram"); assertEquals(2, histogram.getBuckets().size()); @@ -135,7 +131,7 @@ public void testHistogram() { assertEquals(5d, histogram.getBuckets().get(1).getKey()); assertEquals(1, histogram.getBuckets().get(1).getDocCount()); - response = client().prepareSearch("idx").addAggregation(histogram("my_histogram").field("long").interval(5).missing(3)).get(); + response = prepareSearch("idx").addAggregation(histogram("my_histogram").field("long").interval(5).missing(3)).get(); assertNoFailures(response); histogram = response.getAggregations().get("my_histogram"); assertEquals(1, histogram.getBuckets().size()); @@ -144,9 +140,9 @@ public void testHistogram() { } public void testDateHistogram() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(dateHistogram("my_histogram").field("date").calendarInterval(DateHistogramInterval.YEAR).missing("2014-05-07")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("my_histogram").field("date").calendarInterval(DateHistogramInterval.YEAR).missing("2014-05-07") + ).get(); assertNoFailures(response); Histogram histogram = response.getAggregations().get("my_histogram"); assertEquals(2, histogram.getBuckets().size()); @@ -155,9 +151,9 @@ public void testDateHistogram() { assertEquals("2015-01-01T00:00:00.000Z", histogram.getBuckets().get(1).getKeyAsString()); assertEquals(1, histogram.getBuckets().get(1).getDocCount()); - response = client().prepareSearch("idx") - .addAggregation(dateHistogram("my_histogram").field("date").calendarInterval(DateHistogramInterval.YEAR).missing("2015-05-07")) - .get(); + response = prepareSearch("idx").addAggregation( + dateHistogram("my_histogram").field("date").calendarInterval(DateHistogramInterval.YEAR).missing("2015-05-07") + ).get(); assertNoFailures(response); histogram = response.getAggregations().get("my_histogram"); assertEquals(1, histogram.getBuckets().size()); @@ -166,23 +162,21 @@ public void testDateHistogram() { } public void testCardinality() { - SearchResponse response = client().prepareSearch("idx").addAggregation(cardinality("card").field("long").missing(2)).get(); + SearchResponse response = prepareSearch("idx").addAggregation(cardinality("card").field("long").missing(2)).get(); assertNoFailures(response); Cardinality cardinality = response.getAggregations().get("card"); assertEquals(2, cardinality.getValue()); } public void testPercentiles() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(percentiles("percentiles").field("long").missing(1000)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation(percentiles("percentiles").field("long").missing(1000)).get(); assertNoFailures(response); Percentiles percentiles = response.getAggregations().get("percentiles"); assertEquals(1000, percentiles.percentile(100), 0); } public void testStats() { - SearchResponse response = client().prepareSearch("idx").addAggregation(stats("stats").field("long").missing(5)).get(); + SearchResponse response = prepareSearch("idx").addAggregation(stats("stats").field("long").missing(5)).get(); assertNoFailures(response); Stats stats = response.getAggregations().get("stats"); assertEquals(2, stats.getCount()); @@ -190,9 +184,7 @@ public void testStats() { } public void testUnmappedGeoBounds() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geoBounds("bounds").field("non_existing_field").missing("2,1")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation(geoBounds("bounds").field("non_existing_field").missing("2,1")).get(); assertNoFailures(response); GeoBounds bounds = response.getAggregations().get("bounds"); assertThat(bounds.bottomRight().lat(), closeTo(2.0, 1E-5)); @@ -202,7 +194,7 @@ public void testUnmappedGeoBounds() { } public void testGeoBounds() { - SearchResponse response = client().prepareSearch("idx").addAggregation(geoBounds("bounds").field("location").missing("2,1")).get(); + SearchResponse response = prepareSearch("idx").addAggregation(geoBounds("bounds").field("location").missing("2,1")).get(); assertNoFailures(response); GeoBounds bounds = response.getAggregations().get("bounds"); assertThat(bounds.bottomRight().lat(), closeTo(1.0, 1E-5)); @@ -212,9 +204,7 @@ public void testGeoBounds() { } public void testGeoCentroid() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geoCentroid("centroid").field("location").missing("2,1")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation(geoCentroid("centroid").field("location").missing("2,1")).get(); assertNoFailures(response); GeoCentroid centroid = response.getAggregations().get("centroid"); GeoPoint point = new GeoPoint(1.5, 1.5); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java index 3f94cde9cb30a..0af496d83f9db 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java @@ -73,11 +73,9 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSingleValueField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); @@ -107,11 +105,9 @@ public void testSingleValueField() throws Exception { } public void testMultiValueField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); @@ -141,13 +137,11 @@ public void testMultiValueField() throws Exception { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .size(between(1, 5)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .size(between(1, 5)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java index 8333fcd37ea37..8031c467a47ae 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java @@ -225,9 +225,9 @@ private static String getBucketKeyAsString(ZonedDateTime key, ZoneId tz) { } public void testSingleValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -260,15 +260,9 @@ public void testSingleValuedField() throws Exception { } public void testSingleValuedFieldWithTimeZone() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .minDocCount(1) - .timeZone(ZoneId.of("+01:00")) - ) - .execute() - .actionGet(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.DAY).minDocCount(1).timeZone(ZoneId.of("+01:00")) + ).execute().actionGet(); ZoneId tz = ZoneId.of("+01:00"); assertNoFailures(response); @@ -328,11 +322,9 @@ public void testSingleValued_timeZone_epoch() throws Exception { format = format + "||date_optional_time"; } ZoneId tz = ZoneId.of("+01:00"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.DAY).minDocCount(1).timeZone(tz).format(format) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.DAY).minDocCount(1).timeZone(tz).format(format) + ).get(); assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); @@ -362,9 +354,9 @@ public void testSingleValued_timeZone_epoch() throws Exception { } public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.key(true))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.key(true)) + ).get(); assertNoFailures(response); @@ -382,11 +374,9 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { } public void testSingleValuedFieldOrderedByKeyDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.key(false)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.key(false)) + ).get(); assertNoFailures(response); @@ -403,11 +393,9 @@ public void testSingleValuedFieldOrderedByKeyDesc() throws Exception { } public void testSingleValuedFieldOrderedByCountAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.count(true)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.count(true)) + ).get(); assertNoFailures(response); @@ -424,11 +412,9 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { } public void testSingleValuedFieldOrderedByCountDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.count(false)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.count(false)) + ).get(); assertNoFailures(response); @@ -445,11 +431,9 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).subAggregation(sum("sum").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).subAggregation(sum("sum").field("value")) + ).get(); assertNoFailures(response); @@ -504,14 +488,12 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { } public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .order(BucketOrder.aggregation("sum", true)) - .subAggregation(max("sum").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("sum", true)) + .subAggregation(max("sum").field("value")) + ).get(); assertNoFailures(response); @@ -528,14 +510,12 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { } public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .order(BucketOrder.aggregation("sum", false)) - .subAggregation(max("sum").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("sum", false)) + .subAggregation(max("sum").field("value")) + ).get(); assertNoFailures(response); @@ -552,14 +532,12 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception } public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .order(BucketOrder.aggregation("stats", "sum", false)) - .subAggregation(stats("stats").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("stats", "sum", false)) + .subAggregation(stats("stats").field("value")) + ).get(); assertNoFailures(response); @@ -576,14 +554,12 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws } public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .order(BucketOrder.aggregation("max_constant", randomBoolean())) - .subAggregation(max("max_constant").field("constant")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant")) + ).get(); assertNoFailures(response); @@ -602,18 +578,16 @@ public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { boolean asc = true; try { - client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .order(BucketOrder.aggregation("inner_histo>avg", asc)) - .subAggregation( - dateHistogram("inner_histo").calendarInterval(DateHistogramInterval.MONTH) - .field("dates") - .subAggregation(avg("avg").field("value")) - ) - ) - .get(); + prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .order(BucketOrder.aggregation("inner_histo>avg", asc)) + .subAggregation( + dateHistogram("inner_histo").calendarInterval(DateHistogramInterval.MONTH) + .field("dates") + .subAggregation(avg("avg").field("value")) + ) + ).get(); fail("Expected an exception"); } catch (SearchPhaseExecutionException e) { ElasticsearchException[] rootCauses = e.guessRootCauses(); @@ -633,13 +607,11 @@ public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { public void testSingleValuedFieldWithValueScript() throws Exception { Map params = new HashMap<>(); params.put("fieldname", "date"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.LONG_PLUS_ONE_MONTH, params)) - .calendarInterval(DateHistogramInterval.MONTH) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.LONG_PLUS_ONE_MONTH, params)) + .calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -682,9 +654,9 @@ public void testSingleValuedFieldWithValueScript() throws Exception { */ public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(dateHistogram("histo").field("dates").calendarInterval(DateHistogramInterval.MONTH)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("dates").calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -724,11 +696,9 @@ public void testMultiValuedField() throws Exception { } public void testMultiValuedFieldOrderedByCountDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("dates").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.count(false)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("dates").calendarInterval(DateHistogramInterval.MONTH).order(BucketOrder.count(false)) + ).get(); assertNoFailures(response); @@ -773,13 +743,11 @@ public void testMultiValuedFieldOrderedByCountDesc() throws Exception { public void testMultiValuedFieldWithValueScript() throws Exception { Map params = new HashMap<>(); params.put("fieldname", "dates"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("dates") - .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.LONG_PLUS_ONE_MONTH, params)) - .calendarInterval(DateHistogramInterval.MONTH) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("dates") + .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.LONG_PLUS_ONE_MONTH, params)) + .calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -829,12 +797,10 @@ public void testMultiValuedFieldWithValueScript() throws Exception { public void testScriptSingleValue() throws Exception { Map params = new HashMap<>(); params.put("fieldname", "date"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)) - .calendarInterval(DateHistogramInterval.MONTH) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)) + .calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -869,12 +835,10 @@ public void testScriptSingleValue() throws Exception { public void testScriptMultiValued() throws Exception { Map params = new HashMap<>(); params.put("fieldname", "dates"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)) - .calendarInterval(DateHistogramInterval.MONTH) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)) + .calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -923,9 +887,9 @@ public void testScriptMultiValued() throws Exception { */ public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH) + ).get(); assertNoFailures(response); @@ -971,8 +935,7 @@ public void testPartiallyUnmapped() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -1010,8 +973,7 @@ public void testSingleValueWithTimeZone() throws Exception { } indexRandom(true, reqs); - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx2").setQuery(matchAllQuery()) .addAggregation( dateHistogram("date_histo").field("date") .timeZone(ZoneId.of("-02:00")) @@ -1106,16 +1068,14 @@ public void testSingleValueFieldWithExtendedBounds() throws Exception { SearchResponse response = null; try { - response = client().prepareSearch("idx2") - .addAggregation( - dateHistogram("histo").field("date") - .fixedInterval(DateHistogramInterval.days(interval)) - .minDocCount(0) - // when explicitly specifying a format, the extended bounds should be defined by the same format - .extendedBounds(new LongBounds(format(boundsMin, pattern), format(boundsMax, pattern))) - .format(pattern) - ) - .get(); + response = prepareSearch("idx2").addAggregation( + dateHistogram("histo").field("date") + .fixedInterval(DateHistogramInterval.days(interval)) + .minDocCount(0) + // when explicitly specifying a format, the extended bounds should be defined by the same format + .extendedBounds(new LongBounds(format(boundsMin, pattern), format(boundsMax, pattern))) + .format(pattern) + ).get(); if (invalidBoundsError) { fail("Expected an exception to be thrown when bounds.min is greater than bounds.max"); @@ -1175,10 +1135,9 @@ public void testSingleValueFieldWithExtendedBoundsTimezone() throws Exception { SearchResponse response = null; // retrieve those docs with the same time zone and extended bounds - response = client().prepareSearch(index) - .setQuery( - QueryBuilders.rangeQuery("date").from("now/d").to("now/d").includeLower(true).includeUpper(true).timeZone(timezone.getId()) - ) + response = prepareSearch(index).setQuery( + QueryBuilders.rangeQuery("date").from("now/d").to("now/d").includeLower(true).includeUpper(true).timeZone(timezone.getId()) + ) .addAggregation( dateHistogram("histo").field("date") .calendarInterval(DateHistogramInterval.hours(1)) @@ -1236,15 +1195,13 @@ public void testSingleValueFieldWithExtendedBoundsOffset() throws Exception { SearchResponse response = null; // retrieve those docs with the same time zone and extended bounds - response = client().prepareSearch(index) - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.days(1)) - .offset("+6h") - .minDocCount(0) - .extendedBounds(new LongBounds("2016-01-01T06:00:00Z", "2016-01-08T08:00:00Z")) - ) - .get(); + response = prepareSearch(index).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.days(1)) + .offset("+6h") + .minDocCount(0) + .extendedBounds(new LongBounds("2016-01-01T06:00:00Z", "2016-01-08T08:00:00Z")) + ).get(); assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); @@ -1293,8 +1250,7 @@ public void testSingleValueWithMultipleDateFormatsFromMapping() throws Exception } indexRandom(true, reqs); - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx2").setQuery(matchAllQuery()) .addAggregation(dateHistogram("date_histo").field("date").calendarInterval(DateHistogramInterval.DAY)) .get(); @@ -1313,14 +1269,9 @@ public void testSingleValueWithMultipleDateFormatsFromMapping() throws Exception } public void testIssue6965() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .timeZone(ZoneId.of("+01:00")) - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").timeZone(ZoneId.of("+01:00")).calendarInterval(DateHistogramInterval.MONTH).minDocCount(0) + ).get(); assertNoFailures(response); @@ -1362,14 +1313,12 @@ public void testDSTBoundaryIssue9491() throws InterruptedException, ExecutionExc client().prepareIndex("test9491").setSource("d", "2014-11-08T13:00:00Z") ); ensureSearchable("test9491"); - SearchResponse response = client().prepareSearch("test9491") - .addAggregation( - dateHistogram("histo").field("d") - .calendarInterval(DateHistogramInterval.YEAR) - .timeZone(ZoneId.of("Asia/Jerusalem")) - .format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX") - ) - .get(); + SearchResponse response = prepareSearch("test9491").addAggregation( + dateHistogram("histo").field("d") + .calendarInterval(DateHistogramInterval.YEAR) + .timeZone(ZoneId.of("Asia/Jerusalem")) + .format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX") + ).get(); assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); @@ -1386,15 +1335,13 @@ public void testIssue8209() throws InterruptedException, ExecutionException { client().prepareIndex("test8209").setSource("d", "2014-04-30T00:00:00Z") ); ensureSearchable("test8209"); - SearchResponse response = client().prepareSearch("test8209") - .addAggregation( - dateHistogram("histo").field("d") - .calendarInterval(DateHistogramInterval.MONTH) - .format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX") - .timeZone(ZoneId.of("CET")) - .minDocCount(0) - ) - .get(); + SearchResponse response = prepareSearch("test8209").addAggregation( + dateHistogram("histo").field("d") + .calendarInterval(DateHistogramInterval.MONTH) + .format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX") + .timeZone(ZoneId.of("CET")) + .minDocCount(0) + ).get(); assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(4)); @@ -1423,15 +1370,13 @@ public void testFormatIndexUnmapped() throws InterruptedException, ExecutionExce indexRandom(true, client().prepareIndex(indexDateUnmapped).setSource("foo", "bar")); ensureSearchable(indexDateUnmapped); - SearchResponse response = client().prepareSearch(indexDateUnmapped) - .addAggregation( - dateHistogram("histo").field("dateField") - .calendarInterval(DateHistogramInterval.MONTH) - .format("yyyy-MM") - .minDocCount(0) - .extendedBounds(new LongBounds("2018-01", "2018-01")) - ) - .get(); + SearchResponse response = prepareSearch(indexDateUnmapped).addAggregation( + dateHistogram("histo").field("dateField") + .calendarInterval(DateHistogramInterval.MONTH) + .format("yyyy-MM") + .minDocCount(0) + .extendedBounds(new LongBounds("2018-01", "2018-01")) + ).get(); assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); @@ -1450,25 +1395,21 @@ public void testRewriteTimeZone_EpochMillisFormat() throws InterruptedException, assertAcked(indicesAdmin().prepareCreate(index).setMapping("d", "type=date,format=epoch_millis").get()); indexRandom(true, client().prepareIndex(index).setSource("d", "1477954800000")); ensureSearchable(index); - SearchResponse response = client().prepareSearch(index) - .addAggregation( - dateHistogram("histo").field("d").calendarInterval(DateHistogramInterval.MONTH).timeZone(ZoneId.of("Europe/Berlin")) - ) - .get(); + SearchResponse response = prepareSearch(index).addAggregation( + dateHistogram("histo").field("d").calendarInterval(DateHistogramInterval.MONTH).timeZone(ZoneId.of("Europe/Berlin")) + ).get(); assertNoFailures(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("1477954800000")); assertThat(histo.getBuckets().get(0).getDocCount(), equalTo(1L)); - response = client().prepareSearch(index) - .addAggregation( - dateHistogram("histo").field("d") - .calendarInterval(DateHistogramInterval.MONTH) - .timeZone(ZoneId.of("Europe/Berlin")) - .format("yyyy-MM-dd") - ) - .get(); + response = prepareSearch(index).addAggregation( + dateHistogram("histo").field("d") + .calendarInterval(DateHistogramInterval.MONTH) + .timeZone(ZoneId.of("Europe/Berlin")) + .format("yyyy-MM-dd") + ).get(); assertNoFailures(response); histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); @@ -1486,8 +1427,7 @@ public void testRewriteTimeZone_EpochMillisFormat() throws InterruptedException, * "2015-10-25T04:00:00.000+01:00". */ public void testDSTEndTransition() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(new MatchNoneQueryBuilder()) + SearchResponse response = prepareSearch("idx").setQuery(new MatchNoneQueryBuilder()) .addAggregation( dateHistogram("histo").field("date") .timeZone(ZoneId.of("Europe/Oslo")) @@ -1516,8 +1456,7 @@ public void testDSTEndTransition() throws Exception { equalTo(3600000L) ); - response = client().prepareSearch("idx") - .setQuery(new MatchNoneQueryBuilder()) + response = prepareSearch("idx").setQuery(new MatchNoneQueryBuilder()) .addAggregation( dateHistogram("histo").field("date") .timeZone(ZoneId.of("Europe/Oslo")) @@ -1578,8 +1517,7 @@ public void testScriptCaching() throws Exception { // Test that a request using a nondeterministic script does not get cached Map params = new HashMap<>(); params.put("fieldname", "d"); - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( dateHistogram("histo").field("d") .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.CURRENT_DATE, params)) @@ -1598,8 +1536,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( dateHistogram("histo").field("d") .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.LONG_PLUS_ONE_MONTH, params)) @@ -1618,8 +1555,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(dateHistogram("histo").field("d").calendarInterval(DateHistogramInterval.MONTH)) .get(); assertNoFailures(r); @@ -1676,15 +1612,13 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound private void assertMultiSortResponse(int[] expectedDays, BucketOrder... order) { ZonedDateTime[] expectedKeys = Arrays.stream(expectedDays).mapToObj(d -> date(1, d)).toArray(ZonedDateTime[]::new); - SearchResponse response = client().prepareSearch("sort_idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .order(BucketOrder.compound(order)) - .subAggregation(avg("avg_l").field("l")) - .subAggregation(sum("sum_d").field("d")) - ) - .get(); + SearchResponse response = prepareSearch("sort_idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.DAY) + .order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")) + .subAggregation(sum("sum_d").field("d")) + ).get(); assertNoFailures(response); @@ -1722,14 +1656,11 @@ public void testDateNanosHistogram() throws Exception { indexRandom(true, client().prepareIndex("nanos").setId("2").setSource("date", "2000-01-02")); // Search interval 24 hours - SearchResponse r = client().prepareSearch("nanos") - .addAggregation( - dateHistogram("histo").field("date") - .fixedInterval(DateHistogramInterval.seconds(60 * 60 * 24)) - .timeZone(ZoneId.of("Europe/Berlin")) - ) - .addDocValueField("date") - .get(); + SearchResponse r = prepareSearch("nanos").addAggregation( + dateHistogram("histo").field("date") + .fixedInterval(DateHistogramInterval.seconds(60 * 60 * 24)) + .timeZone(ZoneId.of("Europe/Berlin")) + ).addDocValueField("date").get(); assertNoFailures(r); Histogram histogram = r.getAggregations().get("histo"); @@ -1740,12 +1671,9 @@ public void testDateNanosHistogram() throws Exception { assertEquals(946767600000L, ((ZonedDateTime) buckets.get(1).getKey()).toEpochSecond() * 1000); assertEquals(1, buckets.get(1).getDocCount()); - r = client().prepareSearch("nanos") - .addAggregation( - dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.seconds(60 * 60 * 24)).timeZone(ZoneId.of("UTC")) - ) - .addDocValueField("date") - .get(); + r = prepareSearch("nanos").addAggregation( + dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.seconds(60 * 60 * 24)).timeZone(ZoneId.of("UTC")) + ).addDocValueField("date").get(); assertNoFailures(r); histogram = r.getAggregations().get("histo"); @@ -1758,11 +1686,9 @@ public void testDateNanosHistogram() throws Exception { } public void testDateKeyFormatting() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).timeZone(ZoneId.of("America/Edmonton")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH).timeZone(ZoneId.of("America/Edmonton")) + ).get(); assertNoFailures(response); @@ -1774,13 +1700,11 @@ public void testDateKeyFormatting() { } public void testHardBoundsOnDates() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .hardBounds(new LongBounds("2012-02-01T00:00:00.000", "2012-03-03T00:00:00.000")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.DAY) + .hardBounds(new LongBounds("2012-02-01T00:00:00.000", "2012-03-03T00:00:00.000")) + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetIT.java index 40ff2d25e433c..64c1a7c8859fc 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetIT.java @@ -72,8 +72,7 @@ private void prepareIndex(ZonedDateTime date, int numHours, int stepSizeHours, i public void testSingleValueWithPositiveOffset() throws Exception { prepareIndex(date("2014-03-11T00:00:00+00:00"), 5, 1, 0); - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx2").setQuery(matchAllQuery()) .addAggregation( dateHistogram("date_histo").field("date").offset("2h").format(DATE_FORMAT).fixedInterval(DateHistogramInterval.DAY) ) @@ -92,8 +91,7 @@ public void testSingleValueWithPositiveOffset() throws Exception { public void testSingleValueWithNegativeOffset() throws Exception { prepareIndex(date("2014-03-11T00:00:00+00:00"), 5, -1, 0); - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx2").setQuery(matchAllQuery()) .addAggregation( dateHistogram("date_histo").field("date").offset("-2h").format(DATE_FORMAT).fixedInterval(DateHistogramInterval.DAY) ) @@ -116,8 +114,7 @@ public void testSingleValueWithOffsetMinDocCount() throws Exception { prepareIndex(date("2014-03-11T00:00:00+00:00"), 12, 1, 0); prepareIndex(date("2014-03-14T00:00:00+00:00"), 12, 1, 13); - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx2").setQuery(matchAllQuery()) .addAggregation( dateHistogram("date_histo").field("date") .offset("6h") diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java index d0e7a74c9feab..79b9b0b3628d5 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java @@ -127,14 +127,12 @@ public void testDateMath() throws Exception { } else { rangeBuilder.script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)); } - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - rangeBuilder.addUnboundedTo("a long time ago", "now-50y") - .addRange("recently", "now-50y", "now-1y") - .addUnboundedFrom("last year", "now-1y") - .timeZone(ZoneId.of("Etc/GMT+5")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + rangeBuilder.addUnboundedTo("a long time ago", "now-50y") + .addRange("recently", "now-50y", "now-1y") + .addUnboundedFrom("last year", "now-1y") + .timeZone(ZoneId.of("Etc/GMT+5")) + ).get(); assertNoFailures(response); @@ -162,14 +160,9 @@ public void testDateMath() throws Exception { } public void testSingleValueField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("date") - .addUnboundedTo(date(2, 15)) - .addRange(date(2, 15), date(3, 15)) - .addUnboundedFrom(date(3, 15)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("date").addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15)).addUnboundedFrom(date(3, 15)) + ).get(); assertNoFailures(response); @@ -208,14 +201,12 @@ public void testSingleValueField() throws Exception { } public void testSingleValueFieldWithStringDates() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("date") - .addUnboundedTo("2012-02-15") - .addRange("2012-02-15", "2012-03-15") - .addUnboundedFrom("2012-03-15") - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("date") + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-03-15") + .addUnboundedFrom("2012-03-15") + ).get(); assertNoFailures(response); @@ -254,15 +245,13 @@ public void testSingleValueFieldWithStringDates() throws Exception { } public void testSingleValueFieldWithStringDatesWithCustomFormat() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("date") - .format("yyyy-MM-dd") - .addUnboundedTo("2012-02-15") - .addRange("2012-02-15", "2012-03-15") - .addUnboundedFrom("2012-03-15") - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("date") + .format("yyyy-MM-dd") + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-03-15") + .addUnboundedFrom("2012-03-15") + ).get(); assertNoFailures(response); @@ -308,15 +297,13 @@ public void testSingleValueFieldWithDateMath() throws Exception { String mar15Suffix = timeZoneOffset == 0 ? "Z" : date(3, 15, timezone).format(DateTimeFormatter.ofPattern("xxx", Locale.ROOT)); long expectedFirstBucketCount = timeZoneOffset < 0 ? 3L : 2L; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("date") - .addUnboundedTo("2012-02-15") - .addRange("2012-02-15", "2012-02-15||+1M") - .addUnboundedFrom("2012-02-15||+1M") - .timeZone(timezone) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("date") + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-02-15||+1M") + .addUnboundedFrom("2012-02-15||+1M") + .timeZone(timezone) + ).get(); assertNoFailures(response); @@ -355,14 +342,12 @@ public void testSingleValueFieldWithDateMath() throws Exception { } public void testSingleValueFieldWithCustomKey() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("date") - .addUnboundedTo("r1", date(2, 15)) - .addRange("r2", date(2, 15), date(3, 15)) - .addUnboundedFrom("r3", date(3, 15)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("date") + .addUnboundedTo("r1", date(2, 15)) + .addRange("r2", date(2, 15), date(3, 15)) + .addUnboundedFrom("r3", date(3, 15)) + ).get(); assertNoFailures(response); @@ -410,15 +395,13 @@ public void testSingleValueFieldWithCustomKey() throws Exception { */ public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("date") - .addUnboundedTo("r1", date(2, 15)) - .addRange("r2", date(2, 15), date(3, 15)) - .addUnboundedFrom("r3", date(3, 15)) - .subAggregation(sum("sum").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("date") + .addUnboundedTo("r1", date(2, 15)) + .addRange("r2", date(2, 15), date(3, 15)) + .addUnboundedFrom("r3", date(3, 15)) + .subAggregation(sum("sum").field("value")) + ).get(); assertNoFailures(response); @@ -486,14 +469,9 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { */ public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field("dates") - .addUnboundedTo(date(2, 15)) - .addRange(date(2, 15), date(3, 15)) - .addUnboundedFrom(date(3, 15)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field("dates").addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15)).addUnboundedFrom(date(3, 15)) + ).get(); assertNoFailures(response); @@ -578,8 +556,7 @@ public void testPartiallyUnmapped() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -608,7 +585,7 @@ public void testEmptyAggregation() throws Exception { public void testNoRangesInQuery() { try { - client().prepareSearch("idx").addAggregation(dateRange("my_date_range_agg").field("value")).get(); + prepareSearch("idx").addAggregation(dateRange("my_date_range_agg").field("value")).get(); fail(); } catch (SearchPhaseExecutionException spee) { Throwable rootCause = spee.getCause().getCause(); @@ -650,8 +627,7 @@ public void testScriptCaching() throws Exception { // Test that a request using a nondeterministic script does not get cached Map params = new HashMap<>(); params.put("fieldname", "date"); - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( dateRange("foo").field("date") .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.CURRENT_DATE, params)) @@ -673,8 +649,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( dateRange("foo").field("date") .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.DOUBLE_PLUS_ONE_MONTH, params)) @@ -696,8 +671,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( dateRange("foo").field("date") .addRange( @@ -734,8 +708,7 @@ public void testRangeWithFormatStringValue() throws Exception { // using no format should work when to/from is compatible with format in // mapping - SearchResponse searchResponse = client().prepareSearch(indexName) - .setSize(0) + SearchResponse searchResponse = prepareSearch(indexName).setSize(0) .addAggregation(dateRange("date_range").field("date").addRange("00:16:40", "00:50:00").addRange("00:50:00", "01:06:40")) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); @@ -745,8 +718,7 @@ public void testRangeWithFormatStringValue() throws Exception { // using different format should work when to/from is compatible with // format in aggregation - searchResponse = client().prepareSearch(indexName) - .setSize(0) + searchResponse = prepareSearch(indexName).setSize(0) .addAggregation( dateRange("date_range").field("date").addRange("00.16.40", "00.50.00").addRange("00.50.00", "01.06.40").format("HH.mm.ss") ) @@ -758,8 +730,7 @@ public void testRangeWithFormatStringValue() throws Exception { // providing numeric input with format should work, but bucket keys are // different now - searchResponse = client().prepareSearch(indexName) - .setSize(0) + searchResponse = prepareSearch(indexName).setSize(0) .addAggregation( dateRange("date_range").field("date").addRange(1000000, 3000000).addRange(3000000, 4000000).format("epoch_millis") ) @@ -772,8 +743,7 @@ public void testRangeWithFormatStringValue() throws Exception { // providing numeric input without format should throw an exception ElasticsearchException e = expectThrows( ElasticsearchException.class, - () -> client().prepareSearch(indexName) - .setSize(0) + () -> prepareSearch(indexName).setSize(0) .addAggregation(dateRange("date_range").field("date").addRange(1000000, 3000000).addRange(3000000, 4000000)) .get() ); @@ -796,8 +766,7 @@ public void testRangeWithFormatNumericValue() throws Exception { // using no format should work when to/from is compatible with format in // mapping - SearchResponse searchResponse = client().prepareSearch(indexName) - .setSize(0) + SearchResponse searchResponse = prepareSearch(indexName).setSize(0) .addAggregation(dateRange("date_range").field("date").addRange(1000, 3000).addRange(3000, 4000)) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); @@ -806,8 +775,7 @@ public void testRangeWithFormatNumericValue() throws Exception { assertBucket(buckets.get(1), 1L, "3000-4000", 3000000L, 4000000L); // using no format should also work when and to/from are string values - searchResponse = client().prepareSearch(indexName) - .setSize(0) + searchResponse = prepareSearch(indexName).setSize(0) .addAggregation(dateRange("date_range").field("date").addRange("1000", "3000").addRange("3000", "4000")) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); @@ -816,8 +784,7 @@ public void testRangeWithFormatNumericValue() throws Exception { assertBucket(buckets.get(1), 1L, "3000-4000", 3000000L, 4000000L); // also e-notation should work, fractional parts should be truncated - searchResponse = client().prepareSearch(indexName) - .setSize(0) + searchResponse = prepareSearch(indexName).setSize(0) .addAggregation(dateRange("date_range").field("date").addRange(1.0e3, 3000.8123).addRange(3000.8123, 4.0e3)) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); @@ -827,8 +794,7 @@ public void testRangeWithFormatNumericValue() throws Exception { // using different format should work when to/from is compatible with // format in aggregation - searchResponse = client().prepareSearch(indexName) - .setSize(0) + searchResponse = prepareSearch(indexName).setSize(0) .addAggregation( dateRange("date_range").field("date").addRange("00.16.40", "00.50.00").addRange("00.50.00", "01.06.40").format("HH.mm.ss") ) @@ -840,8 +806,7 @@ public void testRangeWithFormatNumericValue() throws Exception { // providing different numeric input with format should work, but bucket // keys are different now - searchResponse = client().prepareSearch(indexName) - .setSize(0) + searchResponse = prepareSearch(indexName).setSize(0) .addAggregation( dateRange("date_range").field("date").addRange(1000000, 3000000).addRange(3000000, 4000000).format("epoch_millis") ) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java index ac0dbf6f5d0a6..49e27ff401740 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java @@ -93,8 +93,7 @@ public void testIssue10719() throws Exception { // Tests that we can refer to nested elements under a sample in a path // statement boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .addAggregation( terms("genres").field("genre") .order(BucketOrder.aggregation("sample>max_price.value", asc)) @@ -126,8 +125,7 @@ public void testSimpleDiversity() throws Exception { DiversifiedAggregationBuilder sampleAgg = new DiversifiedAggregationBuilder("sample").shardSize(100); sampleAgg.field("author").maxDocsPerValue(MAX_DOCS_PER_AUTHOR).executionHint(randomExecutionHint()); sampleAgg.subAggregation(terms("authors").field("author")); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(new TermQueryBuilder("genre", "fantasy")) .setFrom(0) .setSize(60) @@ -153,7 +151,7 @@ public void testNestedDiversity() throws Exception { sampleAgg.subAggregation(terms("authors").field("author")); rootTerms.subAggregation(sampleAgg); - SearchResponse response = client().prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH).addAggregation(rootTerms).get(); + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH).addAggregation(rootTerms).get(); assertNoFailures(response); Terms genres = response.getAggregations().get("genres"); List genreBuckets = genres.getBuckets(); @@ -182,10 +180,7 @@ public void testNestedSamples() throws Exception { sampleAgg.subAggregation(terms("genres").field("genre")); rootSample.subAggregation(sampleAgg); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) - .addAggregation(rootSample) - .get(); + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH).addAggregation(rootSample).get(); assertNoFailures(response); Sampler genreSample = response.getAggregations().get("genreSample"); Sampler sample = genreSample.getAggregations().get("sample"); @@ -249,8 +244,7 @@ public void testRidiculousSizeDiversity() throws Exception { DiversifiedAggregationBuilder sampleAgg = new DiversifiedAggregationBuilder("sample").shardSize(Integer.MAX_VALUE); sampleAgg.field("author").maxDocsPerValue(MAX_DOCS_PER_AUTHOR).executionHint(randomExecutionHint()); sampleAgg.subAggregation(terms("authors").field("author")); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(new TermQueryBuilder("genre", "fantasy")) .setFrom(0) .setSize(60) @@ -261,8 +255,7 @@ public void testRidiculousSizeDiversity() throws Exception { sampleAgg = new DiversifiedAggregationBuilder("sample").shardSize(100); sampleAgg.field("author").maxDocsPerValue(Integer.MAX_VALUE).executionHint(randomExecutionHint()); sampleAgg.subAggregation(terms("authors").field("author")); - response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(new TermQueryBuilder("genre", "fantasy")) .setFrom(0) .setSize(60) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java index 1664db2060e0d..7f18e532d7d7a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java @@ -255,14 +255,12 @@ private void getMultiSortDocs(List builders) throws IOExcep public void testSizeIsZero() { IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("high_card_idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .minDocCount(randomInt(1)) - .size(0) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get() + () -> prepareSearch("high_card_idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .minDocCount(randomInt(1)) + .size(0) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get() ); assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [terms]")); } @@ -277,11 +275,9 @@ public void testMultiValueFieldWithPartitionedFiltering() throws Exception { private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms - SearchResponse allResponse = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse allResponse = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(allResponse); DoubleTerms terms = allResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -292,13 +288,11 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception final int numPartitions = randomIntBetween(2, 4); Set foundTerms = new HashSet<>(); for (int partition = 0; partition < numPartitions; partition++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(field) - .includeExclude(new IncludeExclude(partition, numPartitions)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(field) + .includeExclude(new IncludeExclude(partition, numPartitions)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -312,13 +306,11 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -337,13 +329,11 @@ public void testSingleValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -366,13 +356,11 @@ public void testMultiValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "(long) (_value / 1000 + 1)", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "(long) (_value / 1000 + 1)", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -406,20 +394,18 @@ public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { */ public void testScriptSingleValue() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) - .userValueTypeHint(ValueType.DOUBLE) - .script( - new Script( - ScriptType.INLINE, - CustomScriptPlugin.NAME, - "doc['" + MULTI_VALUED_FIELD_NAME + "'].value", - Collections.emptyMap() - ) + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) + .userValueTypeHint(ValueType.DOUBLE) + .script( + new Script( + ScriptType.INLINE, + CustomScriptPlugin.NAME, + "doc['" + MULTI_VALUED_FIELD_NAME + "'].value", + Collections.emptyMap() ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -438,20 +424,13 @@ public void testScriptSingleValue() throws Exception { } public void testScriptMultiValued() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) - .userValueTypeHint(ValueType.DOUBLE) - .script( - new Script( - ScriptType.INLINE, - CustomScriptPlugin.NAME, - "doc['" + MULTI_VALUED_FIELD_NAME + "']", - Collections.emptyMap() - ) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) + .userValueTypeHint(ValueType.DOUBLE) + .script( + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['" + MULTI_VALUED_FIELD_NAME + "']", Collections.emptyMap()) + ) + ).get(); assertNoFailures(response); @@ -524,18 +503,16 @@ public void testPartiallyUnmappedWithFormat() throws Exception { public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithSubTermsAgg() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("avg_i", asc)) - .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) - .subAggregation( - new TermsAggregationBuilder("subTerms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("avg_i", asc)) + .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) + .subAggregation( + new TermsAggregationBuilder("subTerms").field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ).get(); assertNoFailures(response); @@ -569,14 +546,12 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithSubTer public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("num_tags").field("num_tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter", asc)) - .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("num_tags").field("num_tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter", asc)) + .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) + ).get(); assertNoFailures(response); @@ -606,18 +581,16 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("tags").field("num_tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter1>filter2>max", asc)) - .subAggregation( - filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( - filter("filter2", QueryBuilders.matchAllQuery()).subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME)) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("tags").field("num_tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter1>filter2>max", asc)) + .subAggregation( + filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( + filter("filter2", QueryBuilders.matchAllQuery()).subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME)) ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -663,13 +636,11 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("avg_i", true)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("avg_i", true)) + ).get(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation that doesn't exist"); @@ -682,17 +653,14 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("num_tags", true)) - .subAggregation( - new TermsAggregationBuilder("num_tags").field("num_tags") - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("num_tags", true)) + .subAggregation( + new TermsAggregationBuilder("num_tags").field("num_tags").collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ).get(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation which is not of a metrics type"); @@ -705,14 +673,12 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUnknownMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME + "2") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.foo", true)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME + "2") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.foo", true)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); fail( "Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " @@ -728,14 +694,12 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUnknownMe public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats", true)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats", true)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); fail( "Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " @@ -750,14 +714,12 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.avg", asc)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.avg", asc)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -780,14 +742,12 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws Exception { boolean asc = false; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.avg", asc)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.avg", asc)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -810,14 +770,12 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.variance", asc)) - .subAggregation(extendedStats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.variance", asc)) + .subAggregation(extendedStats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -854,8 +812,7 @@ public void testScriptScore() { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(functionScoreQuery(scriptFunction(scoringScript))) + SearchResponse response = prepareSearch("idx").setQuery(functionScoreQuery(scriptFunction(scoringScript))) .addAggregation( new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) .userValueTypeHint(ValueType.DOUBLE) @@ -920,15 +877,13 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound } private void assertMultiSortResponse(double[] expectedKeys, BucketOrder... order) { - SearchResponse response = client().prepareSearch("sort_idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.compound(order)) - .subAggregation(avg("avg_l").field("l")) - .subAggregation(sum("sum_d").field("d")) - ) - .get(); + SearchResponse response = prepareSearch("sort_idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")) + .subAggregation(sum("sum_d").field("d")) + ).get(); assertNoFailures(response); @@ -983,8 +938,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( new TermsAggregationBuilder("terms").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) @@ -1002,8 +956,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( new TermsAggregationBuilder("terms").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -1021,7 +974,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java index 065bb5924e049..5971e287882f2 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FilterIT.java @@ -79,7 +79,7 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSimple() throws Exception { - SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", termQuery("tag", "tag1"))).get(); + SearchResponse response = prepareSearch("idx").addAggregation(filter("tag1", termQuery("tag", "tag1"))).get(); assertNoFailures(response); @@ -93,7 +93,7 @@ public void testSimple() throws Exception { // https://github.com/elastic/elasticsearch/issues/8438 public void testEmptyFilterDeclarations() throws Exception { QueryBuilder emptyFilter = new BoolQueryBuilder(); - SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", emptyFilter)).get(); + SearchResponse response = prepareSearch("idx").addAggregation(filter("tag1", emptyFilter)).get(); assertNoFailures(response); @@ -103,9 +103,9 @@ public void testEmptyFilterDeclarations() throws Exception { } public void testWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(filter("tag1", termQuery("tag", "tag1")).subAggregation(avg("avg_value").field("value"))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filter("tag1", termQuery("tag", "tag1")).subAggregation(avg("avg_value").field("value")) + ).get(); assertNoFailures(response); @@ -128,9 +128,9 @@ public void testWithSubAggregation() throws Exception { } public void testAsSubAggregation() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field("value").interval(2L).subAggregation(filter("filter", matchAllQuery()))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field("value").interval(2L).subAggregation(filter("filter", matchAllQuery())) + ).get(); assertNoFailures(response); @@ -147,7 +147,7 @@ public void testAsSubAggregation() { public void testWithContextBasedSubAggregation() throws Exception { try { - client().prepareSearch("idx").addAggregation(filter("tag1", termQuery("tag", "tag1")).subAggregation(avg("avg_value"))).get(); + prepareSearch("idx").addAggregation(filter("tag1", termQuery("tag", "tag1")).subAggregation(avg("avg_value"))).get(); fail( "expected execution to fail - an attempt to have a context based numeric sub-aggregation, but there is not value source" @@ -160,8 +160,7 @@ public void testWithContextBasedSubAggregation() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation(histogram("histo").field("value").interval(1L).minDocCount(0).subAggregation(filter("filter", matchAllQuery()))) .get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java index 9e86c4ec80d4d..fa8974371a935 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/FiltersIT.java @@ -98,14 +98,12 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSimple() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filters( - "tags", - randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + filters( + "tags", + randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) ) - .get(); + ).get(); assertNoFailures(response); @@ -128,11 +126,9 @@ public void testSimple() throws Exception { // https://github.com/elastic/elasticsearch/issues/8438 public void testEmptyFilterDeclarations() throws Exception { QueryBuilder emptyFilter = new BoolQueryBuilder(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filters("tags", randomOrder(new KeyedFilter("all", emptyFilter), new KeyedFilter("tag1", termQuery("tag", "tag1")))) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filters("tags", randomOrder(new KeyedFilter("all", emptyFilter), new KeyedFilter("tag1", termQuery("tag", "tag1")))) + ).get(); assertNoFailures(response); @@ -147,14 +143,12 @@ public void testEmptyFilterDeclarations() throws Exception { } public void testWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filters( - "tags", - randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) - ).subAggregation(avg("avg_value").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filters( + "tags", + randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) + ).subAggregation(avg("avg_value").field("value")) + ).get(); assertNoFailures(response); @@ -202,9 +196,9 @@ public void testWithSubAggregation() throws Exception { } public void testAsSubAggregation() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field("value").interval(2L).subAggregation(filters("filters", matchAllQuery()))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field("value").interval(2L).subAggregation(filters("filters", matchAllQuery())) + ).get(); assertNoFailures(response); @@ -224,14 +218,12 @@ public void testAsSubAggregation() { public void testWithContextBasedSubAggregation() throws Exception { try { - client().prepareSearch("idx") - .addAggregation( - filters( - "tags", - randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) - ).subAggregation(avg("avg_value")) - ) - .get(); + prepareSearch("idx").addAggregation( + filters( + "tags", + randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) + ).subAggregation(avg("avg_value")) + ).get(); fail( "expected execution to fail - an attempt to have a context based numeric sub-aggregation, but there is not value source" @@ -244,8 +236,7 @@ public void testWithContextBasedSubAggregation() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -269,8 +260,7 @@ public void testEmptyAggregation() throws Exception { } public void testSimpleNonKeyed() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(filters("tags", termQuery("tag", "tag1"), termQuery("tag", "tag2"))) + SearchResponse response = prepareSearch("idx").addAggregation(filters("tags", termQuery("tag", "tag1"), termQuery("tag", "tag2"))) .get(); assertNoFailures(response); @@ -294,14 +284,12 @@ public void testSimpleNonKeyed() throws Exception { } public void testOtherBucket() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filters( - "tags", - randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) - ).otherBucket(true) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filters( + "tags", + randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) + ).otherBucket(true) + ).get(); assertNoFailures(response); @@ -325,14 +313,12 @@ public void testOtherBucket() throws Exception { } public void testOtherNamedBucket() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filters( - "tags", - randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) - ).otherBucket(true).otherBucketKey("foobar") - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filters( + "tags", + randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) + ).otherBucket(true).otherBucketKey("foobar") + ).get(); assertNoFailures(response); @@ -356,9 +342,9 @@ public void testOtherNamedBucket() throws Exception { } public void testOtherNonKeyed() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(filters("tags", termQuery("tag", "tag1"), termQuery("tag", "tag2")).otherBucket(true)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filters("tags", termQuery("tag", "tag1"), termQuery("tag", "tag2")).otherBucket(true) + ).get(); assertNoFailures(response); @@ -385,14 +371,12 @@ public void testOtherNonKeyed() throws Exception { } public void testOtherWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filters( - "tags", - randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) - ).otherBucket(true).subAggregation(avg("avg_value").field("value")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + filters( + "tags", + randomOrder(new KeyedFilter("tag1", termQuery("tag", "tag1")), new KeyedFilter("tag2", termQuery("tag", "tag2"))) + ).otherBucket(true).subAggregation(avg("avg_value").field("value")) + ).get(); assertNoFailures(response); @@ -456,8 +440,7 @@ public void testOtherWithSubAggregation() throws Exception { } public void testEmptyAggregationWithOtherBucket() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java index a7dee3b1507ee..64441466c7954 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java @@ -142,7 +142,7 @@ public void testSimple() throws Exception { for (Consumer range : ranges) { range.accept(builder); } - SearchResponse response = client().prepareSearch("idx").addAggregation(builder).get(); + SearchResponse response = prepareSearch("idx").addAggregation(builder).get(); assertNoFailures(response); @@ -181,15 +181,13 @@ public void testSimple() throws Exception { } public void testSimpleWithCustomKeys() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") - .unit(DistanceUnit.KILOMETERS) - .addUnboundedTo("ring1", 500) - .addRange("ring2", 500, 1000) - .addUnboundedFrom("ring3", 1000) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") + .unit(DistanceUnit.KILOMETERS) + .addUnboundedTo("ring1", 500) + .addRange("ring2", 500, 1000) + .addUnboundedFrom("ring3", 1000) + ).get(); assertNoFailures(response); @@ -230,15 +228,13 @@ public void testSimpleWithCustomKeys() throws Exception { public void testUnmapped() throws Exception { clusterAdmin().prepareHealth("idx_unmapped").setWaitForYellowStatus().get(); - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation( - geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") - .unit(DistanceUnit.KILOMETERS) - .addUnboundedTo(500) - .addRange(500, 1000) - .addUnboundedFrom(1000) - ) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") + .unit(DistanceUnit.KILOMETERS) + .addUnboundedTo(500) + .addRange(500, 1000) + .addUnboundedFrom(1000) + ).get(); assertNoFailures(response); @@ -324,16 +320,14 @@ public void testPartiallyUnmapped() throws Exception { } public void testWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") - .unit(DistanceUnit.KILOMETERS) - .addUnboundedTo(500) - .addRange(500, 1000) - .addUnboundedFrom(1000) - .subAggregation(terms("cities").field("city").collectMode(randomFrom(SubAggCollectionMode.values()))) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") + .unit(DistanceUnit.KILOMETERS) + .addUnboundedTo(500) + .addRange(500, 1000) + .addUnboundedFrom(1000) + .subAggregation(terms("cities").field("city").collectMode(randomFrom(SubAggCollectionMode.values()))) + ).get(); assertNoFailures(response); @@ -409,8 +403,7 @@ public void testWithSubAggregation() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -441,7 +434,7 @@ public void testEmptyAggregation() throws Exception { public void testNoRangesInQuery() { try { - client().prepareSearch("idx").addAggregation(geoDistance("geo_dist", new GeoPoint(52.3760, 4.894)).field("location")).get(); + prepareSearch("idx").addAggregation(geoDistance("geo_dist", new GeoPoint(52.3760, 4.894)).field("location")).get(); fail(); } catch (SearchPhaseExecutionException spee) { Throwable rootCause = spee.getCause().getCause(); @@ -451,16 +444,14 @@ public void testNoRangesInQuery() { } public void testMultiValues() throws Exception { - SearchResponse response = client().prepareSearch("idx-multi") - .addAggregation( - geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") - .unit(DistanceUnit.KILOMETERS) - .distanceType(org.elasticsearch.common.geo.GeoDistance.ARC) - .addUnboundedTo(500) - .addRange(500, 1000) - .addUnboundedFrom(1000) - ) - .get(); + SearchResponse response = prepareSearch("idx-multi").addAggregation( + geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location") + .unit(DistanceUnit.KILOMETERS) + .distanceType(org.elasticsearch.common.geo.GeoDistance.ARC) + .addUnboundedTo(500) + .addRange(500, 1000) + .addUnboundedFrom(1000) + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java index 078cc9761fb26..2767a1878c11a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java @@ -132,8 +132,7 @@ public void setupSuiteScopeCluster() throws Exception { public void testSimple() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + SearchResponse response = prepareSearch("idx").addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) .get(); assertNoFailures(response); @@ -159,9 +158,9 @@ public void testSimple() throws Exception { public void testMultivalued() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("multi_valued_idx") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - .get(); + SearchResponse response = prepareSearch("multi_valued_idx").addAggregation( + geohashGrid("geohashgrid").field("location").precision(precision) + ).get(); assertNoFailures(response); @@ -181,12 +180,10 @@ public void testFiltered() throws Exception { GeoBoundingBoxQueryBuilder bbox = new GeoBoundingBoxQueryBuilder("location"); bbox.setCorners(smallestGeoHash).queryName("bbox"); for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.filter("filtered", bbox) - .subAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + AggregationBuilders.filter("filtered", bbox) + .subAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + ).get(); assertNoFailures(response); @@ -207,9 +204,9 @@ public void testFiltered() throws Exception { public void testUnmapped() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + geohashGrid("geohashgrid").field("location").precision(precision) + ).get(); assertNoFailures(response); @@ -241,9 +238,9 @@ public void testPartiallyUnmapped() throws Exception { public void testTopMatch() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(1).shardSize(100).precision(precision)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + geohashGrid("geohashgrid").field("location").size(1).shardSize(100).precision(precision) + ).get(); assertNoFailures(response); @@ -270,9 +267,7 @@ public void testSizeIsZero() { final int shardSize = 10000; IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) - .get() + () -> prepareSearch("idx").addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)).get() ); assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [geohashgrid]")); } @@ -282,9 +277,7 @@ public void testShardSizeIsZero() { final int shardSize = 0; IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) - .get() + () -> prepareSearch("idx").addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)).get() ); assertThat(exception.getMessage(), containsString("[shardSize] must be greater than 0. Found [0] in [geohashgrid]")); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java index 80133961ad73a..347b2324027c0 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GlobalIT.java @@ -60,8 +60,7 @@ public void setupSuiteScopeCluster() throws Exception { } public void testWithStatsSubAggregator() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.termQuery("tag", "tag1")) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.termQuery("tag", "tag1")) .addAggregation(global("global").subAggregation(stats("value_stats").field("value"))) .get(); @@ -91,8 +90,7 @@ public void testWithStatsSubAggregator() throws Exception { public void testNonTopLevel() throws Exception { try { - client().prepareSearch("idx") - .setQuery(QueryBuilders.termQuery("tag", "tag1")) + prepareSearch("idx").setQuery(QueryBuilders.termQuery("tag", "tag1")) .addAggregation(global("global").subAggregation(global("inner_global"))) .get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java index b92320c48ed1c..f644c89ddbc58 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java @@ -234,8 +234,7 @@ private void getMultiSortDocs(List builders) throws IOExcep } public void testSingleValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) + SearchResponse response = prepareSearch("idx").addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) .get(); assertNoFailures(response); @@ -257,9 +256,9 @@ public void testSingleValuedField() throws Exception { public void singleValuedField_withOffset() throws Exception { int interval1 = 10; int offset = 5; - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval1).offset(offset)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval1).offset(offset) + ).get(); // from setup we have between 6 and 20 documents, each with value 1 in test field int expectedNumberOfBuckets = (offset >= (numDocs % interval + 1)) ? numValueBuckets : numValueBuckets + 1; @@ -287,9 +286,9 @@ public void singleValuedField_withOffset() throws Exception { */ public void testSingleValuedFieldWithRandomOffset() throws Exception { int offset = randomIntBetween(2, interval); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).offset(offset)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).offset(offset) + ).get(); assertNoFailures(response); // shifting by offset>2 creates new extra bucket [0,offset-1] // if offset is >= number of values in original last bucket, that effect is canceled @@ -320,9 +319,9 @@ public void testSingleValuedFieldWithRandomOffset() throws Exception { } public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(true))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(true)) + ).get(); assertNoFailures(response); @@ -341,9 +340,9 @@ public void testSingleValuedFieldOrderedByKeyAsc() throws Exception { } public void testsingleValuedFieldOrderedByKeyDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false)) + ).get(); assertNoFailures(response); @@ -362,9 +361,9 @@ public void testsingleValuedFieldOrderedByKeyDesc() throws Exception { } public void testSingleValuedFieldOrderedByCountAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(true))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(true)) + ).get(); assertNoFailures(response); @@ -389,9 +388,9 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { } public void testSingleValuedFieldOrderedByCountDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(false))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.count(false)) + ).get(); assertNoFailures(response); @@ -416,13 +415,9 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -458,14 +453,12 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { } public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .order(BucketOrder.aggregation("sum", true)) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("sum", true)) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -500,14 +493,12 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { } public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .order(BucketOrder.aggregation("sum", false)) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("sum", false)) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -542,14 +533,12 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception } public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .order(BucketOrder.aggregation("stats.sum", false)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("stats.sum", false)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -586,14 +575,12 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .order(BucketOrder.aggregation("filter>max", asc)) - .subAggregation(filter("filter", matchAllQuery()).subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME))) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("filter>max", asc)) + .subAggregation(filter("filter", matchAllQuery()).subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME))) + ).get(); assertNoFailures(response); @@ -624,14 +611,12 @@ public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() thro } public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .order(BucketOrder.aggregation("max_constant", randomBoolean())) - .subAggregation(max("max_constant").field("constant")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("max_constant", randomBoolean())) + .subAggregation(max("max_constant").field("constant")) + ).get(); assertNoFailures(response); @@ -652,18 +637,14 @@ public void testSingleValuedFieldOrderedByTieBreaker() throws Exception { public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { boolean asc = true; try { - client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .order(BucketOrder.aggregation("inner_histo>avg", asc)) - .subAggregation( - histogram("inner_histo").interval(interval) - .field(MULTI_VALUED_FIELD_NAME) - .subAggregation(avg("avg").field("value")) - ) - ) - .get(); + prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .order(BucketOrder.aggregation("inner_histo>avg", asc)) + .subAggregation( + histogram("inner_histo").interval(interval).field(MULTI_VALUED_FIELD_NAME).subAggregation(avg("avg").field("value")) + ) + ).get(); fail("Expected an exception"); } catch (SearchPhaseExecutionException e) { ElasticsearchException[] rootCauses = e.guessRootCauses(); @@ -681,13 +662,11 @@ public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())) - .interval(interval) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())) + .interval(interval) + ).get(); assertNoFailures(response); @@ -713,8 +692,7 @@ public void testSingleValuedFieldWithValueScript() throws Exception { } public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval)) + SearchResponse response = prepareSearch("idx").addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval)) .get(); assertNoFailures(response); @@ -734,9 +712,9 @@ public void testMultiValuedField() throws Exception { } public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(MULTI_VALUED_FIELD_NAME).interval(interval).order(BucketOrder.key(false)) + ).get(); assertNoFailures(response); @@ -755,13 +733,11 @@ public void testMultiValuedFieldOrderedByKeyDesc() throws Exception { } public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(MULTI_VALUED_FIELD_NAME) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())) - .interval(interval) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(MULTI_VALUED_FIELD_NAME) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())) + .interval(interval) + ).get(); assertNoFailures(response); @@ -792,12 +768,10 @@ public void testMultiValuedFieldWithValueScript() throws Exception { } public void testScriptSingleValue() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['l_value'].value", emptyMap())) - .interval(interval) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['l_value'].value", emptyMap())) + .interval(interval) + ).get(); assertNoFailures(response); @@ -816,12 +790,10 @@ public void testScriptSingleValue() throws Exception { } public void testScriptMultiValued() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['l_values']", emptyMap())) - .interval(interval) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['l_values']", emptyMap())) + .interval(interval) + ).get(); assertNoFailures(response); @@ -840,9 +812,9 @@ public void testScriptMultiValued() throws Exception { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval) + ).get(); assertNoFailures(response); @@ -909,8 +881,7 @@ public void testPartiallyUnmappedWithExtendedBounds() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME) .interval(1L) @@ -968,14 +939,9 @@ public void testSingleValuedFieldWithExtendedBounds() throws Exception { SearchResponse response = null; try { - response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .minDocCount(0) - .extendedBounds(boundsMin, boundsMax) - ) - .get(); + response = prepareSearch("idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval).minDocCount(0).extendedBounds(boundsMin, boundsMax) + ).get(); if (invalidBoundsError) { fail("Expected an exception to be thrown when bounds.min is greater than bounds.max"); @@ -1044,8 +1010,7 @@ public void testEmptyWithExtendedBounds() throws Exception { SearchResponse response = null; try { - response = client().prepareSearch("idx") - .setQuery(QueryBuilders.termQuery("foo", "bar")) + response = prepareSearch("idx").setQuery(QueryBuilders.termQuery("foo", "bar")) .addAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME) .interval(interval) @@ -1090,8 +1055,7 @@ public void testEmptyWithExtendedBounds() throws Exception { */ public void testExeptionOnNegativerInterval() { try { - client().prepareSearch("empty_bucket_idx") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(-1).minDocCount(0)) + prepareSearch("empty_bucket_idx").addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(-1).minDocCount(0)) .get(); fail(); } catch (IllegalArgumentException e) { @@ -1107,9 +1071,7 @@ public void testDecimalIntervalAndOffset() throws Exception { client().prepareIndex("decimal_values").setId("2").setSource("d", 0.1) ); - SearchResponse r = client().prepareSearch("decimal_values") - .addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05)) - .get(); + SearchResponse r = prepareSearch("decimal_values").addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05)).get(); assertNoFailures(r); Histogram histogram = r.getAggregations().get("histo"); @@ -1148,8 +1110,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( histogram("histo").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", emptyMap())) @@ -1169,8 +1130,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( histogram("histo").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", emptyMap())) @@ -1190,10 +1150,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx") - .setSize(0) - .addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05)) - .get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(histogram("histo").field("d").interval(0.7).offset(0.05)).get(); assertNoFailures(r); assertThat( @@ -1249,21 +1206,17 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound public void testInvalidBounds() { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("empty_bucket_idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME).hardBounds(new DoubleBounds(0.0, 10.0)).extendedBounds(3, 20) - ) - .get() + () -> prepareSearch("empty_bucket_idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).hardBounds(new DoubleBounds(0.0, 10.0)).extendedBounds(3, 20) + ).get() ); assertThat(e.toString(), containsString("Extended bounds have to be inside hard bounds, hard bounds")); e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("empty_bucket_idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME).hardBounds(new DoubleBounds(3.0, null)).extendedBounds(0, 20) - ) - .get() + () -> prepareSearch("empty_bucket_idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME).hardBounds(new DoubleBounds(3.0, null)).extendedBounds(0, 20) + ).get() ); assertThat(e.toString(), containsString("Extended bounds have to be inside hard bounds, hard bounds")); } @@ -1277,9 +1230,9 @@ public void testHardBounds() throws Exception { client().prepareIndex("test").setId("3").setSource("d", 0.1) ); - SearchResponse r = client().prepareSearch("test") - .addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(0.0, null))) - .get(); + SearchResponse r = prepareSearch("test").addAggregation( + histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(0.0, null)) + ).get(); assertNoFailures(r); Histogram histogram = r.getAggregations().get("histo"); @@ -1288,9 +1241,7 @@ public void testHardBounds() throws Exception { assertEquals(0.1, (double) buckets.get(0).getKey(), 0.01d); assertEquals(0.5, (double) buckets.get(4).getKey(), 0.01d); - r = client().prepareSearch("test") - .addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(null, 0.0))) - .get(); + r = prepareSearch("test").addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(null, 0.0))).get(); assertNoFailures(r); histogram = r.getAggregations().get("histo"); @@ -1298,9 +1249,7 @@ public void testHardBounds() throws Exception { assertEquals(1, buckets.size()); assertEquals(-0.6, (double) buckets.get(0).getKey(), 0.01d); - r = client().prepareSearch("test") - .addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(0.0, 0.3))) - .get(); + r = prepareSearch("test").addAggregation(histogram("histo").field("d").interval(0.1).hardBounds(new DoubleBounds(0.0, 0.3))).get(); assertNoFailures(r); histogram = r.getAggregations().get("histo"); @@ -1311,15 +1260,13 @@ public void testHardBounds() throws Exception { } private void assertMultiSortResponse(long[] expectedKeys, BucketOrder... order) { - SearchResponse response = client().prepareSearch("sort_idx") - .addAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(1) - .order(BucketOrder.compound(order)) - .subAggregation(avg("avg_l").field("l")) - .subAggregation(sum("sum_d").field("d")) - ) - .get(); + SearchResponse response = prepareSearch("sort_idx").addAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(1) + .order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")) + .subAggregation(sum("sum_d").field("d")) + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java index 1768773ba7696..0a076721c2e0c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java @@ -64,15 +64,13 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSingleValuedField() { - SearchResponse rsp = client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.ipRange("my_range") - .field("ip") - .addUnboundedTo("192.168.1.0") - .addRange("192.168.1.0", "192.168.1.10") - .addUnboundedFrom("192.168.1.10") - ) - .get(); + SearchResponse rsp = prepareSearch("idx").addAggregation( + AggregationBuilders.ipRange("my_range") + .field("ip") + .addUnboundedTo("192.168.1.0") + .addRange("192.168.1.0", "192.168.1.10") + .addUnboundedFrom("192.168.1.10") + ).get(); assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -97,15 +95,13 @@ public void testSingleValuedField() { } public void testMultiValuedField() { - SearchResponse rsp = client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.ipRange("my_range") - .field("ips") - .addUnboundedTo("192.168.1.0") - .addRange("192.168.1.0", "192.168.1.10") - .addUnboundedFrom("192.168.1.10") - ) - .get(); + SearchResponse rsp = prepareSearch("idx").addAggregation( + AggregationBuilders.ipRange("my_range") + .field("ips") + .addUnboundedTo("192.168.1.0") + .addRange("192.168.1.0", "192.168.1.10") + .addUnboundedFrom("192.168.1.10") + ).get(); assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -130,15 +126,13 @@ public void testMultiValuedField() { } public void testIpMask() { - SearchResponse rsp = client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.ipRange("my_range") - .field("ips") - .addMaskRange("::/0") - .addMaskRange("0.0.0.0/0") - .addMaskRange("2001:db8::/64") - ) - .get(); + SearchResponse rsp = prepareSearch("idx").addAggregation( + AggregationBuilders.ipRange("my_range") + .field("ips") + .addMaskRange("::/0") + .addMaskRange("0.0.0.0/0") + .addMaskRange("2001:db8::/64") + ).get(); assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -190,15 +184,13 @@ public void testPartiallyUnmapped() { } public void testUnmapped() { - SearchResponse rsp = client().prepareSearch("idx_unmapped") - .addAggregation( - AggregationBuilders.ipRange("my_range") - .field("ip") - .addUnboundedTo("192.168.1.0") - .addRange("192.168.1.0", "192.168.1.10") - .addUnboundedFrom("192.168.1.10") - ) - .get(); + SearchResponse rsp = prepareSearch("idx_unmapped").addAggregation( + AggregationBuilders.ipRange("my_range") + .field("ip") + .addUnboundedTo("192.168.1.0") + .addRange("192.168.1.0", "192.168.1.10") + .addUnboundedFrom("192.168.1.10") + ).get(); assertNoFailures(rsp); Range range = rsp.getAggregations().get("my_range"); assertEquals(3, range.getBuckets().size()); @@ -225,12 +217,9 @@ public void testUnmapped() { public void testRejectsScript() { IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.ipRange("my_range") - .script(new Script(ScriptType.INLINE, "mockscript", "dummy", Collections.emptyMap())) - ) - .get() + () -> prepareSearch("idx").addAggregation( + AggregationBuilders.ipRange("my_range").script(new Script(ScriptType.INLINE, "mockscript", "dummy", Collections.emptyMap())) + ).get() ); assertThat(e.getMessage(), containsString("[ip_range] does not support scripts")); } @@ -238,20 +227,18 @@ public void testRejectsScript() { public void testRejectsValueScript() { IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.ipRange("my_range") - .field("ip") - .script(new Script(ScriptType.INLINE, "mockscript", "dummy", Collections.emptyMap())) - ) - .get() + () -> prepareSearch("idx").addAggregation( + AggregationBuilders.ipRange("my_range") + .field("ip") + .script(new Script(ScriptType.INLINE, "mockscript", "dummy", Collections.emptyMap())) + ).get() ); assertThat(e.getMessage(), containsString("[ip_range] does not support scripts")); } public void testNoRangesInQuery() { try { - client().prepareSearch("idx").addAggregation(AggregationBuilders.ipRange("my_range").field("ip")).get(); + prepareSearch("idx").addAggregation(AggregationBuilders.ipRange("my_range").field("ip")).get(); fail(); } catch (SearchPhaseExecutionException spee) { Throwable rootCause = spee.getCause().getCause(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java index 51733d98fd80a..d50ea294287eb 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpTermsIT.java @@ -61,9 +61,9 @@ public void testScriptValue() throws Exception { ); Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['ip'].value", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("index") - .addAggregation(new TermsAggregationBuilder("my_terms").script(script).executionHint(randomExecutionHint())) - .get(); + SearchResponse response = prepareSearch("index").addAggregation( + new TermsAggregationBuilder("my_terms").script(script).executionHint(randomExecutionHint()) + ).get(); assertNoFailures(response); StringTerms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); @@ -89,9 +89,9 @@ public void testScriptValues() throws Exception { ); Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['ip']", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("index") - .addAggregation(new TermsAggregationBuilder("my_terms").script(script).executionHint(randomExecutionHint())) - .get(); + SearchResponse response = prepareSearch("index").addAggregation( + new TermsAggregationBuilder("my_terms").script(script).executionHint(randomExecutionHint()) + ).get(); assertNoFailures(response); StringTerms terms = response.getAggregations().get("my_terms"); assertEquals(2, terms.getBuckets().size()); @@ -116,9 +116,9 @@ public void testMissingValue() throws Exception { client().prepareIndex("index").setId("3").setSource("ip", "127.0.0.1"), client().prepareIndex("index").setId("4").setSource("not_ip", "something") ); - SearchResponse response = client().prepareSearch("index") - .addAggregation(new TermsAggregationBuilder("my_terms").field("ip").missing("127.0.0.1").executionHint(randomExecutionHint())) - .get(); + SearchResponse response = prepareSearch("index").addAggregation( + new TermsAggregationBuilder("my_terms").field("ip").missing("127.0.0.1").executionHint(randomExecutionHint()) + ).get(); assertNoFailures(response); StringTerms terms = response.getAggregations().get("my_terms"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java index d65a246aedc5d..67f244eabbd3b 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java @@ -243,14 +243,12 @@ private void getMultiSortDocs(List builders) throws IOExcep public void testSizeIsZero() { IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("high_card_idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .minDocCount(randomInt(1)) - .size(0) - ) - .get() + () -> prepareSearch("high_card_idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .minDocCount(randomInt(1)) + .size(0) + ).get() ); assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [terms]")); } @@ -265,9 +263,9 @@ public void testMultiValueFieldWithPartitionedFiltering() throws Exception { private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms - SearchResponse allResponse = client().prepareSearch("idx") - .addAggregation(new TermsAggregationBuilder("terms").field(field).collectMode(randomFrom(SubAggCollectionMode.values()))) - .get(); + SearchResponse allResponse = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(field).collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(allResponse); LongTerms terms = allResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -278,13 +276,11 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception final int numPartitions = randomIntBetween(2, 4); Set foundTerms = new HashSet<>(); for (int partition = 0; partition < numPartitions; partition++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(field) - .includeExclude(new IncludeExclude(partition, numPartitions)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(field) + .includeExclude(new IncludeExclude(partition, numPartitions)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -298,13 +294,11 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -324,13 +318,11 @@ public void testSingleValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value - 1", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value - 1", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -354,13 +346,11 @@ public void testMultiValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "floor(_value / 1000 + 1)", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "floor(_value / 1000 + 1)", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -402,13 +392,11 @@ public void testScriptSingleValue() throws Exception { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) - .userValueTypeHint(ValueType.LONG) - .script(script) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) + .userValueTypeHint(ValueType.LONG) + .script(script) + ).get(); assertNoFailures(response); @@ -435,13 +423,11 @@ public void testScriptMultiValued() throws Exception { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) - .userValueTypeHint(ValueType.LONG) - .script(script) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) + .userValueTypeHint(ValueType.LONG) + .script(script) + ).get(); assertNoFailures(response); @@ -514,18 +500,16 @@ public void testPartiallyUnmappedWithFormat() throws Exception { public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithTermsSubAgg() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("avg_i", asc)) - .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) - .subAggregation( - new TermsAggregationBuilder("subTerms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("avg_i", asc)) + .subAggregation(avg("avg_i").field(SINGLE_VALUED_FIELD_NAME)) + .subAggregation( + new TermsAggregationBuilder("subTerms").field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ).get(); assertNoFailures(response); @@ -559,14 +543,12 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscWithTermsS public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("num_tags").field("num_tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter", asc)) - .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("num_tags").field("num_tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter", asc)) + .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) + ).get(); assertNoFailures(response); @@ -596,18 +578,16 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("tags").field("num_tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter1>filter2>max", asc)) - .subAggregation( - filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( - filter("filter2", QueryBuilders.matchAllQuery()).subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME)) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("tags").field("num_tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter1>filter2>max", asc)) + .subAggregation( + filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( + filter("filter2", QueryBuilders.matchAllQuery()).subAggregation(max("max").field(SINGLE_VALUED_FIELD_NAME)) ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -653,13 +633,11 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels( public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("avg_i", true)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("avg_i", true)) + ).get(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation that doesn't exist"); @@ -672,17 +650,14 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("num_tags", true)) - .subAggregation( - new TermsAggregationBuilder("num_tags").field("num_tags") - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("num_tags", true)) + .subAggregation( + new TermsAggregationBuilder("num_tags").field("num_tags").collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ).get(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation which is not of a metrics type"); @@ -695,14 +670,12 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUnknownMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.foo", true)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.foo", true)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); fail( "Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " @@ -718,14 +691,12 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUnknownMe public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats", true)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats", true)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); fail( "Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " @@ -740,14 +711,12 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.avg", asc)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.avg", asc)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -771,14 +740,12 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws Exception { boolean asc = false; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.avg", asc)) - .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.avg", asc)) + .subAggregation(stats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -802,14 +769,12 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.variance", asc)) - .subAggregation(extendedStats("stats").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.variance", asc)) + .subAggregation(extendedStats("stats").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -872,15 +837,13 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound } private void assertMultiSortResponse(long[] expectedKeys, BucketOrder... order) { - SearchResponse response = client().prepareSearch("sort_idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.compound(order)) - .subAggregation(avg("avg_l").field("l")) - .subAggregation(sum("sum_d").field("d")) - ) - .get(); + SearchResponse response = prepareSearch("sort_idx").addAggregation( + new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")) + .subAggregation(sum("sum_d").field("d")) + ).get(); assertNoFailures(response); @@ -935,8 +898,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( new TermsAggregationBuilder("terms").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) @@ -954,8 +916,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( new TermsAggregationBuilder("terms").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -973,7 +934,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java index 3a49c5ba7132e..36ba2a988668a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java @@ -306,8 +306,7 @@ private void testMinDocCountOnTerms(String field, Script script, BucketOrder ord private void testMinDocCountOnTerms(String field, Script script, BucketOrder order, String include, boolean retry) throws Exception { // all terms - final SearchResponse allTermsResponse = client().prepareSearch("idx") - .setSize(0) + final SearchResponse allTermsResponse = prepareSearch("idx").setSize(0) .setQuery(QUERY) .addAggregation( script.apply(terms("terms"), field) @@ -325,8 +324,7 @@ private void testMinDocCountOnTerms(String field, Script script, BucketOrder ord for (long minDocCount = 0; minDocCount < 20; ++minDocCount) { final int size = randomIntBetween(1, cardinality + 2); - final SearchRequest request = client().prepareSearch("idx") - .setSize(0) + final SearchRequest request = prepareSearch("idx").setSize(0) .setQuery(QUERY) .addAggregation( script.apply(terms("terms"), field) @@ -379,8 +377,7 @@ public void testDateHistogramKeyDesc() throws Exception { private void testMinDocCountOnHistogram(BucketOrder order) throws Exception { final int interval = randomIntBetween(1, 3); - final SearchResponse allResponse = client().prepareSearch("idx") - .setSize(0) + final SearchResponse allResponse = prepareSearch("idx").setSize(0) .setQuery(QUERY) .addAggregation(histogram("histo").field("d").interval(interval).order(order).minDocCount(0)) .get(); @@ -388,8 +385,7 @@ private void testMinDocCountOnHistogram(BucketOrder order) throws Exception { final Histogram allHisto = allResponse.getAggregations().get("histo"); for (long minDocCount = 0; minDocCount < 50; ++minDocCount) { - final SearchResponse response = client().prepareSearch("idx") - .setSize(0) + final SearchResponse response = prepareSearch("idx").setSize(0) .setQuery(QUERY) .addAggregation(histogram("histo").field("d").interval(interval).order(order).minDocCount(minDocCount)) .get(); @@ -398,8 +394,7 @@ private void testMinDocCountOnHistogram(BucketOrder order) throws Exception { } private void testMinDocCountOnDateHistogram(BucketOrder order) throws Exception { - final SearchResponse allResponse = client().prepareSearch("idx") - .setSize(0) + final SearchResponse allResponse = prepareSearch("idx").setSize(0) .setQuery(QUERY) .addAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).order(order).minDocCount(0)) .get(); @@ -407,8 +402,7 @@ private void testMinDocCountOnDateHistogram(BucketOrder order) throws Exception final Histogram allHisto = allResponse.getAggregations().get("histo"); for (long minDocCount = 0; minDocCount < 50; ++minDocCount) { - final SearchResponse response = client().prepareSearch("idx") - .setSize(0) + final SearchResponse response = prepareSearch("idx").setSize(0) .setQuery(QUERY) .addAggregation( dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).order(order).minDocCount(minDocCount) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java index d015825d775d3..eb2ad6de7789e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NaNSortingIT.java @@ -145,14 +145,12 @@ private void assertCorrectlySorted(Histogram histo, boolean asc, SubAggregation public void testTerms(String fieldName) { final boolean asc = randomBoolean(); SubAggregation agg = randomFrom(SubAggregation.values()); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").field(fieldName) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(agg.builder()) - .order(BucketOrder.aggregation(agg.sortKey(), asc)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").field(fieldName) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(agg.builder()) + .order(BucketOrder.aggregation(agg.sortKey(), asc)) + ).get(); assertNoFailures(response); final Terms terms = response.getAggregations().get("terms"); @@ -174,14 +172,12 @@ public void testDoubleTerms() { public void testLongHistogram() { final boolean asc = randomBoolean(); SubAggregation agg = randomFrom(SubAggregation.values()); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field("long_value") - .interval(randomIntBetween(1, 2)) - .subAggregation(agg.builder()) - .order(BucketOrder.aggregation(agg.sortKey(), asc)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field("long_value") + .interval(randomIntBetween(1, 2)) + .subAggregation(agg.builder()) + .order(BucketOrder.aggregation(agg.sortKey(), asc)) + ).get(); assertNoFailures(response); final Histogram histo = response.getAggregations().get("histo"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java index e9ec509e3b4c7..2ab107c2580c7 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java @@ -177,9 +177,9 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSimple() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(nested("nested", "nested").subAggregation(stats("nested_value_stats").field("nested.value"))) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + nested("nested", "nested").subAggregation(stats("nested_value_stats").field("nested.value")) + ).get(); assertNoFailures(response); @@ -213,9 +213,9 @@ public void testSimple() throws Exception { } public void testNonExistingNestedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .addAggregation(nested("nested", "value").subAggregation(stats("nested_value_stats").field("nested.value"))) - .get(); + SearchResponse searchResponse = prepareSearch("idx").addAggregation( + nested("nested", "value").subAggregation(stats("nested_value_stats").field("nested.value")) + ).get(); Nested nested = searchResponse.getAggregations().get("nested"); assertThat(nested, Matchers.notNullValue()); @@ -224,11 +224,9 @@ public void testNonExistingNestedField() throws Exception { } public void testNestedWithSubTermsAgg() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - nested("nested", "nested").subAggregation(terms("values").field("nested.value").size(100).collectMode(aggCollectionMode)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + nested("nested", "nested").subAggregation(terms("values").field("nested.value").size(100).collectMode(aggCollectionMode)) + ).get(); assertNoFailures(response); @@ -274,14 +272,12 @@ public void testNestedWithSubTermsAgg() throws Exception { } public void testNestedAsSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("top_values").field("value") - .size(100) - .collectMode(aggCollectionMode) - .subAggregation(nested("nested", "nested").subAggregation(max("max_value").field("nested.value"))) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("top_values").field("value") + .size(100) + .collectMode(aggCollectionMode) + .subAggregation(nested("nested", "nested").subAggregation(max("max_value").field("nested.value"))) + ).get(); assertNoFailures(response); @@ -303,15 +299,13 @@ public void testNestedAsSubAggregation() throws Exception { } public void testNestNestedAggs() throws Exception { - SearchResponse response = client().prepareSearch("idx_nested_nested_aggs") - .addAggregation( - nested("level1", "nested1").subAggregation( - terms("a").field("nested1.a.keyword") - .collectMode(aggCollectionMode) - .subAggregation(nested("level2", "nested1.nested2").subAggregation(sum("sum").field("nested1.nested2.b"))) - ) + SearchResponse response = prepareSearch("idx_nested_nested_aggs").addAggregation( + nested("level1", "nested1").subAggregation( + terms("a").field("nested1.a.keyword") + .collectMode(aggCollectionMode) + .subAggregation(nested("level2", "nested1.nested2").subAggregation(sum("sum").field("nested1.nested2.b"))) ) - .get(); + ).get(); assertNoFailures(response); Nested level1 = response.getAggregations().get("level1"); @@ -339,8 +333,7 @@ public void testNestNestedAggs() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation(histogram("histo").field("value").interval(1L).minDocCount(0).subAggregation(nested("nested", "nested"))) .get(); @@ -475,26 +468,22 @@ public void testParentFilterResolvedCorrectly() throws Exception { }""", XContentType.JSON)); indexRandom(true, indexRequests); - SearchResponse response = client().prepareSearch("idx2") - .addAggregation( - terms("startDate").field("dates.month.start") - .subAggregation( - terms("endDate").field("dates.month.end") - .subAggregation( - terms("period").field("dates.month.label") - .subAggregation( - nested("ctxt_idfier_nested", "comments").subAggregation( - filter("comment_filter", termQuery("comments.identifier", "29111")).subAggregation( - nested("nested_tags", "comments.tags").subAggregation( - terms("tag").field("comments.tags.name") - ) - ) + SearchResponse response = prepareSearch("idx2").addAggregation( + terms("startDate").field("dates.month.start") + .subAggregation( + terms("endDate").field("dates.month.end") + .subAggregation( + terms("period").field("dates.month.label") + .subAggregation( + nested("ctxt_idfier_nested", "comments").subAggregation( + filter("comment_filter", termQuery("comments.identifier", "29111")).subAggregation( + nested("nested_tags", "comments.tags").subAggregation(terms("tag").field("comments.tags.name")) ) ) - ) - ) - ) - .get(); + ) + ) + ) + ).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -584,12 +573,10 @@ public void testNestedSameDocIdProcessedMultipleTime() throws Exception { .get(); refresh(); - SearchResponse response = client().prepareSearch("idx4") - .addAggregation( - terms("category").field("categories") - .subAggregation(nested("property", "property").subAggregation(terms("property_id").field("property.id"))) - ) - .get(); + SearchResponse response = prepareSearch("idx4").addAggregation( + terms("category").field("categories") + .subAggregation(nested("property", "property").subAggregation(terms("property_id").field("property.id"))) + ).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -760,33 +747,29 @@ public void testFilterAggInsideNestedAgg() throws Exception { .get(); refresh(); - SearchResponse response = client().prepareSearch("classes") - .addAggregation( - nested("to_method", "methods").subAggregation( - filter( - "num_string_params", - nestedQuery("methods.parameters", termQuery("methods.parameters.type", "String"), ScoreMode.None) - ) + SearchResponse response = prepareSearch("classes").addAggregation( + nested("to_method", "methods").subAggregation( + filter( + "num_string_params", + nestedQuery("methods.parameters", termQuery("methods.parameters.type", "String"), ScoreMode.None) ) ) - .get(); + ).get(); Nested toMethods = response.getAggregations().get("to_method"); Filter numStringParams = toMethods.getAggregations().get("num_string_params"); assertThat(numStringParams.getDocCount(), equalTo(3L)); - response = client().prepareSearch("classes") - .addAggregation( - nested("to_method", "methods").subAggregation( - terms("return_type").field("methods.return_type") - .subAggregation( - filter( - "num_string_params", - nestedQuery("methods.parameters", termQuery("methods.parameters.type", "String"), ScoreMode.None) - ) + response = prepareSearch("classes").addAggregation( + nested("to_method", "methods").subAggregation( + terms("return_type").field("methods.return_type") + .subAggregation( + filter( + "num_string_params", + nestedQuery("methods.parameters", termQuery("methods.parameters.type", "String"), ScoreMode.None) ) - ) + ) ) - .get(); + ).get(); toMethods = response.getAggregations().get("to_method"); Terms terms = toMethods.getAggregations().get("return_type"); Bucket bucket = terms.getBucketByKey("void"); @@ -812,14 +795,11 @@ public void testExtractInnerHitBuildersWithDuplicateHitName() throws Exception { ); ensureGreen("idxduplicatehitnames"); - SearchRequestBuilder searchRequestBuilder = client().prepareSearch("idxduplicatehitnames") - .setQuery( - boolQuery().should( - nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder("ih1")) - ) - .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder("ih2"))) - .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder("ih1"))) - ); + SearchRequestBuilder searchRequestBuilder = prepareSearch("idxduplicatehitnames").setQuery( + boolQuery().should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder("ih1"))) + .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder("ih2"))) + .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder("ih1"))) + ); assertFailures( searchRequestBuilder, @@ -835,12 +815,11 @@ public void testExtractInnerHitBuildersWithDuplicatePath() throws Exception { ); ensureGreen("idxnullhitnames"); - SearchRequestBuilder searchRequestBuilder = client().prepareSearch("idxnullhitnames") - .setQuery( - boolQuery().should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder())) - .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder())) - .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder())) - ); + SearchRequestBuilder searchRequestBuilder = prepareSearch("idxnullhitnames").setQuery( + boolQuery().should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder())) + .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder())) + .should(nestedQuery("property", termQuery("property.id", 1D), ScoreMode.None).innerHit(new InnerHitBuilder())) + ); assertFailures( searchRequestBuilder, diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RandomSamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RandomSamplerIT.java index a22ed8e66ba9d..66978eba00e26 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RandomSamplerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RandomSamplerIT.java @@ -92,13 +92,11 @@ public void testRandomSampler() { double sampledDocCount = 0.0; for (int i = 0; i < NUM_SAMPLE_RUNS; i++) { - SearchRequest sampledRequest = client().prepareSearch("idx") - .addAggregation( - new RandomSamplerAggregationBuilder("sampler").setProbability(PROBABILITY) - .subAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) - .subAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) - ) - .request(); + SearchRequest sampledRequest = prepareSearch("idx").addAggregation( + new RandomSamplerAggregationBuilder("sampler").setProbability(PROBABILITY) + .subAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) + .subAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) + ).request(); InternalRandomSampler sampler = client().search(sampledRequest).actionGet().getAggregations().get("sampler"); sampleMonotonicValue += ((Avg) sampler.getAggregations().get("mean_monotonic")).getValue(); sampleNumericValue += ((Avg) sampler.getAggregations().get("mean_numeric")).getValue(); @@ -114,8 +112,7 @@ public void testRandomSampler() { double maxCountError = 6.0 * Math.sqrt(PROBABILITY * numDocs / NUM_SAMPLE_RUNS); assertThat(Math.abs(sampledDocCount - expectedDocCount), lessThan(maxCountError)); - SearchResponse trueValueResponse = client().prepareSearch("idx") - .addAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) + SearchResponse trueValueResponse = prepareSearch("idx").addAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) .addAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) .get(); double trueMonotonic = ((Avg) trueValueResponse.getAggregations().get("mean_monotonic")).getValue(); @@ -132,17 +129,15 @@ public void testRandomSamplerHistogram() { Map sampledDocCount = new HashMap<>(); for (int i = 0; i < NUM_SAMPLE_RUNS; i++) { - SearchRequest sampledRequest = client().prepareSearch("idx") - .addAggregation( - new RandomSamplerAggregationBuilder("sampler").setProbability(PROBABILITY) - .subAggregation( - histogram("histo").field(NUMERIC_VALUE) - .interval(5.0) - .subAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) - .subAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) - ) - ) - .request(); + SearchRequest sampledRequest = prepareSearch("idx").addAggregation( + new RandomSamplerAggregationBuilder("sampler").setProbability(PROBABILITY) + .subAggregation( + histogram("histo").field(NUMERIC_VALUE) + .interval(5.0) + .subAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) + .subAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) + ) + ).request(); InternalRandomSampler sampler = client().search(sampledRequest).actionGet().getAggregations().get("sampler"); Histogram histo = sampler.getAggregations().get("histo"); for (Histogram.Bucket bucket : histo.getBuckets()) { @@ -163,14 +158,12 @@ public void testRandomSamplerHistogram() { sampleMonotonicValue.put(key, sampleMonotonicValue.get(key) / NUM_SAMPLE_RUNS); } - SearchResponse trueValueResponse = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(NUMERIC_VALUE) - .interval(5.0) - .subAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) - .subAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) - ) - .get(); + SearchResponse trueValueResponse = prepareSearch("idx").addAggregation( + histogram("histo").field(NUMERIC_VALUE) + .interval(5.0) + .subAggregation(avg("mean_monotonic").field(MONOTONIC_VALUE)) + .subAggregation(avg("mean_numeric").field(NUMERIC_VALUE)) + ).get(); Histogram histogram = trueValueResponse.getAggregations().get("histo"); for (Histogram.Bucket bucket : histogram.getBuckets()) { long numDocs = bucket.getDocCount(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java index b94ed2aed4486..2b5b4d5b1d791 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java @@ -133,14 +133,12 @@ public void setupSuiteScopeCluster() throws Exception { } public void testRangeAsSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").field(MULTI_VALUED_FIELD_NAME) - .size(100) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").field(MULTI_VALUED_FIELD_NAME) + .size(100) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) + ).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); @@ -196,9 +194,9 @@ public void testRangeAsSubAggregation() throws Exception { } public void testSingleValueField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -237,9 +235,9 @@ public void testSingleValueField() throws Exception { } public void testSingleValueFieldWithFormat() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6).format("#")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6).format("#") + ).get(); assertNoFailures(response); @@ -278,11 +276,9 @@ public void testSingleValueFieldWithFormat() throws Exception { } public void testSingleValueFieldWithCustomKey() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo("r1", 3).addRange("r2", 3, 6).addUnboundedFrom("r3", 6) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo("r1", 3).addRange("r2", 3, 6).addUnboundedFrom("r3", 6) + ).get(); assertNoFailures(response); @@ -321,15 +317,13 @@ public void testSingleValueFieldWithCustomKey() throws Exception { } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - range("range").field(SINGLE_VALUED_FIELD_NAME) - .addUnboundedTo(3) - .addRange(3, 6) - .addUnboundedFrom(6) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(SINGLE_VALUED_FIELD_NAME) + .addUnboundedTo(3) + .addRange(3, 6) + .addUnboundedFrom(6) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -393,15 +387,13 @@ public void testSingleValuedFieldWithSubAggregation() throws Exception { } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - range("range").field(SINGLE_VALUED_FIELD_NAME) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) - .addUnboundedTo(3) - .addRange(3, 6) - .addUnboundedFrom(6) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(SINGLE_VALUED_FIELD_NAME) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) + .addUnboundedTo(3) + .addRange(3, 6) + .addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -453,9 +445,9 @@ public void testSingleValuedFieldWithValueScript() throws Exception { */ public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -507,15 +499,13 @@ public void testMultiValuedField() throws Exception { */ public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - range("range").field(MULTI_VALUED_FIELD_NAME) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) - .addUnboundedTo(3) - .addRange(3, 6) - .addUnboundedFrom(6) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(MULTI_VALUED_FIELD_NAME) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) + .addUnboundedTo(3) + .addRange(3, 6) + .addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -577,9 +567,9 @@ public void testScriptSingleValue() throws Exception { "doc['" + SINGLE_VALUED_FIELD_NAME + "'].value", Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(range("range").script(script).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").script(script).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -618,9 +608,9 @@ public void testScriptSingleValue() throws Exception { } public void testEmptyRange() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(-1).addUnboundedFrom(1000)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(-1).addUnboundedFrom(1000) + ).get(); assertNoFailures(response); @@ -651,7 +641,7 @@ public void testEmptyRange() throws Exception { public void testNoRangesInQuery() { try { - client().prepareSearch("idx").addAggregation(range("foobar").field(SINGLE_VALUED_FIELD_NAME)).get(); + prepareSearch("idx").addAggregation(range("foobar").field(SINGLE_VALUED_FIELD_NAME)).get(); fail(); } catch (SearchPhaseExecutionException spee) { Throwable rootCause = spee.getCause().getCause(); @@ -668,9 +658,9 @@ public void testScriptMultiValued() throws Exception { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(range("range").script(script).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").script(script).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -726,9 +716,9 @@ public void testScriptMultiValued() throws Exception { */ public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)) - .get(); + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6) + ).get(); assertNoFailures(response); @@ -810,11 +800,9 @@ public void testPartiallyUnmapped() throws Exception { } public void testOverlappingRanges() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(5).addRange(3, 6).addRange(4, 5).addUnboundedFrom(4) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + range("range").field(MULTI_VALUED_FIELD_NAME).addUnboundedTo(5).addRange(3, 6).addRange(4, 5).addUnboundedFrom(4) + ).get(); assertNoFailures(response); @@ -862,8 +850,7 @@ public void testOverlappingRanges() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field(SINGLE_VALUED_FIELD_NAME) .interval(1L) @@ -922,8 +909,7 @@ public void testScriptCaching() throws Exception { // Test that a request using a nondeterministic script does not get cached Map params = new HashMap<>(); params.put("fieldname", "date"); - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( range("foo").field("i") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) @@ -942,8 +928,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( range("foo").field("i") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -962,7 +947,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(range("foo").field("i").addRange(0, 10)).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(range("foo").field("i").addRange(0, 10)).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java index d40afeb433e38..e90e73eec5bb3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ReverseNestedIT.java @@ -143,18 +143,16 @@ private void insertIdx2(String[][] values) throws Exception { } public void testSimpleReverseNestedToRoot() throws Exception { - SearchResponse response = client().prepareSearch("idx1") - .addAggregation( - nested("nested1", "nested1").subAggregation( - terms("field2").field("nested1.field2") - .subAggregation( - reverseNested("nested1_to_field1").subAggregation( - terms("field1").field("field1").collectMode(randomFrom(SubAggCollectionMode.values())) - ) + SearchResponse response = prepareSearch("idx1").addAggregation( + nested("nested1", "nested1").subAggregation( + terms("field2").field("nested1.field2") + .subAggregation( + reverseNested("nested1_to_field1").subAggregation( + terms("field1").field("field1").collectMode(randomFrom(SubAggCollectionMode.values())) ) - ) + ) ) - .get(); + ).get(); assertNoFailures(response); @@ -330,13 +328,11 @@ public void testSimpleReverseNestedToRoot() throws Exception { } public void testSimpleNested1ToRootToNested2() throws Exception { - SearchResponse response = client().prepareSearch("idx2") - .addAggregation( - nested("nested1", "nested1").subAggregation( - reverseNested("nested1_to_root").subAggregation(nested("root_to_nested2", "nested1.nested2")) - ) + SearchResponse response = prepareSearch("idx2").addAggregation( + nested("nested1", "nested1").subAggregation( + reverseNested("nested1_to_root").subAggregation(nested("root_to_nested2", "nested1.nested2")) ) - .get(); + ).get(); assertNoFailures(response); Nested nested = response.getAggregations().get("nested1"); @@ -351,24 +347,22 @@ public void testSimpleNested1ToRootToNested2() throws Exception { } public void testSimpleReverseNestedToNested1() throws Exception { - SearchResponse response = client().prepareSearch("idx2") - .addAggregation( - nested("nested1", "nested1.nested2").subAggregation( - terms("field2").field("nested1.nested2.field2") - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .size(10000) - .subAggregation( - reverseNested("nested1_to_field1").path("nested1") - .subAggregation( - terms("field1").field("nested1.field1") - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - ) + SearchResponse response = prepareSearch("idx2").addAggregation( + nested("nested1", "nested1.nested2").subAggregation( + terms("field2").field("nested1.nested2.field2") + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .size(10000) + .subAggregation( + reverseNested("nested1_to_field1").path("nested1") + .subAggregation( + terms("field1").field("nested1.field1") + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ) ) - .get(); + ).get(); assertNoFailures(response); @@ -457,17 +451,15 @@ public void testSimpleReverseNestedToNested1() throws Exception { public void testReverseNestedAggWithoutNestedAgg() { try { - client().prepareSearch("idx2") - .addAggregation( - terms("field2").field("nested1.nested2.field2") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation( - reverseNested("nested1_to_field1").subAggregation( - terms("field1").field("nested1.field1").collectMode(randomFrom(SubAggCollectionMode.values())) - ) + prepareSearch("idx2").addAggregation( + terms("field2").field("nested1.nested2.field2") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation( + reverseNested("nested1_to_field1").subAggregation( + terms("field1").field("nested1.field1").collectMode(randomFrom(SubAggCollectionMode.values())) ) - ) - .get(); + ) + ).get(); fail("Expected SearchPhaseExecutionException"); } catch (SearchPhaseExecutionException e) { assertThat(e.getMessage(), is("all shards failed")); @@ -475,8 +467,7 @@ public void testReverseNestedAggWithoutNestedAgg() { } public void testNonExistingNestedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx2").setQuery(matchAllQuery()) .addAggregation(nested("nested2", "nested1.nested2").subAggregation(reverseNested("incorrect").path("nested3"))) .get(); @@ -488,8 +479,7 @@ public void testNonExistingNestedField() throws Exception { assertThat(reverseNested.getDocCount(), is(0L)); // Test that parsing the reverse_nested agg doesn't fail, because the parent nested agg is unmapped: - searchResponse = client().prepareSearch("idx1") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("idx1").setQuery(matchAllQuery()) .addAggregation(nested("incorrect1", "incorrect1").subAggregation(reverseNested("incorrect2").path("incorrect2"))) .get(); @@ -613,22 +603,20 @@ public void testSameParentDocHavingMultipleBuckets() throws Exception { ) .get(); - SearchResponse response = client().prepareSearch("idx3") - .addAggregation( - nested("nested_0", "category").subAggregation( - terms("group_by_category").field("category.name") - .subAggregation( - reverseNested("to_root").subAggregation( - nested("nested_1", "sku").subAggregation( - filter("filter_by_sku", termQuery("sku.sku_type", "bar1")).subAggregation( - count("sku_count").field("sku.sku_type") - ) + SearchResponse response = prepareSearch("idx3").addAggregation( + nested("nested_0", "category").subAggregation( + terms("group_by_category").field("category.name") + .subAggregation( + reverseNested("to_root").subAggregation( + nested("nested_1", "sku").subAggregation( + filter("filter_by_sku", termQuery("sku.sku_type", "bar1")).subAggregation( + count("sku_count").field("sku.sku_type") ) ) ) - ) + ) ) - .get(); + ).get(); assertNoFailures(response); assertHitCount(response, 1); @@ -650,27 +638,25 @@ public void testSameParentDocHavingMultipleBuckets() throws Exception { assertThat(barCount.getValue(), equalTo(3L)); } - response = client().prepareSearch("idx3") - .addAggregation( - nested("nested_0", "category").subAggregation( - terms("group_by_category").field("category.name") - .subAggregation( - reverseNested("to_root").subAggregation( - nested("nested_1", "sku").subAggregation( - filter("filter_by_sku", termQuery("sku.sku_type", "bar1")).subAggregation( - nested("nested_2", "sku.colors").subAggregation( - filter("filter_sku_color", termQuery("sku.colors.name", "red")).subAggregation( - reverseNested("reverse_to_sku").path("sku") - .subAggregation(count("sku_count").field("sku.sku_type")) - ) + response = prepareSearch("idx3").addAggregation( + nested("nested_0", "category").subAggregation( + terms("group_by_category").field("category.name") + .subAggregation( + reverseNested("to_root").subAggregation( + nested("nested_1", "sku").subAggregation( + filter("filter_by_sku", termQuery("sku.sku_type", "bar1")).subAggregation( + nested("nested_2", "sku.colors").subAggregation( + filter("filter_sku_color", termQuery("sku.colors.name", "red")).subAggregation( + reverseNested("reverse_to_sku").path("sku") + .subAggregation(count("sku_count").field("sku.sku_type")) ) ) ) ) ) - ) + ) ) - .get(); + ).get(); assertNoFailures(response); assertHitCount(response, 1); @@ -700,18 +686,16 @@ public void testSameParentDocHavingMultipleBuckets() throws Exception { } public void testFieldAlias() { - SearchResponse response = client().prepareSearch("idx1") - .addAggregation( - nested("nested1", "nested1").subAggregation( - terms("field2").field("nested1.field2") - .subAggregation( - reverseNested("nested1_to_field1").subAggregation( - terms("field1").field("alias").collectMode(randomFrom(SubAggCollectionMode.values())) - ) + SearchResponse response = prepareSearch("idx1").addAggregation( + nested("nested1", "nested1").subAggregation( + terms("field2").field("nested1.field2") + .subAggregation( + reverseNested("nested1_to_field1").subAggregation( + terms("field1").field("alias").collectMode(randomFrom(SubAggCollectionMode.values())) ) - ) + ) ) - .get(); + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java index e7f1f30fd8ad3..2e1bd1812ea4b 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java @@ -91,8 +91,7 @@ public void testIssue10719() throws Exception { // Tests that we can refer to nested elements under a sample in a path // statement boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .addAggregation( terms("genres").field("genre") .order(BucketOrder.aggregation("sample>max_price.value", asc)) @@ -122,8 +121,7 @@ public void testIssue10719() throws Exception { public void testSimpleSampler() throws Exception { SamplerAggregationBuilder sampleAgg = sampler("sample").shardSize(100); sampleAgg.subAggregation(terms("authors").field("author")); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(new TermQueryBuilder("genre", "fantasy")) .setFrom(0) .setSize(60) @@ -144,8 +142,7 @@ public void testSimpleSampler() throws Exception { public void testUnmappedChildAggNoDiversity() throws Exception { SamplerAggregationBuilder sampleAgg = sampler("sample").shardSize(100); sampleAgg.subAggregation(terms("authors").field("author")); - SearchResponse response = client().prepareSearch("idx_unmapped") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("idx_unmapped").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(new TermQueryBuilder("genre", "fantasy")) .setFrom(0) .setSize(60) @@ -179,8 +176,7 @@ public void testPartiallyUnmappedChildAggNoDiversity() throws Exception { public void testRidiculousShardSizeSampler() throws Exception { SamplerAggregationBuilder sampleAgg = sampler("sample").shardSize(Integer.MAX_VALUE); sampleAgg.subAggregation(terms("authors").field("author")); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.QUERY_THEN_FETCH) .setQuery(new TermQueryBuilder("genre", "fantasy")) .setFrom(0) .setSize(60) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java index 63f31d2fbb972..b0f9556bc842b 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardReduceIT.java @@ -87,8 +87,7 @@ public void setupSuiteScopeCluster() throws Exception { } public void testGlobal() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( global("global").subAggregation( dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0) @@ -104,8 +103,7 @@ public void testGlobal() throws Exception { } public void testFilter() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( filter("filter", QueryBuilders.matchAllQuery()).subAggregation( dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0) @@ -121,8 +119,7 @@ public void testFilter() throws Exception { } public void testMissing() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( missing("missing").field("foobar") .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) @@ -137,8 +134,7 @@ public void testMissing() throws Exception { } public void testGlobalWithFilterWithMissing() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( global("global").subAggregation( filter("filter", QueryBuilders.matchAllQuery()).subAggregation( @@ -159,8 +155,7 @@ public void testGlobalWithFilterWithMissing() throws Exception { } public void testNested() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( nested("nested", "nested").subAggregation( dateHistogram("histo").field("nested.date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0) @@ -176,8 +171,7 @@ public void testNested() throws Exception { } public void testStringTerms() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( terms("terms").field("term-s") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -193,8 +187,7 @@ public void testStringTerms() throws Exception { } public void testLongTerms() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( terms("terms").field("term-l") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -210,8 +203,7 @@ public void testLongTerms() throws Exception { } public void testDoubleTerms() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( terms("terms").field("term-d") .collectMode(randomFrom(SubAggCollectionMode.values())) @@ -227,8 +219,7 @@ public void testDoubleTerms() throws Exception { } public void testRange() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( range("range").field("value") .addRange("r1", 0, 10) @@ -244,8 +235,7 @@ public void testRange() throws Exception { } public void testDateRange() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( dateRange("range").field("date") .addRange("r1", "2014-01-01", "2014-01-10") @@ -261,8 +251,7 @@ public void testDateRange() throws Exception { } public void testIpRange() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( ipRange("range").field("ip") .addRange("r1", "10.0.0.1", "10.0.0.10") @@ -278,8 +267,7 @@ public void testIpRange() throws Exception { } public void testHistogram() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( histogram("topHisto").field("value") .interval(5) @@ -295,8 +283,7 @@ public void testHistogram() throws Exception { } public void testDateHistogram() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( dateHistogram("topHisto").field("date") .calendarInterval(DateHistogramInterval.MONTH) @@ -313,8 +300,7 @@ public void testDateHistogram() throws Exception { } public void testGeoHashGrid() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( geohashGrid("grid").field("location") .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) @@ -329,8 +315,7 @@ public void testGeoHashGrid() throws Exception { } public void testGeoTileGrid() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.matchAllQuery()) .addAggregation( geotileGrid("grid").field("location") .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java index dc3cd1c897780..2c0c7766b646c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTermsIT.java @@ -26,8 +26,7 @@ public void testNoShardSizeString() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key").size(3).collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false)) ) @@ -50,8 +49,7 @@ public void testShardSizeEqualsSizeString() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") .size(3) @@ -79,8 +77,7 @@ public void testWithShardSizeString() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") .size(3) @@ -108,8 +105,7 @@ public void testWithShardSizeStringSingleShard() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setRouting(routing1) + SearchResponse response = prepareSearch("idx").setRouting(routing1) .setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") @@ -137,8 +133,7 @@ public void testNoShardSizeTermOrderString() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key").size(3).collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true)) ) @@ -161,8 +156,7 @@ public void testNoShardSizeLong() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key").size(3).collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false)) ) @@ -185,8 +179,7 @@ public void testShardSizeEqualsSizeLong() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") .size(3) @@ -213,8 +206,7 @@ public void testWithShardSizeLong() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") .size(3) @@ -242,8 +234,7 @@ public void testWithShardSizeLongSingleShard() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setRouting(routing1) + SearchResponse response = prepareSearch("idx").setRouting(routing1) .setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") @@ -271,8 +262,7 @@ public void testNoShardSizeTermOrderLong() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key").size(3).collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true)) ) @@ -295,8 +285,7 @@ public void testNoShardSizeDouble() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key").size(3).collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.count(false)) ) @@ -319,8 +308,7 @@ public void testShardSizeEqualsSizeDouble() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") .size(3) @@ -347,8 +335,7 @@ public void testWithShardSizeDouble() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") .size(3) @@ -375,8 +362,7 @@ public void testWithShardSizeDoubleSingleShard() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setRouting(routing1) + SearchResponse response = prepareSearch("idx").setRouting(routing1) .setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key") @@ -404,8 +390,7 @@ public void testNoShardSizeTermOrderDouble() throws Exception { indexData(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("keys").field("key").size(3).collectMode(randomFrom(SubAggCollectionMode.values())).order(BucketOrder.key(true)) ) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java index 1932e640e785e..0180bcff738ba 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java @@ -121,11 +121,13 @@ public void testXContentResponse() throws Exception { if ("text".equals(type) && randomBoolean()) { // Use significant_text on text fields but occasionally run with alternative of // significant_terms on legacy fieldData=true too. - request = client().prepareSearch(INDEX_NAME) - .addAggregation(terms("class").field(CLASS_FIELD).subAggregation(significantText("sig_terms", TEXT_FIELD))); + request = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD).subAggregation(significantText("sig_terms", TEXT_FIELD)) + ); } else { - request = client().prepareSearch(INDEX_NAME) - .addAggregation(terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD))); + request = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD)) + ); } SearchResponse response = request.get(); @@ -226,13 +228,13 @@ public void testPopularTermManyDeletedDocs() throws Exception { SearchRequestBuilder request; if (randomBoolean()) { - request = client().prepareSearch(INDEX_NAME) - .addAggregation( - terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD).minDocCount(1)) - ); + request = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD).minDocCount(1)) + ); } else { - request = client().prepareSearch(INDEX_NAME) - .addAggregation(terms("class").field(CLASS_FIELD).subAggregation(significantText("sig_terms", TEXT_FIELD).minDocCount(1))); + request = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD).subAggregation(significantText("sig_terms", TEXT_FIELD).minDocCount(1)) + ); } request.get(); @@ -266,24 +268,22 @@ public void testBackgroundVsSeparateSet( final boolean useSigText = randomBoolean() && type.equals("text"); SearchRequestBuilder request1; if (useSigText) { - request1 = client().prepareSearch(INDEX_NAME) - .addAggregation( - terms("class").field(CLASS_FIELD) - .subAggregation( - significantText("sig_terms", TEXT_FIELD).minDocCount(1) - .significanceHeuristic(significanceHeuristicExpectingSuperset) - ) - ); + request1 = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD) + .subAggregation( + significantText("sig_terms", TEXT_FIELD).minDocCount(1) + .significanceHeuristic(significanceHeuristicExpectingSuperset) + ) + ); } else { - request1 = client().prepareSearch(INDEX_NAME) - .addAggregation( - terms("class").field(CLASS_FIELD) - .subAggregation( - significantTerms("sig_terms").field(TEXT_FIELD) - .minDocCount(1) - .significanceHeuristic(significanceHeuristicExpectingSuperset) - ) - ); + request1 = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD) + .subAggregation( + significantTerms("sig_terms").field(TEXT_FIELD) + .minDocCount(1) + .significanceHeuristic(significanceHeuristicExpectingSuperset) + ) + ); } SearchResponse response1 = request1.get(); @@ -291,14 +291,13 @@ public void testBackgroundVsSeparateSet( SearchRequestBuilder request2; if (useSigText) { - request2 = client().prepareSearch(INDEX_NAME) - .addAggregation( - filter("0", QueryBuilders.termQuery(CLASS_FIELD, "0")).subAggregation( - significantText("sig_terms", TEXT_FIELD).minDocCount(1) - .backgroundFilter(QueryBuilders.termQuery(CLASS_FIELD, "1")) - .significanceHeuristic(significanceHeuristicExpectingSeparateSets) - ) + request2 = prepareSearch(INDEX_NAME).addAggregation( + filter("0", QueryBuilders.termQuery(CLASS_FIELD, "0")).subAggregation( + significantText("sig_terms", TEXT_FIELD).minDocCount(1) + .backgroundFilter(QueryBuilders.termQuery(CLASS_FIELD, "1")) + .significanceHeuristic(significanceHeuristicExpectingSeparateSets) ) + ) .addAggregation( filter("1", QueryBuilders.termQuery(CLASS_FIELD, "1")).subAggregation( significantText("sig_terms", TEXT_FIELD).minDocCount(1) @@ -307,15 +306,14 @@ public void testBackgroundVsSeparateSet( ) ); } else { - request2 = client().prepareSearch(INDEX_NAME) - .addAggregation( - filter("0", QueryBuilders.termQuery(CLASS_FIELD, "0")).subAggregation( - significantTerms("sig_terms").field(TEXT_FIELD) - .minDocCount(1) - .backgroundFilter(QueryBuilders.termQuery(CLASS_FIELD, "1")) - .significanceHeuristic(significanceHeuristicExpectingSeparateSets) - ) + request2 = prepareSearch(INDEX_NAME).addAggregation( + filter("0", QueryBuilders.termQuery(CLASS_FIELD, "0")).subAggregation( + significantTerms("sig_terms").field(TEXT_FIELD) + .minDocCount(1) + .backgroundFilter(QueryBuilders.termQuery(CLASS_FIELD, "1")) + .significanceHeuristic(significanceHeuristicExpectingSeparateSets) ) + ) .addAggregation( filter("1", QueryBuilders.termQuery(CLASS_FIELD, "1")).subAggregation( significantTerms("sig_terms").field(TEXT_FIELD) @@ -365,29 +363,27 @@ public void testScoresEqualForPositiveAndNegative(SignificanceHeuristic heuristi // check that results for both classes are the same with exclude negatives = false and classes are routing ids SearchRequestBuilder request; if (randomBoolean()) { - request = client().prepareSearch("test") - .addAggregation( - terms("class").field("class") - .subAggregation( - significantTerms("mySignificantTerms").field("text") - .executionHint(randomExecutionHint()) - .significanceHeuristic(heuristic) - .minDocCount(1) - .shardSize(1000) - .size(1000) - ) - ); + request = prepareSearch("test").addAggregation( + terms("class").field("class") + .subAggregation( + significantTerms("mySignificantTerms").field("text") + .executionHint(randomExecutionHint()) + .significanceHeuristic(heuristic) + .minDocCount(1) + .shardSize(1000) + .size(1000) + ) + ); } else { - request = client().prepareSearch("test") - .addAggregation( - terms("class").field("class") - .subAggregation( - significantText("mySignificantTerms", "text").significanceHeuristic(heuristic) - .minDocCount(1) - .shardSize(1000) - .size(1000) - ) - ); + request = prepareSearch("test").addAggregation( + terms("class").field("class") + .subAggregation( + significantText("mySignificantTerms", "text").significanceHeuristic(heuristic) + .minDocCount(1) + .shardSize(1000) + .size(1000) + ) + ); } SearchResponse response = request.get(); assertNoFailures(response); @@ -427,7 +423,7 @@ public void testSubAggregations() throws Exception { .size(1000) .subAggregation(subAgg); - SearchResponse response = client().prepareSearch("test").setQuery(query).addAggregation(agg).get(); + SearchResponse response = prepareSearch("test").setQuery(query).addAggregation(agg).get(); assertNoFailures(response); SignificantTerms sigTerms = response.getAggregations().get("significant_terms"); @@ -479,29 +475,27 @@ public void testScriptScore() throws ExecutionException, InterruptedException, I SearchRequestBuilder request; if ("text".equals(type) && randomBoolean()) { - request = client().prepareSearch(INDEX_NAME) - .addAggregation( - terms("class").field(CLASS_FIELD) - .subAggregation( - significantText("mySignificantTerms", TEXT_FIELD).significanceHeuristic(scriptHeuristic) - .minDocCount(1) - .shardSize(2) - .size(2) - ) - ); + request = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD) + .subAggregation( + significantText("mySignificantTerms", TEXT_FIELD).significanceHeuristic(scriptHeuristic) + .minDocCount(1) + .shardSize(2) + .size(2) + ) + ); } else { - request = client().prepareSearch(INDEX_NAME) - .addAggregation( - terms("class").field(CLASS_FIELD) - .subAggregation( - significantTerms("mySignificantTerms").field(TEXT_FIELD) - .executionHint(randomExecutionHint()) - .significanceHeuristic(scriptHeuristic) - .minDocCount(1) - .shardSize(2) - .size(2) - ) - ); + request = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD) + .subAggregation( + significantTerms("mySignificantTerms").field(TEXT_FIELD) + .executionHint(randomExecutionHint()) + .significanceHeuristic(scriptHeuristic) + .minDocCount(1) + .shardSize(2) + .size(2) + ) + ); } SearchResponse response = request.get(); assertNoFailures(response); @@ -588,13 +582,11 @@ public void testScriptCaching() throws Exception { boolean useSigText = randomBoolean(); SearchResponse r; if (useSigText) { - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(significantText("foo", "t").significanceHeuristic(scriptHeuristic)) .get(); } else { - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(significantTerms("foo").field("s").significanceHeuristic(scriptHeuristic)) .get(); } @@ -613,13 +605,11 @@ public void testScriptCaching() throws Exception { scriptHeuristic = getScriptSignificanceHeuristic(); useSigText = randomBoolean(); if (useSigText) { - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(significantText("foo", "t").significanceHeuristic(scriptHeuristic)) .get(); } else { - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(significantTerms("foo").field("s").significanceHeuristic(scriptHeuristic)) .get(); } @@ -636,9 +626,9 @@ public void testScriptCaching() throws Exception { // Ensure that non-scripted requests are cached as normal if (useSigText) { - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(significantText("foo", "t")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(significantText("foo", "t")).get(); } else { - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(significantTerms("foo").field("s")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(significantTerms("foo").field("s")).get(); } assertNoFailures(r); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java index d010962385232..109c2ec356f19 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java @@ -267,29 +267,25 @@ private void assertUnboundedDocCountError(int size, SearchResponse accurateRespo public void testStringValueField() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -299,29 +295,25 @@ public void testStringValueField() throws Exception { public void testStringValueFieldSingleShard() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -332,8 +324,7 @@ public void testStringValueFieldWithRouting() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse testResponse = client().prepareSearch("idx_with_routing") - .setRouting(String.valueOf(between(1, numRoutingValues))) + SearchResponse testResponse = prepareSearch("idx_with_routing").setRouting(String.valueOf(between(1, numRoutingValues))) .addAggregation( terms("terms").executionHint(randomExecutionHint()) .field(STRING_FIELD_NAME) @@ -352,31 +343,27 @@ public void testStringValueFieldWithRouting() throws Exception { public void testStringValueFieldDocCountAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.count(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.count(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.count(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.count(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -386,31 +373,27 @@ public void testStringValueFieldDocCountAsc() throws Exception { public void testStringValueFieldTermSortAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -420,31 +403,27 @@ public void testStringValueFieldTermSortAsc() throws Exception { public void testStringValueFieldTermSortDesc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.key(false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.key(false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.key(false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.key(false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -454,33 +433,29 @@ public void testStringValueFieldTermSortDesc() throws Exception { public void testStringValueFieldSubAggAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.aggregation("sortAgg", true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.aggregation("sortAgg", true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.aggregation("sortAgg", true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.aggregation("sortAgg", true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(testResponse); @@ -490,33 +465,29 @@ public void testStringValueFieldSubAggAsc() throws Exception { public void testStringValueFieldSubAggDesc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.aggregation("sortAgg", false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.aggregation("sortAgg", false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(STRING_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.aggregation("sortAgg", false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(STRING_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.aggregation("sortAgg", false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(testResponse); @@ -526,29 +497,25 @@ public void testStringValueFieldSubAggDesc() throws Exception { public void testLongValueField() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -558,29 +525,25 @@ public void testLongValueField() throws Exception { public void testLongValueFieldSingleShard() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -591,8 +554,7 @@ public void testLongValueFieldWithRouting() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse testResponse = client().prepareSearch("idx_with_routing") - .setRouting(String.valueOf(between(1, numRoutingValues))) + SearchResponse testResponse = prepareSearch("idx_with_routing").setRouting(String.valueOf(between(1, numRoutingValues))) .addAggregation( terms("terms").executionHint(randomExecutionHint()) .field(LONG_FIELD_NAME) @@ -611,31 +573,27 @@ public void testLongValueFieldWithRouting() throws Exception { public void testLongValueFieldDocCountAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.count(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.count(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.count(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.count(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -645,31 +603,27 @@ public void testLongValueFieldDocCountAsc() throws Exception { public void testLongValueFieldTermSortAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -679,31 +633,27 @@ public void testLongValueFieldTermSortAsc() throws Exception { public void testLongValueFieldTermSortDesc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.key(false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.key(false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.key(false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.key(false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -713,33 +663,29 @@ public void testLongValueFieldTermSortDesc() throws Exception { public void testLongValueFieldSubAggAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.aggregation("sortAgg", true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.aggregation("sortAgg", true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.aggregation("sortAgg", true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.aggregation("sortAgg", true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(testResponse); @@ -749,33 +695,29 @@ public void testLongValueFieldSubAggAsc() throws Exception { public void testLongValueFieldSubAggDesc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.aggregation("sortAgg", false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(DOUBLE_FIELD_NAME)) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.aggregation("sortAgg", false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(DOUBLE_FIELD_NAME)) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(LONG_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.aggregation("sortAgg", false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(DOUBLE_FIELD_NAME)) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(LONG_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.aggregation("sortAgg", false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(DOUBLE_FIELD_NAME)) + ).get(); assertNoFailures(testResponse); @@ -785,29 +727,25 @@ public void testLongValueFieldSubAggDesc() throws Exception { public void testDoubleValueField() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -817,29 +755,25 @@ public void testDoubleValueField() throws Exception { public void testDoubleValueFieldSingleShard() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -850,8 +784,7 @@ public void testDoubleValueFieldWithRouting() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse testResponse = client().prepareSearch("idx_with_routing") - .setRouting(String.valueOf(between(1, numRoutingValues))) + SearchResponse testResponse = prepareSearch("idx_with_routing").setRouting(String.valueOf(between(1, numRoutingValues))) .addAggregation( terms("terms").executionHint(randomExecutionHint()) .field(DOUBLE_FIELD_NAME) @@ -870,31 +803,27 @@ public void testDoubleValueFieldWithRouting() throws Exception { public void testDoubleValueFieldDocCountAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.count(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.count(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.count(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.count(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -904,31 +833,27 @@ public void testDoubleValueFieldDocCountAsc() throws Exception { public void testDoubleValueFieldTermSortAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.key(true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.key(true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -938,31 +863,27 @@ public void testDoubleValueFieldTermSortAsc() throws Exception { public void testDoubleValueFieldTermSortDesc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.key(false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.key(false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.key(false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.key(false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(testResponse); @@ -972,33 +893,29 @@ public void testDoubleValueFieldTermSortDesc() throws Exception { public void testDoubleValueFieldSubAggAsc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.aggregation("sortAgg", true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.aggregation("sortAgg", true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.aggregation("sortAgg", true)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.aggregation("sortAgg", true)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(testResponse); @@ -1008,33 +925,29 @@ public void testDoubleValueFieldSubAggAsc() throws Exception { public void testDoubleValueFieldSubAggDesc() throws Exception { int size = randomIntBetween(1, 20); int shardSize = randomIntBetween(size, size * 2); - SearchResponse accurateResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(10000) - .shardSize(10000) - .order(BucketOrder.aggregation("sortAgg", false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse accurateResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(10000) + .shardSize(10000) + .order(BucketOrder.aggregation("sortAgg", false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(accurateResponse); - SearchResponse testResponse = client().prepareSearch("idx_single_shard") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(DOUBLE_FIELD_NAME) - .showTermDocCountError(true) - .size(size) - .shardSize(shardSize) - .order(BucketOrder.aggregation("sortAgg", false)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) - ) - .get(); + SearchResponse testResponse = prepareSearch("idx_single_shard").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(DOUBLE_FIELD_NAME) + .showTermDocCountError(true) + .size(size) + .shardSize(shardSize) + .order(BucketOrder.aggregation("sortAgg", false)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .subAggregation(sum("sortAgg").field(LONG_FIELD_NAME)) + ).get(); assertNoFailures(testResponse); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java index 9b8da69a13e1e..ffb9539bee735 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsShardMinDocCountIT.java @@ -61,34 +61,30 @@ public void testShardMinDocCountSignificantTermsTest() throws Exception { indexRandom(true, false, indexBuilders); // first, check that indeed when not setting the shardMinDocCount parameter 0 terms are returned - SearchResponse response = client().prepareSearch(index) - .addAggregation( - (filter("inclass", QueryBuilders.termQuery("class", true))).subAggregation( - significantTerms("mySignificantTerms").field("text") - .minDocCount(2) - .size(2) - .shardSize(2) - .executionHint(randomExecutionHint()) - ) + SearchResponse response = prepareSearch(index).addAggregation( + (filter("inclass", QueryBuilders.termQuery("class", true))).subAggregation( + significantTerms("mySignificantTerms").field("text") + .minDocCount(2) + .size(2) + .shardSize(2) + .executionHint(randomExecutionHint()) ) - .get(); + ).get(); assertNoFailures(response); InternalFilter filteredBucket = response.getAggregations().get("inclass"); SignificantTerms sigterms = filteredBucket.getAggregations().get("mySignificantTerms"); assertThat(sigterms.getBuckets().size(), equalTo(0)); - response = client().prepareSearch(index) - .addAggregation( - (filter("inclass", QueryBuilders.termQuery("class", true))).subAggregation( - significantTerms("mySignificantTerms").field("text") - .minDocCount(2) - .shardSize(2) - .shardMinDocCount(2) - .size(2) - .executionHint(randomExecutionHint()) - ) + response = prepareSearch(index).addAggregation( + (filter("inclass", QueryBuilders.termQuery("class", true))).subAggregation( + significantTerms("mySignificantTerms").field("text") + .minDocCount(2) + .shardSize(2) + .shardMinDocCount(2) + .size(2) + .executionHint(randomExecutionHint()) ) - .get(); + ).get(); assertNoFailures(response); filteredBucket = response.getAggregations().get("inclass"); sigterms = filteredBucket.getAggregations().get("mySignificantTerms"); @@ -126,31 +122,27 @@ public void testShardMinDocCountTermsTest() throws Exception { indexRandom(true, false, indexBuilders); // first, check that indeed when not setting the shardMinDocCount parameter 0 terms are returned - SearchResponse response = client().prepareSearch(index) - .addAggregation( - terms("myTerms").field("text") - .minDocCount(2) - .size(2) - .shardSize(2) - .executionHint(randomExecutionHint()) - .order(BucketOrder.key(true)) - ) - .get(); + SearchResponse response = prepareSearch(index).addAggregation( + terms("myTerms").field("text") + .minDocCount(2) + .size(2) + .shardSize(2) + .executionHint(randomExecutionHint()) + .order(BucketOrder.key(true)) + ).get(); assertNoFailures(response); Terms sigterms = response.getAggregations().get("myTerms"); assertThat(sigterms.getBuckets().size(), equalTo(0)); - response = client().prepareSearch(index) - .addAggregation( - terms("myTerms").field("text") - .minDocCount(2) - .shardMinDocCount(2) - .size(2) - .shardSize(2) - .executionHint(randomExecutionHint()) - .order(BucketOrder.key(true)) - ) - .get(); + response = prepareSearch(index).addAggregation( + terms("myTerms").field("text") + .minDocCount(2) + .shardMinDocCount(2) + .size(2) + .shardSize(2) + .executionHint(randomExecutionHint()) + .order(BucketOrder.key(true)) + ).get(); assertNoFailures(response); sigterms = response.getAggregations().get("myTerms"); assertThat(sigterms.getBuckets().size(), equalTo(2)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java index de458c1e2bb76..58976326a8e9c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java @@ -277,15 +277,13 @@ public void testSizeIsZero() { final int minDocCount = randomInt(1); IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("high_card_idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .minDocCount(minDocCount) - .size(0) - ) - .get() + () -> prepareSearch("high_card_idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .minDocCount(minDocCount) + .size(0) + ).get() ); assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [terms]")); } @@ -300,11 +298,9 @@ public void testMultiValueFieldWithPartitionedFiltering() throws Exception { private void runTestFieldWithPartitionedFiltering(String field) throws Exception { // Find total number of unique terms - SearchResponse allResponse = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse allResponse = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(field).size(10000).collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(allResponse); StringTerms terms = allResponse.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -315,13 +311,11 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception final int numPartitions = randomIntBetween(2, 4); Set foundTerms = new HashSet<>(); for (int partition = 0; partition < numPartitions; partition++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").field(field) - .includeExclude(new IncludeExclude(partition, numPartitions)) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").field(field) + .includeExclude(new IncludeExclude(partition, numPartitions)) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); terms = response.getAggregations().get("terms"); assertThat(terms, notNullValue()); @@ -334,14 +328,12 @@ private void runTestFieldWithPartitionedFiltering(String field) throws Exception } public void testSingleValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -359,14 +351,12 @@ public void testSingleValuedFieldWithValueScript() throws Exception { } public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value.substring(0,3)", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value.substring(0,3)", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -382,20 +372,13 @@ public void testMultiValuedFieldWithValueScriptNotUnique() throws Exception { } public void testMultiValuedScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .script( - new Script( - ScriptType.INLINE, - CustomScriptPlugin.NAME, - "doc['" + MULTI_VALUED_FIELD_NAME + "']", - Collections.emptyMap() - ) - ) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .script( + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['" + MULTI_VALUED_FIELD_NAME + "']", Collections.emptyMap()) + ) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(response); @@ -417,14 +400,12 @@ public void testMultiValuedScript() throws Exception { } public void testMultiValuedFieldWithValueScript() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(MULTI_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())) + ).get(); assertNoFailures(response); @@ -465,13 +446,11 @@ public void testScriptSingleValue() throws Exception { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) - .executionHint(randomExecutionHint()) - .script(script) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) + .executionHint(randomExecutionHint()) + .script(script) + ).get(); assertNoFailures(response); @@ -496,13 +475,11 @@ public void testScriptSingleValueExplicitSingleValue() throws Exception { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) - .executionHint(randomExecutionHint()) - .script(script) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) + .executionHint(randomExecutionHint()) + .script(script) + ).get(); assertNoFailures(response); @@ -520,7 +497,7 @@ public void testScriptSingleValueExplicitSingleValue() throws Exception { } public void testScriptMultiValued() throws Exception { - SearchResponse response = client().prepareSearch("idx") + SearchResponse response = prepareSearch("idx") .addAggregation( new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values())) @@ -581,14 +558,11 @@ public void testPartiallyUnmapped() throws Exception { public void testStringTermsNestedIntoPerBucketAggregator() throws Exception { // no execution hint so that the logic that decides whether or not to use ordinals is executed - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - filter("filter", termQuery(MULTI_VALUED_FIELD_NAME, "val3")).subAggregation( - new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + filter("filter", termQuery(MULTI_VALUED_FIELD_NAME, "val3")).subAggregation( + new TermsAggregationBuilder("terms").field(MULTI_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values())) ) - .get(); + ).get(); assertThat(response.getFailedShards(), equalTo(0)); @@ -610,17 +584,15 @@ public void testStringTermsNestedIntoPerBucketAggregator() throws Exception { public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { boolean asc = true; try { - client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("inner_terms>avg", asc)) - .subAggregation( - new TermsAggregationBuilder("inner_terms").field(MULTI_VALUED_FIELD_NAME).subAggregation(avg("avg").field("i")) - ) - ) - .get(); + prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("inner_terms>avg", asc)) + .subAggregation( + new TermsAggregationBuilder("inner_terms").field(MULTI_VALUED_FIELD_NAME).subAggregation(avg("avg").field("i")) + ) + ).get(); fail("Expected an exception"); } catch (SearchPhaseExecutionException e) { ElasticsearchException[] rootCauses = e.guessRootCauses(); @@ -645,15 +617,13 @@ public void testSingleValuedFieldOrderedByIllegalAgg() throws Exception { public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) - .field("tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter", asc)) - .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) + .field("tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter", asc)) + .subAggregation(filter("filter", QueryBuilders.matchAllQuery())) + ).get(); assertNoFailures(response); @@ -683,19 +653,17 @@ public void testSingleValuedFieldOrderedBySingleBucketSubAggregationAsc() throws public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevels() throws Exception { boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) - .field("tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter1>filter2>stats.max", asc)) - .subAggregation( - filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( - filter("filter2", QueryBuilders.matchAllQuery()).subAggregation(stats("stats").field("i")) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) + .field("tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter1>filter2>stats.max", asc)) + .subAggregation( + filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( + filter("filter2", QueryBuilders.matchAllQuery()).subAggregation(stats("stats").field("i")) ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -746,19 +714,17 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS statsNameBuilder.append(randomAlphaOfLengthBetween(3, 10).replace("[", "").replace("]", "").replace(">", "")); String statsName = statsNameBuilder.toString(); boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) - .field("tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter1>" + filter2Name + ">" + statsName + ".max", asc)) - .subAggregation( - filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( - filter(filter2Name, QueryBuilders.matchAllQuery()).subAggregation(stats(statsName).field("i")) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) + .field("tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter1>" + filter2Name + ">" + statsName + ".max", asc)) + .subAggregation( + filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( + filter(filter2Name, QueryBuilders.matchAllQuery()).subAggregation(stats(statsName).field("i")) ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -809,19 +775,17 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS statsNameBuilder.append(randomAlphaOfLengthBetween(3, 10).replace("[", "").replace("]", "").replace(">", "")); String statsName = statsNameBuilder.toString(); boolean asc = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) - .field("tag") - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("filter1>" + filter2Name + ">" + statsName + "[max]", asc)) - .subAggregation( - filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( - filter(filter2Name, QueryBuilders.matchAllQuery()).subAggregation(stats(statsName).field("i")) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("tags").executionHint(randomExecutionHint()) + .field("tag") + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("filter1>" + filter2Name + ">" + statsName + "[max]", asc)) + .subAggregation( + filter("filter1", QueryBuilders.matchAllQuery()).subAggregation( + filter(filter2Name, QueryBuilders.matchAllQuery()).subAggregation(stats(statsName).field("i")) ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -867,14 +831,12 @@ public void testSingleValuedFieldOrderedBySubAggregationAscMultiHierarchyLevelsS public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("avg_i", true)) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("avg_i", true)) + ).get(); fail("Expected search to fail when trying to sort terms aggregation by sug-aggregation that doesn't exist"); @@ -887,17 +849,15 @@ public void testSingleValuedFieldOrderedByMissingSubAggregation() throws Excepti public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("values", true)) - .subAggregation( - new TermsAggregationBuilder("values").field("i").collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - .get(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("values", true)) + .subAggregation( + new TermsAggregationBuilder("values").field("i").collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ).get(); fail( "Expected search to fail when trying to sort terms aggregation by sug-aggregation " @@ -913,15 +873,13 @@ public void testSingleValuedFieldOrderedByNonMetricsOrMultiBucketSubAggregation( public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUnknownMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - SearchResponse response = client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.foo", true)) - .subAggregation(stats("stats").field("i")) - ) - .get(); + SearchResponse response = prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.foo", true)) + .subAggregation(stats("stats").field("i")) + ).get(); fail( "Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " + "with an unknown specified metric to order by. response had " @@ -938,16 +896,13 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithUnknownMe public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric() throws Exception { for (String index : Arrays.asList("idx", "idx_unmapped")) { try { - client().prepareSearch(index) - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats", true)) - .subAggregation(stats("stats").field("i")) - ) - .execute() - .actionGet(); + prepareSearch(index).addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats", true)) + .subAggregation(stats("stats").field("i")) + ).execute().actionGet(); fail( "Expected search to fail when trying to sort terms aggregation by multi-valued sug-aggregation " @@ -962,15 +917,13 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationWithoutMetric public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.avg", asc)) - .subAggregation(stats("stats").field("i")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.avg", asc)) + .subAggregation(stats("stats").field("i")) + ).get(); assertNoFailures(response); @@ -994,15 +947,13 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationAsc() throws E public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws Exception { boolean asc = false; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.avg", asc)) - .subAggregation(stats("stats").field("i")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.avg", asc)) + .subAggregation(stats("stats").field("i")) + ).get(); assertNoFailures(response); @@ -1027,15 +978,13 @@ public void testSingleValuedFieldOrderedByMultiValueSubAggregationDesc() throws public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.sum_of_squares", asc)) - .subAggregation(extendedStats("stats").field("i")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.sum_of_squares", asc)) + .subAggregation(extendedStats("stats").field("i")) + ).get(); assertNoFailures(response); @@ -1060,18 +1009,16 @@ public void testSingleValuedFieldOrderedByMultiValueExtendedStatsAsc() throws Ex public void testSingleValuedFieldOrderedByStatsAggAscWithTermsSubAgg() throws Exception { boolean asc = true; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("stats.sum_of_squares", asc)) - .subAggregation(extendedStats("stats").field("i")) - .subAggregation( - new TermsAggregationBuilder("subTerms").field("s_values").collectMode(randomFrom(SubAggCollectionMode.values())) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("stats.sum_of_squares", asc)) + .subAggregation(extendedStats("stats").field("i")) + .subAggregation( + new TermsAggregationBuilder("subTerms").field("s_values").collectMode(randomFrom(SubAggCollectionMode.values())) + ) + ).get(); assertNoFailures(response); @@ -1146,16 +1093,14 @@ public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAsCompound } private void assertMultiSortResponse(String[] expectedKeys, BucketOrder... order) { - SearchResponse response = client().prepareSearch("sort_idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(SINGLE_VALUED_FIELD_NAME) - .collectMode(randomFrom(SubAggCollectionMode.values())) - .order(BucketOrder.compound(order)) - .subAggregation(avg("avg_l").field("l")) - .subAggregation(sum("sum_d").field("d")) - ) - .get(); + SearchResponse response = prepareSearch("sort_idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(SINGLE_VALUED_FIELD_NAME) + .collectMode(randomFrom(SubAggCollectionMode.values())) + .order(BucketOrder.compound(order)) + .subAggregation(avg("avg_l").field("l")) + .subAggregation(sum("sum_d").field("d")) + ).get(); assertNoFailures(response); @@ -1234,8 +1179,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( new TermsAggregationBuilder("terms").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "Math.random()", Collections.emptyMap())) @@ -1253,8 +1197,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( new TermsAggregationBuilder("terms").field("d") .script(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "'foo_' + _value", Collections.emptyMap())) @@ -1272,7 +1215,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(new TermsAggregationBuilder("terms").field("d")).get(); assertNoFailures(r); assertThat( @@ -1295,7 +1238,7 @@ public void testScriptWithValueType() throws Exception { String source = builder.toString(); try (XContentParser parser = createParser(JsonXContent.jsonXContent, source)) { - SearchResponse response = client().prepareSearch("idx").setSource(new SearchSourceBuilder().parseXContent(parser, true)).get(); + SearchResponse response = prepareSearch("idx").setSource(new SearchSourceBuilder().parseXContent(parser, true)).get(); assertNoFailures(response); LongTerms terms = response.getAggregations().get("terms"); @@ -1309,7 +1252,7 @@ public void testScriptWithValueType() throws Exception { try (XContentParser parser = createParser(JsonXContent.jsonXContent, invalidValueType)) { XContentParseException ex = expectThrows( XContentParseException.class, - () -> client().prepareSearch("idx").setSource(new SearchSourceBuilder().parseXContent(parser, true)).get() + () -> prepareSearch("idx").setSource(new SearchSourceBuilder().parseXContent(parser, true)).get() ); assertThat(ex.getCause(), instanceOf(IllegalArgumentException.class)); assertThat(ex.getCause().getMessage(), containsString("Unknown value type [foobar]")); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java index ce35f65c8a948..44361587dd09e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java @@ -49,14 +49,12 @@ public void testRequestBreaker() throws Exception { ); try { - client().prepareSearch("test") - .addAggregation( - terms("terms").field("field0.keyword") - .collectMode(randomFrom(Aggregator.SubAggCollectionMode.values())) - .order(BucketOrder.aggregation("cardinality", randomBoolean())) - .subAggregation(cardinality("cardinality").precisionThreshold(randomLongBetween(1, 40000)).field("field1.keyword")) - ) - .get(); + prepareSearch("test").addAggregation( + terms("terms").field("field0.keyword") + .collectMode(randomFrom(Aggregator.SubAggCollectionMode.values())) + .order(BucketOrder.aggregation("cardinality", randomBoolean())) + .subAggregation(cardinality("cardinality").precisionThreshold(randomLongBetween(1, 40000)).field("field1.keyword")) + ).get(); } catch (ElasticsearchException e) { if (ExceptionsHelper.unwrap(e, CircuitBreakingException.class) == null) { throw e; diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java index 9a8eb1e7b45cd..ce3140891871e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java @@ -91,8 +91,7 @@ private static double varianceSampling(int... vals) { @Override public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value").interval(1L).minDocCount(0).subAggregation(extendedStats("stats").field("value")) ) @@ -126,8 +125,7 @@ public void testEmptyAggregation() throws Exception { @Override public void testUnmapped() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx_unmapped") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx_unmapped").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").field("value")) .get(); @@ -158,8 +156,7 @@ public void testUnmapped() throws Exception { public void testPartiallyUnmapped() { double sigma = randomDouble() * 5; - ExtendedStats s1 = client().prepareSearch("idx") - .addAggregation(extendedStats("stats").field("value").sigma(sigma)) + ExtendedStats s1 = prepareSearch("idx").addAggregation(extendedStats("stats").field("value").sigma(sigma)) .get() .getAggregations() .get("stats"); @@ -187,8 +184,7 @@ public void testPartiallyUnmapped() { @Override public void testSingleValuedField() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").field("value").sigma(sigma)) .get(); @@ -215,8 +211,7 @@ public void testSingleValuedField() throws Exception { public void testSingleValuedFieldDefaultSigma() throws Exception { // Same as previous test, but uses a default value for sigma - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").field("value")) .get(); @@ -242,8 +237,7 @@ public void testSingleValuedFieldDefaultSigma() throws Exception { public void testSingleValuedField_WithFormatter() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").format("0000.0").field("value").sigma(sigma)) .get(); @@ -280,8 +274,7 @@ public void testSingleValuedField_WithFormatter() throws Exception { @Override public void testSingleValuedFieldGetProperty() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(extendedStats("stats").field("value"))) .get(); @@ -378,8 +371,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { @Override public void testSingleValuedFieldWithValueScript() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( extendedStats("stats").field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -412,8 +404,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("inc", 1); double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( extendedStats("stats").field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + inc", params)) @@ -444,8 +435,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testMultiValuedField() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").field("values").sigma(sigma)) .get(); @@ -487,8 +477,7 @@ public void testMultiValuedField() throws Exception { @Override public void testMultiValuedFieldWithValueScript() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( extendedStats("stats").field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", Collections.emptyMap())) @@ -534,8 +523,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( extendedStats("stats").field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - dec", params)) @@ -578,8 +566,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testScriptSingleValued() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( extendedStats("stats").script( new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value", Collections.emptyMap()) @@ -615,8 +602,7 @@ public void testScriptSingleValuedWithParams() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value + inc", params); double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").script(script).sigma(sigma)) .get(); @@ -643,8 +629,7 @@ public void testScriptSingleValuedWithParams() throws Exception { @Override public void testScriptMultiValued() throws Exception { double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( extendedStats("stats").script( new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['values']", Collections.emptyMap()) @@ -700,8 +685,7 @@ public void testScriptMultiValuedWithParams() throws Exception { ); double sigma = randomDouble() * randomIntBetween(1, 10); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(extendedStats("stats").script(script).sigma(sigma)) .get(); @@ -732,8 +716,7 @@ public void testScriptMultiValuedWithParams() throws Exception { } public void testEmptySubAggregation() { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("value").field("value") .subAggregation(missing("values").field("values").subAggregation(extendedStats("stats").field("value"))) @@ -776,8 +759,7 @@ public void testEmptySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>extendedStats.avg", true))) @@ -866,8 +848,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( extendedStats("foo").field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", Collections.emptyMap())) @@ -885,8 +866,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( extendedStats("foo").field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -904,7 +884,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(extendedStats("foo").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(extendedStats("foo").field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java index 2fef88b7610db..3aebbce43e1e1 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoBoundsIT.java @@ -26,9 +26,9 @@ public class GeoBoundsIT extends SpatialBoundsAggregationTestBase { public void testSingleValuedFieldNearDateLine() { - SearchResponse response = client().prepareSearch(DATELINE_IDX_NAME) - .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); + SearchResponse response = prepareSearch(DATELINE_IDX_NAME).addAggregation( + boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME).wrapLongitude(false) + ).get(); assertNoFailures(response); @@ -50,9 +50,9 @@ public void testSingleValuedFieldNearDateLineWrapLongitude() { GeoPoint geoValuesTopLeft = new GeoPoint(38, 170); GeoPoint geoValuesBottomRight = new GeoPoint(-24, -175); - SearchResponse response = client().prepareSearch(DATELINE_IDX_NAME) - .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME).wrapLongitude(true)) - .get(); + SearchResponse response = prepareSearch(DATELINE_IDX_NAME).addAggregation( + boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME).wrapLongitude(true) + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java index d1ae1589a5c2a..4b12cddde691f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/GeoCentroidIT.java @@ -29,12 +29,9 @@ public class GeoCentroidIT extends CentroidAggregationTestBase { public void testSingleValueFieldAsSubAggToGeohashGrid() { - SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) - .addAggregation( - geohashGrid("geoGrid").field(SINGLE_VALUED_FIELD_NAME) - .subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); + SearchResponse response = prepareSearch(HIGH_CARD_IDX_NAME).addAggregation( + geohashGrid("geoGrid").field(SINGLE_VALUED_FIELD_NAME).subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); GeoGrid grid = response.getAggregations().get("geoGrid"); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java index 59393da57666e..29592bb1d5d04 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java @@ -99,8 +99,7 @@ private void assertConsistent(double[] pcts, PercentileRanks values, long minVal @Override public void testEmptyAggregation() throws Exception { int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -129,8 +128,7 @@ public void testEmptyAggregation() throws Exception { @Override public void testUnmapped() throws Exception { int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx_unmapped") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx_unmapped").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", new double[] { 0, 10, 15, 100 }).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -153,8 +151,7 @@ public void testUnmapped() throws Exception { public void testSingleValuedField() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(minValue, maxValue); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -173,8 +170,7 @@ public void testNullValuesField() throws Exception { final double[] pcts = null; IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .setQuery(matchAllQuery()) + () -> prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -190,8 +186,7 @@ public void testEmptyValuesField() throws Exception { final double[] pcts = new double[0]; IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .setQuery(matchAllQuery()) + () -> prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -206,8 +201,7 @@ public void testEmptyValuesField() throws Exception { public void testSingleValuedFieldGetProperty() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(minValue, maxValue); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( global("global").subAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) @@ -236,8 +230,7 @@ public void testSingleValuedFieldGetProperty() throws Exception { public void testSingleValuedFieldOutsideRange() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = new double[] { minValue - 1, maxValue + 1 }; - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -274,8 +267,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { public void testSingleValuedFieldWithValueScript() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(minValue - 1, maxValue - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -296,8 +288,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); final double[] pcts = randomPercents(minValue - 1, maxValue - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -316,8 +307,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { public void testMultiValuedField() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(minValues, maxValues); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -335,8 +325,7 @@ public void testMultiValuedField() throws Exception { public void testMultiValuedFieldWithValueScript() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(minValues - 1, maxValues - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -354,8 +343,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { public void testMultiValuedFieldWithValueScriptReverse() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(20 - maxValues, 20 - minValues); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -376,8 +364,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); final double[] pcts = randomPercents(minValues - 1, maxValues - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -396,8 +383,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { public void testScriptSingleValued() throws Exception { int sigDigits = randomSignificantDigits(); final double[] pcts = randomPercents(minValue, maxValue); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -420,8 +406,7 @@ public void testScriptSingleValuedWithParams() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value - dec", params); final double[] pcts = randomPercents(minValue - 1, maxValue - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -442,8 +427,7 @@ public void testScriptMultiValued() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['values']", emptyMap()); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -463,8 +447,7 @@ public void testScriptMultiValuedWithParams() throws Exception { Script script = AggregationTestScriptsPlugin.DECREMENT_ALL_VALUES; final double[] pcts = randomPercents(minValues - 1, maxValues - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR) .numberOfSignificantValueDigits(sigDigits) @@ -481,8 +464,7 @@ public void testScriptMultiValuedWithParams() throws Exception { public void testOrderBySubAggregation() { int sigDigits = randomSignificantDigits(); boolean asc = randomBoolean(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(2L) @@ -513,8 +495,7 @@ public void testOrderBySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>ranks.99", true))) @@ -576,8 +557,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentileRanks("foo", new double[] { 50.0 }).method(PercentilesMethod.HDR) .field("d") @@ -596,8 +576,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentileRanks("foo", new double[] { 50.0 }).method(PercentilesMethod.HDR) .field("d") @@ -616,8 +595,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(percentileRanks("foo", new double[] { 50.0 }).method(PercentilesMethod.HDR).field("d")) .get(); assertNoFailures(r); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java index 19a1ce91fc4c8..d0c173f8f14b6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java @@ -102,8 +102,7 @@ private void assertConsistent(double[] pcts, Percentiles percentiles, long minVa @Override public void testEmptyAggregation() throws Exception { int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -133,8 +132,7 @@ public void testEmptyAggregation() throws Exception { @Override public void testUnmapped() throws Exception { int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx_unmapped") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx_unmapped").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -158,8 +156,7 @@ public void testUnmapped() throws Exception { public void testSingleValuedField() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomIntBetween(1, 5); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -178,8 +175,7 @@ public void testSingleValuedField() throws Exception { public void testSingleValuedFieldGetProperty() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( global("global").subAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) @@ -230,8 +226,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { public void testSingleValuedFieldWithValueScript() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -254,8 +249,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -275,8 +269,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { public void testMultiValuedField() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -295,8 +288,7 @@ public void testMultiValuedField() throws Exception { public void testMultiValuedFieldWithValueScript() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -315,8 +307,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { public void testMultiValuedFieldWithValueScriptReverse() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -339,8 +330,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -360,8 +350,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { public void testScriptSingleValued() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -385,8 +374,7 @@ public void testScriptSingleValuedWithParams() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -408,8 +396,7 @@ public void testScriptMultiValued() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['values']", emptyMap()); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -430,8 +417,7 @@ public void testScriptMultiValuedWithParams() throws Exception { final double[] pcts = randomPercentiles(); int sigDigits = randomSignificantDigits(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( percentiles("percentiles").numberOfSignificantValueDigits(sigDigits) .method(PercentilesMethod.HDR) @@ -449,8 +435,7 @@ public void testScriptMultiValuedWithParams() throws Exception { public void testOrderBySubAggregation() { int sigDigits = randomSignificantDigits(); boolean asc = randomBoolean(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(2L) @@ -482,8 +467,7 @@ public void testOrderBySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>percentiles.99", true))) @@ -545,8 +529,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentiles("foo").method(PercentilesMethod.HDR) .field("d") @@ -566,8 +549,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentiles("foo").method(PercentilesMethod.HDR) .field("d") @@ -587,8 +569,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(percentiles("foo").method(PercentilesMethod.HDR).field("d").percentiles(50.0)) .get(); assertNoFailures(r); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java index 844b77334f70a..0b15180e39447 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java @@ -138,9 +138,9 @@ private static MedianAbsoluteDeviationAggregationBuilder randomBuilder() { @Override public void testEmptyAggregation() throws Exception { - final SearchResponse response = client().prepareSearch("empty_bucket_idx") - .addAggregation(histogram("histogram").field("value").interval(1).minDocCount(0).subAggregation(randomBuilder().field("value"))) - .get(); + final SearchResponse response = prepareSearch("empty_bucket_idx").addAggregation( + histogram("histogram").field("value").interval(1).minDocCount(0).subAggregation(randomBuilder().field("value")) + ).get(); assertHitCount(response, 2); @@ -162,10 +162,7 @@ public void testUnmapped() throws Exception { @Override public void testSingleValuedField() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(randomBuilder().field("value")) - .get(); + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(randomBuilder().field("value")).get(); assertHitCount(response, NUMBER_OF_DOCS); @@ -177,8 +174,7 @@ public void testSingleValuedField() throws Exception { @Override public void testSingleValuedFieldGetProperty() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(randomBuilder().field("value"))) .get(); @@ -214,8 +210,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { @Override public void testSingleValuedFieldWithValueScript() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -237,8 +232,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { final Map params = new HashMap<>(); params.put("inc", 1); - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + inc", params)) @@ -257,8 +251,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testMultiValuedField() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomBuilder().field("values")) .get(); @@ -272,8 +265,7 @@ public void testMultiValuedField() throws Exception { @Override public void testMultiValuedFieldWithValueScript() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -294,8 +286,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { final Map params = new HashMap<>(); params.put("inc", 1); - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + inc", params)) @@ -313,8 +304,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testScriptSingleValued() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().script( new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value", Collections.emptyMap()) @@ -335,8 +325,7 @@ public void testScriptSingleValuedWithParams() throws Exception { final Map params = new HashMap<>(); params.put("inc", 1); - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value + inc", params)) ) @@ -354,8 +343,7 @@ public void testScriptSingleValuedWithParams() throws Exception { @Override public void testScriptMultiValued() throws Exception { - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().script( new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['values']", Collections.emptyMap()) @@ -376,8 +364,7 @@ public void testScriptMultiValuedWithParams() throws Exception { final Map params = new HashMap<>(); params.put("inc", 1); - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomBuilder().script( new Script( @@ -404,8 +391,7 @@ public void testScriptMultiValuedWithParams() throws Exception { public void testAsSubAggregation() throws Exception { final int rangeBoundary = (MAX_SAMPLE_VALUE + MIN_SAMPLE_VALUE) / 2; - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( range("range").field("value") .addRange(MIN_SAMPLE_VALUE, rangeBoundary) @@ -448,8 +434,7 @@ public void testAsSubAggregation() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { final int numberOfBuckets = 10; - final SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + final SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .size(numberOfBuckets) @@ -510,8 +495,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( randomBuilder().field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) @@ -529,8 +513,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( randomBuilder().field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) @@ -548,7 +531,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(randomBuilder().field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(randomBuilder().field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java index 41dd285e900f4..e7570f0138422 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java @@ -359,8 +359,7 @@ public void testMap() { Script combineScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op aggregation", Collections.emptyMap()); Script reduceScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op list aggregation", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(scriptedMetric("scripted").mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript)) .get(); assertNoFailures(response); @@ -402,8 +401,7 @@ public void testMapWithParams() { Script combineScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op aggregation", Collections.emptyMap()); Script reduceScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op list aggregation", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(aggregationParams) .mapScript(mapScript) @@ -451,8 +449,7 @@ public void testInitMutatesParams() { Map params = new HashMap<>(); params.put("vars", varsMap); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params) .initScript(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "vars.multiplier = 3", Collections.emptyMap())) @@ -511,8 +508,7 @@ public void testMapCombineWithParams() { ); Script reduceScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op list aggregation", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) @@ -570,8 +566,7 @@ public void testInitMapCombineWithParams() { ); Script reduceScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op list aggregation", Collections.emptyMap()); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params) .initScript(initScript) @@ -638,8 +633,7 @@ public void testInitMapCombineReduceWithParams() { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params) .initScript(initScript) @@ -694,8 +688,7 @@ public void testInitMapCombineReduceGetProperty() throws Exception { Collections.emptyMap() ); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( global("global").subAggregation( scriptedMetric("scripted").params(params) @@ -759,8 +752,7 @@ public void testMapCombineReduceWithParams() { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) @@ -805,8 +797,7 @@ public void testInitMapReduceWithParams() { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params) .initScript(initScript) @@ -853,8 +844,7 @@ public void testMapReduceWithParams() { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) @@ -907,8 +897,7 @@ public void testInitMapCombineReduceWithParamsAndReduceParams() { reduceParams ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params) .initScript(initScript) @@ -942,8 +931,7 @@ public void testInitMapCombineReduceWithParamsStored() { Map params = new HashMap<>(); params.put("vars", varsMap); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params) .initScript(new Script(ScriptType.STORED, null, "initScript_stored", Collections.emptyMap())) @@ -997,8 +985,7 @@ public void testInitMapCombineReduceWithParamsAsSubAgg() { Collections.emptyMap() ); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch("idx").setQuery(matchAllQuery()) .setSize(1000) .addAggregation( histogram("histo").field("l_value") @@ -1070,8 +1057,7 @@ public void testEmptyAggregation() throws Exception { Collections.emptyMap() ); - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -1144,8 +1130,7 @@ public void testScriptCaching() throws Exception { ); // Test that a non-deterministic init script causes the result to not be cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( scriptedMetric("foo").initScript(ndInitScript).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ) @@ -1162,8 +1147,7 @@ public void testScriptCaching() throws Exception { ); // Test that a non-deterministic map script causes the result to not be cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(scriptedMetric("foo").mapScript(ndMapScript).combineScript(combineScript).reduceScript(reduceScript)) .get(); assertNoFailures(r); @@ -1178,8 +1162,7 @@ public void testScriptCaching() throws Exception { ); // Test that a non-deterministic combine script causes the result to not be cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(ndRandom).reduceScript(reduceScript)) .get(); assertNoFailures(r); @@ -1194,8 +1177,7 @@ public void testScriptCaching() throws Exception { ); // NOTE: random reduce scripts don't hit the query shard context (they are done on the coordinator) and so can be cached. - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(ndRandom)) .get(); assertNoFailures(r); @@ -1210,8 +1192,7 @@ public void testScriptCaching() throws Exception { ); // Test that all deterministic scripts cause the request to be cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation(scriptedMetric("foo").mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript)) .get(); assertNoFailures(r); @@ -1232,8 +1213,7 @@ public void testConflictingAggAndScriptParams() { Script combineScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op aggregation", Collections.emptyMap()); Script reduceScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "no-op list aggregation", Collections.emptyMap()); - SearchRequestBuilder builder = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchRequestBuilder builder = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( scriptedMetric("scripted").params(params).mapScript(mapScript).combineScript(combineScript).reduceScript(reduceScript) ); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java index 1b1bba3fc2529..0492ad315c8da 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java @@ -48,8 +48,7 @@ protected Collection> nodePlugins() { @Override public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation(histogram("histo").field("value").interval(1L).minDocCount(0).subAggregation(stats("stats").field("value"))) .get(); @@ -73,10 +72,7 @@ public void testEmptyAggregation() throws Exception { @Override public void testSingleValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(stats("stats").field("value")) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(stats("stats").field("value")).get(); assertShardExecutionState(searchResponse, 0); @@ -94,8 +90,7 @@ public void testSingleValuedField() throws Exception { public void testSingleValuedField_WithFormatter() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(stats("stats").format("0000.0").field("value")) .get(); @@ -117,8 +112,7 @@ public void testSingleValuedField_WithFormatter() throws Exception { @Override public void testSingleValuedFieldGetProperty() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(stats("stats").field("value"))) .get(); @@ -156,10 +150,7 @@ public void testSingleValuedFieldGetProperty() throws Exception { @Override public void testMultiValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(stats("stats").field("values")) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(stats("stats").field("values")).get(); assertShardExecutionState(searchResponse, 0); @@ -180,8 +171,7 @@ public void testMultiValuedField() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>stats.avg", true))) @@ -254,8 +244,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( stats("foo").field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", Collections.emptyMap())) @@ -273,8 +262,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( stats("foo").field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value + 1", Collections.emptyMap())) @@ -292,7 +280,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(stats("foo").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(stats("foo").field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java index 9ea866f889720..5504dd9148e87 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java @@ -77,8 +77,7 @@ public void setupSuiteScopeCluster() throws Exception { @Override public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation(histogram("histo").field("value").interval(1L).minDocCount(0).subAggregation(sum("sum").field("value"))) .get(); @@ -100,10 +99,7 @@ public void testUnmapped() throws Exception {} @Override public void testSingleValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(sum("sum").field("value")) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(sum("sum").field("value")).get(); assertHitCount(searchResponse, 10); @@ -114,8 +110,7 @@ public void testSingleValuedField() throws Exception { } public void testSingleValuedFieldWithFormatter() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(sum("sum").format("0000.0").field("value")) .get(); @@ -131,8 +126,7 @@ public void testSingleValuedFieldWithFormatter() throws Exception { @Override public void testSingleValuedFieldGetProperty() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(sum("sum").field("value"))) .get(); @@ -158,10 +152,7 @@ public void testSingleValuedFieldGetProperty() throws Exception { @Override public void testMultiValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(sum("sum").field("values")) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(sum("sum").field("values")).get(); assertHitCount(searchResponse, 10); @@ -173,8 +164,7 @@ public void testMultiValuedField() throws Exception { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>sum", true))) @@ -232,8 +222,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( sum("foo").field("d").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, RANDOM_SCRIPT, Collections.emptyMap())) ) @@ -250,8 +239,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( sum("foo").field("d").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_SCRIPT, Collections.emptyMap())) ) @@ -268,7 +256,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(sum("foo").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(sum("foo").field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java index 8a1ee38630be9..edcb65f169bcd 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java @@ -96,8 +96,7 @@ private void assertConsistent(double[] pcts, PercentileRanks values, long minVal @Override public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -123,8 +122,7 @@ public void testNullValuesField() throws Exception { final double[] pcts = null; IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .setQuery(matchAllQuery()) + () -> prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.TDIGEST).field("value")) .get() ); @@ -135,8 +133,7 @@ public void testEmptyValuesField() throws Exception { final double[] pcts = new double[0]; IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .setQuery(matchAllQuery()) + () -> prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.TDIGEST).field("value")) .get() ); @@ -145,8 +142,7 @@ public void testEmptyValuesField() throws Exception { @Override public void testUnmapped() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx_unmapped") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx_unmapped").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", new double[] { 0, 10, 15, 100 })).field("value")) .get(); @@ -164,8 +160,7 @@ public void testUnmapped() throws Exception { @Override public void testSingleValuedField() throws Exception { final double[] pcts = randomPercents(minValue, maxValue); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).field("value")) .get(); @@ -178,8 +173,7 @@ public void testSingleValuedField() throws Exception { @Override public void testSingleValuedFieldGetProperty() throws Exception { final double[] pcts = randomPercents(minValue, maxValue); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).field("value"))) .get(); @@ -200,8 +194,7 @@ public void testSingleValuedFieldGetProperty() throws Exception { public void testSingleValuedFieldOutsideRange() throws Exception { final double[] pcts = new double[] { minValue - 1, maxValue + 1 }; - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).field("value")) .get(); @@ -228,8 +221,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { @Override public void testSingleValuedFieldWithValueScript() throws Exception { final double[] pcts = randomPercents(minValue - 1, maxValue - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentileRanks("percentile_ranks", pcts)).field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) @@ -247,8 +239,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); final double[] pcts = randomPercents(minValue - 1, maxValue - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentileRanks("percentile_ranks", pcts)).field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - dec", params)) @@ -264,8 +255,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testMultiValuedField() throws Exception { final double[] pcts = randomPercents(minValues, maxValues); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).field("values")) .get(); @@ -278,8 +268,7 @@ public void testMultiValuedField() throws Exception { @Override public void testMultiValuedFieldWithValueScript() throws Exception { final double[] pcts = randomPercents(minValues - 1, maxValues - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentileRanks("percentile_ranks", pcts)).field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) @@ -294,8 +283,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { public void testMultiValuedFieldWithValueScriptReverse() throws Exception { final double[] pcts = randomPercents(-maxValues, -minValues); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentileRanks("percentile_ranks", pcts)).field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value * -1", emptyMap())) @@ -313,8 +301,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); final double[] pcts = randomPercents(minValues - 1, maxValues - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentileRanks("percentile_ranks", pcts)).field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - dec", params)) @@ -330,8 +317,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testScriptSingleValued() throws Exception { final double[] pcts = randomPercents(minValue, maxValue); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentileRanks("percentile_ranks", pcts)).script( new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value", emptyMap()) @@ -353,8 +339,7 @@ public void testScriptSingleValuedWithParams() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value - dec", params); final double[] pcts = randomPercents(minValue - 1, maxValue - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).script(script)) .get(); @@ -368,8 +353,7 @@ public void testScriptSingleValuedWithParams() throws Exception { public void testScriptMultiValued() throws Exception { final double[] pcts = randomPercents(minValues, maxValues); Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['values']", emptyMap()); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).script(script)) .get(); @@ -384,8 +368,7 @@ public void testScriptMultiValuedWithParams() throws Exception { Script script = AggregationTestScriptsPlugin.DECREMENT_ALL_VALUES; final double[] pcts = randomPercents(minValues - 1, maxValues - 1); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).script(script)) .get(); @@ -397,8 +380,7 @@ public void testScriptMultiValuedWithParams() throws Exception { public void testOrderBySubAggregation() { boolean asc = randomBoolean(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(2L) @@ -425,8 +407,7 @@ public void testOrderBySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>ranks.99", true))) @@ -488,8 +469,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentileRanks("foo", new double[] { 50.0 }).field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "Math.random()", emptyMap())) @@ -507,8 +487,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentileRanks("foo", new double[] { 50.0 }).field("d") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) @@ -526,10 +505,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx") - .setSize(0) - .addAggregation(percentileRanks("foo", new double[] { 50.0 }).field("d")) - .get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(percentileRanks("foo", new double[] { 50.0 }).field("d")).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java index 5a2f246735a32..3e7f4f9780327 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java @@ -102,8 +102,7 @@ private void assertConsistent(double[] pcts, Percentiles percentiles, long minVa @Override public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(1L) @@ -127,8 +126,7 @@ public void testEmptyAggregation() throws Exception { @Override public void testUnmapped() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx_unmapped") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx_unmapped").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).field("value").percentiles(0, 10, 15, 100)) .get(); @@ -146,8 +144,7 @@ public void testUnmapped() throws Exception { @Override public void testSingleValuedField() throws Exception { final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).field("value").percentiles(pcts)) .get(); @@ -160,8 +157,7 @@ public void testSingleValuedField() throws Exception { @Override public void testSingleValuedFieldGetProperty() throws Exception { final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(randomCompression(percentiles("percentiles")).field("value").percentiles(pcts))) .get(); @@ -197,8 +193,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { @Override public void testSingleValuedFieldWithValueScript() throws Exception { final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentiles("percentiles")).field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) @@ -217,8 +212,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentiles("percentiles")).field("value") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - dec", params)) @@ -235,8 +229,7 @@ public void testSingleValuedFieldWithValueScriptWithParams() throws Exception { @Override public void testMultiValuedField() throws Exception { final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).field("values").percentiles(pcts)) .get(); @@ -249,8 +242,7 @@ public void testMultiValuedField() throws Exception { @Override public void testMultiValuedFieldWithValueScript() throws Exception { final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentiles("percentiles")).field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - 1", emptyMap())) @@ -266,8 +258,7 @@ public void testMultiValuedFieldWithValueScript() throws Exception { public void testMultiValuedFieldWithValueScriptReverse() throws Exception { final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentiles("percentiles")).field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value * -1", emptyMap())) @@ -286,8 +277,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { Map params = new HashMap<>(); params.put("dec", 1); final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( randomCompression(percentiles("percentiles")).field("values") .script(new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "_value - dec", params)) @@ -305,8 +295,7 @@ public void testMultiValuedFieldWithValueScriptWithParams() throws Exception { public void testScriptSingleValued() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value", emptyMap()); final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).script(script).percentiles(pcts)) .get(); @@ -324,8 +313,7 @@ public void testScriptSingleValuedWithParams() throws Exception { Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['value'].value - dec", params); final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).script(script).percentiles(pcts)) .get(); @@ -340,8 +328,7 @@ public void testScriptMultiValued() throws Exception { final double[] pcts = randomPercentiles(); Script script = new Script(ScriptType.INLINE, AggregationTestScriptsPlugin.NAME, "doc['values']", emptyMap()); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).script(script).percentiles(pcts)) .get(); @@ -356,8 +343,7 @@ public void testScriptMultiValuedWithParams() throws Exception { Script script = AggregationTestScriptsPlugin.DECREMENT_ALL_VALUES; final double[] pcts = randomPercentiles(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(randomCompression(percentiles("percentiles")).script(script).percentiles(pcts)) .get(); @@ -369,8 +355,7 @@ public void testScriptMultiValuedWithParams() throws Exception { public void testOrderBySubAggregation() { boolean asc = randomBoolean(); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( histogram("histo").field("value") .interval(2L) @@ -397,8 +382,7 @@ public void testOrderBySubAggregation() { @Override public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>percentiles.99", true))) @@ -460,8 +444,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentiles("foo").field("d") .percentiles(50.0) @@ -480,8 +463,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( percentiles("foo").field("d") .percentiles(50.0) @@ -500,7 +482,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d").percentiles(50.0)).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(percentiles("foo").field("d").percentiles(50.0)).get(); assertNoFailures(r); assertThat( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index 685e269516b0f..6622e4d908efe 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -311,13 +311,11 @@ private String key(Terms.Bucket bucket) { } public void testBasics() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) + ).get(); assertNoFailures(response); @@ -347,8 +345,7 @@ public void testBasics() throws Exception { public void testIssue11119() throws Exception { // Test that top_hits aggregation is fed scores if query results size=0 - SearchResponse response = client().prepareSearch("field-collapsing") - .setSize(0) + SearchResponse response = prepareSearch("field-collapsing").setSize(0) .setQuery(matchQuery("text", "x y z")) .addAggregation(terms("terms").executionHint(randomExecutionHint()).field("group").subAggregation(topHits("hits"))) .get(); @@ -380,8 +377,7 @@ public void testIssue11119() throws Exception { // (technically not a test of top_hits but implementation details are // tied up with the need to feed scores into the agg tree even when // users don't want ranked set of query results.) - response = client().prepareSearch("field-collapsing") - .setSize(0) + response = prepareSearch("field-collapsing").setSize(0) .setMinScore(0.0001f) .setQuery(matchQuery("text", "x y z")) .addAggregation(terms("terms").executionHint(randomExecutionHint()).field("group")) @@ -399,14 +395,12 @@ public void testIssue11119() throws Exception { } public void testBreadthFirstWithScoreNeeded() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .collectMode(SubAggCollectionMode.BREADTH_FIRST) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").size(3)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .collectMode(SubAggCollectionMode.BREADTH_FIRST) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").size(3)) + ).get(); assertNoFailures(response); @@ -430,16 +424,14 @@ public void testBreadthFirstWithScoreNeeded() throws Exception { } public void testBreadthFirstWithAggOrderAndScoreNeeded() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .collectMode(SubAggCollectionMode.BREADTH_FIRST) - .field(TERMS_AGGS_FIELD) - .order(BucketOrder.aggregation("max", false)) - .subAggregation(max("max").field(SORT_FIELD)) - .subAggregation(topHits("hits").size(3)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .collectMode(SubAggCollectionMode.BREADTH_FIRST) + .field(TERMS_AGGS_FIELD) + .order(BucketOrder.aggregation("max", false)) + .subAggregation(max("max").field(SORT_FIELD)) + .subAggregation(topHits("hits").size(3)) + ).get(); assertNoFailures(response); @@ -463,8 +455,7 @@ public void testBreadthFirstWithAggOrderAndScoreNeeded() throws Exception { } public void testBasicsGetProperty() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(topHits("hits"))) .get(); @@ -486,17 +477,14 @@ public void testBasicsGetProperty() throws Exception { public void testPagination() throws Exception { int size = randomIntBetween(1, 10); int from = randomIntBetween(0, 10); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)).from(from).size(size)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)).from(from).size(size)) + ).get(); assertNoFailures(response); - SearchResponse control = client().prepareSearch("idx") - .setFrom(from) + SearchResponse control = prepareSearch("idx").setFrom(from) .setSize(size) .setPostFilter(QueryBuilders.termQuery(TERMS_AGGS_FIELD, "val0")) .addSort(SORT_FIELD, SortOrder.DESC) @@ -531,15 +519,13 @@ public void testPagination() throws Exception { } public void testSortByBucket() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .order(BucketOrder.aggregation("max_sort", false)) - .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)).trackScores(true)) - .subAggregation(max("max_sort").field(SORT_FIELD)) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .order(BucketOrder.aggregation("max_sort", false)) + .subAggregation(topHits("hits").sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)).trackScores(true)) + .subAggregation(max("max_sort").field(SORT_FIELD)) + ).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("terms"); @@ -566,8 +552,7 @@ public void testSortByBucket() throws Exception { } public void testFieldCollapsing() throws Exception { - SearchResponse response = client().prepareSearch("field-collapsing") - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("field-collapsing").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(matchQuery("text", "term rare")) .addAggregation( terms("terms").executionHint(randomExecutionHint()) @@ -612,8 +597,7 @@ public void testFieldCollapsing() throws Exception { public void testFetchFeatures() { final boolean seqNoAndTerm = randomBoolean(); - SearchResponse response = client().prepareSearch("idx") - .setQuery(matchQuery("text", "text").queryName("test")) + SearchResponse response = prepareSearch("idx").setQuery(matchQuery("text", "text").queryName("test")) .addAggregation( terms("terms").executionHint(randomExecutionHint()) .field(TERMS_AGGS_FIELD) @@ -683,13 +667,11 @@ public void testFetchFeatures() { public void testInvalidSortField() throws Exception { try { - client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").sort(SortBuilders.fieldSort("xyz").order(SortOrder.DESC))) - ) - .get(); + prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").sort(SortBuilders.fieldSort("xyz").order(SortOrder.DESC))) + ).get(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.toString(), containsString("No mapping found for [xyz] in order to sort on")); @@ -697,7 +679,7 @@ public void testInvalidSortField() throws Exception { } public void testEmptyIndex() throws Exception { - SearchResponse response = client().prepareSearch("empty").addAggregation(topHits("hits")).get(); + SearchResponse response = prepareSearch("empty").addAggregation(topHits("hits")).get(); assertNoFailures(response); TopHits hits = response.getAggregations().get("hits"); @@ -710,8 +692,7 @@ public void testTrackScores() throws Exception { boolean[] trackScores = new boolean[] { true, false }; for (boolean trackScore : trackScores) { logger.info("Track score={}", trackScore); - SearchResponse response = client().prepareSearch("field-collapsing") - .setQuery(matchQuery("text", "term rare")) + SearchResponse response = prepareSearch("field-collapsing").setQuery(matchQuery("text", "term rare")) .addAggregation( terms("terms").field("group") .subAggregation(topHits("hits").trackScores(trackScore).size(1).sort("_index", SortOrder.DESC)) @@ -748,8 +729,7 @@ public void testTrackScores() throws Exception { } public void testTopHitsInNestedSimple() throws Exception { - SearchResponse searchResponse = client().prepareSearch("articles") - .setQuery(matchQuery("title", "title")) + SearchResponse searchResponse = prepareSearch("articles").setQuery(matchQuery("title", "title")) .addAggregation( nested("to-comments", "comments").subAggregation( terms("users").field("comments.user").subAggregation(topHits("top-comments").sort("comments.date", SortOrder.ASC)) @@ -793,8 +773,7 @@ public void testTopHitsInNestedSimple() throws Exception { } public void testTopHitsInSecondLayerNested() throws Exception { - SearchResponse searchResponse = client().prepareSearch("articles") - .setQuery(matchQuery("title", "title")) + SearchResponse searchResponse = prepareSearch("articles").setQuery(matchQuery("title", "title")) .addAggregation( nested("to-comments", "comments").subAggregation( nested("to-reviewers", "comments.reviewers").subAggregation( @@ -896,8 +875,9 @@ public void testNestedFetchFeatures() { matchQuery("comments.message", "comment") ).highlighterType(hlType); - SearchResponse searchResponse = client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "comment").queryName("test"), ScoreMode.Avg)) + SearchResponse searchResponse = prepareSearch("articles").setQuery( + nestedQuery("comments", matchQuery("comments.message", "comment").queryName("test"), ScoreMode.Avg) + ) .addAggregation( nested("to-comments", "comments").subAggregation( topHits("top-comments").size(1) @@ -949,21 +929,19 @@ public void testNestedFetchFeatures() { } public void testTopHitsInNested() throws Exception { - SearchResponse searchResponse = client().prepareSearch("articles") - .addAggregation( - histogram("dates").field("date") - .interval(5) - .subAggregation( - nested("to-comments", "comments").subAggregation( - topHits("comments").highlighter( - new HighlightBuilder().field( - new HighlightBuilder.Field("comments.message").highlightQuery(matchQuery("comments.message", "text")) - ) - ).sort("comments.id", SortOrder.ASC) - ) + SearchResponse searchResponse = prepareSearch("articles").addAggregation( + histogram("dates").field("date") + .interval(5) + .subAggregation( + nested("to-comments", "comments").subAggregation( + topHits("comments").highlighter( + new HighlightBuilder().field( + new HighlightBuilder.Field("comments.message").highlightQuery(matchQuery("comments.message", "text")) + ) + ).sort("comments.id", SortOrder.ASC) ) - ) - .get(); + ) + ).get(); Histogram histogram = searchResponse.getAggregations().get("dates"); for (int i = 0; i < numArticles; i += 5) { @@ -995,38 +973,33 @@ public void testUseMaxDocInsteadOfSize() throws Exception { "idx" ); assertNoFailures( - client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation( - topHits("hits").size(ArrayUtil.MAX_ARRAY_LENGTH - 1) - .sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)) - ) - ) + prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation( + topHits("hits").size(ArrayUtil.MAX_ARRAY_LENGTH - 1).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC)) + ) + ) ); updateIndexSettings(Settings.builder().putNull(IndexSettings.MAX_INNER_RESULT_WINDOW_SETTING.getKey()), "idx"); } public void testTooHighResultWindow() throws Exception { assertNoFailures( - client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").from(50).size(10).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) - ) + prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").from(50).size(10).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) + ) ); Exception e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").from(100).size(10).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) - ) - .get() + () -> prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").from(100).size(10).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) + ).get() ); assertThat( e.getCause().getMessage(), @@ -1034,13 +1007,11 @@ public void testTooHighResultWindow() throws Exception { ); e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").from(10).size(100).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) - ) - .get() + () -> prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").from(10).size(100).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) + ).get() ); assertThat( e.getCause().getMessage(), @@ -1049,32 +1020,28 @@ public void testTooHighResultWindow() throws Exception { updateIndexSettings(Settings.builder().put(IndexSettings.MAX_INNER_RESULT_WINDOW_SETTING.getKey(), 110), "idx"); assertNoFailures( - client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").from(100).size(10).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) - ) + prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").from(100).size(10).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) + ) ); assertNoFailures( - client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").from(10).size(100).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) - ) + prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").from(10).size(100).sort(SortBuilders.fieldSort(SORT_FIELD).order(SortOrder.DESC))) + ) ); updateIndexSettings(Settings.builder().putNull(IndexSettings.MAX_INNER_RESULT_WINDOW_SETTING.getKey()), "idx"); } public void testNoStoredFields() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms("terms").executionHint(randomExecutionHint()) - .field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").storedField("_none_")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms("terms").executionHint(randomExecutionHint()) + .field(TERMS_AGGS_FIELD) + .subAggregation(topHits("hits").storedField("_none_")) + ).get(); assertNoFailures(response); @@ -1129,8 +1096,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script field does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( topHits("foo").scriptField( "bar", @@ -1150,8 +1116,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script sort does not get cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( topHits("foo").sort( SortBuilders.scriptSort( @@ -1173,8 +1138,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script field does not get cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( topHits("foo").scriptField("bar", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "5", Collections.emptyMap())) ) @@ -1191,8 +1155,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script sort does not get cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( topHits("foo").sort( SortBuilders.scriptSort( @@ -1214,7 +1177,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(topHits("foo")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(topHits("foo")).get(); assertNoFailures(r); assertThat( @@ -1233,8 +1196,7 @@ public void testScriptCaching() throws Exception { public void testWithRescore() { // Rescore with default sort on relevancy (score) { - SearchResponse response = client().prepareSearch("idx") - .addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) + SearchResponse response = prepareSearch("idx").addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) .addAggregation(terms("terms").field(TERMS_AGGS_FIELD).subAggregation(topHits("hits"))) .get(); Terms terms = response.getAggregations().get("terms"); @@ -1247,8 +1209,7 @@ public void testWithRescore() { } { - SearchResponse response = client().prepareSearch("idx") - .addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) + SearchResponse response = prepareSearch("idx").addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) .addAggregation(terms("terms").field(TERMS_AGGS_FIELD).subAggregation(topHits("hits").sort(SortBuilders.scoreSort()))) .get(); Terms terms = response.getAggregations().get("terms"); @@ -1262,8 +1223,7 @@ public void testWithRescore() { // Rescore should not be applied if the sort order is not relevancy { - SearchResponse response = client().prepareSearch("idx") - .addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) + SearchResponse response = prepareSearch("idx").addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) .addAggregation( terms("terms").field(TERMS_AGGS_FIELD).subAggregation(topHits("hits").sort(SortBuilders.fieldSort("_index"))) ) @@ -1278,8 +1238,7 @@ public void testWithRescore() { } { - SearchResponse response = client().prepareSearch("idx") - .addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) + SearchResponse response = prepareSearch("idx").addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) .addAggregation( terms("terms").field(TERMS_AGGS_FIELD) .subAggregation(topHits("hits").sort(SortBuilders.scoreSort()).sort(SortBuilders.fieldSort("_index"))) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java index 62f28211c674a..4d5c06d8acdd0 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java @@ -67,8 +67,7 @@ protected Collection> nodePlugins() { } public void testUnmapped() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx_unmapped") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx_unmapped").setQuery(matchAllQuery()) .addAggregation(count("count").field("value")) .get(); @@ -81,10 +80,7 @@ public void testUnmapped() throws Exception { } public void testSingleValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(count("count").field("value")) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(count("count").field("value")).get(); assertHitCount(searchResponse, 10); @@ -95,8 +91,7 @@ public void testSingleValuedField() throws Exception { } public void testSingleValuedFieldGetProperty() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(count("count").field("value"))) .get(); @@ -133,10 +128,7 @@ public void testSingleValuedFieldPartiallyUnmapped() throws Exception { } public void testMultiValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) - .addAggregation(count("count").field("values")) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()).addAggregation(count("count").field("values")).get(); assertHitCount(searchResponse, 10); @@ -147,8 +139,7 @@ public void testMultiValuedField() throws Exception { } public void testSingleValuedScript() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( count("count").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_FIELD_SCRIPT, Collections.emptyMap())) ) @@ -163,8 +154,7 @@ public void testSingleValuedScript() throws Exception { } public void testMultiValuedScript() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( count("count").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, SUM_VALUES_FIELD_SCRIPT, Collections.emptyMap())) ) @@ -180,8 +170,7 @@ public void testMultiValuedScript() throws Exception { public void testSingleValuedScriptWithParams() throws Exception { Map params = Collections.singletonMap("field", "value"); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(count("count").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, SUM_FIELD_PARAMS_SCRIPT, params))) .get(); @@ -195,8 +184,7 @@ public void testSingleValuedScriptWithParams() throws Exception { public void testMultiValuedScriptWithParams() throws Exception { Map params = Collections.singletonMap("field", "values"); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation(count("count").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, SUM_FIELD_PARAMS_SCRIPT, params))) .get(); @@ -235,8 +223,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a nondeterministic script does not get cached - SearchResponse r = client().prepareSearch("cache_test_idx") - .setSize(0) + SearchResponse r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( count("foo").field("d").script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, RANDOM_SCRIPT, Collections.emptyMap())) ) @@ -253,8 +240,7 @@ public void testScriptCaching() throws Exception { ); // Test that a request using a deterministic script gets cached - r = client().prepareSearch("cache_test_idx") - .setSize(0) + r = prepareSearch("cache_test_idx").setSize(0) .addAggregation( count("foo").field("d") .script(new Script(ScriptType.INLINE, METRIC_SCRIPT_ENGINE, VALUE_FIELD_SCRIPT, Collections.emptyMap())) @@ -272,7 +258,7 @@ public void testScriptCaching() throws Exception { ); // Ensure that non-scripted requests are cached as normal - r = client().prepareSearch("cache_test_idx").setSize(0).addAggregation(count("foo").field("d")).get(); + r = prepareSearch("cache_test_idx").setSize(0).addAggregation(count("foo").field("d")).get(); assertNoFailures(r); assertThat( @@ -286,8 +272,7 @@ public void testScriptCaching() throws Exception { } public void testOrderByEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("idx").setQuery(matchAllQuery()) .addAggregation( terms("terms").field("value") .order(BucketOrder.compound(BucketOrder.aggregation("filter>count", true))) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java index 4947f9a3a43e0..195b4a41b617a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketMetricsPipeLineAggregationTestCase.java @@ -127,12 +127,9 @@ private String randomName() { } public void testDocCountTopLevel() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME).interval(interval).extendedBounds(minRandomValue, maxRandomValue) - ) - .addAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">_count")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME).interval(interval).extendedBounds(minRandomValue, maxRandomValue) + ).addAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">_count")).get(); assertNoFailures(response); @@ -157,18 +154,14 @@ public void testDocCountTopLevel() { } public void testDocCountAsSubAgg() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - ) - .subAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">_count")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME).interval(interval).extendedBounds(minRandomValue, maxRandomValue) + ) + .subAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">_count")) + ).get(); assertNoFailures(response); @@ -203,10 +196,9 @@ public void testDocCountAsSubAgg() { } public void testMetricTopLevel() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(terms(termsName).field("tag").subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME))) - .addAggregation(BucketMetricsPipelineAgg("pipeline_agg", termsName + ">sum")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag").subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).addAggregation(BucketMetricsPipelineAgg("pipeline_agg", termsName + ">sum")).get(); assertNoFailures(response); @@ -236,19 +228,17 @@ public void testMetricTopLevel() { } public void testMetricAsSubAgg() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .subAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">sum")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .extendedBounds(minRandomValue, maxRandomValue) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ) + .subAggregation(BucketMetricsPipelineAgg("pipeline_agg", histoName + ">sum")) + ).get(); assertNoFailures(response); @@ -292,21 +282,19 @@ public void testMetricAsSubAgg() { } public void testMetricAsSubAggWithInsertZeros() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .subAggregation( - BucketMetricsPipelineAgg("pipeline_agg", histoName + ">sum").gapPolicy(BucketHelpers.GapPolicy.INSERT_ZEROS) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .extendedBounds(minRandomValue, maxRandomValue) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ) + .subAggregation( + BucketMetricsPipelineAgg("pipeline_agg", histoName + ">sum").gapPolicy(BucketHelpers.GapPolicy.INSERT_ZEROS) + ) + ).get(); assertNoFailures(response); @@ -346,14 +334,11 @@ public void testMetricAsSubAggWithInsertZeros() { } public void testNoBuckets() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .includeExclude(new IncludeExclude(null, "tag.*", null, null)) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .addAggregation(BucketMetricsPipelineAgg("pipeline_agg", termsName + ">sum")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .includeExclude(new IncludeExclude(null, "tag.*", null, null)) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).addAggregation(BucketMetricsPipelineAgg("pipeline_agg", termsName + ">sum")).get(); assertNoFailures(response); @@ -371,19 +356,14 @@ public void testNoBuckets() { } public void testNested() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - ) - .subAggregation(BucketMetricsPipelineAgg("nested_histo_bucket", histoName + ">_count")) - ) - .addAggregation(BucketMetricsPipelineAgg("nested_terms_bucket", termsName + ">nested_histo_bucket." + nestedMetric())) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME).interval(interval).extendedBounds(minRandomValue, maxRandomValue) + ) + .subAggregation(BucketMetricsPipelineAgg("nested_histo_bucket", histoName + ">_count")) + ).addAggregation(BucketMetricsPipelineAgg("nested_terms_bucket", termsName + ">nested_histo_bucket." + nestedMetric())).get(); assertNoFailures(response); @@ -488,7 +468,7 @@ public void testFieldIsntWrittenOutTwice() throws Exception { groupByLicenseAgg.subAggregation(licensePerDayBuilder); groupByLicenseAgg.subAggregation(BucketMetricsPipelineAgg("peak", "licenses_per_day>total_licenses")); - SearchResponse response = client().prepareSearch("foo_*").setSize(0).addAggregation(groupByLicenseAgg).get(); + SearchResponse response = prepareSearch("foo_*").setSize(0).addAggregation(groupByLicenseAgg).get(); BytesReference bytes = XContentHelper.toXContent(response, XContentType.JSON, false); XContentHelper.convertToMap(bytes, false, XContentType.JSON); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java index 56101a7c7a4ce..c53f6c276ceaa 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java @@ -155,24 +155,22 @@ private XContentBuilder newDocBuilder() throws IOException { } public void testInlineScript() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), - "field2Sum", - "field3Sum", - "field4Sum" - ) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Sum", + "field3Sum", + "field4Sum" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -205,24 +203,22 @@ public void testInlineScript() { } public void testInlineScript2() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 / _value2", Collections.emptyMap()), - "field2Sum", - "field3Sum", - "field4Sum" - ) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 / _value2", Collections.emptyMap()), + "field2Sum", + "field3Sum", + "field4Sum" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -255,24 +251,22 @@ public void testInlineScript2() { } public void testInlineScriptWithDateRange() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - dateRange("range").field(FIELD_5_NAME) - .addUnboundedFrom(date) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), - "field2Sum", - "field3Sum", - "field4Sum" - ) + SearchResponse response = prepareSearch("idx").addAggregation( + dateRange("range").field(FIELD_5_NAME) + .addUnboundedFrom(date) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Sum", + "field3Sum", + "field4Sum" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -305,20 +299,18 @@ public void testInlineScriptWithDateRange() { } public void testInlineScriptSingleVariable() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0", Collections.emptyMap()), - "field2Sum" - ) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0", Collections.emptyMap()), + "field2Sum" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -349,22 +341,20 @@ public void testInlineScriptNamedVars() { bucketsPathsMap.put("foo", "field2Sum"); bucketsPathsMap.put("bar", "field3Sum"); bucketsPathsMap.put("baz", "field4Sum"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - bucketsPathsMap, - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "foo + bar + baz", Collections.emptyMap()) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + bucketsPathsMap, + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "foo + bar + baz", Collections.emptyMap()) ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -402,16 +392,14 @@ public void testInlineScriptWithParams() { Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "(_value0 + _value1 + _value2) * factor", params); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation(bucketScript("seriesArithmetic", script, "field2Sum", "field3Sum", "field4Sum")) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation(bucketScript("seriesArithmetic", script, "field2Sum", "field3Sum", "field4Sum")) + ).get(); assertNoFailures(response); @@ -444,24 +432,22 @@ public void testInlineScriptWithParams() { } public void testInlineScriptInsertZeros() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), - "field2Sum", - "field3Sum", - "field4Sum" - ).gapPolicy(GapPolicy.INSERT_ZEROS) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Sum", + "field3Sum", + "field4Sum" + ).gapPolicy(GapPolicy.INSERT_ZEROS) + ) + ).get(); assertNoFailures(response); @@ -496,18 +482,13 @@ public void testInlineScriptInsertZeros() { } public void testInlineScriptReturnNull() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation( - bucketScript( - "nullField", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "return null", Collections.emptyMap()) - ) - ) - ) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation( + bucketScript("nullField", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "return null", Collections.emptyMap())) + ) + ).get(); assertNoFailures(response); @@ -533,24 +514,22 @@ public void testStoredScript() { ) ); - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.STORED, null, "my_script", Collections.emptyMap()), - "field2Sum", - "field3Sum", - "field4Sum" - ) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.STORED, null, "my_script", Collections.emptyMap()), + "field2Sum", + "field3Sum", + "field4Sum" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -583,24 +562,22 @@ public void testStoredScript() { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) - .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) - .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), - "field2Sum", - "field3Sum", - "field4Sum" - ) + SearchResponse response = prepareSearch("idx_unmapped").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(sum("field2Sum").field(FIELD_2_NAME)) + .subAggregation(sum("field3Sum").field(FIELD_3_NAME)) + .subAggregation(sum("field4Sum").field(FIELD_4_NAME)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Sum", + "field3Sum", + "field4Sum" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -823,29 +800,22 @@ public void testObjectBucketPathAgg() throws Exception { public void testInlineScriptWithMultiValueAggregationIllegalBucketsPaths() { try { - client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(percentiles("field2Percentile").field(FIELD_2_NAME).percentiles(10, 50, 90)) - .subAggregation(percentiles("field3Percentile").field(FIELD_3_NAME).percentiles(10, 50, 90)) - .subAggregation(percentiles("field4Percentile").field(FIELD_4_NAME).percentiles(10, 50, 90)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script( - ScriptType.INLINE, - CustomScriptPlugin.NAME, - "_value0 + _value1 + _value2", - Collections.emptyMap() - ), - "field2Percentile", - "field3Percentile", - "field4Percentile" - ) + prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(percentiles("field2Percentile").field(FIELD_2_NAME).percentiles(10, 50, 90)) + .subAggregation(percentiles("field3Percentile").field(FIELD_3_NAME).percentiles(10, 50, 90)) + .subAggregation(percentiles("field4Percentile").field(FIELD_4_NAME).percentiles(10, 50, 90)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Percentile", + "field3Percentile", + "field4Percentile" ) - ) - .get(); + ) + ).get(); fail("Illegal bucketsPaths was provided but no exception was thrown."); } catch (Exception e) { @@ -866,24 +836,22 @@ public void testInlineScriptWithMultiValueAggregationIllegalBucketsPaths() { public void testInlineScriptWithMultiValueAggregation() { int percentile = 90; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(percentiles("field2Percentile").field(FIELD_2_NAME).percentiles(percentile)) - .subAggregation(percentiles("field3Percentile").field(FIELD_3_NAME).percentiles(percentile)) - .subAggregation(percentiles("field4Percentile").field(FIELD_4_NAME).percentiles(percentile)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), - "field2Percentile", - "field3Percentile", - "field4Percentile" - ) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(percentiles("field2Percentile").field(FIELD_2_NAME).percentiles(percentile)) + .subAggregation(percentiles("field3Percentile").field(FIELD_3_NAME).percentiles(percentile)) + .subAggregation(percentiles("field4Percentile").field(FIELD_4_NAME).percentiles(percentile)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Percentile", + "field3Percentile", + "field4Percentile" ) - ) - .get(); + ) + ).get(); assertNoFailures(response); @@ -919,26 +887,22 @@ public void testInlineScriptWithMultiValueAggregationDifferentBucketsPaths() { int percentile10 = 10; int percentile50 = 50; int percentile90 = 90; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - histogram("histo").field(FIELD_1_NAME) - .interval(interval) - .subAggregation(percentiles("field2Percentile").field(FIELD_2_NAME)) - .subAggregation( - percentiles("field3Percentile").field(FIELD_3_NAME).percentiles(percentile10, percentile50, percentile90) + SearchResponse response = prepareSearch("idx").addAggregation( + histogram("histo").field(FIELD_1_NAME) + .interval(interval) + .subAggregation(percentiles("field2Percentile").field(FIELD_2_NAME)) + .subAggregation(percentiles("field3Percentile").field(FIELD_3_NAME).percentiles(percentile10, percentile50, percentile90)) + .subAggregation(percentiles("field4Percentile").field(FIELD_4_NAME).percentiles(percentile90)) + .subAggregation( + bucketScript( + "seriesArithmetic", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), + "field2Percentile.10", + "field3Percentile.50", + "field4Percentile" ) - .subAggregation(percentiles("field4Percentile").field(FIELD_4_NAME).percentiles(percentile90)) - .subAggregation( - bucketScript( - "seriesArithmetic", - new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()), - "field2Percentile.10", - "field3Percentile.50", - "field4Percentile" - ) - ) - ) - .get(); + ) + ).get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java index 87731e40b9a90..e5bece3c404ac 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java @@ -100,8 +100,7 @@ public void setupSuiteScopeCluster() throws Exception { */ public void testGappyIndexWithSigma() { double sigma = randomDoubleBetween(1.0, 6.0, true); - SearchResponse response = client().prepareSearch("idx_gappy") - .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(1L)) + SearchResponse response = prepareSearch("idx_gappy").addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(1L)) .addAggregation(extendedStatsBucket("extended_stats_bucket", "histo>_count").sigma(sigma)) .get(); assertNoFailures(response); @@ -151,19 +150,17 @@ public void testGappyIndexWithSigma() { public void testBadSigmaAsSubAgg() throws Exception { Exception ex = expectThrows( Exception.class, - () -> client().prepareSearch("idx") - .addAggregation( - terms("terms").field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram("histo").field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .subAggregation(extendedStatsBucket("extended_stats_bucket", "histo>sum").sigma(-1.0)) - ) - .get() + () -> prepareSearch("idx").addAggregation( + terms("terms").field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram("histo").field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .extendedBounds(minRandomValue, maxRandomValue) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ) + .subAggregation(extendedStatsBucket("extended_stats_bucket", "histo>sum").sigma(-1.0)) + ).get() ); Throwable cause = ExceptionsHelper.unwrapCause(ex); if (cause == null) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java index ab9987f7d1255..4b41d71ff6e61 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/PercentilesBucketIT.java @@ -68,10 +68,9 @@ protected double getNestedMetric(PercentilesBucket bucket) { } public void testMetricTopLevelDefaultPercents() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(terms(termsName).field("tag").subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME))) - .addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum")) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag").subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum")).get(); assertNoFailures(response); @@ -101,14 +100,11 @@ public void testMetricTopLevelDefaultPercents() throws Exception { } public void testWrongPercents() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .includeExclude(new IncludeExclude(null, "tag.*", null, null)) - .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) - ) - .addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum").setPercents(PERCENTS)) - .get(); + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .includeExclude(new IncludeExclude(null, "tag.*", null, null)) + .subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME)) + ).addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum").setPercents(PERCENTS)).get(); assertNoFailures(response); @@ -134,8 +130,7 @@ public void testBadPercents() throws Exception { double[] badPercents = { -1.0, 110.0 }; try { - client().prepareSearch("idx") - .addAggregation(terms(termsName).field("tag").subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME))) + prepareSearch("idx").addAggregation(terms(termsName).field("tag").subAggregation(sum("sum").field(SINGLE_VALUED_FIELD_NAME))) .addAggregation(percentilesBucket("percentiles_bucket", termsName + ">sum").setPercents(badPercents)) .get(); @@ -161,18 +156,16 @@ public void testBadPercents_asSubAgg() throws Exception { double[] badPercents = { -1.0, 110.0 }; try { - client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - ) - .subAggregation(percentilesBucket("percentiles_bucket", histoName + ">_count").setPercents(badPercents)) - ) - .get(); + prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) + .interval(interval) + .extendedBounds(minRandomValue, maxRandomValue) + ) + .subAggregation(percentilesBucket("percentiles_bucket", histoName + ">_count").setPercents(badPercents)) + ).get(); fail("Illegal percent's were provided but no exception was thrown."); } catch (Exception e) { @@ -194,17 +187,14 @@ public void testBadPercents_asSubAgg() throws Exception { public void testNestedWithDecimal() throws Exception { double[] percent = { 99.9 }; - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - terms(termsName).field("tag") - .order(BucketOrder.key(true)) - .subAggregation( - histogram(histoName).field(SINGLE_VALUED_FIELD_NAME) - .interval(interval) - .extendedBounds(minRandomValue, maxRandomValue) - ) - .subAggregation(percentilesBucket("percentile_histo_bucket", histoName + ">_count").setPercents(percent)) - ) + SearchResponse response = prepareSearch("idx").addAggregation( + terms(termsName).field("tag") + .order(BucketOrder.key(true)) + .subAggregation( + histogram(histoName).field(SINGLE_VALUED_FIELD_NAME).interval(interval).extendedBounds(minRandomValue, maxRandomValue) + ) + .subAggregation(percentilesBucket("percentile_histo_bucket", histoName + ">_count").setPercents(percent)) + ) .addAggregation(percentilesBucket("percentile_terms_bucket", termsName + ">percentile_histo_bucket[99.9]").setPercents(percent)) .get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileCreatingIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileCreatingIndexIT.java index 5f42ac690fdde..eb6dd2f0767f1 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileCreatingIndexIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileCreatingIndexIT.java @@ -64,7 +64,7 @@ private void searchWhileCreatingIndex(boolean createIndex, int numberOfReplicas) ClusterHealthStatus status = clusterAdmin().prepareHealth("test").get().getStatus(); while (status != ClusterHealthStatus.GREEN) { // first, verify that search normal search works - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "test")), 1); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "test")), 1); Client client = client(); SearchResponse searchResponse = client.prepareSearch("test") .setPreference(preference + Integer.toString(counter++)) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java index 8ea750c624872..54cff6efe3d17 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/TransportTwoNodesSearchIT.java @@ -116,8 +116,7 @@ public void testDfsQueryThenFetch() throws Exception { refresh(); int total = 0; - SearchResponse searchResponse = client().prepareSearch("test") - .setSearchType(DFS_QUERY_THEN_FETCH) + SearchResponse searchResponse = prepareSearch("test").setSearchType(DFS_QUERY_THEN_FETCH) .setQuery(termQuery("multi", "test")) .setSize(60) .setExplain(true) @@ -157,8 +156,7 @@ public void testDfsQueryThenFetchWithSort() throws Exception { prepareData(); int total = 0; - SearchResponse searchResponse = client().prepareSearch("test") - .setSearchType(DFS_QUERY_THEN_FETCH) + SearchResponse searchResponse = prepareSearch("test").setSearchType(DFS_QUERY_THEN_FETCH) .setQuery(termQuery("multi", "test")) .setSize(60) .setExplain(true) @@ -195,8 +193,7 @@ public void testQueryThenFetch() throws Exception { prepareData(); int total = 0; - SearchResponse searchResponse = client().prepareSearch("test") - .setSearchType(QUERY_THEN_FETCH) + SearchResponse searchResponse = prepareSearch("test").setSearchType(QUERY_THEN_FETCH) .setQuery(termQuery("multi", "test")) .setSize(60) .setExplain(true) @@ -255,8 +252,7 @@ public void testQueryThenFetchWithSort() throws Exception { prepareData(); int total = 0; - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(termQuery("multi", "test")) + SearchResponse searchResponse = prepareSearch("test").setQuery(termQuery("multi", "test")) .setSize(60) .setExplain(true) .addSort("age", SortOrder.ASC) @@ -357,9 +353,9 @@ public void testFailedMultiSearchWithWrongQuery() throws Exception { logger.info("Start Testing failed multi search with a wrong query"); MultiSearchResponse response = client().prepareMultiSearch() - .add(client().prepareSearch("test").setQuery(new MatchQueryBuilder("foo", "biz"))) - .add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("nid", 2))) - .add(client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test").setQuery(new MatchQueryBuilder("foo", "biz"))) + .add(prepareSearch("test").setQuery(QueryBuilders.termQuery("nid", 2))) + .add(prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())) .get(); assertThat(response.getResponses().length, equalTo(3)); assertThat(response.getResponses()[0].getFailureMessage(), notNullValue()); @@ -381,16 +377,15 @@ public void testFailedMultiSearchWithWrongQueryWithFunctionScore() throws Except MultiSearchResponse response = client().prepareMultiSearch() // Add custom score query with bogus script .add( - client().prepareSearch("test") - .setQuery( - QueryBuilders.functionScoreQuery( - QueryBuilders.termQuery("nid", 1), - new ScriptScoreFunctionBuilder(new Script(ScriptType.INLINE, "bar", "foo", Collections.emptyMap())) - ) + prepareSearch("test").setQuery( + QueryBuilders.functionScoreQuery( + QueryBuilders.termQuery("nid", 1), + new ScriptScoreFunctionBuilder(new Script(ScriptType.INLINE, "bar", "foo", Collections.emptyMap())) ) + ) ) - .add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("nid", 2))) - .add(client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test").setQuery(QueryBuilders.termQuery("nid", 2))) + .add(prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())) .get(); assertThat(response.getResponses().length, equalTo(3)); assertThat(response.getResponses()[0].getFailureMessage(), notNullValue()); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index 2a3b8aeede733..626417d1cf77a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -142,11 +142,9 @@ public void testSimpleNested() throws Exception { ); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(new InnerHitBuilder("comment")) - ) - .get(); + SearchResponse response = prepareSearch("articles").setQuery( + nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(new InnerHitBuilder("comment")) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("1")); @@ -161,11 +159,9 @@ public void testSimpleNested() throws Exception { assertThat(innerHits.getAt(1).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(1).getNestedIdentity().getOffset(), equalTo(1)); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg).innerHit(new InnerHitBuilder("comment")) - ) - .get(); + response = prepareSearch("articles").setQuery( + nestedQuery("comments", matchQuery("comments.message", "elephant"), ScoreMode.Avg).innerHit(new InnerHitBuilder("comment")) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("2")); @@ -184,17 +180,15 @@ public void testSimpleNested() throws Exception { assertThat(innerHits.getAt(2).getNestedIdentity().getField().string(), equalTo("comments")); assertThat(innerHits.getAt(2).getNestedIdentity().getOffset(), equalTo(2)); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message")) - .setExplain(true) - .addFetchField("comments.mes*") - .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) - .setSize(1) - ) + response = prepareSearch("articles").setQuery( + nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setHighlightBuilder(new HighlightBuilder().field("comments.message")) + .setExplain(true) + .addFetchField("comments.mes*") + .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) + .setSize(1) ) - .get(); + ).get(); assertNoFailures(response); innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.getTotalHits().value, equalTo(2L)); @@ -210,13 +204,11 @@ public void testSimpleNested() throws Exception { ); assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().addDocValueField("comments.mes*").setSize(1) - ) + response = prepareSearch("articles").setQuery( + nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().addDocValueField("comments.mes*").setSize(1) ) - .get(); + ).get(); assertNoFailures(response); innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); assertThat(innerHits.getHits().length, equalTo(1)); @@ -258,11 +250,7 @@ public void testRandomNested() throws Exception { new InnerHitBuilder("b").addSort(new FieldSortBuilder("_doc").order(SortOrder.ASC)).setSize(size) ) ); - SearchResponse searchResponse = client().prepareSearch("idx") - .setQuery(boolQuery) - .setSize(numDocs) - .addSort("foo", SortOrder.ASC) - .get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(boolQuery).setSize(numDocs).addSort("foo", SortOrder.ASC).get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, numDocs); @@ -371,17 +359,15 @@ public void testNestedMultipleLayers() throws Exception { indexRandom(true, requests); // Check we can load the first doubly-nested document. - SearchResponse response = client().prepareSearch("articles") - .setQuery( - nestedQuery( - "comments", - nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg).innerHit( - new InnerHitBuilder("remark") - ), - ScoreMode.Avg - ).innerHit(new InnerHitBuilder()) - ) - .get(); + SearchResponse response = prepareSearch("articles").setQuery( + nestedQuery( + "comments", + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg).innerHit( + new InnerHitBuilder("remark") + ), + ScoreMode.Avg + ).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("1")); @@ -402,17 +388,15 @@ public void testNestedMultipleLayers() throws Exception { assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); // Check we can load the second doubly-nested document. - response = client().prepareSearch("articles") - .setQuery( - nestedQuery( - "comments", - nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "neutral"), ScoreMode.Avg).innerHit( - new InnerHitBuilder("remark") - ), - ScoreMode.Avg - ).innerHit(new InnerHitBuilder()) - ) - .get(); + response = prepareSearch("articles").setQuery( + nestedQuery( + "comments", + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "neutral"), ScoreMode.Avg).innerHit( + new InnerHitBuilder("remark") + ), + ScoreMode.Avg + ).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("1")); @@ -433,13 +417,9 @@ public void testNestedMultipleLayers() throws Exception { assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); // Directly refer to the second level: - response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg).innerHit( - new InnerHitBuilder() - ) - ) - .get(); + response = prepareSearch("articles").setQuery( + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("2")); @@ -453,17 +433,15 @@ public void testNestedMultipleLayers() throws Exception { assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getField().string(), equalTo("remarks")); assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery( - "comments", - nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg).innerHit( - new InnerHitBuilder("remark") - ), - ScoreMode.Avg - ).innerHit(new InnerHitBuilder()) - ) - .get(); + response = prepareSearch("articles").setQuery( + nestedQuery( + "comments", + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "bad"), ScoreMode.Avg).innerHit( + new InnerHitBuilder("remark") + ), + ScoreMode.Avg + ).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertSearchHit(response, 1, hasId("2")); @@ -484,8 +462,7 @@ public void testNestedMultipleLayers() throws Exception { assertThat(innerHits.getAt(0).getNestedIdentity().getChild().getOffset(), equalTo(0)); // Check that inner hits contain _source even when it's disabled on the parent request. - response = client().prepareSearch("articles") - .setFetchSource(false) + response = prepareSearch("articles").setFetchSource(false) .setQuery( nestedQuery( "comments", @@ -502,17 +479,15 @@ public void testNestedMultipleLayers() throws Exception { assertNotNull(innerHits.getAt(0).getSourceAsMap()); assertFalse(innerHits.getAt(0).getSourceAsMap().isEmpty()); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery( - "comments", - nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg).innerHit( - new InnerHitBuilder("remark") - ), - ScoreMode.Avg - ).innerHit(new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE)) - ) - .get(); + response = prepareSearch("articles").setQuery( + nestedQuery( + "comments", + nestedQuery("comments.remarks", matchQuery("comments.remarks.message", "good"), ScoreMode.Avg).innerHit( + new InnerHitBuilder("remark") + ), + ScoreMode.Avg + ).innerHit(new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE)) + ).get(); assertNoFailures(response); innerHits = response.getHits().getAt(0).getInnerHits().get("comments"); innerHits = innerHits.getAt(0).getInnerHits().get("remark"); @@ -539,9 +514,9 @@ public void testNestedDefinedAsObject() throws Exception { ); indexRandom(true, requests); - SearchResponse response = client().prepareSearch("articles") - .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(new InnerHitBuilder())) - .get(); + SearchResponse response = prepareSearch("articles").setQuery( + nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.Avg).innerHit(new InnerHitBuilder()) + ).get(); assertNoFailures(response); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); @@ -608,13 +583,11 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { ); indexRandom(true, requests); - SearchResponse resp1 = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.FETCH_SOURCE) - ) + SearchResponse resp1 = prepareSearch("articles").setQuery( + nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.FETCH_SOURCE) ) - .get(); + ).get(); assertNoFailures(resp1); assertHitCount(resp1, 1); SearchHit parent = resp1.getHits().getAt(0); @@ -624,13 +597,11 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { assertThat(inner.getAt(0).getSourceAsString(), equalTo("{\"message\":\"no fox\"}")); assertThat(inner.getAt(1).getSourceAsString(), equalTo("{\"message\":\"fox eat quick\"}")); - SearchResponse response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) - ) + SearchResponse response = prepareSearch("articles").setQuery( + nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) ) - .get(); + ).get(); assertNoFailures(response); assertHitCount(response, 1); SearchHit hit = response.getHits().getAt(0); @@ -646,13 +617,11 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { assertThat(messages.getAt(1).getNestedIdentity().getOffset(), equalTo(0)); assertThat(messages.getAt(1).getNestedIdentity().getChild(), nullValue()); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments.messages", matchQuery("comments.messages.message", "bear"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) - ) + response = prepareSearch("articles").setQuery( + nestedQuery("comments.messages", matchQuery("comments.messages.message", "bear"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) ) - .get(); + ).get(); assertNoFailures(response); assertHitCount(response, 1); hit = response.getHits().getAt(0); @@ -681,13 +650,11 @@ public void testInnerHitsWithObjectFieldThatHasANestedField() throws Exception { ) ); indexRandom(true, requests); - response = client().prepareSearch("articles") - .setQuery( - nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) - ) + response = prepareSearch("articles").setQuery( + nestedQuery("comments.messages", matchQuery("comments.messages.message", "fox"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE) ) - .get(); + ).get(); assertNoFailures(response); assertHitCount(response, 1); hit = response.getHits().getAt(0); @@ -793,11 +760,7 @@ public void testMatchesQueriesNestedInnerHits() throws Exception { query = nestedQuery("nested1", query, ScoreMode.Avg).innerHit( new InnerHitBuilder().addSort(new FieldSortBuilder("nested1.n_field1").order(SortOrder.ASC)) ); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(query) - .setSize(numDocs) - .addSort("field1", SortOrder.ASC) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(query).setSize(numDocs).addSort("field1", SortOrder.ASC).get(); assertNoFailures(searchResponse); assertAllSuccessful(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) numDocs)); @@ -953,7 +916,7 @@ public void testUseMaxDocInsteadOfSize() throws Exception { QueryBuilder query = nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1) ); - assertHitCountAndNoFailures(client().prepareSearch("index2").setQuery(query), 1); + assertHitCountAndNoFailures(prepareSearch("index2").setQuery(query), 1); } public void testTooHighResultWindow() throws Exception { @@ -966,24 +929,21 @@ public void testTooHighResultWindow() throws Exception { .setRefreshPolicy(IMMEDIATE) .get(); assertHitCountAndNoFailures( - client().prepareSearch("index2") - .setQuery( - nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFrom(50).setSize(10).setName("_name") - ) - ), + prepareSearch("index2").setQuery( + nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFrom(50).setSize(10).setName("_name") + ) + ), 1 ); Exception e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("index2") - .setQuery( - nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFrom(100).setSize(10).setName("_name") - ) + () -> prepareSearch("index2").setQuery( + nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFrom(100).setSize(10).setName("_name") ) - .get() + ).get() ); assertThat( e.getCause().getMessage(), @@ -991,13 +951,11 @@ public void testTooHighResultWindow() throws Exception { ); e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("index2") - .setQuery( - nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFrom(10).setSize(100).setName("_name") - ) + () -> prepareSearch("index2").setQuery( + nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFrom(10).setSize(100).setName("_name") ) - .get() + ).get() ); assertThat( e.getCause().getMessage(), @@ -1006,20 +964,18 @@ public void testTooHighResultWindow() throws Exception { updateIndexSettings(Settings.builder().put(IndexSettings.MAX_INNER_RESULT_WINDOW_SETTING.getKey(), 110), "index2"); assertNoFailures( - client().prepareSearch("index2") - .setQuery( - nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFrom(100).setSize(10).setName("_name") - ) + prepareSearch("index2").setQuery( + nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFrom(100).setSize(10).setName("_name") ) + ) ); assertNoFailures( - client().prepareSearch("index2") - .setQuery( - nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( - new InnerHitBuilder().setFrom(10).setSize(100).setName("_name") - ) + prepareSearch("index2").setQuery( + nestedQuery("nested", matchQuery("nested.field", "value1"), ScoreMode.Avg).innerHit( + new InnerHitBuilder().setFrom(10).setSize(100).setName("_name") ) + ) ); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java index 8d340b8401b60..6b790f9e6f090 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java @@ -45,8 +45,7 @@ protected void setup() throws Exception { } public void testThatCustomHighlightersAreSupported() throws IOException { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) .highlighter(new HighlightBuilder().field("name").highlighterType("test-custom")) .get(); assertHighlight(searchResponse, 0, "name", 0, equalTo("standard response for name at position 1")); @@ -59,8 +58,7 @@ public void testThatCustomHighlighterCanBeConfiguredPerField() throws Exception options.put("myFieldOption", "someValue"); highlightConfig.options(options); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) .highlighter(new HighlightBuilder().field(highlightConfig)) .get(); @@ -72,8 +70,7 @@ public void testThatCustomHighlighterCanBeConfiguredGlobally() throws Exception Map options = new HashMap<>(); options.put("myGlobalOption", "someValue"); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) .highlighter(new HighlightBuilder().field("name").highlighterType("test-custom").options(options)) .get(); @@ -82,8 +79,9 @@ public void testThatCustomHighlighterCanBeConfiguredGlobally() throws Exception } public void testThatCustomHighlighterReceivesFieldsInOrder() throws Exception { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchAllQuery()).should(QueryBuilders.termQuery("name", "arbitrary"))) + SearchResponse searchResponse = prepareSearch("test").setQuery( + QueryBuilders.boolQuery().must(QueryBuilders.matchAllQuery()).should(QueryBuilders.termQuery("name", "arbitrary")) + ) .highlighter( new HighlightBuilder().highlighterType("test-custom") .field("name") diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 642dfc2f6cf54..6564264bcd0f2 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -683,7 +683,7 @@ public void testGlobalHighlightingSettingsOverriddenAtFieldLevel() { ) ); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 2, equalTo("test")); assertHighlight(searchResponse, 0, "field1", 1, 2, equalTo("test")); @@ -745,7 +745,7 @@ public void testPlainHighlighter() throws Exception { SearchSourceBuilder source = searchSource().query(termQuery("field1", "test")) .highlighter(highlight().highlighterType("plain").field("field1").order("score").preTags("").postTags("")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a test")); } @@ -763,7 +763,7 @@ public void testPlainHighlighterOrder() throws Exception { SearchSourceBuilder source = searchSource().query(matchQuery("field1", "brown dog")) .highlighter(highlight().highlighterType("plain").field("field1").preTags("").postTags("").fragmentSize(25)); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 3, equalTo("The quick brown fox")); assertHighlight(searchResponse, 0, "field1", 1, 3, equalTo(" jumps over the lazy brown dog")); @@ -775,7 +775,7 @@ public void testPlainHighlighterOrder() throws Exception { highlight().highlighterType("plain").field("field1").order("none").preTags("").postTags("").fragmentSize(25) ); - searchResponse = client().prepareSearch("test").setSource(source).get(); + searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 3, equalTo("The quick brown fox")); assertHighlight(searchResponse, 0, "field1", 1, 3, equalTo(" jumps over the lazy brown dog")); @@ -788,7 +788,7 @@ public void testPlainHighlighterOrder() throws Exception { highlight().highlighterType("plain").order("score").field("field1").preTags("").postTags("").fragmentSize(25) ); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 3, equalTo(" jumps over the lazy brown dog")); assertHighlight(searchResponse, 0, "field1", 1, 3, equalTo("The quick brown fox")); @@ -809,7 +809,7 @@ public void testFastVectorHighlighter() throws Exception { SearchSourceBuilder source = searchSource().query(termQuery("field1", "test")) .highlighter(highlight().field("field1", 100, 0).order("score").preTags("").postTags("")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a test")); @@ -817,7 +817,7 @@ public void testFastVectorHighlighter() throws Exception { source = searchSource().query(matchQuery("field2", "quick")) .highlighter(highlight().field("field2", 30, 1).boundaryChars(new char[] { ' ' })); - searchResponse = client().prepareSearch("test").setSource(source).get(); + searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over")); @@ -825,7 +825,7 @@ public void testFastVectorHighlighter() throws Exception { source = searchSource().query(matchQuery("field2", "quick")) .highlighter(highlight().field(new Field("field2").fragmentSize(30).numOfFragments(1).boundaryChars(new char[] { ' ' }))); - searchResponse = client().prepareSearch("test").setSource(source).get(); + searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over")); } @@ -849,7 +849,7 @@ public void testHighlighterWithSentenceBoundaryScanner() throws Exception { .postTags("") .boundaryScannerType(BoundaryScannerType.SENTENCE) ); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -892,7 +892,7 @@ public void testHighlighterWithSentenceBoundaryScannerAndLocale() throws Excepti .boundaryScannerLocale(Locale.ENGLISH.toLanguageTag()) ); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -931,7 +931,7 @@ public void testHighlighterWithWordBoundaryScanner() throws Exception { .boundaryScannerType(BoundaryScannerType.WORD) ); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -961,7 +961,7 @@ public void testHighlighterWithWordBoundaryScannerAndLocale() throws Exception { .boundaryScannerLocale(Locale.ENGLISH.toLanguageTag()) ); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -1085,7 +1085,7 @@ private void checkMatchedFieldsCase(boolean requireFieldMatch) throws Exception .fragmentSize(25) .highlighterType("fvh") .requireFieldMatch(requireFieldMatch); - SearchRequestBuilder req = client().prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); + SearchRequestBuilder req = prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); // First check highlighting without any matched fields set SearchResponse resp = req.setQuery(queryStringQuery("running scissors").field("foo")).get(); @@ -1103,7 +1103,7 @@ private void checkMatchedFieldsCase(boolean requireFieldMatch) throws Exception .highlighterType("fvh") .requireFieldMatch(requireFieldMatch); fooField.matchedFields("foo", "foo.plain"); - req = client().prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); + req = prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); resp = req.setQuery(queryStringQuery("running scissors").field("foo")).get(); assertHighlight(resp, 0, "foo", 0, equalTo("running with scissors")); @@ -1118,7 +1118,7 @@ private void checkMatchedFieldsCase(boolean requireFieldMatch) throws Exception .highlighterType("fvh") .requireFieldMatch(requireFieldMatch); fooField.matchedFields("foo.plain"); - req = client().prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); + req = prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); resp = req.setQuery(queryStringQuery("foo.plain:running scissors").field("foo")).get(); assertHighlight(resp, 0, "foo", 0, equalTo("running with scissors")); @@ -1129,7 +1129,7 @@ private void checkMatchedFieldsCase(boolean requireFieldMatch) throws Exception .highlighterType("fvh") .requireFieldMatch(requireFieldMatch); fooField.matchedFields("foo", "foo.plain"); - req = client().prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); + req = prepareSearch("test").highlighter(new HighlightBuilder().field(fooField)); resp = req.setQuery(queryStringQuery("foo.plain:running^5 scissors").field("foo")).get(); assertHighlight(resp, 0, "foo", 0, equalTo("running with scissors")); @@ -1635,8 +1635,7 @@ public void testFSHHighlightAllMvFragments() throws Exception { .get(); refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("tags", "tag")) + SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchQuery("tags", "tag")) .highlighter(new HighlightBuilder().field("tags", -1, 0).highlighterType("fvh")) .get(); @@ -1662,7 +1661,7 @@ public void testBoostingQuery() { boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f) ).highlighter(highlight().field("field2").order("score").preTags("").postTags("")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); } @@ -1678,7 +1677,7 @@ public void testBoostingQueryTermVector() throws IOException { boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f) ).highlighter(highlight().field("field2").order("score").preTags("").postTags("")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); } @@ -1700,8 +1699,7 @@ public void testPlainHighlightDifferentFragmenter() throws Exception { .get(); refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchPhraseQuery("tags", "long tag")) + SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchPhraseQuery("tags", "long tag")) .highlighter( new HighlightBuilder().field( new HighlightBuilder.Field("tags").highlighterType("plain").fragmentSize(-1).numOfFragments(2).fragmenter("simple") @@ -1719,8 +1717,7 @@ public void testPlainHighlightDifferentFragmenter() throws Exception { equalTo("here is another one that is very long tag and has the tag token near the end") ); - response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchPhraseQuery("tags", "long tag")) + response = prepareSearch("test").setQuery(QueryBuilders.matchPhraseQuery("tags", "long tag")) .highlighter( new HighlightBuilder().field( new Field("tags").highlighterType("plain").fragmentSize(-1).numOfFragments(2).fragmenter("span") @@ -1739,8 +1736,7 @@ public void testPlainHighlightDifferentFragmenter() throws Exception { ); assertFailures( - client().prepareSearch("test") - .setQuery(QueryBuilders.matchPhraseQuery("tags", "long tag")) + prepareSearch("test").setQuery(QueryBuilders.matchPhraseQuery("tags", "long tag")) .highlighter( new HighlightBuilder().field( new Field("tags").highlighterType("plain").fragmentSize(-1).numOfFragments(2).fragmenter("invalid") @@ -1758,8 +1754,7 @@ public void testPlainHighlighterMultipleFields() { indexDoc("test", "1", "field1", "The quick brown fox", "field2", "The slow brown fox"); refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("field1", "fox")) + SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchQuery("field1", "fox")) .highlighter( new HighlightBuilder().field(new HighlightBuilder.Field("field1").preTags("<1>").postTags("").requireFieldMatch(true)) .field(new HighlightBuilder.Field("field2").preTags("<2>").postTags("").requireFieldMatch(false)) @@ -1783,8 +1778,7 @@ public void testFastVectorHighlighterMultipleFields() { indexDoc("test", "1", "field1", "The quick brown fox", "field2", "The slow brown fox"); refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("field1", "fox")) + SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchQuery("field1", "fox")) .highlighter( new HighlightBuilder().field(new HighlightBuilder.Field("field1").preTags("<1>").postTags("").requireFieldMatch(true)) .field(new HighlightBuilder.Field("field2").preTags("<2>").postTags("").requireFieldMatch(false)) @@ -1801,8 +1795,7 @@ public void testMissingStoredField() throws Exception { refresh(); // This query used to fail when the field to highlight was absent - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("field", "highlight")) + SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchQuery("field", "highlight")) .highlighter( new HighlightBuilder().field( new HighlightBuilder.Field("highlight_field").fragmentSize(-1).numOfFragments(1).fragmenter("simple") @@ -1843,8 +1836,7 @@ public void testNumericHighlighting() throws Exception { // Highlighting of numeric fields is not supported, but it should not raise errors // (this behavior is consistent with version 0.20) assertHitCount( - client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("text", "test")) + prepareSearch("test").setQuery(QueryBuilders.matchQuery("text", "test")) .highlighter( new HighlightBuilder().field("text") .field("byte") @@ -1871,9 +1863,7 @@ public void testResetTwice() throws Exception { // Mock tokenizer will throw an exception if it is resetted twice assertHitCount( - client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("text", "test")) - .highlighter(new HighlightBuilder().field("text")), + prepareSearch("test").setQuery(QueryBuilders.matchQuery("text", "test")).highlighter(new HighlightBuilder().field("text")), 1L ); } @@ -1893,8 +1883,7 @@ public void testHighlightUsesHighlightQuery() throws IOException { for (String type : ALL_TYPES) { HighlightBuilder.Field field = new HighlightBuilder.Field("text"); HighlightBuilder highlightBuilder = new HighlightBuilder().field(field).highlighterType(type); - SearchRequestBuilder search = client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("text", "testing")) + SearchRequestBuilder search = prepareSearch("test").setQuery(QueryBuilders.matchQuery("text", "testing")) .highlighter(highlightBuilder); Matcher searchQueryMatcher = equalTo("Testing the highlight query feature"); @@ -1905,7 +1894,7 @@ public void testHighlightUsesHighlightQuery() throws IOException { Matcher hlQueryMatcher = equalTo("Testing the highlight query feature"); field.highlightQuery(matchQuery("text", "query")); highlightBuilder = new HighlightBuilder().field(field); - search = client().prepareSearch("test").setQuery(QueryBuilders.matchQuery("text", "testing")).highlighter(highlightBuilder); + search = prepareSearch("test").setQuery(QueryBuilders.matchQuery("text", "testing")).highlighter(highlightBuilder); response = search.get(); assertHighlight(response, 0, "text", 0, hlQueryMatcher); @@ -1939,98 +1928,98 @@ public void testHighlightNoMatchSize() throws IOException { // When you don't set noMatchSize you don't get any results if there isn't anything to highlight. HighlightBuilder.Field field = new HighlightBuilder.Field("text").fragmentSize(21).numOfFragments(1).highlighterType("plain"); - SearchResponse response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + SearchResponse response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); // When noMatchSize is set to 0 you also shouldn't get any field.highlighterType("plain").noMatchSize(0); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); // When noMatchSize is between 0 and the size of the string field.highlighterType("plain").noMatchSize(21); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so")); // The FVH also works but the fragment is longer than the plain highlighter because of boundary_max_scan field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); // Unified hl also works but the fragment is longer than the plain highlighter because of the boundary is the word field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); // We can also ask for a fragment longer than the input string and get the whole string for (String type : new String[] { "plain", "unified" }) { field.highlighterType(type).noMatchSize(text.length() * 2).numOfFragments(0); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo(text)); } field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo(text)); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo(text)); // We can also ask for a fragment exactly the size of the input field and get the whole field field.highlighterType("plain").noMatchSize(text.length()); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo(text)); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo(text)); // unified hl returns the first sentence as the noMatchSize does not cross sentence boundary. field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo(text)); // You can set noMatchSize globally in the highlighter as well field.highlighterType("plain").noMatchSize(null); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so")); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); // We don't break if noMatchSize is less than zero though field.highlighterType("plain").noMatchSize(randomIntBetween(Integer.MIN_VALUE, -1)); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); } @@ -2053,15 +2042,15 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { .numOfFragments(1) .highlighterType("plain") .noMatchSize(21); - SearchResponse response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + SearchResponse response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so")); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); // And noMatchSize returns nothing when the first entry is empty string! @@ -2070,16 +2059,16 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds("2"); field.highlighterType("plain"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); // except for the unified highlighter which starts from the first string with actual content field.highlighterType("unified"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("I am short")); // But if the field was actually empty then you should get no highlighting field @@ -2087,15 +2076,15 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { refresh(); idsQueryBuilder = QueryBuilders.idsQuery().addIds("3"); field.highlighterType("plain"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("unified"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); // Same for if the field doesn't even exist on the document @@ -2104,28 +2093,28 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { idsQueryBuilder = QueryBuilders.idsQuery().addIds("4"); field.highlighterType("plain"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("unified"); - response = client().prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "postings"); // Again same if the field isn't mapped field = new HighlightBuilder.Field("unmapped").highlighterType("plain").noMatchSize(21); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertNotHighlighted(response, 0, "text"); } @@ -2149,31 +2138,31 @@ public void testHighlightNoMatchSizeNumberOfFragments() { .numOfFragments(0) .highlighterType("plain") .noMatchSize(20); - SearchResponse response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + SearchResponse response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("This is the first")); field.highlighterType("fvh"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("This is the first sentence")); field.highlighterType("unified"); - response = client().prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 1, equalTo("This is the first sentence")); // if there's a match we only return the values with matches (whole value as number_of_fragments == 0) MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("text", "third fifth"); field.highlighterType("plain"); - response = client().prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 2, equalTo("This is the third sentence. This is the fourth sentence.")); assertHighlight(response, 0, "text", 1, 2, equalTo("This is the fifth sentence")); field.highlighterType("fvh"); - response = client().prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 2, equalTo("This is the third sentence. This is the fourth sentence.")); assertHighlight(response, 0, "text", 1, 2, equalTo("This is the fifth sentence")); field.highlighterType("unified"); - response = client().prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + response = prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 2, equalTo("This is the third sentence. This is the fourth sentence.")); assertHighlight(response, 0, "text", 1, 2, equalTo("This is the fifth sentence")); } @@ -2256,8 +2245,7 @@ public void testPostingsHighlighterMultipleFields() throws Exception { ); refresh(); - SearchResponse response = client().prepareSearch("test") - .setQuery(QueryBuilders.matchQuery("field1", "fox")) + SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchQuery("field1", "fox")) .highlighter(new HighlightBuilder().field(new Field("field1").preTags("<1>").postTags("").requireFieldMatch(true))) .get(); assertHighlight(response, 0, "field1", 0, 1, equalTo("The quick brown <1>fox. Second sentence.")); @@ -2705,7 +2693,7 @@ public void testPostingsHighlighterPrefixQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(prefixQuery("field2", "qui")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, 0, @@ -2727,7 +2715,7 @@ public void testPostingsHighlighterFuzzyQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(fuzzyQuery("field2", "quck")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -2750,7 +2738,7 @@ public void testPostingsHighlighterRegexpQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(regexpQuery("field2", "qu[a-l]+k")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -2773,7 +2761,7 @@ public void testPostingsHighlighterWildcardQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(wildcardQuery("field2", "qui*")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, @@ -2785,7 +2773,7 @@ public void testPostingsHighlighterWildcardQuery() throws Exception { ); source = searchSource().query(wildcardQuery("field2", "qu*k")).highlighter(highlight().field("field2")); - searchResponse = client().prepareSearch("test").setSource(source).get(); + searchResponse = prepareSearch("test").setSource(source).get(); assertHitCount(searchResponse, 1L); assertHighlight( @@ -2808,7 +2796,7 @@ public void testPostingsHighlighterTermRangeQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(rangeQuery("field2").gte("aaaa").lt("zzzz")) .highlighter(highlight().field("field2")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("aaab")); } @@ -2825,7 +2813,7 @@ public void testPostingsHighlighterQueryString() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(queryStringQuery("qui*").defaultField("field2")) .highlighter(highlight().field("field2")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( searchResponse, 0, @@ -2846,7 +2834,7 @@ public void testPostingsHighlighterRegexpQueryWithinConstantScoreQuery() throws logger.info("--> highlighting and searching on field1"); SearchSourceBuilder source = searchSource().query(constantScoreQuery(regexpQuery("field1", "pho[a-z]+"))) .highlighter(highlight().field("field1")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); } @@ -2863,7 +2851,7 @@ public void testPostingsHighlighterMultiTermQueryMultipleLevels() throws Excepti .should(matchQuery("field1", "test")) .should(constantScoreQuery(queryStringQuery("field1:photo*"))) ).highlighter(highlight().field("field1")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); } @@ -2878,7 +2866,7 @@ public void testPostingsHighlighterPrefixQueryWithinBooleanQuery() throws Except SearchSourceBuilder source = searchSource().query( boolQuery().must(prefixQuery("field1", "photo")).should(matchQuery("field1", "test").minimumShouldMatch("0")) ).highlighter(highlight().field("field1")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); } @@ -2893,7 +2881,7 @@ public void testPostingsHighlighterQueryStringWithinFilteredQuery() throws Excep SearchSourceBuilder source = searchSource().query( boolQuery().must(queryStringQuery("field1:photo*")).mustNot(existsQuery("field_null")) ).highlighter(highlight().field("field1")); - SearchResponse searchResponse = client().prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); } @@ -2950,8 +2938,7 @@ public void testDoesNotHighlightTypeName() throws Exception { indexRandom(true, client().prepareIndex("test").setSource("foo", "test typename")); for (String highlighter : ALL_TYPES) { - SearchResponse response = client().prepareSearch("test") - .setQuery(matchQuery("foo", "test")) + SearchResponse response = prepareSearch("test").setQuery(matchQuery("foo", "test")) .highlighter(new HighlightBuilder().field("foo").highlighterType(highlighter).requireFieldMatch(false)) .get(); assertHighlight(response, 0, "foo", 0, 1, equalTo("test typename")); @@ -2978,8 +2965,7 @@ public void testDoesNotHighlightAliasFilters() throws Exception { indexRandom(true, client().prepareIndex("test").setSource("foo", "test japanese")); for (String highlighter : ALL_TYPES) { - SearchResponse response = client().prepareSearch("filtered_alias") - .setQuery(matchQuery("foo", "test")) + SearchResponse response = prepareSearch("filtered_alias").setQuery(matchQuery("foo", "test")) .highlighter(new HighlightBuilder().field("foo").highlighterType(highlighter).requireFieldMatch(false)) .get(); assertHighlight(response, 0, "foo", 0, 1, equalTo("test japanese")); @@ -3059,10 +3045,9 @@ private

> void phraseBoostTestCaseForClauses( ) { Matcher highlightedMatcher = Matchers.either(containsString("highlight words together")) .or(containsString("highlight words together")); - SearchRequestBuilder search = client().prepareSearch("test") - .highlighter( - new HighlightBuilder().field("field1", 100, 1).order("score").highlighterType(highlighterType).requireFieldMatch(true) - ); + SearchRequestBuilder search = prepareSearch("test").highlighter( + new HighlightBuilder().field("field1", 100, 1).order("score").highlighterType(highlighterType).requireFieldMatch(true) + ); // Try with a bool query phrase.boost(boost); @@ -3337,8 +3322,7 @@ public void testHighlightQueryRewriteDatesWithNow() throws Exception { ); ensureSearchable("index-1"); for (int i = 0; i < 5; i++) { - final SearchResponse r1 = client().prepareSearch("index-1") - .addSort("d", SortOrder.DESC) + final SearchResponse r1 = prepareSearch("index-1").addSort("d", SortOrder.DESC) .setTrackScores(true) .highlighter(highlight().field("field").preTags("").postTags("")) .setQuery( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java index 41917a672206a..dc1b14cd5bfd1 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java @@ -685,7 +685,7 @@ public void testSearchFieldsMetadata() throws Exception { .setRefreshPolicy(IMMEDIATE) .get(); - SearchResponse searchResponse = client().prepareSearch("my-index").addStoredField("field1").addStoredField("_routing").get(); + SearchResponse searchResponse = prepareSearch("my-index").addStoredField("field1").addStoredField("_routing").get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).field("field1"), nullValue()); @@ -753,7 +753,7 @@ public void testGetFieldsComplexField() throws Exception { String field = "field1.field2.field3.field4"; - SearchResponse searchResponse = client().prepareSearch("my-index").addStoredField(field).get(); + SearchResponse searchResponse = prepareSearch("my-index").addStoredField(field).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getAt(0).field(field).getValues().size(), equalTo(2)); assertThat(searchResponse.getHits().getAt(0).field(field).getValues().get(0).toString(), equalTo("value1")); @@ -765,9 +765,9 @@ public void testSingleValueFieldDatatField() throws ExecutionException, Interrup assertAcked(indicesAdmin().prepareCreate("test").setMapping("test_field", "type=keyword").get()); indexRandom(true, client().prepareIndex("test").setId("1").setSource("test_field", "foobar")); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setSource(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).docValueField("test_field")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setSource( + new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()).docValueField("test_field") + ).get(); assertHitCount(searchResponse, 1); Map fields = searchResponse.getHits().getHits()[0].getFields(); assertThat(fields.get("test_field").getValue(), equalTo("foobar")); @@ -1024,7 +1024,7 @@ public void testScriptFields() throws Exception { } indexRandom(true, reqs); ensureSearchable(); - SearchRequestBuilder req = client().prepareSearch("index"); + SearchRequestBuilder req = prepareSearch("index"); for (String field : Arrays.asList("s", "ms", "l", "ml", "d", "md")) { req.addScriptField( field, @@ -1269,7 +1269,7 @@ public void testLoadMetadata() throws Exception { .setSource(jsonBuilder().startObject().field("field1", "value").endObject()) ); - SearchResponse response = client().prepareSearch("test").addStoredField("field1").get(); + SearchResponse response = prepareSearch("test").addStoredField("field1").get(); assertNoFailures(response); assertHitCount(response, 1); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java index 7d006c2fa754c..3484559e6b04f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java @@ -54,8 +54,7 @@ public void testFieldValueFactor() throws IOException { // document 2 scores higher because 17 > 5 assertOrderedSearchHits( - client().prepareSearch("test") - .setExplain(randomBoolean()) + prepareSearch("test").setExplain(randomBoolean()) .setQuery(functionScoreQuery(simpleQueryStringQuery("foo"), fieldValueFactorFunction("test"))), "2", "1" @@ -63,8 +62,7 @@ public void testFieldValueFactor() throws IOException { // try again, but this time explicitly use the do-nothing modifier assertOrderedSearchHits( - client().prepareSearch("test") - .setExplain(randomBoolean()) + prepareSearch("test").setExplain(randomBoolean()) .setQuery( functionScoreQuery( simpleQueryStringQuery("foo"), @@ -77,8 +75,7 @@ public void testFieldValueFactor() throws IOException { // document 1 scores higher because 1/5 > 1/17 assertOrderedSearchHits( - client().prepareSearch("test") - .setExplain(randomBoolean()) + prepareSearch("test").setExplain(randomBoolean()) .setQuery( functionScoreQuery( simpleQueryStringQuery("foo"), @@ -91,8 +88,7 @@ public void testFieldValueFactor() throws IOException { // doc 3 doesn't have a "test" field, so an exception will be thrown try { - SearchResponse response = client().prepareSearch("test") - .setExplain(randomBoolean()) + SearchResponse response = prepareSearch("test").setExplain(randomBoolean()) .setQuery(functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("test"))) .get(); assertFailures(response); @@ -102,8 +98,7 @@ public void testFieldValueFactor() throws IOException { // doc 3 doesn't have a "test" field but we're defaulting it to 100 so it should be last assertOrderedSearchHits( - client().prepareSearch("test") - .setExplain(randomBoolean()) + prepareSearch("test").setExplain(randomBoolean()) .setQuery( functionScoreQuery( matchAllQuery(), @@ -116,8 +111,7 @@ public void testFieldValueFactor() throws IOException { ); // field is not mapped but we're defaulting it to 100 so all documents should have the same score - SearchResponse response = client().prepareSearch("test") - .setExplain(randomBoolean()) + SearchResponse response = prepareSearch("test").setExplain(randomBoolean()) .setQuery( functionScoreQuery( matchAllQuery(), @@ -132,8 +126,7 @@ public void testFieldValueFactor() throws IOException { // -1 divided by 0 is infinity, which should provoke an exception. try { - response = client().prepareSearch("test") - .setExplain(randomBoolean()) + response = prepareSearch("test").setExplain(randomBoolean()) .setQuery( functionScoreQuery( simpleQueryStringQuery("foo"), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java index ff1db506e73d8..a9ab85bf79173 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java @@ -166,80 +166,70 @@ public void testScoreAccessWithinScript() throws Exception { // Test for accessing _score Script script = new Script(ScriptType.INLINE, NAME, "log(doc['index'].value + (factor * _score))", params); - SearchResponse resp = client().prepareSearch("test") - .setQuery( - functionScoreQuery( - matchQuery("body", "foo"), - new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { - new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } - ) + SearchResponse resp = prepareSearch("test").setQuery( + functionScoreQuery( + matchQuery("body", "foo"), + new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { + new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } ) - .get(); + ).get(); assertNoFailures(resp); SearchHit firstHit = resp.getHits().getAt(0); assertThat(firstHit.getScore(), greaterThan(1f)); // Test for accessing _score.intValue() script = new Script(ScriptType.INLINE, NAME, "log(doc['index'].value + (factor * _score.intValue()))", params); - resp = client().prepareSearch("test") - .setQuery( - functionScoreQuery( - matchQuery("body", "foo"), - new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { - new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } - ) + resp = prepareSearch("test").setQuery( + functionScoreQuery( + matchQuery("body", "foo"), + new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { + new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } ) - .get(); + ).get(); assertNoFailures(resp); firstHit = resp.getHits().getAt(0); assertThat(firstHit.getScore(), greaterThan(1f)); // Test for accessing _score.longValue() script = new Script(ScriptType.INLINE, NAME, "log(doc['index'].value + (factor * _score.longValue()))", params); - resp = client().prepareSearch("test") - .setQuery( - functionScoreQuery( - matchQuery("body", "foo"), - new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { - new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } - ) + resp = prepareSearch("test").setQuery( + functionScoreQuery( + matchQuery("body", "foo"), + new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { + new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } ) - .get(); + ).get(); assertNoFailures(resp); firstHit = resp.getHits().getAt(0); assertThat(firstHit.getScore(), greaterThan(1f)); // Test for accessing _score.floatValue() script = new Script(ScriptType.INLINE, NAME, "log(doc['index'].value + (factor * _score.floatValue()))", params); - resp = client().prepareSearch("test") - .setQuery( - functionScoreQuery( - matchQuery("body", "foo"), - new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { - new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } - ) + resp = prepareSearch("test").setQuery( + functionScoreQuery( + matchQuery("body", "foo"), + new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { + new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } ) - .get(); + ).get(); assertNoFailures(resp); firstHit = resp.getHits().getAt(0); assertThat(firstHit.getScore(), greaterThan(1f)); // Test for accessing _score.doubleValue() script = new Script(ScriptType.INLINE, NAME, "log(doc['index'].value + (factor * _score.doubleValue()))", params); - resp = client().prepareSearch("test") - .setQuery( - functionScoreQuery( - matchQuery("body", "foo"), - new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { - new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), - new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } - ) + resp = prepareSearch("test").setQuery( + functionScoreQuery( + matchQuery("body", "foo"), + new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { + new FunctionScoreQueryBuilder.FilterFunctionBuilder(fieldValueFactorFunction("index").factor(2)), + new FunctionScoreQueryBuilder.FilterFunctionBuilder(scriptFunction(script)) } ) - .get(); + ).get(); assertNoFailures(resp); firstHit = resp.getHits().getAt(0); assertThat(firstHit.getScore(), greaterThan(1f)); @@ -254,10 +244,9 @@ public void testSeedReportedInExplain() throws Exception { int seed = 12345678; - SearchResponse resp = client().prepareSearch("test") - .setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(seed).setField(SeqNoFieldMapper.NAME))) - .setExplain(true) - .get(); + SearchResponse resp = prepareSearch("test").setQuery( + functionScoreQuery(matchAllQuery(), randomFunction().seed(seed).setField(SeqNoFieldMapper.NAME)) + ).setExplain(true).get(); assertNoFailures(resp); assertEquals(1, resp.getHits().getTotalHits().value); SearchHit firstHit = resp.getHits().getAt(0); @@ -268,13 +257,13 @@ public void testNoDocs() throws Exception { createIndex("test"); ensureGreen(); - SearchResponse resp = client().prepareSearch("test") - .setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(1234).setField(SeqNoFieldMapper.NAME))) - .get(); + SearchResponse resp = prepareSearch("test").setQuery( + functionScoreQuery(matchAllQuery(), randomFunction().seed(1234).setField(SeqNoFieldMapper.NAME)) + ).get(); assertNoFailures(resp); assertEquals(0, resp.getHits().getTotalHits().value); - resp = client().prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery(), randomFunction())).get(); + resp = prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery(), randomFunction())).get(); assertNoFailures(resp); assertEquals(0, resp.getHits().getTotalHits().value); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java index 2ae9824ab6381..7d3e8c7be03c4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java @@ -194,7 +194,7 @@ public void testGeoDistanceAggregation() throws IOException { refresh(); - SearchRequestBuilder search = client().prepareSearch("test"); + SearchRequestBuilder search = prepareSearch("test"); String name = "TestPosition"; search.setQuery(QueryBuilders.matchAllQuery()) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPolygonIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPolygonIT.java index 814365595705a..1f46b6603482e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPolygonIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoPolygonIT.java @@ -142,7 +142,7 @@ public void testSimplePolygon() throws Exception { points.add(new GeoPoint(40.8, -74.1)); points.add(new GeoPoint(40.8, -74.0)); points.add(new GeoPoint(40.7, -74.0)); - SearchResponse searchResponse = client().prepareSearch("test") // from NY + SearchResponse searchResponse = prepareSearch("test") // from NY .setQuery(boolQuery().must(geoPolygonQuery("location", points))) .get(); assertHitCount(searchResponse, 4); @@ -158,7 +158,7 @@ public void testSimpleUnclosedPolygon() throws Exception { points.add(new GeoPoint(40.7, -74.1)); points.add(new GeoPoint(40.8, -74.1)); points.add(new GeoPoint(40.8, -74.0)); - SearchResponse searchResponse = client().prepareSearch("test") // from NY + SearchResponse searchResponse = prepareSearch("test") // from NY .setQuery(boolQuery().must(geoPolygonQuery("location", points))) .get(); assertHitCount(searchResponse, 4); @@ -176,7 +176,7 @@ public void testFieldAlias() { points.add(new GeoPoint(40.8, -74.0)); points.add(new GeoPoint(40.7, -74.0)); assertHitCount( - client().prepareSearch("test") // from NY + prepareSearch("test") // from NY .setQuery(boolQuery().must(geoPolygonQuery("alias", points))), 4 ); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java index 659f6af68d612..32d00a4a925ac 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java @@ -229,16 +229,16 @@ public void testMoreLikeThisWithAliases() throws Exception { ); logger.info("Running moreLikeThis on beta shard"); - SearchResponse response = client().prepareSearch("beta") - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)) - .get(); + SearchResponse response = prepareSearch("beta").setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1) + ).get(); assertHitCount(response, 1L); assertThat(response.getHits().getAt(0).getId(), equalTo("3")); logger.info("Running moreLikeThis on release shard"); - response = client().prepareSearch("release") - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)) - .get(); + response = prepareSearch("release").setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1) + ).get(); assertHitCount(response, 1L); assertThat(response.getHits().getAt(0).getId(), equalTo("2")); @@ -607,7 +607,7 @@ public void testMoreLikeThisMultiValueFields() throws Exception { .minDocFreq(1) .maxQueryTerms(max_query_terms) .minimumShouldMatch("0%"); - SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); + SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, max_query_terms); } @@ -640,7 +640,7 @@ public void testMinimumShouldMatch() throws ExecutionException, InterruptedExcep .minDocFreq(1) .minimumShouldMatch(minimumShouldMatch); logger.info("Testing with minimum_should_match = {}", minimumShouldMatch); - SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); + SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); if (minimumShouldMatch.equals("0%")) { assertHitCount(response, 10); @@ -670,7 +670,7 @@ public void testMoreLikeThisArtificialDocs() throws Exception { .minDocFreq(0) .maxQueryTerms(100) .minimumShouldMatch("100%"); // strict all terms must match! - SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); + SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, 1); } @@ -696,14 +696,14 @@ public void testMoreLikeThisMalformedArtificialDocs() throws Exception { MoreLikeThisQueryBuilder mltQuery = moreLikeThisQuery(new Item[] { new Item("test", malformedFieldDoc) }).minTermFreq(0) .minDocFreq(0) .minimumShouldMatch("0%"); - SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); + SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, 0); logger.info("Checking with an empty document ..."); XContentBuilder emptyDoc = jsonBuilder().startObject().endObject(); mltQuery = moreLikeThisQuery(null, new Item[] { new Item("test", emptyDoc) }).minTermFreq(0).minDocFreq(0).minimumShouldMatch("0%"); - response = client().prepareSearch("test").setQuery(mltQuery).get(); + response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, 0); @@ -715,7 +715,7 @@ public void testMoreLikeThisMalformedArtificialDocs() throws Exception { mltQuery = moreLikeThisQuery(null, new Item[] { new Item("test", normalDoc) }).minTermFreq(0) .minDocFreq(0) .minimumShouldMatch("100%"); // strict all terms must match but date is ignored - response = client().prepareSearch("test").setQuery(mltQuery).get(); + response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, 1); } @@ -744,7 +744,7 @@ public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedExcep .minDocFreq(0) .maxQueryTerms(100) .minimumShouldMatch("0%"); - SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); + SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, numFields); @@ -759,7 +759,7 @@ public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedExcep .include(true) .minimumShouldMatch("0%"); - response = client().prepareSearch("test").setQuery(mltQuery).get(); + response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, numFields - (i + 1)); } @@ -783,7 +783,7 @@ public void testSelectFields() throws IOException, ExecutionException, Interrupt .minDocFreq(0) .include(true) .minimumShouldMatch("1%"); - SearchResponse response = client().prepareSearch("test").setQuery(mltQuery).get(); + SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -791,7 +791,7 @@ public void testSelectFields() throws IOException, ExecutionException, Interrupt .minDocFreq(0) .include(true) .minimumShouldMatch("1%"); - response = client().prepareSearch("test").setQuery(mltQuery).get(); + response = prepareSearch("test").setQuery(mltQuery).get(); assertNoFailures(response); assertHitCount(response, 1); } @@ -810,7 +810,7 @@ public void testWithRouting() throws IOException { ); moreLikeThisQueryBuilder.minTermFreq(1); moreLikeThisQueryBuilder.minDocFreq(1); - SearchResponse searchResponse = client().prepareSearch("index").setQuery(moreLikeThisQueryBuilder).get(); + SearchResponse searchResponse = prepareSearch("index").setQuery(moreLikeThisQueryBuilder).get(); assertEquals(2, searchResponse.getHits().getTotalHits().value); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java index 6b3193c4b383c..aa418288b8ebf 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/msearch/MultiSearchIT.java @@ -43,9 +43,9 @@ public void testSimpleMultiSearch() { client().prepareIndex("test").setId("2").setSource("field", "yyy").get(); refresh(); MultiSearchResponse response = client().prepareMultiSearch() - .add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "xxx"))) - .add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "yyy"))) - .add(client().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "xxx"))) + .add(prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "yyy"))) + .add(prepareSearch("test").setQuery(QueryBuilders.matchAllQuery())) .get(); for (MultiSearchResponse.Item item : response) { @@ -73,7 +73,7 @@ public void testSimpleMultiSearchMoreRequests() { request.maxConcurrentSearchRequests(randomIntBetween(1, numSearchRequests)); } for (int i = 0; i < numSearchRequests; i++) { - request.add(client().prepareSearch("test")); + request.add(prepareSearch("test")); } MultiSearchResponse response = client().multiSearch(request).actionGet(); @@ -96,9 +96,9 @@ public void testCCSCheckCompatibility() throws Exception { client().prepareIndex("test").setId("2").setSource("field", "yyy").get(); refresh(); MultiSearchResponse response = client().prepareMultiSearch() - .add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "xxx"))) - .add(client().prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "yyy"))) - .add(client().prepareSearch("test").setQuery(new DummyQueryBuilder() { + .add(prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "xxx"))) + .add(prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "yyy"))) + .add(prepareSearch("test").setQuery(new DummyQueryBuilder() { @Override public TransportVersion getMinimalSupportedVersion() { return transportVersion; diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/NestedWithMinScoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/NestedWithMinScoreIT.java index ce1b4bf30d498..e238a254b7843 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/NestedWithMinScoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/NestedWithMinScoreIT.java @@ -111,6 +111,6 @@ public void testNestedWithMinScore() throws Exception { if (randomBoolean()) { source.trackTotalHitsUpTo(randomBoolean() ? Integer.MAX_VALUE : randomIntBetween(1, 1000)); } - ElasticsearchAssertions.assertSearchHits(client().prepareSearch("test").setSource(source), "d1"); + ElasticsearchAssertions.assertSearchHits(prepareSearch("test").setSource(source), "d1"); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java index 598c65b8c999d..2deedccec4192 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java @@ -51,9 +51,9 @@ public void testSimpleNested() throws Exception { ensureGreen(); // check on no data, see it works - SearchResponse searchResponse = client().prepareSearch("test").get(); + SearchResponse searchResponse = prepareSearch("test").get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); - searchResponse = client().prepareSearch("test").setQuery(termQuery("n_field1", "n_value1_1")).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("n_field1", "n_value1_1")).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); client().prepareIndex("test") @@ -83,24 +83,22 @@ public void testSimpleNested() throws Exception { // check the numDocs assertDocumentCount("test", 3); - searchResponse = client().prepareSearch("test").setQuery(termQuery("n_field1", "n_value1_1")).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("n_field1", "n_value1_1")).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); // search for something that matches the nested doc, and see that we don't find the nested doc - searchResponse = client().prepareSearch("test").setQuery(matchAllQuery()).get(); + searchResponse = prepareSearch("test").setQuery(matchAllQuery()).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test").setQuery(termQuery("n_field1", "n_value1_1")).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("n_field1", "n_value1_1")).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); // now, do a nested query - searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) + searchResponse = prepareSearch("test").setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) + searchResponse = prepareSearch("test").setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .get(); assertNoFailures(searchResponse); @@ -130,44 +128,38 @@ public void testSimpleNested() throws Exception { refresh(); assertDocumentCount("test", 6); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.n_field1", "n_value1_1")).must(termQuery("nested1.n_field2", "n_value2_1")), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.n_field1", "n_value1_1")).must(termQuery("nested1.n_field2", "n_value2_1")), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); // filter - searchResponse = client().prepareSearch("test") - .setQuery( - boolQuery().must(matchAllQuery()) - .mustNot( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.n_field1", "n_value1_1")).must(termQuery("nested1.n_field2", "n_value2_1")), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()) + .mustNot( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.n_field1", "n_value1_1")).must(termQuery("nested1.n_field2", "n_value2_1")), + ScoreMode.Avg ) - ) - .get(); + ) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); // check with type prefix - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.n_field1", "n_value1_1")).must(termQuery("nested1.n_field2", "n_value2_1")), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.n_field1", "n_value1_1")).must(termQuery("nested1.n_field2", "n_value2_1")), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); @@ -178,8 +170,7 @@ public void testSimpleNested() throws Exception { refresh(); assertDocumentCount("test", 3); - searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) + searchResponse = prepareSearch("test").setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1_1"), ScoreMode.Avg)) .get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); @@ -247,93 +238,81 @@ public void testMultiNested() throws Exception { assertDocumentCount("test", 7); // do some multi nested queries - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1", termQuery("nested1.field1", "1"), ScoreMode.Avg)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + nestedQuery("nested1", termQuery("nested1.field1", "1"), ScoreMode.Avg) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "2"), ScoreMode.Avg)) - .get(); + searchResponse = prepareSearch("test").setQuery( + nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "2"), ScoreMode.Avg) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.field1", "1")) - .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "2"), ScoreMode.Avg)), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.field1", "1")) + .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "2"), ScoreMode.Avg)), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.field1", "1")) - .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "3"), ScoreMode.Avg)), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.field1", "1")) + .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "3"), ScoreMode.Avg)), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.field1", "1")) - .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "4"), ScoreMode.Avg)), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.field1", "1")) + .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "4"), ScoreMode.Avg)), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.field1", "1")) - .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "5"), ScoreMode.Avg)), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.field1", "1")) + .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "5"), ScoreMode.Avg)), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.field1", "4")) - .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "5"), ScoreMode.Avg)), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.field1", "4")) + .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "5"), ScoreMode.Avg)), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test") - .setQuery( - nestedQuery( - "nested1", - boolQuery().must(termQuery("nested1.field1", "4")) - .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "2"), ScoreMode.Avg)), - ScoreMode.Avg - ) + searchResponse = prepareSearch("test").setQuery( + nestedQuery( + "nested1", + boolQuery().must(termQuery("nested1.field1", "4")) + .must(nestedQuery("nested1.nested2", termQuery("nested1.nested2.field2", "2"), ScoreMode.Avg)), + ScoreMode.Avg ) - .get(); + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); } @@ -442,10 +421,9 @@ public void testExplain() throws Exception { .setRefreshPolicy(IMMEDIATE) .get(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1"), ScoreMode.Total)) - .setExplain(true) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + nestedQuery("nested1", termQuery("nested1.n_field1", "n_value1"), ScoreMode.Total) + ).setExplain(true).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); Explanation explanation = searchResponse.getHits().getHits()[0].getExplanation(); @@ -526,7 +504,7 @@ public void testSimpleNestedSorting() throws Exception { .get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") + SearchResponse searchResponse = prepareSearch("test") .setQuery(QueryBuilders.matchAllQuery()) .addSort(SortBuilders.fieldSort("nested1.field1").order(SortOrder.ASC).setNestedSort(new NestedSortBuilder("nested1"))) @@ -540,7 +518,7 @@ public void testSimpleNestedSorting() throws Exception { assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1")); assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("4")); - searchResponse = client().prepareSearch("test") + searchResponse = prepareSearch("test") .setQuery(QueryBuilders.matchAllQuery()) .addSort(SortBuilders.fieldSort("nested1.field1").order(SortOrder.DESC).setNestedSort(new NestedSortBuilder("nested1"))) @@ -638,8 +616,7 @@ public void testSimpleNestedSortingWithNestedFilterMissing() throws Exception { .get(); refresh(); - SearchRequestBuilder searchRequestBuilder = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) + SearchRequestBuilder searchRequestBuilder = prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) .addSort( SortBuilders.fieldSort("nested1.field1") .setNestedSort(new NestedSortBuilder("nested1").setFilter(termQuery("nested1.field2", true))) @@ -661,8 +638,7 @@ public void testSimpleNestedSortingWithNestedFilterMissing() throws Exception { assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3")); assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("10")); - searchRequestBuilder = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) + searchRequestBuilder = prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) .addSort( SortBuilders.fieldSort("nested1.field1") .setNestedSort(new NestedSortBuilder("nested1").setFilter(termQuery("nested1.field2", true))) @@ -1523,8 +1499,9 @@ public void testNestedSortingWithNestedFilterAsFilter() throws Exception { assertTrue(indexResponse2.getShardInfo().getSuccessful() > 0); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .addSort(SortBuilders.fieldSort("users.first").setNestedSort(new NestedSortBuilder("users")).order(SortOrder.ASC)) + SearchResponse searchResponse = prepareSearch("test").addSort( + SortBuilders.fieldSort("users.first").setNestedSort(new NestedSortBuilder("users")).order(SortOrder.ASC) + ) .addSort( SortBuilders.fieldSort("users.first") .order(SortOrder.ASC) @@ -1586,9 +1563,9 @@ public void testCheckFixedBitSetCache() throws Exception { assertThat(clusterStatsResponse.getIndicesStats().getSegments().getBitsetMemoryInBytes(), equalTo(0L)); // only when querying with nested the fixed bitsets are loaded - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(nestedQuery("array1", termQuery("array1.field1", "value1"), ScoreMode.Avg)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + nestedQuery("array1", termQuery("array1.field1", "value1"), ScoreMode.Avg) + ).get(); assertNoFailures(searchResponse); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(5L)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/VectorNestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/VectorNestedIT.java index 015dc9628de21..9219641f1d3bf 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/VectorNestedIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/VectorNestedIT.java @@ -66,10 +66,9 @@ public void testSimpleNested() throws Exception { assertThat(getResponse.getSourceAsBytes(), notNullValue()); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setKnnSearch(List.of(new KnnSearchBuilder("nested.vector", new float[] { 1, 1, 1 }, 1, 1, null))) - .setAllowPartialSearchResults(false) - .get(); + SearchResponse searchResponse = prepareSearch("test").setKnnSearch( + List.of(new KnnSearchBuilder("nested.vector", new float[] { 1, 1, 1 }, 1, 1, null)) + ).setAllowPartialSearchResults(false).get(); assertThat(searchResponse.getHits().getHits().length, greaterThan(0)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java index e981c1fd5c0c0..2a1bfa36ad25d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java @@ -123,8 +123,7 @@ protected void setupSuiteScopeCluster() throws Exception { } public void testSimpleProfile() { - SearchResponse response = client().prepareSearch("idx") - .setProfile(true) + SearchResponse response = prepareSearch("idx").setProfile(true) .addAggregation(histogram("histo").field(NUMBER_FIELD).interval(1L)) .get(); assertNoFailures(response); @@ -159,8 +158,7 @@ public void testSimpleProfile() { } public void testMultiLevelProfile() { - SearchResponse response = client().prepareSearch("idx") - .setProfile(true) + SearchResponse response = prepareSearch("idx").setProfile(true) .addAggregation( histogram("histo").field(NUMBER_FIELD) .interval(1L) @@ -246,8 +244,7 @@ private void assertRemapTermsDebugInfo(ProfileResult termsAggResult, String... d } public void testMultiLevelProfileBreadthFirst() { - SearchResponse response = client().prepareSearch("idx") - .setProfile(true) + SearchResponse response = prepareSearch("idx").setProfile(true) .addAggregation( histogram("histo").field(NUMBER_FIELD) .interval(1L) @@ -320,8 +317,7 @@ public void testMultiLevelProfileBreadthFirst() { } public void testDiversifiedAggProfile() { - SearchResponse response = client().prepareSearch("idx") - .setProfile(true) + SearchResponse response = prepareSearch("idx").setProfile(true) .addAggregation( diversifiedSampler("diversify").shardSize(10) .field(STRING_FIELD) @@ -376,8 +372,7 @@ public void testDiversifiedAggProfile() { } public void testComplexProfile() { - SearchResponse response = client().prepareSearch("idx") - .setProfile(true) + SearchResponse response = prepareSearch("idx").setProfile(true) .addAggregation( histogram("histo").field(NUMBER_FIELD) .interval(1L) @@ -593,8 +588,7 @@ public void testComplexProfile() { } public void testNoProfile() { - SearchResponse response = client().prepareSearch("idx") - .setProfile(false) + SearchResponse response = prepareSearch("idx").setProfile(false) .addAggregation( histogram("histo").field(NUMBER_FIELD) .interval(1L) @@ -642,8 +636,7 @@ public void testFilterByFilter() throws InterruptedException, IOException { } indexRandom(true, false, builders); - SearchResponse response = client().prepareSearch("dateidx") - .setProfile(true) + SearchResponse response = prepareSearch("dateidx").setProfile(true) .addAggregation( new DateHistogramAggregationBuilder("histo").field("date") .calendarInterval(DateHistogramInterval.MONTH) @@ -720,8 +713,7 @@ public void testDateHistogramFilterByFilterDisabled() throws InterruptedExceptio } indexRandom(true, false, builders); - SearchResponse response = client().prepareSearch("date_filter_by_filter_disabled") - .setProfile(true) + SearchResponse response = prepareSearch("date_filter_by_filter_disabled").setProfile(true) .addAggregation(new DateHistogramAggregationBuilder("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)) .get(); assertNoFailures(response); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java index b3fb2cb93713f..32334deb268b4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java @@ -115,15 +115,13 @@ public void testProfileMatchesRegular() throws Exception { QueryBuilder q = randomQueryBuilder(stringFields, numericFields, numDocs, 3); logger.debug("Query: {}", q); - SearchRequestBuilder vanilla = client().prepareSearch("test") - .setQuery(q) + SearchRequestBuilder vanilla = prepareSearch("test").setQuery(q) .setProfile(false) .addSort("id.keyword", SortOrder.ASC) .setSearchType(SearchType.QUERY_THEN_FETCH) .setRequestCache(false); - SearchRequestBuilder profile = client().prepareSearch("test") - .setQuery(q) + SearchRequestBuilder profile = prepareSearch("test").setQuery(q) .setProfile(true) .addSort("id.keyword", SortOrder.ASC) .setSearchType(SearchType.QUERY_THEN_FETCH) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java index 45f8b47efd870..099100a7a67e3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ExistsIT.java @@ -37,8 +37,8 @@ public class ExistsIT extends ESIntegTestCase { // TODO: move this to a unit test somewhere... public void testEmptyIndex() throws Exception { createIndex("test"); - assertNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.existsQuery("foo"))); - assertNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("foo")))); + assertNoFailures(prepareSearch("test").setQuery(QueryBuilders.existsQuery("foo"))); + assertNoFailures(prepareSearch("test").setQuery(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("foo")))); } public void testExists() throws Exception { @@ -113,14 +113,14 @@ public void testExists() throws Exception { expected.put("vec", 2); final long numDocs = sources.length; - SearchResponse allDocs = client().prepareSearch("idx").setSize(sources.length).get(); + SearchResponse allDocs = prepareSearch("idx").setSize(sources.length).get(); assertNoFailures(allDocs); assertHitCount(allDocs, numDocs); for (Map.Entry entry : expected.entrySet()) { final String fieldName = entry.getKey(); final int count = entry.getValue(); // exists - SearchResponse resp = client().prepareSearch("idx").setQuery(QueryBuilders.existsQuery(fieldName)).get(); + SearchResponse resp = prepareSearch("idx").setQuery(QueryBuilders.existsQuery(fieldName)).get(); assertNoFailures(resp); try { assertEquals( @@ -199,7 +199,7 @@ public void testFieldAlias() throws Exception { String fieldName = entry.getKey(); int expectedCount = entry.getValue(); - SearchResponse response = client().prepareSearch("idx").setQuery(QueryBuilders.existsQuery(fieldName)).get(); + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.existsQuery(fieldName)).get(); assertNoFailures(response); assertHitCount(response, expectedCount); } @@ -231,7 +231,7 @@ public void testFieldAliasWithNoDocValues() throws Exception { indexRequests.add(client().prepareIndex("idx").setSource("foo", 43)); indexRandom(true, false, indexRequests); - SearchResponse response = client().prepareSearch("idx").setQuery(QueryBuilders.existsQuery("foo-alias")).get(); + SearchResponse response = prepareSearch("idx").setQuery(QueryBuilders.existsQuery("foo-alias")).get(); assertNoFailures(response); assertHitCount(response, 2); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/IntervalQueriesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/IntervalQueriesIT.java index c2ccfe4ee9694..1e18c0ca3c59c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/IntervalQueriesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/IntervalQueriesIT.java @@ -56,11 +56,9 @@ public void testEmptyIntervalsWithNestedMappings() throws InterruptedException { client().prepareIndex("nested").setId("3").setSource("text", "quick") ); - SearchResponse resp = client().prepareSearch("nested") - .setQuery( - new IntervalQueryBuilder("empty_text", new IntervalsSourceProvider.Match("an empty query", 0, true, null, null, null)) - ) - .get(); + SearchResponse resp = prepareSearch("nested").setQuery( + new IntervalQueryBuilder("empty_text", new IntervalsSourceProvider.Match("an empty query", 0, true, null, null, null)) + ).get(); assertEquals(0, resp.getFailedShards()); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java index 3af7502cefead..f251ab5cb6269 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/MultiMatchQueryIT.java @@ -267,13 +267,11 @@ private XContentBuilder createMapping() throws IOException { public void testDefaults() throws ExecutionException, InterruptedException { MatchQueryParser.Type type = MatchQueryParser.Type.BOOLEAN; - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category").operator(Operator.OR) - ) + SearchResponse searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category").operator(Operator.OR) ) - .get(); + ).get(); Set topNIds = Sets.newHashSet("theone", "theother"); for (int i = 0; i < searchResponse.getHits().getHits().length; i++) { topNIds.remove(searchResponse.getHits().getAt(i).getId()); @@ -283,92 +281,78 @@ public void testDefaults() throws ExecutionException, InterruptedException { assertThat(topNIds, empty()); assertThat(searchResponse.getHits().getHits()[0].getScore(), greaterThan(searchResponse.getHits().getHits()[1].getScore())); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category").operator(Operator.OR) - .type(type) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category").operator(Operator.OR) + .type(type) ) - .get(); + ).get(); assertFirstHit(searchResponse, anyOf(hasId("theone"), hasId("theother"))); assertThat(searchResponse.getHits().getHits()[0].getScore(), greaterThan(searchResponse.getHits().getHits()[1].getScore())); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("marvel hero", "full_name", "first_name", "last_name", "category").operator(Operator.OR).type(type) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("marvel hero", "full_name", "first_name", "last_name", "category").operator(Operator.OR).type(type) ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theother")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category").operator(Operator.AND).type(type) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category").operator(Operator.AND).type(type) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category").operator(Operator.AND).type(type) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category").operator(Operator.AND).type(type) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); } public void testPhraseType() { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("Man the Ultimate", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase") - .operator(Operator.OR) - .type(MatchQueryParser.Type.PHRASE) - ) + SearchResponse searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("Man the Ultimate", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase") + .operator(Operator.OR) + .type(MatchQueryParser.Type.PHRASE) ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("ultimate2")); assertHitCount(searchResponse, 1L); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("Captain", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase").operator( - Operator.OR - ).type(MatchQueryParser.Type.PHRASE) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("Captain", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase").operator( + Operator.OR + ).type(MatchQueryParser.Type.PHRASE) ) - .get(); + ).get(); assertThat(searchResponse.getHits().getTotalHits().value, greaterThan(1L)); assertSearchHitsWithoutFailures( - client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("the Ul", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase").operator( - Operator.OR - ).type(MatchQueryParser.Type.PHRASE_PREFIX) - ) - ), + prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("the Ul", "full_name_phrase", "first_name_phrase", "last_name_phrase", "category_phrase").operator( + Operator.OR + ).type(MatchQueryParser.Type.PHRASE_PREFIX) + ) + ), "ultimate2", "ultimate1" ); } public void testSingleField() throws NoSuchFieldException, IllegalAccessException { - SearchResponse searchResponse = client().prepareSearch("test").setQuery(randomizeType(multiMatchQuery("15", "skill"))).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(randomizeType(multiMatchQuery("15", "skill"))).get(); assertNoFailures(searchResponse); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("15", "skill", "int-field")).analyzer("category")) + searchResponse = prepareSearch("test").setQuery(randomizeType(multiMatchQuery("15", "skill", "int-field")).analyzer("category")) .get(); assertNoFailures(searchResponse); assertFirstHit(searchResponse, hasId("theone")); @@ -409,7 +393,7 @@ public void testSingleField() throws NoSuchFieldException, IllegalAccessExceptio builder.append(RandomPicks.randomFrom(random(), query)).append(" "); } MultiMatchQueryBuilder multiMatchQueryBuilder = randomizeType(multiMatchQuery(builder.toString(), field)); - SearchResponse multiMatchResp = client().prepareSearch("test") + SearchResponse multiMatchResp = prepareSearch("test") // id sort field is a tie, in case hits have the same score, // the hits will be sorted the same consistently .addSort("_score", SortOrder.DESC) @@ -418,7 +402,7 @@ public void testSingleField() throws NoSuchFieldException, IllegalAccessExceptio .get(); MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(field, builder.toString()); - SearchResponse matchResp = client().prepareSearch("test") + SearchResponse matchResp = prepareSearch("test") // id tie sort .addSort("_score", SortOrder.DESC) .addSort("id", SortOrder.ASC) @@ -443,7 +427,7 @@ public void testSingleField() throws NoSuchFieldException, IllegalAccessExceptio public void testEquivalence() { - final int numDocs = (int) client().prepareSearch("test").setSize(0).setQuery(matchAllQuery()).get().getHits().getTotalHits().value; + final int numDocs = (int) prepareSearch("test").setSize(0).setQuery(matchAllQuery()).get().getHits().getTotalHits().value; int numIters = scaledRandomIntBetween(5, 10); for (int i = 0; i < numIters; i++) { { @@ -451,15 +435,13 @@ public void testEquivalence() { MultiMatchQueryBuilder multiMatchQueryBuilder = randomBoolean() ? multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category") : multiMatchQuery("marvel hero captain america", "*_name", randomBoolean() ? "category" : "categ*"); - SearchResponse left = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse left = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery(randomizeType(multiMatchQueryBuilder.operator(Operator.OR).type(type))) .get(); - SearchResponse right = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse right = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -479,8 +461,7 @@ public void testEquivalence() { MultiMatchQueryBuilder multiMatchQueryBuilder = randomBoolean() ? multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category") : multiMatchQuery("captain america", "*_name", randomBoolean() ? "category" : "categ*"); - SearchResponse left = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse left = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -488,8 +469,7 @@ public void testEquivalence() { ) .get(); - SearchResponse right = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse right = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -509,8 +489,7 @@ public void testEquivalence() { { String minShouldMatch = randomBoolean() ? null : "" + between(0, 1); - SearchResponse left = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse left = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -522,8 +501,7 @@ public void testEquivalence() { ) .get(); - SearchResponse right = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse right = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -540,8 +518,7 @@ public void testEquivalence() { String minShouldMatch = randomBoolean() ? null : "" + between(0, 1); SearchResponse left; if (randomBoolean()) { - left = client().prepareSearch("test") - .setSize(numDocs) + left = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -553,8 +530,7 @@ public void testEquivalence() { ) .get(); } else { - left = client().prepareSearch("test") - .setSize(numDocs) + left = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -566,8 +542,7 @@ public void testEquivalence() { ) .get(); } - SearchResponse right = client().prepareSearch("test") - .setSize(numDocs) + SearchResponse right = prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()) .addSort(SortBuilders.fieldSort("id")) .setQuery( @@ -584,208 +559,175 @@ public void testEquivalence() { } public void testCrossFieldMode() throws ExecutionException, InterruptedException { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america", "full_name", "first_name", "last_name").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).operator(Operator.OR) - ) + SearchResponse searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america", "full_name", "first_name", "last_name").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) + .operator(Operator.OR) ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).operator(Operator.OR) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).operator(Operator.OR) ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theother")); assertSecondHit(searchResponse, hasId("theone")); assertThat(searchResponse.getHits().getHits()[0].getScore(), greaterThan(searchResponse.getHits().getHits()[1].getScore())); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("marvel hero", "full_name", "first_name", "last_name", "category").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).operator(Operator.OR) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("marvel hero", "full_name", "first_name", "last_name", "category").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).operator(Operator.OR) ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theother")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).analyzer("category").lenient(true).operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).analyzer("category").lenient(true).operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill", "int-field").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).analyzer("category").lenient(true).operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill", "int-field").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).analyzer("category").lenient(true).operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america 15", "skill", "full_name", "first_name", "last_name", "category", "int-field").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).analyzer("category").lenient(true).operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america 15", "skill", "full_name", "first_name", "last_name", "category", "int-field").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).analyzer("category").lenient(true).operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america 15", "first_name", "last_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) - .lenient(true) - .analyzer("category") - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america 15", "first_name", "last_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) + .lenient(true) + .analyzer("category") ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("15", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).analyzer("category"))) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("15", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).analyzer("category")) + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("25 15", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).analyzer("category"))) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("25 15", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).analyzer("category")) + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("25 15", "int-field", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).analyzer("category") - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("25 15", "int-field", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).analyzer("category") ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("25 15", "first_name", "int-field", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) - .analyzer("category") - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("25 15", "first_name", "int-field", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) + .analyzer("category") ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("25 15", "int-field", "skill", "first_name").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) - .analyzer("category") - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("25 15", "int-field", "skill", "first_name").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) + .analyzer("category") ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("25 15", "int-field", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) - .analyzer("category") - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("25 15", "int-field", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) + .analyzer("category") ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).analyzer("category").operator(Operator.OR) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).analyzer("category").operator(Operator.OR) ) - .get(); + ).get(); assertFirstHit(searchResponse, hasId("theone")); // test group based on analyzer -- all fields are grouped into a cross field search - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).analyzer("category").operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).analyzer("category").operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); // counter example assertHitCount( - client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( - randomBoolean() ? MultiMatchQueryBuilder.Type.CROSS_FIELDS : MultiMatchQueryBuilder.DEFAULT_TYPE - ).operator(Operator.AND) - ) - ), + prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( + randomBoolean() ? MultiMatchQueryBuilder.Type.CROSS_FIELDS : MultiMatchQueryBuilder.DEFAULT_TYPE + ).operator(Operator.AND) + ) + ), 0L ); // counter example assertHitCount( - client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( - randomBoolean() ? MultiMatchQueryBuilder.Type.CROSS_FIELDS : MultiMatchQueryBuilder.DEFAULT_TYPE - ).operator(Operator.AND) - ) - ), + prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("captain america marvel hero", "first_name", "last_name", "category").type( + randomBoolean() ? MultiMatchQueryBuilder.Type.CROSS_FIELDS : MultiMatchQueryBuilder.DEFAULT_TYPE + ).operator(Operator.AND) + ) + ), 0L ); // test if boosts work - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("the ultimate", "full_name", "first_name", "category").field("last_name", 10) - .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) - .operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("the ultimate", "full_name", "first_name", "category").field("last_name", 10) + .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) + .operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("ultimate1")); // has ultimate in the last_name and that is boosted assertSecondHit(searchResponse, hasId("ultimate2")); @@ -793,55 +735,47 @@ public void testCrossFieldMode() throws ExecutionException, InterruptedException // since we try to treat the matching fields as one field scores are very similar but we have a small bias towards the // more frequent field that acts as a tie-breaker internally - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("the ultimate", "full_name", "first_name", "last_name", "category").type( - MultiMatchQueryBuilder.Type.CROSS_FIELDS - ).operator(Operator.AND) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("the ultimate", "full_name", "first_name", "last_name", "category").type( + MultiMatchQueryBuilder.Type.CROSS_FIELDS + ).operator(Operator.AND) ) - .get(); + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("ultimate2")); assertSecondHit(searchResponse, hasId("ultimate1")); assertThat(searchResponse.getHits().getHits()[0].getScore(), greaterThan(searchResponse.getHits().getHits()[1].getScore())); // Test group based on numeric fields - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("15", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("15", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)) + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("15", "skill", "first_name").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("15", "skill", "first_name").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)) + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); // Two numeric fields together caused trouble at one point! - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("15", "int-field", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS))) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("15", "int-field", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)) + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType(multiMatchQuery("15", "int-field", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)) - ) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("15", "int-field", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)) + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("theone")); - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("alpha 15", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).lenient(true) - ) - ) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("alpha 15", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).lenient(true)) + ).get(); /* * Doesn't find the one because "alpha 15" isn't a number and we don't * break on spaces. @@ -850,21 +784,18 @@ public void testCrossFieldMode() throws ExecutionException, InterruptedException assertFirstHit(searchResponse, hasId("ultimate1")); // Lenient wasn't always properly lenient with two numeric fields - searchResponse = client().prepareSearch("test") - .setQuery( - randomizeType( - multiMatchQuery("alpha 15", "int-field", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS) - .lenient(true) - ) + searchResponse = prepareSearch("test").setQuery( + randomizeType( + multiMatchQuery("alpha 15", "int-field", "first_name", "skill").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS).lenient(true) ) - .get(); + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("ultimate1")); // Check that cross fields works with date fields - searchResponse = client().prepareSearch("test") - .setQuery(randomizeType(multiMatchQuery("now", "f*", "date").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)).lenient(true)) - .get(); + searchResponse = prepareSearch("test").setQuery( + randomizeType(multiMatchQuery("now", "f*", "date").type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)).lenient(true) + ).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("nowHero")); } @@ -888,8 +819,7 @@ public void testFuzzyFieldLevelBoosting() throws InterruptedException, Execution builders.add(client().prepareIndex(idx).setId("2").setSource("title", "bar", "body", "foo")); indexRandom(true, false, builders); - SearchResponse searchResponse = client().prepareSearch(idx) - .setExplain(true) + SearchResponse searchResponse = prepareSearch(idx).setExplain(true) .setQuery(multiMatchQuery("foo").field("title", 100).field("body").fuzziness(Fuzziness.ZERO)) .get(); SearchHit[] hits = searchResponse.getHits().getHits(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/QueryStringIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/QueryStringIT.java index 8b83c9d18a850..882e18eb593aa 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/QueryStringIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/QueryStringIT.java @@ -49,15 +49,15 @@ public void testBasicAllQuery() throws Exception { reqs.add(client().prepareIndex("test").setId("3").setSource("f3", "foo bar baz")); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(queryStringQuery("foo")).get(); + SearchResponse resp = prepareSearch("test").setQuery(queryStringQuery("foo")).get(); assertHitCount(resp, 2L); assertHits(resp.getHits(), "1", "3"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("bar")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("bar")).get(); assertHitCount(resp, 2L); assertHits(resp.getHits(), "1", "3"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("Bar")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("Bar")).get(); assertHitCount(resp, 3L); assertHits(resp.getHits(), "1", "2", "3"); } @@ -68,19 +68,19 @@ public void testWithDate() throws Exception { reqs.add(client().prepareIndex("test").setId("2").setSource("f1", "bar", "f_date", "2015/09/01")); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(queryStringQuery("foo bar")).get(); + SearchResponse resp = prepareSearch("test").setQuery(queryStringQuery("foo bar")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("\"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("\"2015/09/02\"")).get(); assertHits(resp.getHits(), "1"); assertHitCount(resp, 1L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("bar \"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("bar \"2015/09/02\"")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("\"2015/09/02\" \"2015/09/01\"")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("\"2015/09/02\" \"2015/09/01\"")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); } @@ -95,19 +95,19 @@ public void testWithLotsOfTypes() throws Exception { ); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(queryStringQuery("foo bar")).get(); + SearchResponse resp = prepareSearch("test").setQuery(queryStringQuery("foo bar")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("\"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("\"2015/09/02\"")).get(); assertHits(resp.getHits(), "1"); assertHitCount(resp, 1L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("127.0.0.2 \"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("127.0.0.2 \"2015/09/02\"")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("127.0.0.1 OR 1.8")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("127.0.0.1 OR 1.8")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); } @@ -118,31 +118,31 @@ public void testDocWithAllTypes() throws Exception { reqs.add(client().prepareIndex("test").setId("1").setSource(docBody, XContentType.JSON)); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(queryStringQuery("foo")).get(); + SearchResponse resp = prepareSearch("test").setQuery(queryStringQuery("foo")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("Bar")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("Bar")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("Baz")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("Baz")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("19")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("19")).get(); assertHits(resp.getHits(), "1"); // nested doesn't match because it's hidden - resp = client().prepareSearch("test").setQuery(queryStringQuery("1476383971")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("1476383971")).get(); assertHits(resp.getHits(), "1"); // bool doesn't match - resp = client().prepareSearch("test").setQuery(queryStringQuery("7")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("7")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("23")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("23")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("1293")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("1293")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("42")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("42")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("1.7")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("1.7")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("1.5")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("1.5")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(queryStringQuery("127.0.0.1")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("127.0.0.1")).get(); assertHits(resp.getHits(), "1"); // binary doesn't match // suggest doesn't match @@ -156,15 +156,15 @@ public void testKeywordWithWhitespace() throws Exception { reqs.add(client().prepareIndex("test").setId("3").setSource("f1", "foo bar")); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(queryStringQuery("foo")).get(); + SearchResponse resp = prepareSearch("test").setQuery(queryStringQuery("foo")).get(); assertHits(resp.getHits(), "3"); assertHitCount(resp, 1L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("bar")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("bar")).get(); assertHits(resp.getHits(), "2", "3"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(queryStringQuery("Foo Bar")).get(); + resp = prepareSearch("test").setQuery(queryStringQuery("Foo Bar")).get(); assertHits(resp.getHits(), "1", "2", "3"); assertHitCount(resp, 3L); } @@ -180,11 +180,9 @@ public void testAllFields() throws Exception { reqs.add(client().prepareIndex("test_1").setId("1").setSource("f1", "foo", "f2", "eggplant")); indexRandom(true, false, reqs); - assertHitCount(client().prepareSearch("test_1").setQuery(queryStringQuery("foo eggplant").defaultOperator(Operator.AND)), 0L); + assertHitCount(prepareSearch("test_1").setQuery(queryStringQuery("foo eggplant").defaultOperator(Operator.AND)), 0L); - SearchResponse resp = client().prepareSearch("test_1") - .setQuery(queryStringQuery("foo eggplant").defaultOperator(Operator.OR)) - .get(); + SearchResponse resp = prepareSearch("test_1").setQuery(queryStringQuery("foo eggplant").defaultOperator(Operator.OR)).get(); assertHits(resp.getHits(), "1"); assertHitCount(resp, 1L); } @@ -195,11 +193,11 @@ public void testPhraseQueryOnFieldWithNoPositions() throws Exception { reqs.add(client().prepareIndex("test").setId("2").setSource("f1", "foo bar", "f4", "chicken parmesan")); indexRandom(true, false, reqs); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("\"eggplant parmesan\"").lenient(true)), 0L); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("\"eggplant parmesan\"").lenient(true)), 0L); Exception exc = expectThrows( Exception.class, - () -> client().prepareSearch("test").setQuery(queryStringQuery("f4:\"eggplant parmesan\"").lenient(false)).get() + () -> prepareSearch("test").setQuery(queryStringQuery("f4:\"eggplant parmesan\"").lenient(false)).get() ); IllegalStateException ise = (IllegalStateException) ExceptionsHelper.unwrap(exc, IllegalStateException.class); assertNotNull(ise); @@ -207,10 +205,7 @@ public void testPhraseQueryOnFieldWithNoPositions() throws Exception { } public void testBooleanStrictQuery() throws Exception { - Exception e = expectThrows( - Exception.class, - () -> client().prepareSearch("test").setQuery(queryStringQuery("foo").field("f_bool")).get() - ); + Exception e = expectThrows(Exception.class, () -> prepareSearch("test").setQuery(queryStringQuery("foo").field("f_bool")).get()); assertThat( ExceptionsHelper.unwrap(e, IllegalArgumentException.class).getMessage(), containsString("Can't parse boolean value [foo], expected [true] or [false]") @@ -220,7 +215,7 @@ public void testBooleanStrictQuery() throws Exception { public void testAllFieldsWithSpecifiedLeniency() throws IOException { Exception e = expectThrows( Exception.class, - () -> client().prepareSearch("test").setQuery(queryStringQuery("f_date:[now-2D TO now]").lenient(false)).get() + () -> prepareSearch("test").setQuery(queryStringQuery("f_date:[now-2D TO now]").lenient(false)).get() ); assertThat(e.getCause().getMessage(), containsString("unit [D] not supported for date math [-2D]")); } @@ -232,7 +227,7 @@ public void testFieldAlias() throws Exception { indexRequests.add(client().prepareIndex("test").setId("3").setSource("f3", "another value", "f2", "three")); indexRandom(true, false, indexRequests); - SearchResponse response = client().prepareSearch("test").setQuery(queryStringQuery("value").field("f3_alias")).get(); + SearchResponse response = prepareSearch("test").setQuery(queryStringQuery("value").field("f3_alias")).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -246,7 +241,7 @@ public void testFieldAliasWithEmbeddedFieldNames() throws Exception { indexRequests.add(client().prepareIndex("test").setId("3").setSource("f3", "another value", "f2", "three")); indexRandom(true, false, indexRequests); - SearchResponse response = client().prepareSearch("test").setQuery(queryStringQuery("f3_alias:value AND f2:three")).get(); + SearchResponse response = prepareSearch("test").setQuery(queryStringQuery("f3_alias:value AND f2:three")).get(); assertNoFailures(response); assertHitCount(response, 1); @@ -260,7 +255,7 @@ public void testFieldAliasWithWildcardField() throws Exception { indexRequests.add(client().prepareIndex("test").setId("3").setSource("f3", "another value", "f2", "three")); indexRandom(true, false, indexRequests); - SearchResponse response = client().prepareSearch("test").setQuery(queryStringQuery("value").field("f3_*")).get(); + SearchResponse response = prepareSearch("test").setQuery(queryStringQuery("value").field("f3_*")).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -274,7 +269,7 @@ public void testFieldAliasOnDisallowedFieldType() throws Exception { // The wildcard field matches aliases for both a text and geo_point field. // By default, the geo_point field should be ignored when building the query. - SearchResponse response = client().prepareSearch("test").setQuery(queryStringQuery("text").field("f*_alias")).get(); + SearchResponse response = prepareSearch("test").setQuery(queryStringQuery("text").field("f*_alias")).get(); assertNoFailures(response); assertHitCount(response, 1); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ScriptScoreQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ScriptScoreQueryIT.java index 40b8000b30b7e..c9c7c2a56eea9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/ScriptScoreQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/ScriptScoreQueryIT.java @@ -73,7 +73,7 @@ public void testScriptScore() { Map params = new HashMap<>(); params.put("param1", 0.1); Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['field2'].value * param1", params); - SearchResponse resp = client().prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script)).get(); + SearchResponse resp = prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script)).get(); assertNoFailures(resp); assertOrderedSearchHits(resp, "10", "8", "6", "4", "2"); assertFirstHit(resp, hasScore(1.0f)); @@ -81,9 +81,7 @@ public void testScriptScore() { assertThirdHit(resp, hasScore(0.6f)); // applying min score - resp = client().prepareSearch("test-index") - .setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script).setMinScore(0.6f)) - .get(); + resp = prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script).setMinScore(0.6f)).get(); assertNoFailures(resp); assertOrderedSearchHits(resp, "10", "8", "6"); } @@ -100,7 +98,7 @@ public void testScriptScoreBoolQuery() { params.put("param1", 0.1); Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['field2'].value * param1", params); QueryBuilder boolQuery = boolQuery().should(matchQuery("field1", "text1")).should(matchQuery("field1", "text10")); - SearchResponse resp = client().prepareSearch("test-index").setQuery(scriptScoreQuery(boolQuery, script)).get(); + SearchResponse resp = prepareSearch("test-index").setQuery(scriptScoreQuery(boolQuery, script)).get(); assertNoFailures(resp); assertOrderedSearchHits(resp, "10", "1"); assertFirstHit(resp, hasScore(1.0f)); @@ -120,7 +118,7 @@ public void testRewrittenQuery() { RangeQueryBuilder rangeQB = new RangeQueryBuilder("field1").from("2019-01-01"); // the query should be rewritten to from:null Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['field2'].value * param1", Map.of("param1", 0.1)); - SearchResponse resp = client().prepareSearch("test-index2").setQuery(scriptScoreQuery(rangeQB, script)).get(); + SearchResponse resp = prepareSearch("test-index2").setQuery(scriptScoreQuery(rangeQB, script)).get(); assertNoFailures(resp); assertOrderedSearchHits(resp, "3", "2", "1"); } @@ -136,14 +134,14 @@ public void testDisallowExpensiveQueries() { // Execute with search.allow_expensive_queries = null => default value = true => success Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['field2'].value * param1", Map.of("param1", 0.1)); - assertNoFailures(client().prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script))); + assertNoFailures(prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script))); // Set search.allow_expensive_queries to "false" => assert failure updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", false)); ElasticsearchException e = expectThrows( ElasticsearchException.class, - () -> client().prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script)).get() + () -> prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script)).get() ); assertEquals( "[script score] queries cannot be executed when 'search.allow_expensive_queries' is set to false.", @@ -152,7 +150,7 @@ public void testDisallowExpensiveQueries() { // Set search.allow_expensive_queries to "true" => success updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", true)); - assertNoFailures(client().prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script))); + assertNoFailures(prepareSearch("test-index").setQuery(scriptScoreQuery(matchQuery("field1", "text0"), script))); } finally { updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", (String) null)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java index c35bfee6e73d7..199f730b65aea 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -219,30 +219,24 @@ public void testConstantScoreQuery() throws Exception { assertThat(searchHit, hasScore(1.0f)); } - searchResponse = client().prepareSearch("test") - .setQuery( - boolQuery().must(matchAllQuery()).must(constantScoreQuery(matchQuery("field1", "quick")).boost(1.0f + random().nextFloat())) - ) - .get(); + searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).must(constantScoreQuery(matchQuery("field1", "quick")).boost(1.0f + random().nextFloat())) + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasScore(searchResponse.getHits().getAt(1).getScore())); - client().prepareSearch("test").setQuery(constantScoreQuery(matchQuery("field1", "quick")).boost(1.0f + random().nextFloat())).get(); + prepareSearch("test").setQuery(constantScoreQuery(matchQuery("field1", "quick")).boost(1.0f + random().nextFloat())).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasScore(searchResponse.getHits().getAt(1).getScore())); - searchResponse = client().prepareSearch("test") - .setQuery( - constantScoreQuery( - boolQuery().must(matchAllQuery()) - .must( - constantScoreQuery(matchQuery("field1", "quick")).boost( - 1.0f + (random.nextBoolean() ? 0.0f : random.nextFloat()) - ) - ) - ) + searchResponse = prepareSearch("test").setQuery( + constantScoreQuery( + boolQuery().must(matchAllQuery()) + .must( + constantScoreQuery(matchQuery("field1", "quick")).boost(1.0f + (random.nextBoolean() ? 0.0f : random.nextFloat())) + ) ) - .get(); + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasScore(searchResponse.getHits().getAt(1).getScore())); for (SearchHit searchHit : searchResponse.getHits().getHits()) { @@ -260,19 +254,16 @@ public void testConstantScoreQuery() throws Exception { int queryRounds = scaledRandomIntBetween(10, 20); for (int i = 0; i < queryRounds; i++) { MatchQueryBuilder matchQuery = matchQuery("f", English.intToEnglish(between(0, num))); - searchResponse = client().prepareSearch("test_1").setQuery(constantScoreQuery(matchQuery)).setSize(num).get(); + searchResponse = prepareSearch("test_1").setQuery(constantScoreQuery(matchQuery)).setSize(num).get(); long totalHits = searchResponse.getHits().getTotalHits().value; SearchHits hits = searchResponse.getHits(); for (SearchHit searchHit : hits) { assertThat(searchHit, hasScore(1.0f)); } - searchResponse = client().prepareSearch("test_1") - .setQuery( - boolQuery().must(matchAllQuery()) - .must(constantScoreQuery(matchQuery).boost(1.0f + (random.nextBoolean() ? 0.0f : random.nextFloat()))) - ) - .setSize(num) - .get(); + searchResponse = prepareSearch("test_1").setQuery( + boolQuery().must(matchAllQuery()) + .must(constantScoreQuery(matchQuery).boost(1.0f + (random.nextBoolean() ? 0.0f : random.nextFloat()))) + ).setSize(num).get(); hits = searchResponse.getHits(); assertThat(hits.getTotalHits().value, equalTo(totalHits)); if (totalHits > 1) { @@ -295,11 +286,11 @@ public void testAllDocsQueryString() throws InterruptedException, ExecutionExcep int iters = scaledRandomIntBetween(100, 200); for (int i = 0; i < iters; i++) { - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("*:*^10.0").boost(10.0f)), 2L); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("*:*^10.0").boost(10.0f)), 2L); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().must(matchAllQuery()).must(constantScoreQuery(matchAllQuery()))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).must(constantScoreQuery(matchAllQuery())) + ).get(); assertHitCount(searchResponse, 2L); assertThat((double) searchResponse.getHits().getAt(0).getScore(), closeTo(2.0, 0.1)); assertThat((double) searchResponse.getHits().getAt(1).getScore(), closeTo(2.0, 0.1)); @@ -542,10 +533,10 @@ public void testFiltersWithCustomCacheKey() throws Exception { client().prepareIndex("test").setId("1").setSource("field1", "value1").get(); refresh(); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("field1", "value1"))), 1L); } public void testMatchQueryNumeric() throws Exception { @@ -845,8 +836,8 @@ public void testEmptytermsQuery() throws Exception { client().prepareIndex("test").setId("3").setSource("term", "3"), client().prepareIndex("test").setId("4").setSource("term", "4") ); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("term", new String[0]))), 0L); - assertHitCount(client().prepareSearch("test").setQuery(idsQuery()), 0L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("term", new String[0]))), 0L); + assertHitCount(prepareSearch("test").setQuery(idsQuery()), 0L); } public void testTermsQuery() throws Exception { @@ -859,41 +850,35 @@ public void testTermsQuery() throws Exception { client().prepareIndex("test").setId("3").setSource("str", "3", "lng", 3L, "dbl", 3.0d), client().prepareIndex("test").setId("4").setSource("str", "4", "lng", 4L, "dbl", 4.0d) ); - assertSearchHitsWithoutFailures(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "1", "4"))), "1", "4"); + assertSearchHitsWithoutFailures(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "1", "4"))), "1", "4"); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 3 }))), + prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 3 }))), "2", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 3 }))), + prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 3 }))), "2", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new int[] { 1, 3 }))), + prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new int[] { 1, 3 }))), "1", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new float[] { 2, 4 }))), + prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new float[] { 2, 4 }))), "2", "4" ); // test partial matching - assertSearchHitsWithoutFailures(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "2", "5"))), "2"); - assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 5 }))), - "2" - ); - assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 5 }))), - "2" - ); + assertSearchHitsWithoutFailures(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "2", "5"))), "2"); + assertSearchHitsWithoutFailures(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 2, 5 }))), "2"); + assertSearchHitsWithoutFailures(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 2, 5 }))), "2"); // test valid type, but no matching terms - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "5", "6"))), 0L); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 5, 6 }))), 0L); - assertHitCount(client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 5, 6 }))), 0L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("str", "5", "6"))), 0L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("dbl", new double[] { 5, 6 }))), 0L); + assertHitCount(prepareSearch("test").setQuery(constantScoreQuery(termsQuery("lng", new long[] { 5, 6 }))), 0L); } public void testTermsLookupFilter() throws Exception { @@ -973,68 +958,62 @@ public void testTermsLookupFilter() throws Exception { client().prepareIndex("test").setId("4").setSource("term", "4") ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))), "1", "3" ); // same as above, just on the _id... assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("_id", new TermsLookup("lookup", "1", "terms"))), + prepareSearch("test").setQuery(termsLookupQuery("_id", new TermsLookup("lookup", "1", "terms"))), "1", "3" ); // another search with same parameters... assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "1", "terms"))), "1", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "2", "terms"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "2", "terms"))), "2" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "3", "terms"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "3", "terms"))), "2", "4" ); - assertHitCount(client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "4", "terms"))), 0L); + assertHitCount(prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "4", "terms"))), 0L); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "1", "arr.term"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "1", "arr.term"))), "1", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "2", "arr.term"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "2", "arr.term"))), "2" ); assertSearchHitsWithoutFailures( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "3", "arr.term"))), + prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup2", "3", "arr.term"))), "2", "4" ); - assertHitCount( - client().prepareSearch("test").setQuery(termsLookupQuery("not_exists", new TermsLookup("lookup2", "3", "arr.term"))), - 0L - ); + assertHitCount(prepareSearch("test").setQuery(termsLookupQuery("not_exists", new TermsLookup("lookup2", "3", "arr.term"))), 0L); // index "lookup" id "missing" document does not exist: ignore the lookup terms - assertHitCount( - client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "missing", "terms"))), - 0L - ); + assertHitCount(prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup", "missing", "terms"))), 0L); // index "lookup3" has the source disabled: ignore the lookup terms - assertHitCount(client().prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup3", "1", "terms"))), 0L); + assertHitCount(prepareSearch("test").setQuery(termsLookupQuery("term", new TermsLookup("lookup3", "1", "terms"))), 0L); } public void testBasicQueryById() throws Exception { @@ -1102,82 +1081,82 @@ public void testNumericTermsAndRanges() throws Exception { SearchResponse searchResponse; logger.info("--> term query on 1"); - searchResponse = client().prepareSearch("test").setQuery(termQuery("num_byte", 1)).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("num_byte", 1)).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termQuery("num_short", 1)).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("num_short", 1)).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termQuery("num_integer", 1)).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("num_integer", 1)).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termQuery("num_long", 1)).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("num_long", 1)).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termQuery("num_float", 1)).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("num_float", 1)).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termQuery("num_double", 1)).get(); + searchResponse = prepareSearch("test").setQuery(termQuery("num_double", 1)).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); logger.info("--> terms query on 1"); - searchResponse = client().prepareSearch("test").setQuery(termsQuery("num_byte", new int[] { 1 })).get(); + searchResponse = prepareSearch("test").setQuery(termsQuery("num_byte", new int[] { 1 })).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termsQuery("num_short", new int[] { 1 })).get(); + searchResponse = prepareSearch("test").setQuery(termsQuery("num_short", new int[] { 1 })).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termsQuery("num_integer", new int[] { 1 })).get(); + searchResponse = prepareSearch("test").setQuery(termsQuery("num_integer", new int[] { 1 })).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termsQuery("num_long", new int[] { 1 })).get(); + searchResponse = prepareSearch("test").setQuery(termsQuery("num_long", new int[] { 1 })).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termsQuery("num_float", new double[] { 1 })).get(); + searchResponse = prepareSearch("test").setQuery(termsQuery("num_float", new double[] { 1 })).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(termsQuery("num_double", new double[] { 1 })).get(); + searchResponse = prepareSearch("test").setQuery(termsQuery("num_double", new double[] { 1 })).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); logger.info("--> term filter on 1"); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_byte", 1))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_byte", 1))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_short", 1))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_short", 1))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_integer", 1))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_integer", 1))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_long", 1))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_long", 1))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_float", 1))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_float", 1))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_double", 1))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termQuery("num_double", 1))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); logger.info("--> terms filter on 1"); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_byte", new int[] { 1 }))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_byte", new int[] { 1 }))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_short", new int[] { 1 }))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_short", new int[] { 1 }))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_integer", new int[] { 1 }))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_integer", new int[] { 1 }))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_long", new int[] { 1 }))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_long", new int[] { 1 }))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_float", new int[] { 1 }))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_float", new int[] { 1 }))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_double", new int[] { 1 }))).get(); + searchResponse = prepareSearch("test").setQuery(constantScoreQuery(termsQuery("num_double", new int[] { 1 }))).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); } @@ -1206,25 +1185,23 @@ public void testNumericRangeFilter_2826() throws Exception { client().prepareIndex("test").setId("4").setSource("field1", "test2", "num_long", 4).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setPostFilter(boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setPostFilter( + boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4)) + ).get(); assertHitCount(searchResponse, 4L); // This made 2826 fail! (only with bit based filters) - searchResponse = client().prepareSearch("test") - .setPostFilter(boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4))) - .get(); + searchResponse = prepareSearch("test").setPostFilter( + boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4)) + ).get(); assertHitCount(searchResponse, 4L); // This made #2979 fail! - searchResponse = client().prepareSearch("test") - .setPostFilter( - boolQuery().must(termQuery("field1", "test1")) - .should(rangeQuery("num_long").from(1).to(2)) - .should(rangeQuery("num_long").from(3).to(4)) - ) - .get(); + searchResponse = prepareSearch("test").setPostFilter( + boolQuery().must(termQuery("field1", "test1")) + .should(rangeQuery("num_long").from(1).to(2)) + .should(rangeQuery("num_long").from(3).to(4)) + ).get(); assertHitCount(searchResponse, 2L); } @@ -1244,14 +1221,12 @@ public void testMustNot() throws IOException, ExecutionException, InterruptedExc client().prepareIndex("test").setId("4").setSource("description", "foo") ); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .get(); assertHitCount(searchResponse, 4L); - searchResponse = client().prepareSearch("test") - .setQuery(boolQuery().mustNot(matchQuery("description", "anything"))) + searchResponse = prepareSearch("test").setQuery(boolQuery().mustNot(matchQuery("description", "anything"))) .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .get(); assertHitCount(searchResponse, 2L); @@ -1288,7 +1263,7 @@ public void testIntervals() throws InterruptedException { } } }"""; - SearchResponse response = client().prepareSearch("test").setQuery(wrapperQuery(json)).get(); + SearchResponse response = prepareSearch("test").setQuery(wrapperQuery(json)).get(); assertHitCount(response, 1L); } @@ -1304,10 +1279,11 @@ public void testSimpleSpan() throws IOException, ExecutionException, Interrupted client().prepareIndex("test").setId("4").setSource("description", "foo") ); - assertHitCount(client().prepareSearch("test").setQuery(spanOrQuery(spanTermQuery("description", "bar"))), 1L); + assertHitCount(prepareSearch("test").setQuery(spanOrQuery(spanTermQuery("description", "bar"))), 1L); assertHitCount( - client().prepareSearch("test") - .setQuery(spanNearQuery(spanTermQuery("description", "foo"), 3).addClause(spanTermQuery("description", "other"))), + prepareSearch("test").setQuery( + spanNearQuery(spanTermQuery("description", "foo"), 3).addClause(spanTermQuery("description", "other")) + ), 3L ); } @@ -1321,27 +1297,16 @@ public void testSpanMultiTermQuery() throws IOException { client().prepareIndex("test").setId("4").setSource("description", "fop", "count", 4).get(); refresh(); + assertHitCount(prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(fuzzyQuery("description", "fop")))), 4); + assertHitCount(prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(prefixQuery("description", "fo")))), 4); + assertHitCount(prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(wildcardQuery("description", "oth*")))), 3); assertHitCount( - client().prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(fuzzyQuery("description", "fop")))), - 4 - ); - assertHitCount( - client().prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(prefixQuery("description", "fo")))), - 4 - ); - assertHitCount( - client().prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(wildcardQuery("description", "oth*")))), - 3 - ); - assertHitCount( - client().prepareSearch("test") - .setQuery(spanOrQuery(spanMultiTermQueryBuilder(QueryBuilders.rangeQuery("description").from("ffa").to("foo")))), - 3 - ); - assertHitCount( - client().prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(regexpQuery("description", "fo{2}")))), + prepareSearch("test").setQuery( + spanOrQuery(spanMultiTermQueryBuilder(QueryBuilders.rangeQuery("description").from("ffa").to("foo"))) + ), 3 ); + assertHitCount(prepareSearch("test").setQuery(spanOrQuery(spanMultiTermQueryBuilder(regexpQuery("description", "fo{2}")))), 3); } public void testSpanNot() throws IOException, ExecutionException, InterruptedException { @@ -1352,39 +1317,36 @@ public void testSpanNot() throws IOException, ExecutionException, InterruptedExc refresh(); assertHitCount( - client().prepareSearch("test") - .setQuery( - spanNotQuery( - spanNearQuery(QueryBuilders.spanTermQuery("description", "quick"), 1).addClause( - QueryBuilders.spanTermQuery("description", "fox") - ), - spanTermQuery("description", "brown") - ) - ), + prepareSearch("test").setQuery( + spanNotQuery( + spanNearQuery(QueryBuilders.spanTermQuery("description", "quick"), 1).addClause( + QueryBuilders.spanTermQuery("description", "fox") + ), + spanTermQuery("description", "brown") + ) + ), 1L ); assertHitCount( - client().prepareSearch("test") - .setQuery( - spanNotQuery( - spanNearQuery(QueryBuilders.spanTermQuery("description", "quick"), 1).addClause( - QueryBuilders.spanTermQuery("description", "fox") - ), - spanTermQuery("description", "sleeping") - ).dist(5) - ), + prepareSearch("test").setQuery( + spanNotQuery( + spanNearQuery(QueryBuilders.spanTermQuery("description", "quick"), 1).addClause( + QueryBuilders.spanTermQuery("description", "fox") + ), + spanTermQuery("description", "sleeping") + ).dist(5) + ), 1L ); assertHitCount( - client().prepareSearch("test") - .setQuery( - spanNotQuery( - spanNearQuery(QueryBuilders.spanTermQuery("description", "quick"), 1).addClause( - QueryBuilders.spanTermQuery("description", "fox") - ), - spanTermQuery("description", "jumped") - ).pre(1).post(1) - ), + prepareSearch("test").setQuery( + spanNotQuery( + spanNearQuery(QueryBuilders.spanTermQuery("description", "quick"), 1).addClause( + QueryBuilders.spanTermQuery("description", "fox") + ), + spanTermQuery("description", "jumped") + ).pre(1).post(1) + ), 1L ); } @@ -1438,8 +1400,7 @@ public void testSimpleDFSQuery() throws IOException { refresh(); assertNoFailures( - client().prepareSearch("test") - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery( boolQuery().must(termQuery("online", true)) .must( @@ -1464,20 +1425,20 @@ public void testMultiFieldQueryString() { client().prepareIndex("test").setId("1").setSource("field1", "value1", "field2", "value2").setRefreshPolicy(IMMEDIATE).get(); logger.info("regular"); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("value1").field("field1").field("field2")), 1); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("field\\*:value1")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("value1").field("field1").field("field2")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("field\\*:value1")), 1); logger.info("prefix"); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("value*").field("field1").field("field2")), 1); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("field\\*:value*")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("value*").field("field1").field("field2")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("field\\*:value*")), 1); logger.info("wildcard"); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("v?lue*").field("field1").field("field2")), 1); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("field\\*:v?lue*")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("v?lue*").field("field1").field("field2")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("field\\*:v?lue*")), 1); logger.info("fuzzy"); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("value~").field("field1").field("field2")), 1); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("field\\*:value~")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("value~").field("field1").field("field2")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("field\\*:value~")), 1); logger.info("regexp"); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("/value[01]/").field("field1").field("field2")), 1); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("field\\*:/value[01]/")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("/value[01]/").field("field1").field("field2")), 1); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("field\\*:/value[01]/")), 1); } // see #3797 @@ -1487,9 +1448,9 @@ public void testMultiMatchLenientIssue3797() { client().prepareIndex("test").setId("1").setSource("field1", 123, "field2", "value2").get(); refresh(); - assertHitCount(client().prepareSearch("test").setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true)), 1L); - assertHitCount(client().prepareSearch("test").setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true)), 1L); - assertHitCount(client().prepareSearch("test").setQuery(multiMatchQuery("value2").field("field2", 2).lenient(true)), 1L); + assertHitCount(prepareSearch("test").setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true)), 1L); + assertHitCount(prepareSearch("test").setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true)), 1L); + assertHitCount(prepareSearch("test").setQuery(multiMatchQuery("value2").field("field2", 2).lenient(true)), 1L); } public void testMinScore() throws ExecutionException, InterruptedException { @@ -1501,9 +1462,9 @@ public void testMinScore() throws ExecutionException, InterruptedException { client().prepareIndex("test").setId("4").setSource("score", 0.5).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(functionScoreQuery(ScoreFunctionBuilders.fieldValueFactorFunction("score").missing(1.0)).setMinScore(1.5f)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + functionScoreQuery(ScoreFunctionBuilders.fieldValueFactorFunction("score").missing(1.0)).setMinScore(1.5f) + ).get(); assertHitCount(searchResponse, 2); assertFirstHit(searchResponse, hasId("3")); assertSecondHit(searchResponse, hasId("1")); @@ -1516,28 +1477,24 @@ public void testQueryStringWithSlopAndFields() { client().prepareIndex("test").setId("2").setSource("desc", "one two three", "type", "product").get(); refresh(); - assertHitCount(client().prepareSearch("test").setQuery(QueryBuilders.queryStringQuery("\"one two\"").defaultField("desc")), 2); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.queryStringQuery("\"one two\"").defaultField("desc")), 2); assertHitCount( - client().prepareSearch("test") - .setPostFilter(QueryBuilders.termQuery("type", "customer")) + prepareSearch("test").setPostFilter(QueryBuilders.termQuery("type", "customer")) .setQuery(QueryBuilders.queryStringQuery("\"one two\"").field("desc")), 1 ); assertHitCount( - client().prepareSearch("test") - .setPostFilter(QueryBuilders.termQuery("type", "product")) + prepareSearch("test").setPostFilter(QueryBuilders.termQuery("type", "product")) .setQuery(QueryBuilders.queryStringQuery("\"one three\"~5").field("desc")), 1 ); assertHitCount( - client().prepareSearch("test") - .setPostFilter(QueryBuilders.termQuery("type", "customer")) + prepareSearch("test").setPostFilter(QueryBuilders.termQuery("type", "customer")) .setQuery(QueryBuilders.queryStringQuery("\"one two\"").defaultField("desc")), 1 ); assertHitCount( - client().prepareSearch("test") - .setPostFilter(QueryBuilders.termQuery("type", "customer")) + prepareSearch("test").setPostFilter(QueryBuilders.termQuery("type", "customer")) .setQuery(QueryBuilders.queryStringQuery("\"one two\"").defaultField("desc")), 1 ); @@ -1556,8 +1513,8 @@ public void testDateProvidedAsNumber() throws InterruptedException { client().prepareIndex("test").setId("6").setSource("field", 999999999999L) ); - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(rangeQuery("field").gte(1000000000000L)), 4); - assertHitCount(client().prepareSearch("test").setSize(0).setQuery(rangeQuery("field").gte(999999999999L)), 6); + assertHitCount(prepareSearch("test").setSize(0).setQuery(rangeQuery("field").gte(1000000000000L)), 4); + assertHitCount(prepareSearch("test").setSize(0).setQuery(rangeQuery("field").gte(999999999999L)), 6); } public void testRangeQueryWithTimeZone() throws Exception { @@ -1574,65 +1531,63 @@ public void testRangeQueryWithTimeZone() throws Exception { .setSource("date", Instant.now().atZone(ZoneOffset.ofHours(1)).toInstant().toEpochMilli(), "num", 4) ); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T00:00:00").to("2014-01-01T00:59:00")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T00:00:00").to("2014-01-01T00:59:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("1")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2013-12-31T23:00:00").to("2013-12-31T23:59:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2013-12-31T23:00:00").to("2013-12-31T23:59:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("2")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T01:00:00").to("2014-01-01T01:59:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T01:00:00").to("2014-01-01T01:59:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("3")); // We explicitly define a time zone in the from/to dates so whatever the time zone is, it won't be used - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T00:00:00Z").to("2014-01-01T00:59:00Z").timeZone("+10:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T00:00:00Z").to("2014-01-01T00:59:00Z").timeZone("+10:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("1")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2013-12-31T23:00:00Z").to("2013-12-31T23:59:00Z").timeZone("+10:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2013-12-31T23:00:00Z").to("2013-12-31T23:59:00Z").timeZone("+10:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("2")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T01:00:00Z").to("2014-01-01T01:59:00Z").timeZone("+10:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T01:00:00Z").to("2014-01-01T01:59:00Z").timeZone("+10:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("3")); // We define a time zone to be applied to the filter and from/to have no time zone - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T03:00:00").to("2014-01-01T03:59:00").timeZone("+03:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T03:00:00").to("2014-01-01T03:59:00").timeZone("+03:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("1")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T02:00:00").to("2014-01-01T02:59:00").timeZone("+03:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T02:00:00").to("2014-01-01T02:59:00").timeZone("+03:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("2")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T04:00:00").to("2014-01-01T04:59:00").timeZone("+03:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01T04:00:00").to("2014-01-01T04:59:00").timeZone("+03:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("3")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01").to("2014-01-01T00:59:00").timeZone("-01:00")) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date").from("2014-01-01").to("2014-01-01T00:59:00").timeZone("-01:00") + ).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("3")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("date").from("now/d-1d").timeZone("+01:00")) - .get(); + searchResponse = prepareSearch("test").setQuery(QueryBuilders.rangeQuery("date").from("now/d-1d").timeZone("+01:00")).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), is("4")); } @@ -1670,17 +1625,15 @@ public void testRangeQueryWithLocaleMapping() throws Exception { ); assertHitCount( - client().prepareSearch("test") - .setQuery( - QueryBuilders.rangeQuery("date_field").gte("Di, 05 Dez 2000 02:55:00 -0800").lte("Do, 07 Dez 2000 00:00:00 -0800") - ), + prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date_field").gte("Di, 05 Dez 2000 02:55:00 -0800").lte("Do, 07 Dez 2000 00:00:00 -0800") + ), 1L ); assertHitCount( - client().prepareSearch("test") - .setQuery( - QueryBuilders.rangeQuery("date_field").gte("Di, 05 Dez 2000 02:55:00 -0800").lte("Fr, 08 Dez 2000 00:00:00 -0800") - ), + prepareSearch("test").setQuery( + QueryBuilders.rangeQuery("date_field").gte("Di, 05 Dez 2000 02:55:00 -0800").lte("Fr, 08 Dez 2000 00:00:00 -0800") + ), 2L ); } @@ -1712,8 +1665,7 @@ public void testQueryStringParserCache() throws Exception { createIndex("test"); indexRandom(true, false, client().prepareIndex("test").setId("1").setSource("nameTokens", "xyz")); - SearchResponse response = client().prepareSearch("test") - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(QueryBuilders.queryStringQuery("xyz").boost(100)) .get(); assertThat(response.getHits().getTotalHits().value, equalTo(1L)); @@ -1721,8 +1673,7 @@ public void testQueryStringParserCache() throws Exception { float first = response.getHits().getAt(0).getScore(); for (int i = 0; i < 100; i++) { - response = client().prepareSearch("test") - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + response = prepareSearch("test").setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(QueryBuilders.queryStringQuery("xyz").boost(100)) .get(); @@ -1743,7 +1694,7 @@ public void testRangeQueryRangeFields_24744() throws Exception { refresh(); RangeQueryBuilder range = new RangeQueryBuilder("int_range").relation("intersects").from(Integer.MIN_VALUE).to(Integer.MAX_VALUE); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(range).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(range).get(); assertHitCount(searchResponse, 1); } @@ -1784,7 +1735,7 @@ public void testNestedQueryWithFieldAlias() throws Exception { QueryBuilders.termQuery("section.route_length_miles", 42), ScoreMode.Max ); - assertHitCount(client().prepareSearch("index").setQuery(nestedQuery), 1); + assertHitCount(prepareSearch("index").setQuery(nestedQuery), 1); } public void testFieldAliasesForMetaFields() throws Exception { @@ -1957,7 +1908,7 @@ public void testIssueFuzzyInsideSpanMulti() { refresh(); BoolQueryBuilder query = boolQuery().filter(spanMultiTermQueryBuilder(fuzzyQuery("field", "foobarbiz").rewrite("constant_score"))); - assertHitCount(client().prepareSearch("test").setQuery(query), 1); + assertHitCount(prepareSearch("test").setQuery(query), 1); } public void testFetchIdFieldQuery() { @@ -1969,7 +1920,7 @@ public void testFetchIdFieldQuery() { ensureGreen(); refresh(); - SearchResponse response = client().prepareSearch("test").addFetchField("_id").setSize(docCount).get(); + SearchResponse response = prepareSearch("test").addFetchField("_id").setSize(docCount).get(); SearchHit[] hits = response.getHits().getHits(); assertEquals(docCount, hits.length); for (SearchHit hit : hits) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java index 80c3e73e76bd5..c41aa2067b9c4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java @@ -324,7 +324,7 @@ public void testLenientFlagBeingTooLenient() throws Exception { BoolQueryBuilder q = boolQuery().should(simpleQueryStringQuery("bar").field("num").field("body").lenient(true)); // the bug is that this would be parsed into basically a match_all // query and this would match both documents - assertSearchHitsWithoutFailures(client().prepareSearch("test").setQuery(q), "1"); + assertSearchHitsWithoutFailures(prepareSearch("test").setQuery(q), "1"); } public void testSimpleQueryStringAnalyzeWildcard() throws ExecutionException, InterruptedException, IOException { @@ -395,15 +395,15 @@ public void testBasicAllQuery() throws Exception { reqs.add(client().prepareIndex("test").setId("3").setSource("f3", "foo bar baz")); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); + SearchResponse resp = prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); assertHitCount(resp, 2L); assertHits(resp.getHits(), "1", "3"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("bar")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("bar")).get(); assertHitCount(resp, 2L); assertHits(resp.getHits(), "1", "3"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("Bar")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("Bar")).get(); assertHitCount(resp, 3L); assertHits(resp.getHits(), "1", "2", "3"); } @@ -418,19 +418,19 @@ public void testWithDate() throws Exception { reqs.add(client().prepareIndex("test").setId("2").setSource("f1", "bar", "f_date", "2015/09/01")); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo bar")).get(); + SearchResponse resp = prepareSearch("test").setQuery(simpleQueryStringQuery("foo bar")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("\"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("\"2015/09/02\"")).get(); assertHits(resp.getHits(), "1"); assertHitCount(resp, 1L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("bar \"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("bar \"2015/09/02\"")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("\"2015/09/02\" \"2015/09/01\"")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("\"2015/09/02\" \"2015/09/01\"")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); } @@ -449,19 +449,19 @@ public void testWithLotsOfTypes() throws Exception { ); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo bar")).get(); + SearchResponse resp = prepareSearch("test").setQuery(simpleQueryStringQuery("foo bar")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("\"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("\"2015/09/02\"")).get(); assertHits(resp.getHits(), "1"); assertHitCount(resp, 1L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("127.0.0.2 \"2015/09/02\"")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("127.0.0.2 \"2015/09/02\"")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("127.0.0.1 1.8")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("127.0.0.1 1.8")).get(); assertHits(resp.getHits(), "1", "2"); assertHitCount(resp, 2L); } @@ -476,38 +476,38 @@ public void testDocWithAllTypes() throws Exception { reqs.add(client().prepareIndex("test").setId("1").setSource(docBody, XContentType.JSON)); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); + SearchResponse resp = prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("Bar")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("Bar")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("Baz")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("Baz")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("19")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("19")).get(); assertHits(resp.getHits(), "1"); // nested doesn't match because it's hidden - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("1476383971")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("1476383971")).get(); assertHits(resp.getHits(), "1"); // bool doesn't match - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("7")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("7")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("23")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("23")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("1293")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("1293")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("42")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("42")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("1.7")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("1.7")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("1.5")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("1.5")).get(); assertHits(resp.getHits(), "1"); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("127.0.0.1")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("127.0.0.1")).get(); assertHits(resp.getHits(), "1"); // binary doesn't match // suggest doesn't match // geo_point doesn't match // geo_shape doesn't match - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo Bar 19 127.0.0.1").defaultOperator(Operator.AND)).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("foo Bar 19 127.0.0.1").defaultOperator(Operator.AND)).get(); assertHits(resp.getHits(), "1"); } @@ -522,11 +522,11 @@ public void testKeywordWithWhitespace() throws Exception { reqs.add(client().prepareIndex("test").setId("3").setSource("f1", "foo bar")); indexRandom(true, false, reqs); - SearchResponse resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); + SearchResponse resp = prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); assertHits(resp.getHits(), "3"); assertHitCount(resp, 1L); - resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("bar")).get(); + resp = prepareSearch("test").setQuery(simpleQueryStringQuery("bar")).get(); assertHits(resp.getHits(), "2", "3"); assertHitCount(resp, 2L); } @@ -542,7 +542,7 @@ public void testAllFieldsWithSpecifiedLeniency() throws Exception { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo123").lenient(false)).get() + () -> prepareSearch("test").setQuery(simpleQueryStringQuery("foo123").lenient(false)).get() ); assertThat(e.getDetailedMessage(), containsString("NumberFormatException: For input string: \"foo123\"")); } @@ -558,7 +558,7 @@ public void testFieldAlias() throws Exception { indexRequests.add(client().prepareIndex("test").setId("3").setSource("f3", "another value", "f2", "three")); indexRandom(true, false, indexRequests); - SearchResponse response = client().prepareSearch("test").setQuery(simpleQueryStringQuery("value").field("f3_alias")).get(); + SearchResponse response = prepareSearch("test").setQuery(simpleQueryStringQuery("value").field("f3_alias")).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -576,7 +576,7 @@ public void testFieldAliasWithWildcardField() throws Exception { indexRequests.add(client().prepareIndex("test").setId("3").setSource("f3", "another value", "f2", "three")); indexRandom(true, false, indexRequests); - SearchResponse response = client().prepareSearch("test").setQuery(simpleQueryStringQuery("value").field("f3_*")).get(); + SearchResponse response = prepareSearch("test").setQuery(simpleQueryStringQuery("value").field("f3_*")).get(); assertNoFailures(response); assertHitCount(response, 2); @@ -594,7 +594,7 @@ public void testFieldAliasOnDisallowedFieldType() throws Exception { // The wildcard field matches aliases for both a text and boolean field. // By default, the boolean field should be ignored when building the query. - SearchResponse response = client().prepareSearch("test").setQuery(queryStringQuery("text").field("f*_alias")).get(); + SearchResponse response = prepareSearch("test").setQuery(queryStringQuery("text").field("f*_alias")).get(); assertNoFailures(response); assertHitCount(response, 1); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java index cf2a7f130bd98..3b19d55efef71 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java @@ -217,8 +217,7 @@ public void testCustomPreferenceUnaffectedByOtherShardMovements() { final String customPreference = randomAlphaOfLength(10); - final String nodeId = client().prepareSearch("test") - .setQuery(matchAllQuery()) + final String nodeId = prepareSearch("test").setQuery(matchAllQuery()) .setPreference(customPreference) .get() .getHits() @@ -260,7 +259,7 @@ public void testCustomPreferenceUnaffectedByOtherShardMovements() { } private static void assertSearchesSpecificNode(String index, String customPreference, String nodeId) { - final SearchResponse searchResponse = client().prepareSearch(index).setQuery(matchAllQuery()).setPreference(customPreference).get(); + final SearchResponse searchResponse = prepareSearch(index).setQuery(matchAllQuery()).setPreference(customPreference).get(); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); assertThat(searchResponse.getHits().getAt(0).getShard().getNodeId(), equalTo(nodeId)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java index 499aba8fd57d3..294768a319b09 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java @@ -224,14 +224,14 @@ public void testDisallowExpensiveQueries() { // Execute with search.allow_expensive_queries = null => default value = false => success Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > 1", Collections.emptyMap()); - assertNoFailures(client().prepareSearch("test-index").setQuery(scriptQuery(script))); + assertNoFailures(prepareSearch("test-index").setQuery(scriptQuery(script))); updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", false)); // Set search.allow_expensive_queries to "false" => assert failure ElasticsearchException e = expectThrows( ElasticsearchException.class, - () -> client().prepareSearch("test-index").setQuery(scriptQuery(script)).get() + () -> prepareSearch("test-index").setQuery(scriptQuery(script)).get() ); assertEquals( "[script] queries cannot be executed when 'search.allow_expensive_queries' is set to false.", @@ -240,7 +240,7 @@ public void testDisallowExpensiveQueries() { // Set search.allow_expensive_queries to "true" => success updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", true)); - assertNoFailures(client().prepareSearch("test-index").setQuery(scriptQuery(script))); + assertNoFailures(prepareSearch("test-index").setQuery(scriptQuery(script))); } finally { updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", (String) null)); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/DuelScrollIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/DuelScrollIT.java index 53bbe47149d32..c63aa19beb42e 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/DuelScrollIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/DuelScrollIT.java @@ -37,8 +37,7 @@ public class DuelScrollIT extends ESIntegTestCase { public void testDuelQueryThenFetch() throws Exception { TestContext context = create(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH); - SearchResponse control = client().prepareSearch("index") - .setSearchType(context.searchType) + SearchResponse control = prepareSearch("index").setSearchType(context.searchType) .addSort(context.sort) .setSize(context.numDocs) .get(); @@ -47,8 +46,7 @@ public void testDuelQueryThenFetch() throws Exception { assertThat(sh.getTotalHits().value, equalTo((long) context.numDocs)); assertThat(sh.getHits().length, equalTo(context.numDocs)); - SearchResponse searchScrollResponse = client().prepareSearch("index") - .setSearchType(context.searchType) + SearchResponse searchScrollResponse = prepareSearch("index").setSearchType(context.searchType) .addSort(context.sort) .setSize(context.scrollRequestSize) .setScroll("10m") @@ -215,8 +213,7 @@ private int createIndex(boolean singleShard) throws Exception { private void testDuelIndexOrder(SearchType searchType, boolean trackScores, int numDocs) throws Exception { final int size = scaledRandomIntBetween(5, numDocs + 5); - final SearchResponse control = client().prepareSearch("test") - .setSearchType(searchType) + final SearchResponse control = prepareSearch("test").setSearchType(searchType) .setSize(numDocs) .setQuery(QueryBuilders.matchQuery("foo", "true")) .addSort(SortBuilders.fieldSort("_doc")) @@ -224,8 +221,7 @@ private void testDuelIndexOrder(SearchType searchType, boolean trackScores, int .get(); assertNoFailures(control); - SearchResponse scroll = client().prepareSearch("test") - .setSearchType(searchType) + SearchResponse scroll = prepareSearch("test").setSearchType(searchType) .setSize(size) .setQuery(QueryBuilders.matchQuery("foo", "true")) .addSort(SortBuilders.fieldSort("_doc")) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java index 6b6192ddcce96..6fb19890a183f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java @@ -469,8 +469,7 @@ public void testDeepScrollingDoesNotBlowUp() throws Exception { updateIndexSettings(Settings.builder().put(IndexSettings.MAX_RESULT_WINDOW_SETTING.getKey(), Integer.MAX_VALUE), "index"); for (SearchType searchType : SearchType.values()) { - SearchRequestBuilder builder = client().prepareSearch("index") - .setSearchType(searchType) + SearchRequestBuilder builder = prepareSearch("index").setSearchType(searchType) .setQuery(QueryBuilders.matchAllQuery()) .setSize(Integer.MAX_VALUE) .setScroll("1m"); @@ -491,7 +490,7 @@ public void testThatNonExistingScrollIdReturnsCorrectException() throws Exceptio client().prepareIndex("index").setId("1").setSource("field", "value").execute().get(); refresh(); - SearchResponse searchResponse = client().prepareSearch("index").setSize(1).setScroll("1m").get(); + SearchResponse searchResponse = prepareSearch("index").setSize(1).setScroll("1m").get(); assertThat(searchResponse.getScrollId(), is(notNullValue())); ClearScrollResponse clearScrollResponse = client().prepareClearScroll().addScrollId(searchResponse.getScrollId()).get(); @@ -507,7 +506,7 @@ public void testStringSortMissingAscTerminates() throws Exception { client().prepareIndex("test").setId("1").setSource("some_field", "test").get(); refresh(); - SearchResponse response = client().prepareSearch("test") + SearchResponse response = prepareSearch("test") .addSort(new FieldSortBuilder("no_field").order(SortOrder.ASC).missing("_last")) .setScroll("1m") @@ -520,7 +519,7 @@ public void testStringSortMissingAscTerminates() throws Exception { assertHitCount(response, 1); assertNoSearchHits(response); - response = client().prepareSearch("test") + response = prepareSearch("test") .addSort(new FieldSortBuilder("no_field").order(SortOrder.ASC).missing("_first")) .setScroll("1m") @@ -650,8 +649,7 @@ public void testScrollRewrittenToMatchNoDocs() { SearchResponse resp = null; try { int totalHits = 0; - resp = client().prepareSearch("test") - .setQuery(new RangeQueryBuilder("created_date").gte("2020-01-02").lte("2020-01-03")) + resp = prepareSearch("test").setQuery(new RangeQueryBuilder("created_date").gte("2020-01-02").lte("2020-01-03")) .setMaxConcurrentShardRequests(randomIntBetween(1, 3)) // sometimes fan out shard requests one by one .setSize(randomIntBetween(1, 2)) .setScroll(TimeValue.timeValueMinutes(1)) @@ -680,16 +678,14 @@ public void testRestartDataNodesDuringScrollSearch() throws Exception { index("prod", "prod-" + i, Map.of()); } indicesAdmin().prepareRefresh().get(); - SearchResponse respFromDemoIndex = client().prepareSearch("demo") - .setSize(randomIntBetween(1, 10)) + SearchResponse respFromDemoIndex = prepareSearch("demo").setSize(randomIntBetween(1, 10)) .setQuery(new MatchAllQueryBuilder()) .setScroll(TimeValue.timeValueMinutes(5)) .get(); internalCluster().restartNode(dataNode, new InternalTestCluster.RestartCallback()); ensureGreen("demo", "prod"); - SearchResponse respFromProdIndex = client().prepareSearch("prod") - .setSize(randomIntBetween(1, 10)) + SearchResponse respFromProdIndex = prepareSearch("prod").setSize(randomIntBetween(1, 10)) .setQuery(new MatchAllQueryBuilder()) .setScroll(TimeValue.timeValueMinutes(5)) .get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java index 87a500691aab5..3ac8b103ce910 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/searchafter/SearchAfterIT.java @@ -67,8 +67,7 @@ public void testsShouldFail() throws Exception { { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .addSort("field1", SortOrder.ASC) + () -> prepareSearch("test").addSort("field1", SortOrder.ASC) .setQuery(matchAllQuery()) .searchAfter(new Object[] { 0 }) .setScroll("1m") @@ -83,8 +82,7 @@ public void testsShouldFail() throws Exception { { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .addSort("field1", SortOrder.ASC) + () -> prepareSearch("test").addSort("field1", SortOrder.ASC) .setQuery(matchAllQuery()) .searchAfter(new Object[] { 0 }) .setFrom(10) @@ -99,7 +97,7 @@ public void testsShouldFail() throws Exception { { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test").setQuery(matchAllQuery()).searchAfter(new Object[] { 0.75f }).get() + () -> prepareSearch("test").setQuery(matchAllQuery()).searchAfter(new Object[] { 0.75f }).get() ); assertTrue(e.shardFailures().length > 0); for (ShardSearchFailure failure : e.shardFailures()) { @@ -110,8 +108,7 @@ public void testsShouldFail() throws Exception { { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .addSort("field2", SortOrder.DESC) + () -> prepareSearch("test").addSort("field2", SortOrder.DESC) .addSort("field1", SortOrder.ASC) .setQuery(matchAllQuery()) .searchAfter(new Object[] { 1 }) @@ -126,8 +123,7 @@ public void testsShouldFail() throws Exception { { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .setQuery(matchAllQuery()) + () -> prepareSearch("test").setQuery(matchAllQuery()) .addSort("field1", SortOrder.ASC) .searchAfter(new Object[] { 1, 2 }) .get() @@ -141,8 +137,7 @@ public void testsShouldFail() throws Exception { { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .setQuery(matchAllQuery()) + () -> prepareSearch("test").setQuery(matchAllQuery()) .addSort("field1", SortOrder.ASC) .searchAfter(new Object[] { "toto" }) .get() @@ -162,8 +157,7 @@ public void testWithNullStrings() throws InterruptedException { client().prepareIndex("test").setId("0").setSource("field1", 0), client().prepareIndex("test").setId("1").setSource("field1", 100, "field2", "toto") ); - SearchResponse searchResponse = client().prepareSearch("test") - .addSort("field1", SortOrder.ASC) + SearchResponse searchResponse = prepareSearch("test").addSort("field1", SortOrder.ASC) .addSort("field2", SortOrder.ASC) .setQuery(matchAllQuery()) .searchAfter(new Object[] { 0, null }) @@ -235,8 +229,7 @@ public void testWithCustomFormatSortValueOfDateField() throws Exception { .add(new IndexRequest("test").id("5").source("start_date", "2017-01-20", "end_date", "2025-05-28")) .get(); - SearchResponse resp = client().prepareSearch("test") - .addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) + SearchResponse resp = prepareSearch("test").addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) .addSort(SortBuilders.fieldSort("end_date").setFormat("yyyy-MM-dd")) .setSize(2) .get(); @@ -244,8 +237,7 @@ public void testWithCustomFormatSortValueOfDateField() throws Exception { assertThat(resp.getHits().getHits()[0].getSortValues(), arrayContaining("22/01/2015", "2022-07-23")); assertThat(resp.getHits().getHits()[1].getSortValues(), arrayContaining("21/02/2016", "2024-03-24")); - resp = client().prepareSearch("test") - .addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) + resp = prepareSearch("test").addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) .addSort(SortBuilders.fieldSort("end_date").setFormat("yyyy-MM-dd")) .searchAfter(new String[] { "21/02/2016", "2024-03-24" }) .setSize(2) @@ -254,8 +246,7 @@ public void testWithCustomFormatSortValueOfDateField() throws Exception { assertThat(resp.getHits().getHits()[0].getSortValues(), arrayContaining("20/01/2017", "2025-05-28")); assertThat(resp.getHits().getHits()[1].getSortValues(), arrayContaining("23/04/2018", "2021-02-22")); - resp = client().prepareSearch("test") - .addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) + resp = prepareSearch("test").addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) .addSort(SortBuilders.fieldSort("end_date")) // it's okay because end_date has the format "yyyy-MM-dd" .searchAfter(new String[] { "21/02/2016", "2024-03-24" }) .setSize(2) @@ -264,15 +255,13 @@ public void testWithCustomFormatSortValueOfDateField() throws Exception { assertThat(resp.getHits().getHits()[0].getSortValues(), arrayContaining("20/01/2017", 1748390400000L)); assertThat(resp.getHits().getHits()[1].getSortValues(), arrayContaining("23/04/2018", 1613952000000L)); - SearchRequestBuilder searchRequest = client().prepareSearch("test") - .addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) + SearchRequestBuilder searchRequest = prepareSearch("test").addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) .addSort(SortBuilders.fieldSort("end_date").setFormat("epoch_millis")) .searchAfter(new Object[] { "21/02/2016", 1748390400000L }) .setSize(2); assertNoFailures(searchRequest); - searchRequest = client().prepareSearch("test") - .addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) + searchRequest = prepareSearch("test").addSort(SortBuilders.fieldSort("start_date").setFormat("dd/MM/yyyy")) .addSort(SortBuilders.fieldSort("end_date").setFormat("epoch_millis")) // wrong format .searchAfter(new Object[] { "21/02/2016", "23/04/2018" }) .setSize(2); @@ -334,7 +323,7 @@ private void assertSearchFromWithSortValues(String indexName, List> int offset = 0; Object[] sortValues = null; while (offset < documents.size()) { - SearchRequestBuilder req = client().prepareSearch(indexName); + SearchRequestBuilder req = prepareSearch(indexName); for (int i = 0; i < documents.get(0).size(); i++) { req.addSort("field" + Integer.toString(i), SortOrder.ASC); } @@ -441,8 +430,7 @@ public void testScrollAndSearchAfterWithBigIndex() { Collections.sort(timestamps); // scroll with big index { - SearchResponse resp = client().prepareSearch("test") - .setSize(randomIntBetween(50, 100)) + SearchResponse resp = prepareSearch("test").setSize(randomIntBetween(50, 100)) .setQuery(new MatchAllQueryBuilder()) .addSort(new FieldSortBuilder("timestamp")) .setScroll(TimeValue.timeValueMinutes(5)) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java index 61bdeb0f6ade3..45a64491de693 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java @@ -63,14 +63,11 @@ protected Collection> nodePlugins() { } public void testSearchNullIndex() { - expectThrows( - NullPointerException.class, - () -> client().prepareSearch((String) null).setQuery(QueryBuilders.termQuery("_id", "XXX1")).get() - ); + expectThrows(NullPointerException.class, () -> prepareSearch((String) null).setQuery(QueryBuilders.termQuery("_id", "XXX1")).get()); expectThrows( NullPointerException.class, - () -> client().prepareSearch((String[]) null).setQuery(QueryBuilders.termQuery("_id", "XXX1")).get() + () -> prepareSearch((String[]) null).setQuery(QueryBuilders.termQuery("_id", "XXX1")).get() ); } @@ -192,35 +189,32 @@ public void testSimpleDateRange() throws Exception { ensureGreen(); refresh(); assertHitCountAndNoFailures( - client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d").lte("2010-01-04||+2d/d")), + prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d").lte("2010-01-04||+2d/d")), 2L ); assertHitCountAndNoFailures( - client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lte("2010-01-06T02:00")), + prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lte("2010-01-06T02:00")), 2L ); assertHitCountAndNoFailures( - client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lt("2010-01-06T02:00")), + prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05T02:00").lt("2010-01-06T02:00")), 1L ); assertHitCountAndNoFailures( - client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("2010-01-05T02:00").lt("2010-01-06T02:00")), + prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("2010-01-05T02:00").lt("2010-01-06T02:00")), 0L ); - assertHitCount( - client().prepareSearch("test").setQuery(QueryBuilders.queryStringQuery("field:[2010-01-03||+2d TO 2010-01-04||+2d/d]")), - 2L - ); + assertHitCount(prepareSearch("test").setQuery(QueryBuilders.queryStringQuery("field:[2010-01-03||+2d TO 2010-01-04||+2d/d]")), 2L); // a string value of "1000" should be parsed as the year 1000 and return all three docs - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("1000")), 3L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("1000")), 3L); // a numeric value of 1000 should be parsed as 1000 millis since epoch and return only docs after 1970 - SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(1000)).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(1000)).get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, 2L); String[] expectedIds = new String[] { "1", "2" }; @@ -240,14 +234,14 @@ public void testRangeQueryKeyword() throws Exception { ensureGreen(); refresh(); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lte("B")), 2L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("A").lte("B")), 1L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lt("B")), 1L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte(null).lt("C")), 3L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("B").lt(null)), 2L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(null).lt(null)), 4L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("").lt(null)), 4L); - assertHitCountAndNoFailures(client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("").lt(null)), 3L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lte("B")), 2L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("A").lte("B")), 1L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("A").lt("B")), 1L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte(null).lt("C")), 3L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("B").lt(null)), 2L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt(null).lt(null)), 4L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("").lt(null)), 4L); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gt("").lt(null)), 3L); } public void testSimpleTerminateAfterCount() throws Exception { @@ -267,18 +261,12 @@ public void testSimpleTerminateAfterCount() throws Exception { SearchResponse searchResponse; for (int i = 1; i < max; i++) { - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(max)) - .setTerminateAfter(i) - .get(); + searchResponse = prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(max)).setTerminateAfter(i).get(); assertHitCount(searchResponse, i); assertTrue(searchResponse.isTerminatedEarly()); } - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(max)) - .setTerminateAfter(2 * max) - .get(); + searchResponse = prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(max)).setTerminateAfter(2 * max).get(); assertHitCount(searchResponse, max); assertFalse(searchResponse.isTerminatedEarly()); @@ -301,8 +289,7 @@ public void testSimpleIndexSortEarlyTerminate() throws Exception { SearchResponse searchResponse; for (int i = 1; i < max; i++) { - searchResponse = client().prepareSearch("test") - .addDocValueField("rank") + searchResponse = prepareSearch("test").addDocValueField("rank") .setTrackTotalHits(false) .addSort("rank", SortOrder.ASC) .setSize(i) @@ -318,19 +305,18 @@ public void testInsaneFromAndSize() throws Exception { createIndex("idx"); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertWindowFails(client().prepareSearch("idx").setFrom(Integer.MAX_VALUE)); - assertWindowFails(client().prepareSearch("idx").setSize(Integer.MAX_VALUE)); + assertWindowFails(prepareSearch("idx").setFrom(Integer.MAX_VALUE)); + assertWindowFails(prepareSearch("idx").setSize(Integer.MAX_VALUE)); } public void testTooLargeFromAndSize() throws Exception { createIndex("idx"); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertWindowFails(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY))); - assertWindowFails(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1)); + assertWindowFails(prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY))); + assertWindowFails(prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1)); assertWindowFails( - client().prepareSearch("idx") - .setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) + prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) .setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) ); } @@ -339,11 +325,10 @@ public void testLargeFromAndSizeSucceeds() throws Exception { createIndex("idx"); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) - 10), 1); - assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1); + assertHitCount(prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) - 10), 1); + assertHitCount(prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1); assertHitCount( - client().prepareSearch("idx") - .setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) / 2) + prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) / 2) .setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) / 2 - 1), 1 ); @@ -356,11 +341,10 @@ public void testTooLargeFromAndSizeOkBySetting() throws Exception { ).get(); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1); - assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1), 1); + assertHitCount(prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1); + assertHitCount(prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1), 1); assertHitCount( - client().prepareSearch("idx") - .setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) + prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) .setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1 ); @@ -375,11 +359,10 @@ public void testTooLargeFromAndSizeOkByDynamicSetting() throws Exception { ); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1); - assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1), 1); + assertHitCount(prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1); + assertHitCount(prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1), 1); assertHitCount( - client().prepareSearch("idx") - .setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) + prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)) .setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)), 1 ); @@ -389,11 +372,10 @@ public void testTooLargeFromAndSizeBackwardsCompatibilityRecommendation() throws prepareCreate("idx").setSettings(Settings.builder().put(IndexSettings.MAX_RESULT_WINDOW_SETTING.getKey(), Integer.MAX_VALUE)).get(); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10), 1); - assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10), 1); + assertHitCount(prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10), 1); + assertHitCount(prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10), 1); assertHitCount( - client().prepareSearch("idx") - .setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10) + prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10) .setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10), 1 ); @@ -413,10 +395,7 @@ public void testTooLargeRescoreOkBySetting() throws Exception { .get(); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount( - client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), - 1 - ); + assertHitCount(prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), 1); } public void testTooLargeRescoreOkByResultWindowSetting() throws Exception { @@ -430,10 +409,7 @@ public void testTooLargeRescoreOkByResultWindowSetting() throws Exception { ).get(); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount( - client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), - 1 - ); + assertHitCount(prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), 1); } public void testTooLargeRescoreOkByDynamicSetting() throws Exception { @@ -442,10 +418,7 @@ public void testTooLargeRescoreOkByDynamicSetting() throws Exception { updateIndexSettings(Settings.builder().put(IndexSettings.MAX_RESCORE_WINDOW_SETTING.getKey(), defaultMaxWindow * 2), "idx"); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount( - client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), - 1 - ); + assertHitCount(prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), 1); } public void testTooLargeRescoreOkByDynamicResultWindowSetting() throws Exception { @@ -458,10 +431,7 @@ public void testTooLargeRescoreOkByDynamicResultWindowSetting() throws Exception ); indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); - assertHitCount( - client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), - 1 - ); + assertHitCount(prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)), 1); } public void testQueryNumericFieldWithRegex() throws Exception { @@ -469,7 +439,7 @@ public void testQueryNumericFieldWithRegex() throws Exception { ensureGreen("idx"); try { - client().prepareSearch("idx").setQuery(QueryBuilders.regexpQuery("num", "34")).get(); + prepareSearch("idx").setQuery(QueryBuilders.regexpQuery("num", "34")).get(); fail("SearchPhaseExecutionException should have been thrown"); } catch (SearchPhaseExecutionException ex) { assertThat(ex.getRootCause().getMessage(), containsString("Can only use regexp queries on keyword and text fields")); @@ -490,7 +460,7 @@ public void testTermQueryBigInt() throws Exception { XContentParser parser = createParser(JsonXContent.jsonXContent, queryJson); parser.nextToken(); TermQueryBuilder query = TermQueryBuilder.fromXContent(parser); - SearchResponse searchResponse = client().prepareSearch("idx").setQuery(query).get(); + SearchResponse searchResponse = prepareSearch("idx").setQuery(query).get(); assertEquals(1, searchResponse.getHits().getTotalHits().value); } @@ -505,7 +475,7 @@ public void testTooLongRegexInRegexpQuery() throws Exception { } SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("idx").setQuery(QueryBuilders.regexpQuery("num", regexp.toString())).get() + () -> prepareSearch("idx").setQuery(QueryBuilders.regexpQuery("num", regexp.toString())).get() ); assertThat( e.getRootCause().getMessage(), @@ -536,8 +506,7 @@ private void assertWindowFails(SearchRequestBuilder search) { } private void assertRescoreWindowFails(int windowSize) { - SearchRequestBuilder search = client().prepareSearch("idx") - .addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(windowSize)); + SearchRequestBuilder search = prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(windowSize)); SearchPhaseExecutionException e = expectThrows(SearchPhaseExecutionException.class, () -> search.get()); assertThat( e.toString(), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java index e32bc4e9b35ad..a1e860ba848bb 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java @@ -92,16 +92,14 @@ public void testSearchSort() throws Exception { for (String field : new String[] { "_id", "random_int", "static_int" }) { int fetchSize = randomIntBetween(10, 100); // test _doc sort - SearchRequestBuilder request = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchRequestBuilder request = prepareSearch("test").setQuery(matchAllQuery()) .setScroll(new Scroll(TimeValue.timeValueSeconds(10))) .setSize(fetchSize) .addSort(SortBuilders.fieldSort("_doc")); assertSearchSlicesWithScroll(request, field, max, numDocs); // test numeric sort - request = client().prepareSearch("test") - .setQuery(matchAllQuery()) + request = prepareSearch("test").setQuery(matchAllQuery()) .setScroll(new Scroll(TimeValue.timeValueSeconds(10))) .addSort(SortBuilders.fieldSort("random_int")) .setSize(fetchSize); @@ -114,12 +112,11 @@ public void testWithPreferenceAndRoutings() throws Exception { int totalDocs = randomIntBetween(100, 1000); setupIndex(totalDocs, numShards); { - SearchResponse sr = client().prepareSearch("test").setQuery(matchAllQuery()).setPreference("_shards:1,4").setSize(0).get(); + SearchResponse sr = prepareSearch("test").setQuery(matchAllQuery()).setPreference("_shards:1,4").setSize(0).get(); int numDocs = (int) sr.getHits().getTotalHits().value; int max = randomIntBetween(2, numShards * 3); int fetchSize = randomIntBetween(10, 100); - SearchRequestBuilder request = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchRequestBuilder request = prepareSearch("test").setQuery(matchAllQuery()) .setScroll(new Scroll(TimeValue.timeValueSeconds(10))) .setSize(fetchSize) .setPreference("_shards:1,4") @@ -127,12 +124,11 @@ public void testWithPreferenceAndRoutings() throws Exception { assertSearchSlicesWithScroll(request, "_id", max, numDocs); } { - SearchResponse sr = client().prepareSearch("test").setQuery(matchAllQuery()).setRouting("foo", "bar").setSize(0).get(); + SearchResponse sr = prepareSearch("test").setQuery(matchAllQuery()).setRouting("foo", "bar").setSize(0).get(); int numDocs = (int) sr.getHits().getTotalHits().value; int max = randomIntBetween(2, numShards * 3); int fetchSize = randomIntBetween(10, 100); - SearchRequestBuilder request = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchRequestBuilder request = prepareSearch("test").setQuery(matchAllQuery()) .setScroll(new Scroll(TimeValue.timeValueSeconds(10))) .setSize(fetchSize) .setRouting("foo", "bar") @@ -222,8 +218,7 @@ private void assertSearchSlicesWithPointInTime(String sliceField, String sortFie for (int id = 0; id < numSlice; id++) { int numSliceResults = 0; - SearchRequestBuilder request = client().prepareSearch("test") - .slice(new SliceBuilder(sliceField, id, numSlice)) + SearchRequestBuilder request = prepareSearch("test").slice(new SliceBuilder(sliceField, id, numSlice)) .setPointInTime(new PointInTimeBuilder(pointInTimeId)) .addSort(SortBuilders.fieldSort(sortField)) .setSize(randomIntBetween(10, 100)); @@ -257,8 +252,7 @@ public void testInvalidFields() throws Exception { setupIndex(0, 1); SearchPhaseExecutionException exc = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .setQuery(matchAllQuery()) + () -> prepareSearch("test").setQuery(matchAllQuery()) .setScroll(new Scroll(TimeValue.timeValueSeconds(10))) .slice(new SliceBuilder("invalid_random_int", 0, 10)) .get() @@ -269,8 +263,7 @@ public void testInvalidFields() throws Exception { exc = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test") - .setQuery(matchAllQuery()) + () -> prepareSearch("test").setQuery(matchAllQuery()) .setScroll(new Scroll(TimeValue.timeValueSeconds(10))) .slice(new SliceBuilder("invalid_random_kw", 0, 10)) .get() diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java index 2adadc5e34fb6..2d3af1f555d4f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java @@ -280,8 +280,7 @@ public void testRandomSorting() throws IOException, InterruptedException, Execut indexRandom(true, builders); { int size = between(1, denseBytes.size()); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .setSize(size) .addSort("dense_bytes", SortOrder.ASC) .get(); @@ -327,8 +326,7 @@ public void test3078() { client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", Integer.toString(i)).get(); } refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("field").order(SortOrder.ASC)) .get(); assertThat(searchResponse.getHits().getAt(0).getSortValues()[0].toString(), equalTo("1")); @@ -339,8 +337,7 @@ public void test3078() { client().prepareIndex("test").setId(Integer.toString(1)).setSource("field", Integer.toString(1)).get(); refresh(); - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("field").order(SortOrder.ASC)) .get(); assertThat(searchResponse.getHits().getAt(0).getSortValues()[0].toString(), equalTo("1")); @@ -350,8 +347,7 @@ public void test3078() { // reindex - no refresh client().prepareIndex("test").setId(Integer.toString(1)).setSource("field", Integer.toString(1)).get(); - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("field").order(SortOrder.ASC)) .get(); assertThat(searchResponse.getHits().getAt(0).getSortValues()[0].toString(), equalTo("1")); @@ -363,8 +359,7 @@ public void test3078() { refresh(); client().prepareIndex("test").setId(Integer.toString(1)).setSource("field", Integer.toString(1)).get(); - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("field").order(SortOrder.ASC)) .get(); assertThat(searchResponse.getHits().getAt(0).getSortValues()[0].toString(), equalTo("1")); @@ -372,8 +367,7 @@ public void test3078() { assertThat(searchResponse.getHits().getAt(2).getSortValues()[0].toString(), equalTo("100")); refresh(); - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("field").order(SortOrder.ASC)) .get(); assertThat(searchResponse.getHits().getAt(0).getSortValues()[0].toString(), equalTo("1")); @@ -391,29 +385,27 @@ public void testScoreSortDirection() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.fieldValueFactorFunction("field"))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + QueryBuilders.functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.fieldValueFactorFunction("field")) + ).get(); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); assertThat(searchResponse.getHits().getAt(1).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(0).getScore())); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2")); assertThat(searchResponse.getHits().getAt(2).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(1).getScore())); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.fieldValueFactorFunction("field"))) - .addSort("_score", SortOrder.DESC) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.fieldValueFactorFunction("field")) + ).addSort("_score", SortOrder.DESC).get(); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); assertThat(searchResponse.getHits().getAt(1).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(0).getScore())); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2")); assertThat(searchResponse.getHits().getAt(2).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(1).getScore())); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); - searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.fieldValueFactorFunction("field"))) - .addSort("_score", SortOrder.DESC) - .get(); + searchResponse = prepareSearch("test").setQuery( + QueryBuilders.functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.fieldValueFactorFunction("field")) + ).addSort("_score", SortOrder.DESC).get(); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2")); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -429,17 +421,16 @@ public void testScoreSortDirectionWithFunctionScore() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("field"))) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("field")) + ).get(); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); assertThat(searchResponse.getHits().getAt(1).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(0).getScore())); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("2")); assertThat(searchResponse.getHits().getAt(2).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(1).getScore())); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); - searchResponse = client().prepareSearch("test") - .setQuery(functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("field"))) + searchResponse = prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("field"))) .addSort("_score", SortOrder.DESC) .get(); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); @@ -448,8 +439,7 @@ public void testScoreSortDirectionWithFunctionScore() throws Exception { assertThat(searchResponse.getHits().getAt(2).getScore(), Matchers.lessThan(searchResponse.getHits().getAt(1).getScore())); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); - searchResponse = client().prepareSearch("test") - .setQuery(functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("field"))) + searchResponse = prepareSearch("test").setQuery(functionScoreQuery(matchAllQuery(), fieldValueFactorFunction("field"))) .addSort("_score", SortOrder.DESC) .get(); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); @@ -464,11 +454,7 @@ public void testIssue2986() { client().prepareIndex("test").setId("2").setSource("{\"field1\":\"value2\"}", XContentType.JSON).get(); client().prepareIndex("test").setId("3").setSource("{\"field1\":\"value3\"}", XContentType.JSON).get(); refresh(); - SearchResponse result = client().prepareSearch("test") - .setQuery(matchAllQuery()) - .setTrackScores(true) - .addSort("field1", SortOrder.ASC) - .get(); + SearchResponse result = prepareSearch("test").setQuery(matchAllQuery()).setTrackScores(true).addSort("field1", SortOrder.ASC).get(); for (SearchHit hit : result.getHits()) { assertFalse(Float.isNaN(hit.getScore())); @@ -496,8 +482,7 @@ public void testIssue2991() { client().prepareIndex("test").setId("2").setSource("tag", "beta").get(); refresh(); - SearchResponse resp = client().prepareSearch("test") - .setSize(2) + SearchResponse resp = prepareSearch("test").setSize(2) .setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("tag").order(SortOrder.ASC)) .get(); @@ -506,8 +491,7 @@ public void testIssue2991() { assertFirstHit(resp, hasId("1")); assertSecondHit(resp, hasId("2")); - resp = client().prepareSearch("test") - .setSize(2) + resp = prepareSearch("test").setSize(2) .setQuery(matchAllQuery()) .addSort(SortBuilders.fieldSort("tag").order(SortOrder.DESC)) .get(); @@ -949,24 +933,23 @@ public void testSortMissingDates() throws IOException { format = type.equals("date") ? "strict_date_optional_time" : "strict_date_optional_time_nanos"; } - SearchResponse searchResponse = client().prepareSearch(index) - .addSort(SortBuilders.fieldSort("mydate").order(SortOrder.ASC).setFormat(format)) - .get(); + SearchResponse searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.ASC).setFormat(format) + ).get(); assertHitsInOrder(searchResponse, new String[] { "1", "2", "3" }); - searchResponse = client().prepareSearch(index) - .addSort(SortBuilders.fieldSort("mydate").order(SortOrder.ASC).missing("_first").setFormat(format)) - .get(); + searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.ASC).missing("_first").setFormat(format) + ).get(); assertHitsInOrder(searchResponse, new String[] { "3", "1", "2" }); - searchResponse = client().prepareSearch(index) - .addSort(SortBuilders.fieldSort("mydate").order(SortOrder.DESC).setFormat(format)) + searchResponse = prepareSearch(index).addSort(SortBuilders.fieldSort("mydate").order(SortOrder.DESC).setFormat(format)) .get(); assertHitsInOrder(searchResponse, new String[] { "2", "1", "3" }); - searchResponse = client().prepareSearch(index) - .addSort(SortBuilders.fieldSort("mydate").order(SortOrder.DESC).missing("_first").setFormat(format)) - .get(); + searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.DESC).missing("_first").setFormat(format) + ).get(); assertHitsInOrder(searchResponse, new String[] { "3", "2", "1" }); } } @@ -1011,32 +994,24 @@ public void testSortMissingDatesMixedTypes() throws IOException { } String index = "test*"; - SearchResponse searchResponse = client().prepareSearch(index) - .addSort(SortBuilders.fieldSort("mydate").order(SortOrder.ASC).setFormat(format).setNumericType("date_nanos")) - .addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)) - .get(); + SearchResponse searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.ASC).setFormat(format).setNumericType("date_nanos") + ).addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)).get(); assertHitsInOrder(searchResponse, new String[] { "1", "2", "4", "5", "3", "6" }); - searchResponse = client().prepareSearch(index) - .addSort( - SortBuilders.fieldSort("mydate").order(SortOrder.ASC).missing("_first").setFormat(format).setNumericType("date_nanos") - ) - .addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)) - .get(); + searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.ASC).missing("_first").setFormat(format).setNumericType("date_nanos") + ).addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)).get(); assertHitsInOrder(searchResponse, new String[] { "3", "6", "1", "2", "4", "5" }); - searchResponse = client().prepareSearch(index) - .addSort(SortBuilders.fieldSort("mydate").order(SortOrder.DESC).setFormat(format).setNumericType("date_nanos")) - .addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)) - .get(); + searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.DESC).setFormat(format).setNumericType("date_nanos") + ).addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)).get(); assertHitsInOrder(searchResponse, new String[] { "5", "4", "2", "1", "3", "6" }); - searchResponse = client().prepareSearch(index) - .addSort( - SortBuilders.fieldSort("mydate").order(SortOrder.DESC).missing("_first").setFormat(format).setNumericType("date_nanos") - ) - .addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)) - .get(); + searchResponse = prepareSearch(index).addSort( + SortBuilders.fieldSort("mydate").order(SortOrder.DESC).missing("_first").setFormat(format).setNumericType("date_nanos") + ).addSort(SortBuilders.fieldSort("other_field").order(SortOrder.ASC)).get(); assertHitsInOrder(searchResponse, new String[] { "3", "6", "5", "4", "2", "1" }); } } @@ -1736,9 +1711,9 @@ public void testSortDuelBetweenSingleShardAndMultiShardIndex() throws Exception SortOrder order = randomBoolean() ? SortOrder.ASC : SortOrder.DESC; int from = between(0, 256); int size = between(0, 256); - SearchResponse multiShardResponse = client().prepareSearch("test1").setFrom(from).setSize(size).addSort(sortField, order).get(); + SearchResponse multiShardResponse = prepareSearch("test1").setFrom(from).setSize(size).addSort(sortField, order).get(); assertNoFailures(multiShardResponse); - SearchResponse singleShardResponse = client().prepareSearch("test2").setFrom(from).setSize(size).addSort(sortField, order).get(); + SearchResponse singleShardResponse = prepareSearch("test2").setFrom(from).setSize(size).addSort(sortField, order).get(); assertNoFailures(singleShardResponse); assertThat(multiShardResponse.getHits().getTotalHits().value, equalTo(singleShardResponse.getHits().getTotalHits().value)); @@ -1763,13 +1738,13 @@ public void testCustomFormat() throws Exception { client().prepareIndex("test").setId("2").setSource("ip", "2001:db8::ff00:42:8329") ); - SearchResponse response = client().prepareSearch("test").addSort(SortBuilders.fieldSort("ip")).get(); + SearchResponse response = prepareSearch("test").addSort(SortBuilders.fieldSort("ip")).get(); assertNoFailures(response); assertEquals(2, response.getHits().getTotalHits().value); assertArrayEquals(new String[] { "192.168.1.7" }, response.getHits().getAt(0).getSortValues()); assertArrayEquals(new String[] { "2001:db8::ff00:42:8329" }, response.getHits().getAt(1).getSortValues()); - response = client().prepareSearch("test").addSort(SortBuilders.fieldSort("ip")).searchAfter(new Object[] { "192.168.1.7" }).get(); + response = prepareSearch("test").addSort(SortBuilders.fieldSort("ip")).searchAfter(new Object[] { "192.168.1.7" }).get(); assertNoFailures(response); assertEquals(2, response.getHits().getTotalHits().value); assertEquals(1, response.getHits().getHits().length); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java index 49d5bebfdf4a3..0518525486388 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java @@ -148,8 +148,7 @@ public void testDistanceSortingMVFields() throws Exception { indicesAdmin().prepareRefresh().get(); // Order: Asc - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.ASC)) .get(); @@ -162,8 +161,7 @@ public void testDistanceSortingMVFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(4).getSortValues()[0]).doubleValue(), closeTo(2029.0d, 10d)); // Order: Asc, Mode: max - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.ASC).sortMode(SortMode.MAX)) .get(); @@ -176,8 +174,7 @@ public void testDistanceSortingMVFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(4).getSortValues()[0]).doubleValue(), closeTo(8572.0d, 10d)); // Order: Desc - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.DESC)) .get(); @@ -190,8 +187,7 @@ public void testDistanceSortingMVFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(4).getSortValues()[0]).doubleValue(), closeTo(0d, 10d)); // Order: Desc, Mode: min - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.DESC).sortMode(SortMode.MIN)) .get(); @@ -203,8 +199,7 @@ public void testDistanceSortingMVFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(421.2d, 10d)); assertThat(((Number) searchResponse.getHits().getAt(4).getSortValues()[0]).doubleValue(), closeTo(0d, 10d)); - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).sortMode(SortMode.AVG).order(SortOrder.ASC)) .get(); @@ -216,8 +211,7 @@ public void testDistanceSortingMVFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(2874d, 10d)); assertThat(((Number) searchResponse.getHits().getAt(4).getSortValues()[0]).doubleValue(), closeTo(5301d, 10d)); - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).sortMode(SortMode.AVG).order(SortOrder.DESC)) .get(); @@ -230,8 +224,7 @@ public void testDistanceSortingMVFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(4).getSortValues()[0]).doubleValue(), closeTo(0d, 10d)); try { - client().prepareSearch("test") - .setQuery(matchAllQuery()) + prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).sortMode(SortMode.SUM)); fail("sum should not be supported for sorting by geo distance"); } catch (IllegalArgumentException e) { @@ -283,8 +276,7 @@ public void testDistanceSortingWithMissingGeoPoint() throws Exception { refresh(); // Order: Asc - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.ASC)) .get(); @@ -294,8 +286,7 @@ public void testDistanceSortingWithMissingGeoPoint() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(1).getSortValues()[0]).doubleValue(), equalTo(Double.POSITIVE_INFINITY)); // Order: Desc - searchResponse = client().prepareSearch("test") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("test").setQuery(matchAllQuery()) .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.DESC)) .get(); @@ -425,8 +416,7 @@ public void testDistanceSortingNestedFields() throws Exception { ); // Order: Asc - SearchResponse searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .order(SortOrder.ASC) @@ -442,8 +432,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(2029.0d, 10d)); // Order: Asc, Mode: max - searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .order(SortOrder.ASC) @@ -460,8 +449,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(8572.0d, 10d)); // Order: Desc - searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .order(SortOrder.DESC) @@ -477,8 +465,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(0d, 10d)); // Order: Desc, Mode: min - searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .order(SortOrder.DESC) @@ -494,8 +481,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).doubleValue(), closeTo(462.1d, 10d)); assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(0d, 10d)); - searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .sortMode(SortMode.AVG) @@ -511,8 +497,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).doubleValue(), closeTo(2874.0d, 10d)); assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(5301.0d, 10d)); - searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .setNestedSort(new NestedSortBuilder("branches")) @@ -528,8 +513,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).doubleValue(), closeTo(1157.0d, 10d)); assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), closeTo(0d, 10d)); - searchResponse = client().prepareSearch("companies") - .setQuery(matchAllQuery()) + searchResponse = prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .setNestedSort(new NestedSortBuilder("branches").setFilter(termQuery("branches.name", "brooklyn"))) @@ -546,8 +530,7 @@ public void testDistanceSortingNestedFields() throws Exception { assertThat(((Number) searchResponse.getHits().getAt(3).getSortValues()[0]).doubleValue(), equalTo(Double.POSITIVE_INFINITY)); try { - client().prepareSearch("companies") - .setQuery(matchAllQuery()) + prepareSearch("companies").setQuery(matchAllQuery()) .addSort( SortBuilders.geoDistanceSort("branches.location", 40.7143528, -74.0059731) .sortMode(SortMode.SUM) @@ -584,8 +567,7 @@ public void testGeoDistanceFilter() throws IOException { client().prepareGet("locations", "1").get(); assertHitCount( - client().prepareSearch("locations") - .setQuery(QueryBuilders.matchAllQuery()) + prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()) .setPostFilter(QueryBuilders.geoDistanceQuery("pin").geoDistance(GeoDistance.ARC).point(lat, lon).distance("1m")), 1 ); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/source/MetadataFetchingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/source/MetadataFetchingIT.java index 710905cf3302d..1860082c833ad 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/source/MetadataFetchingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/source/MetadataFetchingIT.java @@ -34,12 +34,12 @@ public void testSimple() { client().prepareIndex("test").setId("1").setSource("field", "value").get(); refresh(); - SearchResponse response = client().prepareSearch("test").storedFields("_none_").setFetchSource(false).setVersion(true).get(); + SearchResponse response = prepareSearch("test").storedFields("_none_").setFetchSource(false).setVersion(true).get(); assertThat(response.getHits().getAt(0).getId(), nullValue()); assertThat(response.getHits().getAt(0).getSourceAsString(), nullValue()); assertThat(response.getHits().getAt(0).getVersion(), notNullValue()); - response = client().prepareSearch("test").storedFields("_none_").get(); + response = prepareSearch("test").storedFields("_none_").get(); assertThat(response.getHits().getAt(0).getId(), nullValue()); assertThat(response.getHits().getAt(0).getSourceAsString(), nullValue()); } @@ -50,8 +50,7 @@ public void testInnerHits() { client().prepareIndex("test").setId("1").setSource("field", "value", "nested", Collections.singletonMap("title", "foo")).get(); refresh(); - SearchResponse response = client().prepareSearch("test") - .storedFields("_none_") + SearchResponse response = prepareSearch("test").storedFields("_none_") .setFetchSource(false) .setQuery( new NestedQueryBuilder("nested", new TermQueryBuilder("nested.title", "foo"), ScoreMode.Total).innerHit( @@ -77,12 +76,12 @@ public void testWithRouting() { client().prepareIndex("test").setId("1").setSource("field", "value").setRouting("toto").get(); refresh(); - SearchResponse response = client().prepareSearch("test").storedFields("_none_").setFetchSource(false).get(); + SearchResponse response = prepareSearch("test").storedFields("_none_").setFetchSource(false).get(); assertThat(response.getHits().getAt(0).getId(), nullValue()); assertThat(response.getHits().getAt(0).field("_routing"), nullValue()); assertThat(response.getHits().getAt(0).getSourceAsString(), nullValue()); - response = client().prepareSearch("test").storedFields("_none_").get(); + response = prepareSearch("test").storedFields("_none_").get(); assertThat(response.getHits().getAt(0).getId(), nullValue()); assertThat(response.getHits().getAt(0).getSourceAsString(), nullValue()); } @@ -97,7 +96,7 @@ public void testInvalid() { { SearchPhaseExecutionException exc = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test").setFetchSource(true).storedFields("_none_").get() + () -> prepareSearch("test").setFetchSource(true).storedFields("_none_").get() ); Throwable rootCause = ExceptionsHelper.unwrap(exc, SearchException.class); assertNotNull(rootCause); @@ -107,7 +106,7 @@ public void testInvalid() { { SearchPhaseExecutionException exc = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch("test").storedFields("_none_").addFetchField("field").get() + () -> prepareSearch("test").storedFields("_none_").addFetchField("field").get() ); Throwable rootCause = ExceptionsHelper.unwrap(exc, SearchException.class); assertNotNull(rootCause); @@ -117,14 +116,14 @@ public void testInvalid() { { IllegalArgumentException exc = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("test").storedFields("_none_", "field1").setVersion(true).get() + () -> prepareSearch("test").storedFields("_none_", "field1").setVersion(true).get() ); assertThat(exc.getMessage(), equalTo("cannot combine _none_ with other fields")); } { IllegalArgumentException exc = expectThrows( IllegalArgumentException.class, - () -> client().prepareSearch("test").storedFields("_none_").storedFields("field1").setVersion(true).get() + () -> prepareSearch("test").storedFields("_none_").storedFields("field1").setVersion(true).get() ); assertThat(exc.getMessage(), equalTo("cannot combine _none_ with other fields")); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java index ac6c1a7c2d554..3fcbc5cf4add6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/source/SourceFetchingIT.java @@ -23,13 +23,13 @@ public void testSourceDefaultBehavior() { indexDoc("test", "1", "field", "value"); refresh(); - SearchResponse response = client().prepareSearch("test").get(); + SearchResponse response = prepareSearch("test").get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); - response = client().prepareSearch("test").addStoredField("bla").get(); + response = prepareSearch("test").addStoredField("bla").get(); assertThat(response.getHits().getAt(0).getSourceAsString(), nullValue()); - response = client().prepareSearch("test").addStoredField("_source").get(); + response = prepareSearch("test").addStoredField("_source").get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); } @@ -41,22 +41,22 @@ public void testSourceFiltering() { client().prepareIndex("test").setId("1").setSource("field1", "value", "field2", "value2").get(); refresh(); - SearchResponse response = client().prepareSearch("test").setFetchSource(false).get(); + SearchResponse response = prepareSearch("test").setFetchSource(false).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), nullValue()); - response = client().prepareSearch("test").setFetchSource(true).get(); + response = prepareSearch("test").setFetchSource(true).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); - response = client().prepareSearch("test").setFetchSource("field1", null).get(); + response = prepareSearch("test").setFetchSource("field1", null).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); assertThat((String) response.getHits().getAt(0).getSourceAsMap().get("field1"), equalTo("value")); - response = client().prepareSearch("test").setFetchSource("hello", null).get(); + response = prepareSearch("test").setFetchSource("hello", null).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(0)); - response = client().prepareSearch("test").setFetchSource(new String[] { "*" }, new String[] { "field2" }).get(); + response = prepareSearch("test").setFetchSource(new String[] { "*" }, new String[] { "field2" }).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); assertThat((String) response.getHits().getAt(0).getSourceAsMap().get("field1"), equalTo("value")); @@ -74,12 +74,12 @@ public void testSourceWithWildcardFiltering() { client().prepareIndex("test").setId("1").setSource("field", "value").get(); refresh(); - SearchResponse response = client().prepareSearch("test").setFetchSource(new String[] { "*.notexisting", "field" }, null).get(); + SearchResponse response = prepareSearch("test").setFetchSource(new String[] { "*.notexisting", "field" }, null).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); assertThat((String) response.getHits().getAt(0).getSourceAsMap().get("field"), equalTo("value")); - response = client().prepareSearch("test").setFetchSource(new String[] { "field.notexisting.*", "field" }, null).get(); + response = prepareSearch("test").setFetchSource(new String[] { "field.notexisting.*", "field" }, null).get(); assertThat(response.getHits().getAt(0).getSourceAsString(), notNullValue()); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); assertThat((String) response.getHits().getAt(0).getSourceAsMap().get("field"), equalTo("value")); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java index 93a2000cda7a4..c782b01742a67 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java @@ -159,19 +159,17 @@ public void testTextAndGlobalText() throws Exception { } indexRandom(true, indexRequestBuilders); CompletionSuggestionBuilder noText = SuggestBuilders.completionSuggestion(FIELD); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", noText).setGlobalText("sugg")) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", noText).setGlobalText("sugg") + ).get(); assertSuggestions(searchResponse, "foo", "suggestion10", "suggestion9", "suggestion8", "suggestion7", "suggestion6"); CompletionSuggestionBuilder withText = SuggestBuilders.completionSuggestion(FIELD).text("sugg"); - searchResponse = client().prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", withText)).get(); + searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", withText)).get(); assertSuggestions(searchResponse, "foo", "suggestion10", "suggestion9", "suggestion8", "suggestion7", "suggestion6"); // test that suggestion text takes precedence over global text - searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", withText).setGlobalText("bogus")) - .get(); + searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", withText).setGlobalText("bogus")).get(); assertSuggestions(searchResponse, "foo", "suggestion10", "suggestion9", "suggestion8", "suggestion7", "suggestion6"); } @@ -280,7 +278,7 @@ public void testSuggestDocument() throws Exception { indexRandom(true, indexRequestBuilders); CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg").size(numDocs); - SearchResponse searchResponse = client().prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix)).get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix)).get(); CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo"); CompletionSuggestion.Entry options = completionSuggestion.getEntries().get(0); assertThat(options.getOptions().size(), equalTo(numDocs)); @@ -316,8 +314,7 @@ public void testSuggestDocumentNoSource() throws Exception { indexRandom(true, indexRequestBuilders); CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg").size(numDocs); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", prefix)) + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix)) .setFetchSource(false) .get(); CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo"); @@ -357,8 +354,7 @@ public void testSuggestDocumentSourceFiltering() throws Exception { indexRandom(true, indexRequestBuilders); CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg").size(numDocs); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", prefix)) + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix)) .setFetchSource("a", "b") .get(); CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo"); @@ -385,8 +381,7 @@ public void testSuggestEmptyIndex() throws IOException { createIndexAndMapping(mapping); CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("v"); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", prefix)) + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix)) .setFetchSource("a", "b") .get(); Suggest suggest = searchResponse.getSuggest(); @@ -466,9 +461,9 @@ public void testThatWeightCanBeAString() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("testSuggestions", new CompletionSuggestionBuilder(FIELD).text("test").size(10))) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("testSuggestions", new CompletionSuggestionBuilder(FIELD).text("test").size(10)) + ).get(); assertSuggestions(searchResponse, "testSuggestions", "testing"); Suggest.Suggestion.Entry.Option option = searchResponse.getSuggest() @@ -696,11 +691,9 @@ public void testThatUpgradeToMultiFieldsWorks() throws Exception { .get(); assertThat(putMappingResponse.isAcknowledged(), is(true)); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion("suggs", SuggestBuilders.completionSuggestion(FIELD + ".suggest").text("f").size(10)) - ) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("suggs", SuggestBuilders.completionSuggestion(FIELD + ".suggest").text("f").size(10)) + ).get(); assertSuggestions(searchResponse, "suggs"); client().prepareIndex(INDEX) @@ -710,11 +703,9 @@ public void testThatUpgradeToMultiFieldsWorks() throws Exception { .get(); ensureGreen(INDEX); - SearchResponse afterReindexingResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion("suggs", SuggestBuilders.completionSuggestion(FIELD + ".suggest").text("f").size(10)) - ) - .get(); + SearchResponse afterReindexingResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("suggs", SuggestBuilders.completionSuggestion(FIELD + ".suggest").text("f").size(10)) + ).get(); assertSuggestions(afterReindexingResponse, "suggs", "Foo Fighters"); } @@ -730,19 +721,14 @@ public void testThatFuzzySuggesterWorks() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("Nirv").size(10))) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("Nirv").size(10)) + ).get(); assertSuggestions(searchResponse, false, "foo", "Nirvana"); - searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD).prefix("Nirw", Fuzziness.ONE).size(10) - ) - ) - .get(); + searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("Nirw", Fuzziness.ONE).size(10)) + ).get(); assertSuggestions(searchResponse, false, "foo", "Nirvana"); } @@ -759,25 +745,15 @@ public void testThatFuzzySuggesterSupportsEditDistances() throws Exception { refresh(); // edit distance 1 - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD).prefix("Norw", Fuzziness.ONE).size(10) - ) - ) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("Norw", Fuzziness.ONE).size(10)) + ).get(); assertSuggestions(searchResponse, false, "foo"); // edit distance 2 - searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD).prefix("Norw", Fuzziness.TWO).size(10) - ) - ) - .get(); + searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("Norw", Fuzziness.TWO).size(10)) + ).get(); assertSuggestions(searchResponse, false, "foo", "Nirvana"); } @@ -793,26 +769,17 @@ public void testThatFuzzySuggesterSupportsTranspositions() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD) - .prefix("Nriv", FuzzyOptions.builder().setTranspositions(false).build()) - .size(10) - ) + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion( + "foo", + SuggestBuilders.completionSuggestion(FIELD).prefix("Nriv", FuzzyOptions.builder().setTranspositions(false).build()).size(10) ) - .get(); + ).get(); assertSuggestions(searchResponse, false, "foo"); - searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD).prefix("Nriv", Fuzziness.ONE).size(10) - ) - ) - .get(); + searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("Nriv", Fuzziness.ONE).size(10)) + ).get(); assertSuggestions(searchResponse, false, "foo", "Nirvana"); } @@ -828,28 +795,20 @@ public void testThatFuzzySuggesterSupportsMinPrefixLength() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD) - .prefix("Nriva", FuzzyOptions.builder().setFuzzyMinLength(6).build()) - .size(10) - ) + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion( + "foo", + SuggestBuilders.completionSuggestion(FIELD).prefix("Nriva", FuzzyOptions.builder().setFuzzyMinLength(6).build()).size(10) ) - .get(); + ).get(); assertSuggestions(searchResponse, false, "foo"); - searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD) - .prefix("Nrivan", FuzzyOptions.builder().setFuzzyMinLength(6).build()) - .size(10) - ) + searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion( + "foo", + SuggestBuilders.completionSuggestion(FIELD).prefix("Nrivan", FuzzyOptions.builder().setFuzzyMinLength(6).build()).size(10) ) - .get(); + ).get(); assertSuggestions(searchResponse, false, "foo", "Nirvana"); } @@ -865,28 +824,20 @@ public void testThatFuzzySuggesterSupportsNonPrefixLength() throws Exception { refresh(); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD) - .prefix("Nirw", FuzzyOptions.builder().setFuzzyPrefixLength(4).build()) - .size(10) - ) + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion( + "foo", + SuggestBuilders.completionSuggestion(FIELD).prefix("Nirw", FuzzyOptions.builder().setFuzzyPrefixLength(4).build()).size(10) ) - .get(); + ).get(); assertSuggestions(searchResponse, false, "foo"); - searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion( - "foo", - SuggestBuilders.completionSuggestion(FIELD) - .prefix("Nirvo", FuzzyOptions.builder().setFuzzyPrefixLength(4).build()) - .size(10) - ) + searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion( + "foo", + SuggestBuilders.completionSuggestion(FIELD).prefix("Nirvo", FuzzyOptions.builder().setFuzzyPrefixLength(4).build()).size(10) ) - .get(); + ).get(); assertSuggestions(searchResponse, false, "foo", "Nirvana"); } @@ -906,8 +857,7 @@ public void testThatFuzzySuggesterIsUnicodeAware() throws Exception { .prefix("öööи", FuzzyOptions.builder().setUnicodeAware(true).build()) .size(10); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", completionSuggestionBuilder)) + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", completionSuggestionBuilder)) .get(); assertSuggestions(searchResponse, false, "foo", "ööööö"); @@ -915,18 +865,14 @@ public void testThatFuzzySuggesterIsUnicodeAware() throws Exception { completionSuggestionBuilder = SuggestBuilders.completionSuggestion(FIELD) .prefix("öööи", FuzzyOptions.builder().setUnicodeAware(false).build()) .size(10); - searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", completionSuggestionBuilder)) - .get(); + searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", completionSuggestionBuilder)).get(); assertSuggestions(searchResponse, false, "foo"); // increasing edit distance instead of unicode awareness works again, as this is only a single character completionSuggestionBuilder = SuggestBuilders.completionSuggestion(FIELD) .prefix("öööи", FuzzyOptions.builder().setUnicodeAware(false).setFuzziness(Fuzziness.TWO).build()) .size(10); - searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", completionSuggestionBuilder)) - .get(); + searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", completionSuggestionBuilder)).get(); assertSuggestions(searchResponse, false, "foo", "ööööö"); } @@ -967,12 +913,11 @@ public void testThatStatsAreWorking() throws Exception { refresh(); ensureGreen(); // load the fst index into ram - client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("f"))) - .get(); - client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(otherField).prefix("f"))) + prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(FIELD).prefix("f"))) .get(); + prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("foo", SuggestBuilders.completionSuggestion(otherField).prefix("f")) + ).get(); // Get all stats IndicesStatsResponse indicesStatsResponse = indicesAdmin().prepareStats(INDEX).setIndices(INDEX).setCompletion(true).get(); @@ -1020,7 +965,7 @@ public void testThatSortingOnCompletionFieldReturnsUsefulException() throws Exce SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch(INDEX).addSort(new FieldSortBuilder(FIELD)).get() + () -> prepareSearch(INDEX).addSort(new FieldSortBuilder(FIELD)).get() ); assertThat(e.status().getStatus(), is(400)); assertThat(e.toString(), containsString("Fielddata is not supported on field [" + FIELD + "] of type [completion]")); @@ -1143,15 +1088,14 @@ public void testSkipDuplicates() throws Exception { .skipDuplicates(true) .size(numUnique); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion("suggestions", completionSuggestionBuilder)) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion("suggestions", completionSuggestionBuilder) + ).get(); assertSuggestions(searchResponse, true, "suggestions", expected); } public void assertSuggestions(String suggestionName, SuggestionBuilder suggestBuilder, String... suggestions) { - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion(suggestionName, suggestBuilder)) + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion(suggestionName, suggestBuilder)) .get(); assertSuggestions(searchResponse, suggestionName, suggestions); } @@ -1164,11 +1108,9 @@ public void assertSuggestions(String suggestion, String... suggestions) { public void assertSuggestionsNotInOrder(String suggestString, String... suggestions) { String suggestionName = RandomStrings.randomAsciiLettersOfLength(random(), 10); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest( - new SuggestBuilder().addSuggestion(suggestionName, SuggestBuilders.completionSuggestion(FIELD).text(suggestString).size(10)) - ) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest( + new SuggestBuilder().addSuggestion(suggestionName, SuggestBuilders.completionSuggestion(FIELD).text(suggestString).size(10)) + ).get(); assertSuggestions(searchResponse, false, suggestionName, suggestions); } @@ -1309,7 +1251,7 @@ public void testPrunedSegments() throws IOException { refresh(); assertSuggestions("b"); - assertThat(2L, equalTo(client().prepareSearch(INDEX).setSize(0).get().getHits().getTotalHits().value)); + assertThat(2L, equalTo(prepareSearch(INDEX).setSize(0).get().getHits().getTotalHits().value)); for (IndexShardSegments seg : indicesAdmin().prepareSegments().get().getIndices().get(INDEX)) { ShardSegments[] shards = seg.shards(); for (ShardSegments shardSegments : shards) { @@ -1411,11 +1353,9 @@ public void testIssue5930() throws IOException { SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch(INDEX) - .addAggregation( - AggregationBuilders.terms("suggest_agg").field(FIELD).collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get() + () -> prepareSearch(INDEX).addAggregation( + AggregationBuilders.terms("suggest_agg").field(FIELD).collectMode(randomFrom(SubAggCollectionMode.values())) + ).get() ); assertThat(e.toString(), containsString("Fielddata is not supported on field [" + FIELD + "] of type [completion]")); } @@ -1492,8 +1432,7 @@ public void testSuggestOnlyExplain() throws Exception { } indexRandom(true, indexRequestBuilders); CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg"); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .setExplain(true) + SearchResponse searchResponse = prepareSearch(INDEX).setExplain(true) .suggest(new SuggestBuilder().addSuggestion("foo", prefix)) .get(); assertSuggestions(searchResponse, "foo", "suggestion10", "suggestion9", "suggestion8", "suggestion7", "suggestion6"); @@ -1528,8 +1467,7 @@ public void testCompletionWithCollapse() throws Exception { indicesAdmin().prepareRefresh(index).get(); CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(suggestField).prefix("sug").size(1); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse searchResponse = prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()) .setFrom(1) .setSize(1) .setCollapse(new CollapseBuilder("collapse_field")) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java index 7b2ba7cb46770..ea4c21cc06fe5 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java @@ -619,9 +619,7 @@ public void testGeoField() throws Exception { Collections.singletonList(GeoQueryContext.builder().setGeoPoint(new GeoPoint(52.52, 13.4)).build()) ) ); - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion(suggestionName, context)) - .get(); + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion(suggestionName, context)).get(); assertEquals(searchResponse.getSuggest().size(), 1); assertEquals( @@ -671,8 +669,7 @@ public void testSkipDuplicatesWithContexts() throws Exception { } public void assertSuggestions(String suggestionName, SuggestionBuilder suggestBuilder, String... suggestions) { - SearchResponse searchResponse = client().prepareSearch(INDEX) - .suggest(new SuggestBuilder().addSuggestion(suggestionName, suggestBuilder)) + SearchResponse searchResponse = prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion(suggestionName, suggestBuilder)) .get(); CompletionSuggestSearchIT.assertSuggestions(searchResponse, suggestionName, suggestions); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/ConcurrentSnapshotsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/ConcurrentSnapshotsIT.java index 96a2fe69732df..d68301a310722 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/ConcurrentSnapshotsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/ConcurrentSnapshotsIT.java @@ -941,7 +941,7 @@ public void testQueuedSnapshotsWaitingForShardReady() throws Exception { indexDoc(testIndex, Integer.toString(i), "foo", "bar" + i); } refresh(); - assertThat(client().prepareSearch(testIndex).setSize(0).get().getHits().getTotalHits().value, equalTo(100L)); + assertThat(prepareSearch(testIndex).setSize(0).get().getHits().getTotalHits().value, equalTo(100L)); logger.info("--> start relocations"); allowNodes(testIndex, 1); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java index 8a617a1243ac1..7f5cacdfc935a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -1615,7 +1615,7 @@ public void testRestoreSnapshotWithCorruptedGlobalState() throws Exception { assertThat(restoreSnapshotResponse.getRestoreInfo().successfulShards(), equalTo(snapshotInfo.successfulShards())); ensureGreen("test-idx-1", "test-idx-2"); - assertHitCount(client().prepareSearch("test-idx-*").setSize(0), 3); + assertHitCount(prepareSearch("test-idx-*").setSize(0), 3); } /** @@ -1684,7 +1684,7 @@ public void testRestoreSnapshotWithCorruptedIndexMetadata() throws Exception { ensureGreen(); for (Map.Entry entry : nbDocsPerIndex.entrySet()) { if (isRestorableIndex.test(entry.getKey())) { - assertHitCount(client().prepareSearch(entry.getKey()).setSize(0), entry.getValue().longValue()); + assertHitCount(prepareSearch(entry.getKey()).setSize(0), entry.getValue().longValue()); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java b/server/src/internalClusterTest/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java index d319eab2b192b..d6dad537afaea 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java @@ -63,8 +63,8 @@ public void testThreadNames() throws Exception { indexRandom(true, builders); int numSearches = randomIntBetween(2, 100); for (int i = 0; i < numSearches; i++) { - assertNoFailures(client().prepareSearch("idx").setQuery(QueryBuilders.termQuery("str_value", "s" + i))); - assertNoFailures(client().prepareSearch("idx").setQuery(QueryBuilders.termQuery("l_value", i))); + assertNoFailures(prepareSearch("idx").setQuery(QueryBuilders.termQuery("str_value", "s" + i))); + assertNoFailures(prepareSearch("idx").setQuery(QueryBuilders.termQuery("l_value", i))); } Set threadNames = new HashSet<>(); for (long l : threadBean.getAllThreadIds()) { diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java index 8b7731c6b3505..f5912872e004a 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/ShardSizeTestCase.java @@ -78,11 +78,11 @@ protected void indexData() throws Exception { indexRandom(true, docs); - SearchResponse resp = client().prepareSearch("idx").setRouting(routing1).setQuery(matchAllQuery()).get(); + SearchResponse resp = prepareSearch("idx").setRouting(routing1).setQuery(matchAllQuery()).get(); assertNoFailures(resp); long totalOnOne = resp.getHits().getTotalHits().value; assertThat(totalOnOne, is(15L)); - resp = client().prepareSearch("idx").setRouting(routing2).setQuery(matchAllQuery()).get(); + resp = prepareSearch("idx").setRouting(routing2).setQuery(matchAllQuery()).get(); assertNoFailures(resp); long totalOnTwo = resp.getHits().getTotalHits().value; assertThat(totalOnTwo, is(12L)); diff --git a/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java b/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java index 9ef137cefeeb3..57fe0bfe3a9e3 100644 --- a/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java +++ b/server/src/test/java/org/elasticsearch/test/search/aggregations/bucket/SharedSignificantTermsTestMethods.java @@ -27,6 +27,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.significantTerms; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.ESIntegTestCase.client; +import static org.elasticsearch.test.ESIntegTestCase.prepareSearch; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @@ -47,10 +48,9 @@ public static void aggregateAndCheckFromSeveralShards(ESIntegTestCase testCase) } private static void checkSignificantTermsAggregationCorrect(ESIntegTestCase testCase) { - SearchResponse response = client().prepareSearch(INDEX_NAME) - .addAggregation(terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD))) - .execute() - .actionGet(); + SearchResponse response = prepareSearch(INDEX_NAME).addAggregation( + terms("class").field(CLASS_FIELD).subAggregation(significantTerms("sig_terms").field(TEXT_FIELD)) + ).execute().actionGet(); assertNoFailures(response); StringTerms classes = response.getAggregations().get("class"); Assert.assertThat(classes.getBuckets().size(), equalTo(2)); diff --git a/test/external-modules/seek-tracking-directory/src/internalClusterTest/java/org/elasticsearch/test/seektracker/SeekTrackerPluginIT.java b/test/external-modules/seek-tracking-directory/src/internalClusterTest/java/org/elasticsearch/test/seektracker/SeekTrackerPluginIT.java index 784730efff999..d9c239905f343 100644 --- a/test/external-modules/seek-tracking-directory/src/internalClusterTest/java/org/elasticsearch/test/seektracker/SeekTrackerPluginIT.java +++ b/test/external-modules/seek-tracking-directory/src/internalClusterTest/java/org/elasticsearch/test/seektracker/SeekTrackerPluginIT.java @@ -45,7 +45,7 @@ public void testSeekTrackerPlugin() throws InterruptedException { } indexRandom(true, docs); - client().prepareSearch("index").setQuery(QueryBuilders.termQuery("field", "term2")).get(); + prepareSearch("index").setQuery(QueryBuilders.termQuery("field", "term2")).get(); SeekStatsResponse response = client().execute(SeekTrackerPlugin.SEEK_STATS_ACTION, new SeekStatsRequest("index")).actionGet(); List shardSeekStats = response.getSeekStats().get("index"); diff --git a/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java index 4a9a7cba2abed..40da3fb0432d3 100644 --- a/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java @@ -139,7 +139,7 @@ protected void checkTransientErrorsDuringRecoveryAreRetried(String recoveryActio assertFalse(stateResponse.getState().getRoutingNodes().node(blueNodeId).isEmpty()); - assertHitCount(client().prepareSearch(indexName), numDocs); + assertHitCount(prepareSearch(indexName), numDocs); logger.info("--> will temporarily interrupt recovery action between blue & red on [{}]", recoveryActionToBlock); @@ -241,7 +241,7 @@ public void checkDisconnectsWhileRecovering(String recoveryActionToBlock) throws assertFalse(stateResponse.getState().getRoutingNodes().node(blueNodeId).isEmpty()); - assertHitCount(client().prepareSearch(indexName), numDocs); + assertHitCount(prepareSearch(indexName), numDocs); final boolean dropRequests = randomBoolean(); logger.info("--> will {} between blue & red on [{}]", dropRequests ? "drop requests" : "break connection", recoveryActionToBlock); @@ -342,7 +342,7 @@ public void checkDisconnectsDuringRecovery(boolean useSnapshotBasedRecoveries) t } indexRandom(true, requests); ensureSearchable(indexName); - assertHitCount(client().prepareSearch(indexName), numDocs); + assertHitCount(prepareSearch(indexName), numDocs); if (useSnapshotBasedRecoveries) { createSnapshotThatCanBeUsedDuringRecovery(indexName); @@ -457,7 +457,7 @@ public void sendRequest( } for (int i = 0; i < 10; i++) { - assertHitCount(client().prepareSearch(indexName), numDocs); + assertHitCount(prepareSearch(indexName), numDocs); } } diff --git a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java index 61be2eb433618..cfff900e372f5 100644 --- a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java @@ -295,7 +295,7 @@ protected void testSnapshotAndRestore(boolean recreateRepositoryBeforeRestore) t docCounts[i] = iterations(10, 1000); logger.info("--> create random index {} with {} records", indexNames[i], docCounts[i]); addRandomDocuments(indexNames[i], docCounts[i]); - assertHitCount(client().prepareSearch(indexNames[i]).setSize(0), docCounts[i]); + assertHitCount(prepareSearch(indexNames[i]).setSize(0), docCounts[i]); } final String snapshotName = randomName(); @@ -319,7 +319,7 @@ protected void testSnapshotAndRestore(boolean recreateRepositoryBeforeRestore) t logger.info("--> add random documents to {}", index); addRandomDocuments(index, randomIntBetween(10, 1000)); } else { - int docCount = (int) client().prepareSearch(index).setSize(0).get().getHits().getTotalHits().value; + int docCount = (int) prepareSearch(index).setSize(0).get().getHits().getTotalHits().value; int deleteCount = randomIntBetween(1, docCount); logger.info("--> delete {} random documents from {}", deleteCount, index); for (int i = 0; i < deleteCount; i++) { @@ -348,7 +348,7 @@ protected void testSnapshotAndRestore(boolean recreateRepositoryBeforeRestore) t ensureGreen(TimeValue.timeValueSeconds(120)); for (int i = 0; i < indexCount; i++) { - assertHitCount(client().prepareSearch(indexNames[i]).setSize(0), docCounts[i]); + assertHitCount(prepareSearch(indexNames[i]).setSize(0), docCounts[i]); } logger.info("--> delete snapshot {}:{}", repoName, snapshotName); @@ -392,7 +392,7 @@ public void testMultipleSnapshotAndRollback() throws Exception { addRandomDocuments(indexName, docCount); } // Check number of documents in this iteration - docCounts[i] = (int) client().prepareSearch(indexName).setSize(0).get().getHits().getTotalHits().value; + docCounts[i] = (int) prepareSearch(indexName).setSize(0).get().getHits().getTotalHits().value; logger.info("--> create snapshot {}:{} with {} documents", repoName, snapshotName + "-" + i, docCounts[i]); assertSuccessfulSnapshot( clusterAdmin().prepareCreateSnapshot(repoName, snapshotName + "-" + i).setWaitForCompletion(true).setIndices(indexName) @@ -415,7 +415,7 @@ public void testMultipleSnapshotAndRollback() throws Exception { ); ensureGreen(); - assertHitCount(client().prepareSearch(indexName).setSize(0), docCounts[iterationToRestore]); + assertHitCount(prepareSearch(indexName).setSize(0), docCounts[iterationToRestore]); } for (int i = 0; i < iterationCount; i++) { diff --git a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESFsBasedRepositoryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESFsBasedRepositoryIntegTestCase.java index c51869d5b0797..8e94b3fa41fcf 100644 --- a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESFsBasedRepositoryIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESFsBasedRepositoryIntegTestCase.java @@ -46,7 +46,7 @@ public void testMissingDirectoriesNotCreatedInReadonlyRepository() throws IOExce int docCount = iterations(10, 1000); logger.info("--> create random index {} with {} records", indexName, docCount); addRandomDocuments(indexName, docCount); - assertHitCount(client().prepareSearch(indexName).setSize(0), docCount); + assertHitCount(prepareSearch(indexName).setSize(0), docCount); final String snapshotName = randomName(); logger.info("--> create snapshot {}:{}", repoName, snapshotName); diff --git a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESMockAPIBasedRepositoryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESMockAPIBasedRepositoryIntegTestCase.java index a5bed4f75a7b9..1e66dd061d9b5 100644 --- a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESMockAPIBasedRepositoryIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESMockAPIBasedRepositoryIntegTestCase.java @@ -166,7 +166,7 @@ public final void testSnapshotWithLargeSegmentFiles() throws Exception { flushAndRefresh(index); ForceMergeResponse forceMerge = client().admin().indices().prepareForceMerge(index).setFlush(true).setMaxNumSegments(1).get(); assertThat(forceMerge.getSuccessfulShards(), equalTo(1)); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); final String snapshot = "snapshot"; assertSuccessfulSnapshot(clusterAdmin().prepareCreateSnapshot(repository, snapshot).setWaitForCompletion(true).setIndices(index)); @@ -175,7 +175,7 @@ public final void testSnapshotWithLargeSegmentFiles() throws Exception { assertSuccessfulRestore(clusterAdmin().prepareRestoreSnapshot(repository, snapshot).setWaitForCompletion(true)); ensureGreen(index); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); assertAcked(clusterAdmin().prepareDeleteSnapshot(repository, snapshot).get()); } @@ -193,7 +193,7 @@ public void testRequestStats() throws Exception { flushAndRefresh(index); ForceMergeResponse forceMerge = client().admin().indices().prepareForceMerge(index).setFlush(true).setMaxNumSegments(1).get(); assertThat(forceMerge.getSuccessfulShards(), equalTo(1)); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); final String snapshot = "snapshot"; assertSuccessfulSnapshot(clusterAdmin().prepareCreateSnapshot(repository, snapshot).setWaitForCompletion(true).setIndices(index)); @@ -202,7 +202,7 @@ public void testRequestStats() throws Exception { assertSuccessfulRestore(clusterAdmin().prepareRestoreSnapshot(repository, snapshot).setWaitForCompletion(true)); ensureGreen(index); - assertHitCount(client().prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); + assertHitCount(prepareSearch(index).setSize(0).setTrackTotalHits(true), nbDocs); assertAcked(clusterAdmin().prepareDeleteSnapshot(repository, snapshot).get()); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java index 9bfe15cb7f0b9..ea94f342a953e 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/AbstractTermsTestCase.java @@ -33,14 +33,12 @@ private static long sumOfDocCounts(Terms terms) { public void testOtherDocCount(String... fieldNames) { for (String fieldName : fieldNames) { - SearchResponse allTerms = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(fieldName) - .size(10000) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse allTerms = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(fieldName) + .size(10000) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(allTerms); Terms terms = allTerms.getAggregations().get("terms"); @@ -50,15 +48,13 @@ public void testOtherDocCount(String... fieldNames) { for (int size = 1; size < totalNumTerms + 2; size += randomIntBetween(1, 5)) { for (int shardSize = size; shardSize <= totalNumTerms + 2; shardSize += randomIntBetween(1, 5)) { - SearchResponse resp = client().prepareSearch("idx") - .addAggregation( - new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) - .field(fieldName) - .size(size) - .shardSize(shardSize) - .collectMode(randomFrom(SubAggCollectionMode.values())) - ) - .get(); + SearchResponse resp = prepareSearch("idx").addAggregation( + new TermsAggregationBuilder("terms").executionHint(randomExecutionHint()) + .field(fieldName) + .size(size) + .shardSize(shardSize) + .collectMode(randomFrom(SubAggCollectionMode.values())) + ).get(); assertNoFailures(resp); terms = resp.getAggregations().get("terms"); assertEquals(Math.min(size, totalNumTerms), terms.getBuckets().size()); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java index 571e269bf532d..7b4e591051e61 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/AbstractGeoTestCase.java @@ -229,8 +229,7 @@ public void setupSuiteScopeCluster() throws Exception { // value for NUMBER_FIELD_NAME. This will check that after random indexing each document only has 1 value for // NUMBER_FIELD_NAME and it is the correct value. Following this initial change its seems that this call was getting // more that 2000 hits (actual value was 2059) so now it will also check to ensure all hits have the correct index and type. - SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) - .addStoredField(NUMBER_FIELD_NAME) + SearchResponse response = prepareSearch(HIGH_CARD_IDX_NAME).addStoredField(NUMBER_FIELD_NAME) .addSort(SortBuilders.fieldSort(NUMBER_FIELD_NAME).order(SortOrder.ASC)) .setSize(5000) .get(); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java index 5af696fb644d3..45ec7c00bd441 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java @@ -34,8 +34,7 @@ public abstract class CentroidAggregationTestBase extends AbstractGeoTestCase { protected abstract ValuesSourceAggregationBuilder centroidAgg(String name); public void testEmptyAggregation() { - SearchResponse response = client().prepareSearch(EMPTY_IDX_NAME) - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch(EMPTY_IDX_NAME).setQuery(matchAllQuery()) .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); assertNoFailures(response); @@ -49,8 +48,7 @@ public void testEmptyAggregation() { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch(UNMAPPED_IDX_NAME) - .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) + SearchResponse response = prepareSearch(UNMAPPED_IDX_NAME).addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); assertNoFailures(response); @@ -75,8 +73,7 @@ public void testPartiallyUnmapped() { } public void testSingleValuedField() { - SearchResponse response = client().prepareSearch(IDX_NAME) - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch(IDX_NAME).setQuery(matchAllQuery()) .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)) .get(); assertNoFailures(response); @@ -89,8 +86,7 @@ public void testSingleValuedField() { } public void testSingleValueFieldGetProperty() { - SearchResponse response = client().prepareSearch(IDX_NAME) - .setQuery(matchAllQuery()) + SearchResponse response = prepareSearch(IDX_NAME).setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))) .get(); assertNoFailures(response); @@ -116,8 +112,7 @@ public void testSingleValueFieldGetProperty() { } public void testMultiValuedField() throws Exception { - SearchResponse searchResponse = client().prepareSearch(IDX_NAME) - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch(IDX_NAME).setQuery(matchAllQuery()) .addAggregation(centroidAgg(aggName()).field(MULTI_VALUED_FIELD_NAME)) .get(); assertNoFailures(searchResponse); diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java index 1b318eb4952c2..f63401d34b624 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java @@ -39,7 +39,7 @@ public abstract class SpatialBoundsAggregationTestBase e protected abstract void assertBoundsLimits(SpatialBounds spatialBounds); public void testSingleValuedField() throws Exception { - SearchResponse response = client().prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get(); + SearchResponse response = prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get(); assertNoFailures(response); @@ -55,8 +55,7 @@ public void testSingleValuedField() throws Exception { } public void testSingleValuedField_getProperty() { - SearchResponse searchResponse = client().prepareSearch(IDX_NAME) - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch(IDX_NAME).setQuery(matchAllQuery()) .addAggregation(global("global").subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))) .get(); @@ -98,7 +97,7 @@ public void testSingleValuedField_getProperty() { } public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), MULTI_VALUED_FIELD_NAME)).get(); + SearchResponse response = prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), MULTI_VALUED_FIELD_NAME)).get(); assertNoFailures(response); @@ -114,9 +113,7 @@ public void testMultiValuedField() throws Exception { } public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch(UNMAPPED_IDX_NAME) - .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) - .get(); + SearchResponse response = prepareSearch(UNMAPPED_IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get(); assertNoFailures(response); @@ -148,8 +145,7 @@ public void testPartiallyUnmapped() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch(EMPTY_IDX_NAME) - .setQuery(matchAllQuery()) + SearchResponse searchResponse = prepareSearch(EMPTY_IDX_NAME).setQuery(matchAllQuery()) .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) .get(); @@ -167,9 +163,9 @@ public void testEmptyAggregation() throws Exception { * This test forces the bounds {@link MetricsAggregator} to resize the {@link BigArray}s it uses to ensure they are resized correctly */ public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() { - SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) - .addAggregation(terms("terms").field(NUMBER_FIELD_NAME).subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))) - .get(); + SearchResponse response = prepareSearch(HIGH_CARD_IDX_NAME).addAggregation( + terms("terms").field(NUMBER_FIELD_NAME).subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) + ).get(); assertNoFailures(response); @@ -190,9 +186,7 @@ public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() { } public void testSingleValuedFieldWithZeroLon() { - SearchResponse response = client().prepareSearch(IDX_ZERO_NAME) - .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)) - .get(); + SearchResponse response = prepareSearch(IDX_ZERO_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get(); assertNoFailures(response); diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java index 3e9d840cb7ed8..6aa245d429e51 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java @@ -184,7 +184,7 @@ public void testIgnoreMalformed() throws Exception { ); indexRandom(true, client().prepareIndex("test").setId("0").setSource("shape", polygonGeoJson)); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(matchAllQuery()).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); } @@ -217,9 +217,9 @@ public void testIndexShapeRouting() throws Exception { indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, XContentType.JSON).setRouting("ABC")); - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(queryBuilder().shapeQuery("shape", "0").indexedShapeIndex("test").indexedShapeRouting("ABC")) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery( + queryBuilder().shapeQuery("shape", "0").indexedShapeIndex("test").indexedShapeRouting("ABC") + ).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); } @@ -248,8 +248,7 @@ public void testDisallowExpensiveQueries() throws InterruptedException, IOExcept // Execute with search.allow_expensive_queries to false updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", false)); - SearchRequestBuilder builder = client().prepareSearch("test") - .setQuery(queryBuilder().shapeQuery("shape", new Circle(0, 0, 77000))); + SearchRequestBuilder builder = prepareSearch("test").setQuery(queryBuilder().shapeQuery("shape", new Circle(0, 0, 77000))); if (allowExpensiveQueries()) { assertThat(builder.get().getHits().getTotalHits().value, equalTo(1L)); } else { diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java index 09c134ad8ef96..547ec9f2578e0 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java @@ -57,16 +57,16 @@ public void testIndexPolygonDateLine() throws Exception { indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, XContentType.JSON)); - SearchResponse searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(-179.75, 1))).get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(-179.75, 1))).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(90, 1))).get(); + searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(90, 1))).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); - searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(-180, 1))).get(); + searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(-180, 1))).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(180, 1))).get(); + searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Point(180, 1))).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index a557841ba34bd..b145dc28e34a0 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -49,6 +49,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.ClearScrollResponse; import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.TransportSearchAction; import org.elasticsearch.action.support.DefaultShardOperationFailedException; @@ -1064,8 +1065,7 @@ public void waitForDocs(final long numDocs, final BackgroundIndexer indexer) thr if (lastKnownCount >= numDocs) { try { - long count = client().prepareSearch() - .setTrackTotalHits(true) + long count = prepareSearch().setTrackTotalHits(true) .setSize(0) .setQuery(matchAllQuery()) .get() @@ -1095,6 +1095,10 @@ public void waitForDocs(final long numDocs, final BackgroundIndexer indexer) thr }, maxWaitTimeMs, TimeUnit.MILLISECONDS); } + public static SearchRequestBuilder prepareSearch(String... indices) { + return client().prepareSearch(indices); + } + /** * Retrieves the persistent tasks with the requested task name from the given cluster state. */ diff --git a/x-pack/plugin/analytics/src/internalClusterTest/java/org/elasticsearch/xpack/analytics/multiterms/MultiTermsWithRequestBreakerIT.java b/x-pack/plugin/analytics/src/internalClusterTest/java/org/elasticsearch/xpack/analytics/multiterms/MultiTermsWithRequestBreakerIT.java index 714892558d423..f8b276dbbf6a5 100644 --- a/x-pack/plugin/analytics/src/internalClusterTest/java/org/elasticsearch/xpack/analytics/multiterms/MultiTermsWithRequestBreakerIT.java +++ b/x-pack/plugin/analytics/src/internalClusterTest/java/org/elasticsearch/xpack/analytics/multiterms/MultiTermsWithRequestBreakerIT.java @@ -55,16 +55,14 @@ public void testRequestBreaker() throws Exception { ); try { - client().prepareSearch("test") - .addAggregation( - new MultiTermsAggregationBuilder("xxx").terms( - List.of( - new MultiValuesSourceFieldConfig.Builder().setFieldName("field0.keyword").build(), - new MultiValuesSourceFieldConfig.Builder().setFieldName("field1.keyword").build() - ) + prepareSearch("test").addAggregation( + new MultiTermsAggregationBuilder("xxx").terms( + List.of( + new MultiValuesSourceFieldConfig.Builder().setFieldName("field0.keyword").build(), + new MultiValuesSourceFieldConfig.Builder().setFieldName("field1.keyword").build() ) ) - .get(); + ).get(); } catch (ElasticsearchException e) { if (ExceptionsHelper.unwrap(e, CircuitBreakingException.class) == null) { throw e; diff --git a/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotIT.java b/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotIT.java index d17637a3fd5a9..02f61498fa93a 100644 --- a/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotIT.java +++ b/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotIT.java @@ -116,15 +116,12 @@ public void testSnapshotAndRestore() throws Exception { assertHits(sourceIdx, builders.length, sourceHadDeletions); assertMappings(sourceIdx, requireRouting, useNested); SearchPhaseExecutionException e = expectThrows(SearchPhaseExecutionException.class, () -> { - client().prepareSearch(sourceIdx).setQuery(QueryBuilders.idsQuery().addIds("" + randomIntBetween(0, builders.length))).get(); + prepareSearch(sourceIdx).setQuery(QueryBuilders.idsQuery().addIds("" + randomIntBetween(0, builders.length))).get(); }); assertTrue(e.toString().contains("_source only indices can't be searched or filtered")); // can-match phase pre-filters access to non-existing field - assertEquals( - 0, - client().prepareSearch(sourceIdx).setQuery(QueryBuilders.termQuery("field1", "bar")).get().getHits().getTotalHits().value - ); + assertEquals(0, prepareSearch(sourceIdx).setQuery(QueryBuilders.termQuery("field1", "bar")).get().getHits().getTotalHits().value); // make sure deletes do not work String idToDelete = "" + randomIntBetween(0, builders.length); expectThrows(ClusterBlockException.class, () -> client().prepareDelete(sourceIdx, idToDelete).setRouting("r" + idToDelete).get()); @@ -144,16 +141,11 @@ public void testSnapshotAndRestoreWithNested() throws Exception { assertMappings(sourceIdx, requireRouting, true); SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch(sourceIdx) - .setQuery(QueryBuilders.idsQuery().addIds("" + randomIntBetween(0, builders.length))) - .get() + () -> prepareSearch(sourceIdx).setQuery(QueryBuilders.idsQuery().addIds("" + randomIntBetween(0, builders.length))).get() ); assertTrue(e.toString().contains("_source only indices can't be searched or filtered")); // can-match phase pre-filters access to non-existing field - assertEquals( - 0, - client().prepareSearch(sourceIdx).setQuery(QueryBuilders.termQuery("field1", "bar")).get().getHits().getTotalHits().value - ); + assertEquals(0, prepareSearch(sourceIdx).setQuery(QueryBuilders.termQuery("field1", "bar")).get().getHits().getTotalHits().value); // make sure deletes do not work String idToDelete = "" + randomIntBetween(0, builders.length); expectThrows(ClusterBlockException.class, () -> client().prepareDelete(sourceIdx, idToDelete).setRouting("r" + idToDelete).get()); @@ -259,10 +251,7 @@ private static void assertMappings(String sourceIdx, boolean requireRouting, boo } private void assertHits(String index, int numDocsExpected, boolean sourceHadDeletions) { - SearchResponse searchResponse = client().prepareSearch(index) - .addSort(SeqNoFieldMapper.NAME, SortOrder.ASC) - .setSize(numDocsExpected) - .get(); + SearchResponse searchResponse = prepareSearch(index).addSort(SeqNoFieldMapper.NAME, SortOrder.ASC).setSize(numDocsExpected).get(); BiConsumer assertConsumer = (res, allowHoles) -> { SearchHits hits = res.getHits(); long i = 0; @@ -283,8 +272,7 @@ private void assertHits(String index, int numDocsExpected, boolean sourceHadDele }; assertConsumer.accept(searchResponse, sourceHadDeletions); assertEquals(numDocsExpected, searchResponse.getHits().getTotalHits().value); - searchResponse = client().prepareSearch(index) - .addSort(SeqNoFieldMapper.NAME, SortOrder.ASC) + searchResponse = prepareSearch(index).addSort(SeqNoFieldMapper.NAME, SortOrder.ASC) .setScroll("1m") .slice(new SliceBuilder(SeqNoFieldMapper.NAME, randomIntBetween(0, 1), 2)) .setSize(randomIntBetween(1, 10)) @@ -348,7 +336,7 @@ private IndexRequestBuilder[] snapshotAndRestore(final String sourceIdx, final b } indexRandom(true, builders); flushAndRefresh(); - assertHitCount(client().prepareSearch(sourceIdx).setQuery(QueryBuilders.idsQuery().addIds("0")), 1); + assertHitCount(prepareSearch(sourceIdx).setQuery(QueryBuilders.idsQuery().addIds("0")), 1); createSnapshot(repo, snapshot, Collections.singletonList(sourceIdx)); diff --git a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java index 727441f5dba10..d86f090456061 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java +++ b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java @@ -99,8 +99,7 @@ public void testSort() { for (String index : new String[] { "idx", "idx-sort" }) { // asc sort { - SearchResponse response = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .get(); @@ -114,8 +113,7 @@ public void testSort() { } // desc sort { - SearchResponse response = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.DESC) .get(); @@ -129,8 +127,7 @@ public void testSort() { } // asc sort with search_after as Long { - SearchResponse response = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .searchAfter(new Long[] { 100L }) @@ -145,8 +142,7 @@ public void testSort() { } // asc sort with search_after as BigInteger { - SearchResponse response = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .searchAfter(new BigInteger[] { new BigInteger("18446744073709551614") }) @@ -161,8 +157,7 @@ public void testSort() { } // asc sort with search_after as BigInteger in String format { - SearchResponse response = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .searchAfter(new String[] { "18446744073709551614" }) @@ -177,8 +172,7 @@ public void testSort() { } // asc sort with search_after of negative value should fail { - SearchRequestBuilder srb = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchRequestBuilder srb = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .searchAfter(new Long[] { -1L }); @@ -187,8 +181,7 @@ public void testSort() { } // asc sort with search_after of value>=2^64 should fail { - SearchRequestBuilder srb = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchRequestBuilder srb = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.ASC) .searchAfter(new BigInteger[] { new BigInteger("18446744073709551616") }); @@ -197,8 +190,7 @@ public void testSort() { } // desc sort with search_after as BigInteger { - SearchResponse response = client().prepareSearch(index) - .setQuery(QueryBuilders.matchAllQuery()) + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()) .setSize(numDocs) .addSort("ul_field", SortOrder.DESC) .searchAfter(new BigInteger[] { new BigInteger("18446744073709551615") }) @@ -217,7 +209,7 @@ public void testSort() { public void testAggs() { // terms agg { - SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(terms("ul_terms").field("ul_field")).get(); + SearchResponse response = prepareSearch("idx").setSize(0).addAggregation(terms("ul_terms").field("ul_field")).get(); assertNoFailures(response); Terms terms = response.getAggregations().get("ul_terms"); @@ -240,8 +232,7 @@ public void testAggs() { // histogram agg { - SearchResponse response = client().prepareSearch("idx") - .setSize(0) + SearchResponse response = prepareSearch("idx").setSize(0) .addAggregation(histogram("ul_histo").field("ul_field").interval(9E18).minDocCount(0)) .get(); assertNoFailures(response); @@ -259,8 +250,7 @@ public void testAggs() { // range agg { - SearchResponse response = client().prepareSearch("idx") - .setSize(0) + SearchResponse response = prepareSearch("idx").setSize(0) .addAggregation( range("ul_range").field("ul_field").addUnboundedTo(9.0E18).addRange(9.0E18, 1.8E19).addUnboundedFrom(1.8E19) ) @@ -280,7 +270,7 @@ public void testAggs() { // sum agg { - SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(sum("ul_sum").field("ul_field")).get(); + SearchResponse response = prepareSearch("idx").setSize(0).addAggregation(sum("ul_sum").field("ul_field")).get(); assertNoFailures(response); Sum sum = response.getAggregations().get("ul_sum"); double expectedSum = Arrays.stream(values).mapToDouble(Number::doubleValue).sum(); @@ -288,14 +278,14 @@ public void testAggs() { } // max agg { - SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(max("ul_max").field("ul_field")).get(); + SearchResponse response = prepareSearch("idx").setSize(0).addAggregation(max("ul_max").field("ul_field")).get(); assertNoFailures(response); Max max = response.getAggregations().get("ul_max"); assertEquals(1.8446744073709551615E19, max.value(), 0.001); } // min agg { - SearchResponse response = client().prepareSearch("idx").setSize(0).addAggregation(min("ul_min").field("ul_field")).get(); + SearchResponse response = prepareSearch("idx").setSize(0).addAggregation(min("ul_min").field("ul_field")).get(); assertNoFailures(response); Min min = response.getAggregations().get("ul_min"); assertEquals(0, min.value(), 0.001); @@ -318,17 +308,15 @@ public void testSortDifferentFormatsShouldFail() { } public void testRangeQuery() { - SearchResponse response = client().prepareSearch("idx") - .setSize(0) + SearchResponse response = prepareSearch("idx").setSize(0) .setQuery(new RangeQueryBuilder("ul_field").to("9.0E18").includeUpper(false)) .get(); assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - response = client().prepareSearch("idx") - .setSize(0) + response = prepareSearch("idx").setSize(0) .setQuery(new RangeQueryBuilder("ul_field").from("9.0E18").to("1.8E19").includeUpper(false)) .get(); assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - response = client().prepareSearch("idx").setSize(0).setQuery(new RangeQueryBuilder("ul_field").from("1.8E19")).get(); + response = prepareSearch("idx").setSize(0).setQuery(new RangeQueryBuilder("ul_field").from("1.8E19")).get(); assertThat(response.getHits().getTotalHits().value, equalTo(4L)); } } diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/CategorizationIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/CategorizationIT.java index a2fa26914e083..f0b23b087537b 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/CategorizationIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/CategorizationIT.java @@ -303,9 +303,9 @@ public void testCategorizationStatePersistedOnSwitchToRealtime() throws Exceptio // before closing the job to prove that it was persisted in the background at the // end of lookback rather than when the job was closed. assertBusy(() -> { - SearchResponse stateDocsResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setQuery(QueryBuilders.idsQuery().addIds(CategorizerState.documentId(job.getId(), 1))) - .get(); + SearchResponse stateDocsResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setQuery( + QueryBuilders.idsQuery().addIds(CategorizerState.documentId(job.getId(), 1)) + ).get(); SearchHit[] hits = stateDocsResponse.getHits().getHits(); assertThat(hits, arrayWithSize(1)); @@ -554,14 +554,11 @@ private static Job.Builder newJobBuilder(String id, List categorizationF private List getCategorizerStats(String jobId) throws IOException { - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)) - .setQuery( - QueryBuilders.boolQuery() - .filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), CategorizerStats.RESULT_TYPE_VALUE)) - .filter(QueryBuilders.termQuery(Job.ID.getPreferredName(), jobId)) - ) - .setSize(1000) - .get(); + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)).setQuery( + QueryBuilders.boolQuery() + .filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), CategorizerStats.RESULT_TYPE_VALUE)) + .filter(QueryBuilders.termQuery(Job.ID.getPreferredName(), jobId)) + ).setSize(1000).get(); List stats = new ArrayList<>(); for (SearchHit hit : searchResponse.getHits().getHits()) { diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationHousePricingIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationHousePricingIT.java index ee15832d68488..610a492ef078f 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationHousePricingIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationHousePricingIT.java @@ -1566,7 +1566,7 @@ public void testFeatureImportanceValues() throws Exception { waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); // obtain addition information for investigation of #90599 String modelId = getModelId(jobId); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationIT.java index 3ddb05fd0218f..0b1edd0f35538 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ClassificationIT.java @@ -145,7 +145,7 @@ public void testSingleNumericFeatureAndMixedTrainingAndNonTrainingRows() throws waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); @@ -210,7 +210,7 @@ public void testWithDatastreams() throws Exception { waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); @@ -259,7 +259,7 @@ public void testWithOnlyTrainingRowsAndTrainingPercentIsHundred() throws Excepti waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); @@ -348,7 +348,7 @@ public void testWithCustomFeatureProcessors() throws Exception { waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); @@ -425,7 +425,7 @@ public void testWithOnlyTrainingRowsAndTrainingPercentIsFifty( int trainingRowsCount = 0; int nonTrainingRowsCount = 0; client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); @@ -569,7 +569,7 @@ public void testStopAndRestart() throws Exception { waitUntilAnalyticsIsStopped(jobId); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); @@ -840,7 +840,7 @@ public void testDeleteExpiredData_RemovesUnusedState() throws Exception { // Now calling the _delete_expired_data API should remove unused state assertThat(deleteExpiredData().isDeleted(), is(true)); - SearchResponse stateIndexSearchResponse = client().prepareSearch(".ml-state*").execute().actionGet(); + SearchResponse stateIndexSearchResponse = prepareSearch(".ml-state*").execute().actionGet(); assertThat(stateIndexSearchResponse.getHits().getTotalHits().value, equalTo(0L)); } @@ -928,7 +928,7 @@ public void testWithSearchRuntimeMappings() throws Exception { waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse destData = client().prepareSearch(destIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse destData = prepareSearch(destIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : destData.getHits()) { Map destDoc = hit.getSourceAsMap(); Map resultsObject = getFieldValue(destDoc, "ml"); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DataFrameAnalysisCustomFeatureIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DataFrameAnalysisCustomFeatureIT.java index 91d302c381f5b..153f3aa46c359 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DataFrameAnalysisCustomFeatureIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DataFrameAnalysisCustomFeatureIT.java @@ -134,7 +134,7 @@ public void testNGramCustomFeature() throws Exception { waitUntilAnalyticsIsStopped(jobId); client().admin().indices().refresh(new RefreshRequest(destIndex)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getFieldValue(destDoc, "ml"); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DatafeedWithAggsIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DatafeedWithAggsIT.java index dab0bb54a6762..9773a4d3b3d82 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DatafeedWithAggsIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DatafeedWithAggsIT.java @@ -164,21 +164,14 @@ private void testDfWithAggs(AggregatorFactories.Builder aggs, Detector.Builder d ); // Confirm that it's possible to search for the same buckets by @timestamp - proves that @timestamp works as a field alias assertThat( - client().prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)) - .setQuery( - QueryBuilders.boolQuery() - .filter(QueryBuilders.termQuery("job_id", jobId)) - .filter(QueryBuilders.termQuery("result_type", "bucket")) - .filter( - QueryBuilders.rangeQuery("@timestamp") - .gte(bucket.getTimestamp().getTime()) - .lte(bucket.getTimestamp().getTime()) - ) - ) - .setTrackTotalHits(true) - .get() - .getHits() - .getTotalHits().value, + prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)).setQuery( + QueryBuilders.boolQuery() + .filter(QueryBuilders.termQuery("job_id", jobId)) + .filter(QueryBuilders.termQuery("result_type", "bucket")) + .filter( + QueryBuilders.rangeQuery("@timestamp").gte(bucket.getTimestamp().getTime()).lte(bucket.getTimestamp().getTime()) + ) + ).setTrackTotalHits(true).get().getHits().getTotalHits().value, equalTo(1L) ); } diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java index 15026a719590e..0bdf68cdbb5aa 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java @@ -268,14 +268,12 @@ private void testExpiredDeletion(Float customThrottle, int numUnusedState) throw retainAllSnapshots("snapshots-retention-with-retain"); - long totalModelSizeStatsBeforeDelete = client().prepareSearch("*") - .setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN) + long totalModelSizeStatsBeforeDelete = prepareSearch("*").setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN) .setQuery(QueryBuilders.termQuery("result_type", "model_size_stats")) .get() .getHits() .getTotalHits().value; - long totalNotificationsCountBeforeDelete = client().prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX) - .get() + long totalNotificationsCountBeforeDelete = prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX).get() .getHits() .getTotalHits().value; assertThat(totalModelSizeStatsBeforeDelete, greaterThan(0L)); @@ -321,14 +319,12 @@ private void testExpiredDeletion(Float customThrottle, int numUnusedState) throw assertThat(getRecords("results-and-snapshots-retention").size(), equalTo(0)); assertThat(getModelSnapshots("results-and-snapshots-retention").size(), equalTo(1)); - long totalModelSizeStatsAfterDelete = client().prepareSearch("*") - .setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN) + long totalModelSizeStatsAfterDelete = prepareSearch("*").setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN) .setQuery(QueryBuilders.termQuery("result_type", "model_size_stats")) .get() .getHits() .getTotalHits().value; - long totalNotificationsCountAfterDelete = client().prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX) - .get() + long totalNotificationsCountAfterDelete = prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX).get() .getHits() .getTotalHits().value; assertThat(totalModelSizeStatsAfterDelete, equalTo(totalModelSizeStatsBeforeDelete)); @@ -347,8 +343,7 @@ private void testExpiredDeletion(Float customThrottle, int numUnusedState) throw } // Verify .ml-state doesn't contain unused state documents - SearchResponse stateDocsResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setFetchSource(false) + SearchResponse stateDocsResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setFetchSource(false) .setTrackTotalHits(true) .setSize(10000) .get(); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java index a8825ea46774c..079443f5cf422 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java @@ -186,8 +186,7 @@ public void testScope() throws Exception { // Wait until the notification that the filter was updated is indexed assertBusy(() -> { - SearchResponse searchResponse = client().prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX) - .setSize(1) + SearchResponse searchResponse = prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX).setSize(1) .addSort("timestamp", SortOrder.DESC) .setQuery( QueryBuilders.boolQuery() diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/InterimResultsDeletedAfterReopeningJobIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/InterimResultsDeletedAfterReopeningJobIT.java index 4cc4811c5bfe0..2a76fffc5559e 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/InterimResultsDeletedAfterReopeningJobIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/InterimResultsDeletedAfterReopeningJobIT.java @@ -108,7 +108,7 @@ private static Map createRecord(long timestamp, String byFieldVa private void assertNoInterimResults(String jobId) { String indexName = AnomalyDetectorsIndex.jobResultsAliasedName(jobId); - SearchResponse search = client().prepareSearch(indexName).setSize(1000).setQuery(QueryBuilders.termQuery("is_interim", true)).get(); + SearchResponse search = prepareSearch(indexName).setSize(1000).setQuery(QueryBuilders.termQuery("is_interim", true)).get(); assertThat(search.getHits().getTotalHits().value, equalTo(0L)); } } diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java index 5218dafc1af10..c57a983c2c61d 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java @@ -286,9 +286,9 @@ protected void assertThatNumberOfAnnotationsIsEqualTo(int expectedNumberOfAnnota } protected ForecastRequestStats getForecastStats(String jobId, String forecastId) { - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)) - .setQuery(QueryBuilders.idsQuery().addIds(ForecastRequestStats.documentId(jobId, forecastId))) - .get(); + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)).setQuery( + QueryBuilders.idsQuery().addIds(ForecastRequestStats.documentId(jobId, forecastId)) + ).get(); if (searchResponse.getHits().getHits().length == 0) { return null; @@ -313,8 +313,7 @@ protected ForecastRequestStats getForecastStats(String jobId, String forecastId) protected List getForecastStats() { List forecastStats = new ArrayList<>(); - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*") - .setSize(1000) + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*").setSize(1000) .setQuery( QueryBuilders.boolQuery() .filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), ForecastRequestStats.RESULT_TYPE_VALUE)) @@ -339,22 +338,20 @@ protected List getForecastStats() { } protected long countForecastDocs(String jobId, String forecastId) { - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*") - .setQuery( - QueryBuilders.boolQuery() - .filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), Forecast.RESULT_TYPE_VALUE)) - .filter(QueryBuilders.termQuery(Job.ID.getPreferredName(), jobId)) - .filter(QueryBuilders.termQuery(Forecast.FORECAST_ID.getPreferredName(), forecastId)) - ) - .execute() - .actionGet(); + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*").setQuery( + QueryBuilders.boolQuery() + .filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), Forecast.RESULT_TYPE_VALUE)) + .filter(QueryBuilders.termQuery(Job.ID.getPreferredName(), jobId)) + .filter(QueryBuilders.termQuery(Forecast.FORECAST_ID.getPreferredName(), forecastId)) + ).execute().actionGet(); return searchResponse.getHits().getTotalHits().value; } protected List getForecasts(String jobId, ForecastRequestStats forecastRequestStats) { List forecasts = new ArrayList<>(); - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*") - .setSize((int) forecastRequestStats.getRecordCount()) + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*").setSize( + (int) forecastRequestStats.getRecordCount() + ) .setQuery( QueryBuilders.boolQuery() .filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), Forecast.RESULT_TYPE_VALUE)) diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeDataFrameAnalyticsIntegTestCase.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeDataFrameAnalyticsIntegTestCase.java index 3894c46e7a818..628400120bd41 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeDataFrameAnalyticsIntegTestCase.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeDataFrameAnalyticsIntegTestCase.java @@ -248,7 +248,7 @@ protected List getProgress(String id) { protected SearchResponse searchStoredProgress(String jobId) { String docId = StoredProgress.documentId(jobId); - return client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setQuery(QueryBuilders.idsQuery().addIds(docId)).get(); + return prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setQuery(QueryBuilders.idsQuery().addIds(docId)).get(); } protected void assertExactlyOneInferenceModelPersisted(String jobId) { @@ -260,9 +260,9 @@ protected void assertAtLeastOneInferenceModelPersisted(String jobId) { } private void assertInferenceModelPersisted(String jobId, Matcher modelHitsArraySizeMatcher) { - SearchResponse searchResponse = client().prepareSearch(InferenceIndexConstants.LATEST_INDEX_NAME) - .setQuery(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery(TrainedModelConfig.TAGS.getPreferredName(), jobId))) - .get(); + SearchResponse searchResponse = prepareSearch(InferenceIndexConstants.LATEST_INDEX_NAME).setQuery( + QueryBuilders.boolQuery().filter(QueryBuilders.termQuery(TrainedModelConfig.TAGS.getPreferredName(), jobId)) + ).get(); // If the job is stopped during writing_results phase and it is then restarted, there is a chance two trained models // were persisted as there is no way currently for the process to be certain the model was persisted. assertThat( @@ -294,23 +294,20 @@ protected void waitUntilSomeProgressHasBeenMadeForPhase(String jobId, String pha } protected String getModelId(String jobId) { - SearchResponse searchResponse = client().prepareSearch(InferenceIndexConstants.LATEST_INDEX_NAME) - .setQuery(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery(TrainedModelConfig.TAGS.getPreferredName(), jobId))) - .get(); + SearchResponse searchResponse = prepareSearch(InferenceIndexConstants.LATEST_INDEX_NAME).setQuery( + QueryBuilders.boolQuery().filter(QueryBuilders.termQuery(TrainedModelConfig.TAGS.getPreferredName(), jobId)) + ).get(); assertThat(searchResponse.getHits().getHits(), arrayWithSize(1)); return searchResponse.getHits().getHits()[0].getId(); } protected TrainedModelMetadata getModelMetadata(String modelId) { - SearchResponse response = client().prepareSearch(InferenceIndexConstants.INDEX_PATTERN) - .setQuery( - QueryBuilders.boolQuery() - .filter(QueryBuilders.termQuery("model_id", modelId)) - .filter(QueryBuilders.termQuery(InferenceIndexConstants.DOC_TYPE.getPreferredName(), TrainedModelMetadata.NAME)) - ) - .setSize(1) - .get(); + SearchResponse response = prepareSearch(InferenceIndexConstants.INDEX_PATTERN).setQuery( + QueryBuilders.boolQuery() + .filter(QueryBuilders.termQuery("model_id", modelId)) + .filter(QueryBuilders.termQuery(InferenceIndexConstants.DOC_TYPE.getPreferredName(), TrainedModelMetadata.NAME)) + ).setSize(1).get(); assertThat(response.getHits().getHits(), arrayWithSize(1)); try ( @@ -364,7 +361,7 @@ protected static void assertThatAuditMessagesMatch(String configId, String... ex protected static Set getTrainingRowsIds(String index) { Set trainingRowsIds = new HashSet<>(); - SearchResponse hits = client().prepareSearch(index).setSize(10000).get(); + SearchResponse hits = prepareSearch(index).setSize(10000).get(); for (SearchHit hit : hits.getHits()) { Map sourceAsMap = hit.getSourceAsMap(); assertThat(sourceAsMap.containsKey("ml"), is(true)); @@ -381,9 +378,9 @@ protected static Set getTrainingRowsIds(String index) { } protected static void assertModelStatePersisted(String stateDocId) { - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setQuery(QueryBuilders.idsQuery().addIds(stateDocId)) - .get(); + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setQuery( + QueryBuilders.idsQuery().addIds(stateDocId) + ).get(); assertThat("Hits were: " + Strings.toString(searchResponse.getHits()), searchResponse.getHits().getHits(), is(arrayWithSize(1))); } diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java index 36d5c71f399a5..d2fa397ab5d6b 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java @@ -159,10 +159,9 @@ private static DatafeedConfig newDatafeed(String datafeedId, String jobId) { } private Set modelPlotTerms(String jobId, String fieldName) { - SearchResponse searchResponse = client().prepareSearch(".ml-anomalies-" + jobId) - .setQuery(QueryBuilders.termQuery("result_type", "model_plot")) - .addAggregation(AggregationBuilders.terms("model_plot_terms").field(fieldName)) - .get(); + SearchResponse searchResponse = prepareSearch(".ml-anomalies-" + jobId).setQuery( + QueryBuilders.termQuery("result_type", "model_plot") + ).addAggregation(AggregationBuilders.terms("model_plot_terms").field(fieldName)).get(); Terms aggregation = searchResponse.getAggregations().get("model_plot_terms"); return aggregation.getBuckets().stream().map(agg -> agg.getKeyAsString()).collect(Collectors.toSet()); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/OutlierDetectionWithMissingFieldsIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/OutlierDetectionWithMissingFieldsIT.java index 2265ad8e934ce..4433193e306f6 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/OutlierDetectionWithMissingFieldsIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/OutlierDetectionWithMissingFieldsIT.java @@ -89,7 +89,7 @@ public void testMissingFields() throws Exception { assertThat(stats.getDataCounts().getTestDocsCount(), equalTo(0L)); assertThat(stats.getDataCounts().getSkippedDocsCount(), equalTo(2L)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).get(); for (SearchHit hit : sourceData.getHits()) { GetResponse destDocGetResponse = client().prepareGet().setIndex(config.getDest().getIndex()).setId(hit.getId()).get(); assertThat(destDocGetResponse.isExists(), is(true)); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/PersistJobIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/PersistJobIT.java index 57702d205a31b..ad0d9135ebb96 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/PersistJobIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/PersistJobIT.java @@ -64,8 +64,7 @@ public void testPersistJobOnGracefulShutdown_givenTimeAdvancedAfterNoNewData() t long job1CloseTime = System.currentTimeMillis() / 1000; // Check that state has been persisted - SearchResponse stateDocsResponse1 = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setFetchSource(false) + SearchResponse stateDocsResponse1 = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setFetchSource(false) .setTrackTotalHits(true) .setSize(10000) .get(); @@ -104,8 +103,7 @@ public void testPersistJobOnGracefulShutdown_givenTimeAdvancedAfterNoNewData() t closeJob(jobId); // Check that a new state record exists. - SearchResponse stateDocsResponse2 = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setFetchSource(false) + SearchResponse stateDocsResponse2 = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setFetchSource(false) .setTrackTotalHits(true) .setSize(10000) .get(); @@ -143,8 +141,7 @@ public void testPersistJobOnGracefulShutdown_givenNoDataAndTimeAdvanced() throws closeJob(jobId); // Check that state has been persisted - SearchResponse stateDocsResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setFetchSource(false) + SearchResponse stateDocsResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setFetchSource(false) .setTrackTotalHits(true) .setSize(10000) .get(); @@ -170,8 +167,7 @@ public void testPersistJobOnGracefulShutdown_givenNoDataAndTimeAdvanced() throws closeJob(jobId); deleteJob(jobId); - stateDocsResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setFetchSource(false) + stateDocsResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setFetchSource(false) .setTrackTotalHits(true) .setSize(10000) .get(); @@ -199,11 +195,11 @@ public void testPersistJobOnGracefulShutdown_givenNoDataAndNoTimeAdvance() throw closeJob(jobId); // Check that state has not been persisted - SearchResponse stateDocsResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).get(); + SearchResponse stateDocsResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).get(); assertThat(Arrays.asList(stateDocsResponse.getHits().getHits()), empty()); // Check that results have not been persisted - SearchResponse resultsDocsResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)).get(); + SearchResponse resultsDocsResponse = prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId)).get(); assertThat(Arrays.asList(resultsDocsResponse.getHits().getHits()), empty()); deleteJob(jobId); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RegressionIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RegressionIT.java index 993a65cf45bcf..7b977640e9b3a 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RegressionIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RegressionIT.java @@ -117,7 +117,7 @@ public void testSingleNumericFeatureAndMixedTrainingAndNonTrainingRows() throws // for debugging List> badDocuments = new ArrayList<>(); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getMlResultsObjectFromDestDoc(destDoc); @@ -207,7 +207,7 @@ public void testWithOnlyTrainingRowsAndTrainingPercentIsHundred() throws Excepti startAnalytics(jobId); waitUntilAnalyticsIsStopped(jobId); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map resultsObject = getMlResultsObjectFromDestDoc(getDestDoc(config, hit)); @@ -266,7 +266,7 @@ public void testWithOnlyTrainingRowsAndTrainingPercentIsFifty() throws Exception int trainingRowsCount = 0; int nonTrainingRowsCount = 0; - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map resultsObject = getMlResultsObjectFromDestDoc(getDestDoc(config, hit)); @@ -344,7 +344,7 @@ public void testStopAndRestart() throws Exception { waitUntilAnalyticsIsStopped(jobId); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map resultsObject = getMlResultsObjectFromDestDoc(getDestDoc(config, hit)); @@ -440,7 +440,7 @@ public void testDeleteExpiredData_RemovesUnusedState() throws Exception { // Now calling the _delete_expired_data API should remove unused state assertThat(deleteExpiredData().isDeleted(), is(true)); - SearchResponse stateIndexSearchResponse = client().prepareSearch(".ml-state*").execute().actionGet(); + SearchResponse stateIndexSearchResponse = prepareSearch(".ml-state*").execute().actionGet(); assertThat(stateIndexSearchResponse.getHits().getTotalHits().value, equalTo(0L)); } @@ -498,7 +498,7 @@ public void testWithDatastream() throws Exception { startAnalytics(jobId); waitUntilAnalyticsIsStopped(jobId); - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getMlResultsObjectFromDestDoc(destDoc); @@ -607,7 +607,7 @@ public void testAliasFields() throws Exception { double predictionErrorSum = 0.0; - SearchResponse sourceData = client().prepareSearch(sourceIndex).setSize(totalDocCount).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setSize(totalDocCount).get(); StringBuilder targetsPredictions = new StringBuilder(); // used to investigate #90599 for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); @@ -704,7 +704,7 @@ public void testWithCustomFeatureProcessors() throws Exception { waitUntilAnalyticsIsStopped(jobId); // for debugging - SearchResponse sourceData = client().prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : sourceData.getHits()) { Map destDoc = getDestDoc(config, hit); Map resultsObject = getMlResultsObjectFromDestDoc(destDoc); @@ -795,7 +795,7 @@ public void testWithSearchRuntimeMappings() throws Exception { startAnalytics(jobId); waitUntilAnalyticsIsStopped(jobId); - SearchResponse destData = client().prepareSearch(destIndex).setTrackTotalHits(true).setSize(1000).get(); + SearchResponse destData = prepareSearch(destIndex).setTrackTotalHits(true).setSize(1000).get(); for (SearchHit hit : destData.getHits()) { Map destDoc = hit.getSourceAsMap(); Map resultsObject = getMlResultsObjectFromDestDoc(destDoc); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RevertModelSnapshotIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RevertModelSnapshotIT.java index 4e82720532454..90614703cc171 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RevertModelSnapshotIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RevertModelSnapshotIT.java @@ -289,8 +289,7 @@ record = new HashMap<>(); } private Quantiles getQuantiles(String jobId) { - SearchResponse response = client().prepareSearch(".ml-state*") - .setQuery(QueryBuilders.idsQuery().addIds(Quantiles.documentId(jobId))) + SearchResponse response = prepareSearch(".ml-state*").setQuery(QueryBuilders.idsQuery().addIds(Quantiles.documentId(jobId))) .setSize(1) .get(); SearchHits hits = response.getHits(); diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RunDataFrameAnalyticsIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RunDataFrameAnalyticsIT.java index b495eb860b75d..3910ab0c1c523 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RunDataFrameAnalyticsIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/RunDataFrameAnalyticsIT.java @@ -102,7 +102,7 @@ public void testOutlierDetectionWithFewDocuments() throws Exception { assertThat(stats.getDataCounts().getTestDocsCount(), equalTo(0L)); assertThat(stats.getDataCounts().getSkippedDocsCount(), equalTo(0L)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).get(); double scoreOfOutlier = 0.0; double scoreOfNonOutlier = -1.0; for (SearchHit hit : sourceData.getHits()) { @@ -230,12 +230,11 @@ public void testOutlierDetectionWithEnoughDocumentsToScroll() throws Exception { waitUntilAnalyticsIsStopped(id); // Check we've got all docs - SearchResponse searchResponse = client().prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); + SearchResponse searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) docCount)); // Check they all have an outlier_score - searchResponse = client().prepareSearch(config.getDest().getIndex()) - .setTrackTotalHits(true) + searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true) .setQuery(QueryBuilders.existsQuery("custom_ml.outlier_score")) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) docCount)); @@ -312,7 +311,7 @@ public void testOutlierDetectionWithMoreFieldsThanDocValueFieldLimit() throws Ex startAnalytics(id); waitUntilAnalyticsIsStopped(id); - SearchResponse sourceData = client().prepareSearch(sourceIndex).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).get(); for (SearchHit hit : sourceData.getHits()) { GetResponse destDocGetResponse = client().prepareGet().setIndex(config.getDest().getIndex()).setId(hit.getId()).get(); assertThat(destDocGetResponse.isExists(), is(true)); @@ -392,10 +391,9 @@ public void testStopOutlierDetectionWithEnoughDocumentsToScroll() throws Excepti return; } - SearchResponse searchResponse = client().prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); + SearchResponse searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); if (searchResponse.getHits().getTotalHits().value == docCount) { - searchResponse = client().prepareSearch(config.getDest().getIndex()) - .setTrackTotalHits(true) + searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true) .setQuery(QueryBuilders.existsQuery("custom_ml.outlier_score")) .get(); logger.debug("We stopped during analysis: [{}] < [{}]", searchResponse.getHits().getTotalHits().value, docCount); @@ -459,12 +457,11 @@ public void testOutlierDetectionWithMultipleSourceIndices() throws Exception { waitUntilAnalyticsIsStopped(id); // Check we've got all docs - SearchResponse searchResponse = client().prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); + SearchResponse searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) bulkRequestBuilder.numberOfActions())); // Check they all have an outlier_score - searchResponse = client().prepareSearch(config.getDest().getIndex()) - .setTrackTotalHits(true) + searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true) .setQuery(QueryBuilders.existsQuery("ml.outlier_score")) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) bulkRequestBuilder.numberOfActions())); @@ -523,12 +520,11 @@ public void testOutlierDetectionWithPreExistingDestIndex() throws Exception { waitUntilAnalyticsIsStopped(id); // Check we've got all docs - SearchResponse searchResponse = client().prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); + SearchResponse searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) bulkRequestBuilder.numberOfActions())); // Check they all have an outlier_score - searchResponse = client().prepareSearch(config.getDest().getIndex()) - .setTrackTotalHits(true) + searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true) .setQuery(QueryBuilders.existsQuery("ml.outlier_score")) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) bulkRequestBuilder.numberOfActions())); @@ -690,12 +686,11 @@ public void testOutlierDetectionStopAndRestart() throws Exception { waitUntilAnalyticsIsStopped(id); // Check we've got all docs - SearchResponse searchResponse = client().prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); + SearchResponse searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) docCount)); // Check they all have an outlier_score - searchResponse = client().prepareSearch(config.getDest().getIndex()) - .setTrackTotalHits(true) + searchResponse = prepareSearch(config.getDest().getIndex()).setTrackTotalHits(true) .setQuery(QueryBuilders.existsQuery("custom_ml.outlier_score")) .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) docCount)); @@ -750,7 +745,7 @@ public void testOutlierDetectionWithCustomParams() throws Exception { startAnalytics(id); waitUntilAnalyticsIsStopped(id); - SearchResponse sourceData = client().prepareSearch(sourceIndex).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).get(); double scoreOfOutlier = 0.0; double scoreOfNonOutlier = -1.0; for (SearchHit hit : sourceData.getHits()) { @@ -859,7 +854,7 @@ public void testOutlierDetection_GivenIndexWithRuntimeFields() throws Exception assertThat(stats.getDataCounts().getTestDocsCount(), equalTo(0L)); assertThat(stats.getDataCounts().getSkippedDocsCount(), equalTo(0L)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).get(); double scoreOfOutlier = 0.0; double scoreOfNonOutlier = -1.0; for (SearchHit hit : sourceData.getHits()) { @@ -964,7 +959,7 @@ public void testOutlierDetection_GivenSearchRuntimeMappings() throws Exception { assertThat(stats.getDataCounts().getTestDocsCount(), equalTo(0L)); assertThat(stats.getDataCounts().getSkippedDocsCount(), equalTo(0L)); - SearchResponse sourceData = client().prepareSearch(sourceIndex).get(); + SearchResponse sourceData = prepareSearch(sourceIndex).get(); double scoreOfOutlier = 0.0; double scoreOfNonOutlier = -1.0; for (SearchHit hit : sourceData.getHits()) { diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java index eeb8e54892380..45d1e57a52f46 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java @@ -248,8 +248,7 @@ public void testAddEventsToOpenJob() throws Exception { // Wait until the notification that the process was updated is indexed assertBusy(() -> { - SearchResponse searchResponse = client().prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX) - .setSize(1) + SearchResponse searchResponse = prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX).setSize(1) .addSort("timestamp", SortOrder.DESC) .setQuery( QueryBuilders.boolQuery() @@ -335,8 +334,7 @@ public void testAddOpenedJobToGroupWithCalendar() throws Exception { // Wait until the notification that the job was updated is indexed assertBusy(() -> { - SearchResponse searchResponse = client().prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX) - .setSize(1) + SearchResponse searchResponse = prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX).setSize(1) .addSort("timestamp", SortOrder.DESC) .setQuery( QueryBuilders.boolQuery() @@ -422,8 +420,7 @@ public void testNewJobWithGlobalCalendar() throws Exception { // Wait until the notification that the job was updated is indexed assertBusy(() -> { - SearchResponse searchResponse = client().prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX) - .setSize(1) + SearchResponse searchResponse = prepareSearch(NotificationsIndex.NOTIFICATIONS_INDEX).setSize(1) .addSort("timestamp", SortOrder.DESC) .setQuery( QueryBuilders.boolQuery() diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextAggregationIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextAggregationIT.java index 309ca2211b1c7..d356fe49f9120 100644 --- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextAggregationIT.java +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextAggregationIT.java @@ -39,8 +39,7 @@ public void setupCluster() { } public void testAggregation() { - SearchResponse response = client().prepareSearch(DATA_INDEX) - .setSize(0) + SearchResponse response = prepareSearch(DATA_INDEX).setSize(0) .setTrackTotalHits(false) .addAggregation( new CategorizeTextAggregationBuilder("categorize", "msg").subAggregation(AggregationBuilders.max("max").field("time")) @@ -57,8 +56,7 @@ public void testAggregation() { } public void testAggregationWithOnlyOneBucket() { - SearchResponse response = client().prepareSearch(DATA_INDEX) - .setSize(0) + SearchResponse response = prepareSearch(DATA_INDEX).setSize(0) .setTrackTotalHits(false) .addAggregation( new CategorizeTextAggregationBuilder("categorize", "msg").size(1) @@ -73,8 +71,7 @@ public void testAggregationWithOnlyOneBucket() { } public void testAggregationWithBroadCategories() { - SearchResponse response = client().prepareSearch(DATA_INDEX) - .setSize(0) + SearchResponse response = prepareSearch(DATA_INDEX).setSize(0) .setTrackTotalHits(false) .addAggregation( // Overriding the similarity threshold to just 11% (default is 70%) results in the diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextDistributedIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextDistributedIT.java index 6f32acd5f08d8..c91b2e8d5b540 100644 --- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextDistributedIT.java +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/CategorizeTextDistributedIT.java @@ -82,11 +82,9 @@ public void testDistributedCategorizeText() { .collect(Collectors.toSet()); assertThat(nodesWithShards, hasSize(internalCluster().size())); - SearchResponse searchResponse = client().prepareSearch(indexName) - .addAggregation(new CategorizeTextAggregationBuilder("categories", "message")) - .setSize(0) - .execute() - .actionGet(); + SearchResponse searchResponse = prepareSearch(indexName).addAggregation( + new CategorizeTextAggregationBuilder("categories", "message") + ).setSize(0).execute().actionGet(); InternalCategorizationAggregation aggregation = searchResponse.getAggregations().get("categories"); assertThat(aggregation, notNullValue()); diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/NetworkDisruptionIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/NetworkDisruptionIT.java index e2dc111007aac..6a3ff6551503f 100644 --- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/NetworkDisruptionIT.java +++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/NetworkDisruptionIT.java @@ -81,12 +81,9 @@ public void testJobRelocation() throws Exception { assertEquals(newJobNode, finalJobNode); // The job running on the original node should have been killed, and hence should not have persisted quantiles - SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setQuery(QueryBuilders.idsQuery().addIds(Quantiles.documentId(job.getId()))) - .setTrackTotalHits(true) - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .execute() - .actionGet(); + SearchResponse searchResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setQuery( + QueryBuilders.idsQuery().addIds(Quantiles.documentId(job.getId())) + ).setTrackTotalHits(true).setIndicesOptions(IndicesOptions.lenientExpandOpen()).execute().actionGet(); assertEquals(0L, searchResponse.getHits().getTotalHits().value); CloseJobAction.Request closeJobRequest = new CloseJobAction.Request(job.getId()); @@ -94,12 +91,9 @@ public void testJobRelocation() throws Exception { assertTrue(closeJobResponse.isClosed()); // The relocated job was closed rather than killed, and hence should have persisted quantiles - searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()) - .setQuery(QueryBuilders.idsQuery().addIds(Quantiles.documentId(job.getId()))) - .setTrackTotalHits(true) - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .execute() - .actionGet(); + searchResponse = prepareSearch(AnomalyDetectorsIndex.jobStateIndexPattern()).setQuery( + QueryBuilders.idsQuery().addIds(Quantiles.documentId(job.getId())) + ).setTrackTotalHits(true).setIndicesOptions(IndicesOptions.lenientExpandOpen()).execute().actionGet(); assertEquals(1L, searchResponse.getHits().getTotalHits().value); } } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/persistence/MockClientBuilder.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/persistence/MockClientBuilder.java index d34343a4dc200..59a79def9bd10 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/persistence/MockClientBuilder.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/persistence/MockClientBuilder.java @@ -29,14 +29,10 @@ import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; -import org.elasticsearch.search.sort.SortBuilder; -import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xcontent.XContentBuilder; -import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -109,22 +105,6 @@ public MockClientBuilder prepareCreate(String index) { return this; } - public MockClientBuilder prepareSearch(String index, int from, int size, SearchResponse response, ArgumentCaptor filter) { - SearchRequestBuilder builder = mock(SearchRequestBuilder.class); - when(builder.addSort(any(SortBuilder.class))).thenReturn(builder); - when(builder.setQuery(filter.capture())).thenReturn(builder); - when(builder.setPostFilter(filter.capture())).thenReturn(builder); - when(builder.setFrom(eq(from))).thenReturn(builder); - when(builder.setSize(eq(size))).thenReturn(builder); - when(builder.setFetchSource(eq(true))).thenReturn(builder); - when(builder.addDocValueField(any(String.class))).thenReturn(builder); - when(builder.addDocValueField(any(String.class), any(String.class))).thenReturn(builder); - when(builder.addSort(any(String.class), any(SortOrder.class))).thenReturn(builder); - when(builder.get()).thenReturn(response); - when(client.prepareSearch(eq(index))).thenReturn(builder); - return this; - } - public MockClientBuilder prepareSearches(String index, SearchRequestBuilder first, SearchRequestBuilder... searches) { when(client.prepareSearch(eq(index))).thenReturn(first, searches); return this; diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java index 29b044e81f7d6..02c9a8e2f210c 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java @@ -78,11 +78,9 @@ public void testMultipleNodes() throws Exception { flush(ALL_MONITORING_INDICES); refresh(); - SearchResponse response = client().prepareSearch(ALL_MONITORING_INDICES) - .setQuery(QueryBuilders.termQuery("type", NodeStatsMonitoringDoc.TYPE)) - .setSize(0) - .addAggregation(AggregationBuilders.terms("nodes_ids").field("node_stats.node_id")) - .get(); + SearchResponse response = prepareSearch(ALL_MONITORING_INDICES).setQuery( + QueryBuilders.termQuery("type", NodeStatsMonitoringDoc.TYPE) + ).setSize(0).addAggregation(AggregationBuilders.terms("nodes_ids").field("node_stats.node_id")).get(); for (Aggregation aggregation : response.getAggregations()) { assertThat(aggregation, instanceOf(StringTerms.class)); diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java index 503d83162df5e..91dff9abcc5e2 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java @@ -111,7 +111,7 @@ public void testExport() throws Exception { assertThat(indexExists(".monitoring-*"), is(true)); ensureYellowAndNoInitializingShards(".monitoring-*"); - SearchResponse response = client().prepareSearch(".monitoring-*").get(); + SearchResponse response = prepareSearch(".monitoring-*").get(); assertThat((long) nbDocs, lessThanOrEqualTo(response.getHits().getTotalHits().value)); }); @@ -125,8 +125,7 @@ public void testExport() throws Exception { ensureYellowAndNoInitializingShards(".monitoring-*"); assertThat( - client().prepareSearch(".monitoring-es-*") - .setSize(0) + prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "cluster_stats")) .get() .getHits() @@ -135,8 +134,7 @@ public void testExport() throws Exception { ); assertThat( - client().prepareSearch(".monitoring-es-*") - .setSize(0) + prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "index_recovery")) .get() .getHits() @@ -145,8 +143,7 @@ public void testExport() throws Exception { ); assertThat( - client().prepareSearch(".monitoring-es-*") - .setSize(0) + prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "index_stats")) .get() .getHits() @@ -155,8 +152,7 @@ public void testExport() throws Exception { ); assertThat( - client().prepareSearch(".monitoring-es-*") - .setSize(0) + prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "indices_stats")) .get() .getHits() @@ -165,8 +161,7 @@ public void testExport() throws Exception { ); assertThat( - client().prepareSearch(".monitoring-es-*") - .setSize(0) + prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "shards")) .get() .getHits() @@ -174,8 +169,7 @@ public void testExport() throws Exception { greaterThan(0L) ); - SearchResponse response = client().prepareSearch(".monitoring-es-*") - .setSize(0) + SearchResponse response = prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "node_stats")) .addAggregation(terms("agg_nodes_ids").field("node_stats.node_id")) .get(); @@ -212,8 +206,7 @@ public void testExport() throws Exception { ensureYellowAndNoInitializingShards(".monitoring-*"); refresh(".monitoring-es-*"); - SearchResponse response = client().prepareSearch(".monitoring-es-*") - .setSize(0) + SearchResponse response = prepareSearch(".monitoring-es-*").setSize(0) .setQuery(QueryBuilders.termQuery("type", "node_stats")) .addAggregation( terms("agg_nodes_ids").field("node_stats.node_id").subAggregation(max("agg_last_time_collected").field("timestamp")) @@ -271,7 +264,7 @@ private void checkMonitoringDocs() { DateFormatter dateParser = DateFormatter.forPattern("strict_date_time"); DateFormatter dateFormatter = DateFormatter.forPattern(customTimeFormat).withZone(ZoneOffset.UTC); - SearchResponse searchResponse = client().prepareSearch(".monitoring-*").setSize(100).get(); + SearchResponse searchResponse = prepareSearch(".monitoring-*").setSize(100).get(); assertThat(searchResponse.getHits().getTotalHits().value, greaterThan(0L)); for (SearchHit hit : searchResponse.getHits().getHits()) { diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterResourceIntegTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterResourceIntegTests.java index d96ee3726a032..71590943e4bf6 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterResourceIntegTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterResourceIntegTests.java @@ -265,7 +265,7 @@ private void assertWatchesExist() { SearchSourceBuilder searchSource = SearchSourceBuilder.searchSource() .query(QueryBuilders.matchQuery("metadata.xpack.cluster_uuid", clusterUUID)); Set watchIds = new HashSet<>(Arrays.asList(ClusterAlertsUtil.WATCH_IDS)); - for (SearchHit hit : client().prepareSearch(".watches").setSource(searchSource).get().getHits().getHits()) { + for (SearchHit hit : prepareSearch(".watches").setSource(searchSource).get().getHits().getHits()) { String watchId = ObjectPath.eval("metadata.xpack.watch", hit.getSourceAsMap()); assertNotNull("Missing watch ID", watchId); assertTrue("found unexpected watch id", watchIds.contains(watchId)); @@ -289,7 +289,7 @@ private void assertNoWatchesExist() { String clusterUUID = clusterService().state().getMetadata().clusterUUID(); SearchSourceBuilder searchSource = SearchSourceBuilder.searchSource() .query(QueryBuilders.matchQuery("metadata.xpack.cluster_uuid", clusterUUID)); - SearchResponse searchResponse = client().prepareSearch(".watches").setSource(searchSource).get(); + SearchResponse searchResponse = prepareSearch(".watches").setSource(searchSource).get(); if (searchResponse.getHits().getTotalHits().value > 0) { List invalidWatches = new ArrayList<>(); for (SearchHit hit : searchResponse.getHits().getHits()) { diff --git a/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankCoordinatorCanMatchIT.java b/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankCoordinatorCanMatchIT.java index 640d4ca2f9392..c5fac43723b70 100644 --- a/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankCoordinatorCanMatchIT.java +++ b/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankCoordinatorCanMatchIT.java @@ -130,8 +130,7 @@ public void testCanMatchCoordinator() throws Exception { }); // match 2 separate shard with no overlap in queries - SearchResponse response = client().prepareSearch("time_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("time_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -150,8 +149,7 @@ public void testCanMatchCoordinator() throws Exception { assertEquals(3, response.getSkippedShards()); // match 2 shards with overlap in queries - response = client().prepareSearch("time_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("time_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -170,8 +168,7 @@ public void testCanMatchCoordinator() throws Exception { assertEquals(3, response.getSkippedShards()); // match one shard with one query in range and one query out of range - response = client().prepareSearch("time_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("time_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -190,8 +187,7 @@ public void testCanMatchCoordinator() throws Exception { assertEquals(4, response.getSkippedShards()); // match no shards, but still use one to generate a search response - response = client().prepareSearch("time_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("time_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -210,8 +206,7 @@ public void testCanMatchCoordinator() throws Exception { assertEquals(4, response.getSkippedShards()); // match one shard with with no overlap in queries - response = client().prepareSearch("time_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("time_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -230,8 +225,7 @@ public void testCanMatchCoordinator() throws Exception { assertEquals(4, response.getSkippedShards()); // match one shard with exact overlap in queries - response = client().prepareSearch("time_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("time_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) diff --git a/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankMultiShardIT.java b/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankMultiShardIT.java index 63afb47273cb1..3db050e071aa7 100644 --- a/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankMultiShardIT.java +++ b/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankMultiShardIT.java @@ -139,8 +139,7 @@ public void setupSuiteScopeCluster() throws Exception { public void testTotalDocsSmallerThanSize() { float[] queryVector = { 0.0f }; KnnSearchBuilder knnSearch = new KnnSearchBuilder("vector", queryVector, 3, 3, null); - SearchResponse response = client().prepareSearch("tiny_index") - .setRankBuilder(new RRFRankBuilder(100, 1)) + SearchResponse response = prepareSearch("tiny_index").setRankBuilder(new RRFRankBuilder(100, 1)) .setKnnSearch(List.of(knnSearch)) .setQuery(QueryBuilders.termQuery("text", "term")) .addFetchField("vector") @@ -170,8 +169,7 @@ public void testTotalDocsSmallerThanSize() { public void testBM25AndKnn() { float[] queryVector = { 500.0f }; KnnSearchBuilder knnSearch = new KnnSearchBuilder("vector_asc", queryVector, 101, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(101, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(101, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearch)) .setQuery( @@ -211,8 +209,7 @@ public void testMultipleOnlyKnn() { float[] queryVectorDesc = { 500.0f }; KnnSearchBuilder knnSearchAsc = new KnnSearchBuilder("vector_asc", queryVectorAsc, 51, 1001, null); KnnSearchBuilder knnSearchDesc = new KnnSearchBuilder("vector_desc", queryVectorDesc, 51, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(51, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(51, 1)) .setTrackTotalHits(true) .setKnnSearch(List.of(knnSearchAsc, knnSearchDesc)) .addFetchField("vector_asc") @@ -262,8 +259,7 @@ public void testBM25AndMultipleKnn() { float[] queryVectorDesc = { 500.0f }; KnnSearchBuilder knnSearchAsc = new KnnSearchBuilder("vector_asc", queryVectorAsc, 51, 1001, null); KnnSearchBuilder knnSearchDesc = new KnnSearchBuilder("vector_desc", queryVectorDesc, 51, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(51, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(51, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearchAsc, knnSearchDesc)) .setQuery( @@ -332,8 +328,7 @@ public void testBM25AndMultipleKnn() { public void testBM25AndKnnWithBucketAggregation() { float[] queryVector = { 500.0f }; KnnSearchBuilder knnSearch = new KnnSearchBuilder("vector_asc", queryVector, 101, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(101, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(101, 1)) .setTrackTotalHits(true) .setKnnSearch(List.of(knnSearch)) .setQuery( @@ -389,8 +384,7 @@ public void testMultipleOnlyKnnWithAggregation() { float[] queryVectorDesc = { 500.0f }; KnnSearchBuilder knnSearchAsc = new KnnSearchBuilder("vector_asc", queryVectorAsc, 51, 1001, null); KnnSearchBuilder knnSearchDesc = new KnnSearchBuilder("vector_desc", queryVectorDesc, 51, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(51, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(51, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearchAsc, knnSearchDesc)) .addFetchField("vector_asc") @@ -456,8 +450,7 @@ public void testBM25AndMultipleKnnWithAggregation() { float[] queryVectorDesc = { 500.0f }; KnnSearchBuilder knnSearchAsc = new KnnSearchBuilder("vector_asc", queryVectorAsc, 51, 1001, null); KnnSearchBuilder knnSearchDesc = new KnnSearchBuilder("vector_desc", queryVectorDesc, 51, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(51, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(51, 1)) .setTrackTotalHits(true) .setKnnSearch(List.of(knnSearchAsc, knnSearchDesc)) .setQuery( @@ -542,8 +535,7 @@ public void testBM25AndMultipleKnnWithAggregation() { public void testMultiBM25() { for (SearchType searchType : SearchType.CURRENTLY_SUPPORTED) { - SearchResponse response = client().prepareSearch("nrd_index") - .setSearchType(searchType) + SearchResponse response = prepareSearch("nrd_index").setSearchType(searchType) .setRankBuilder(new RRFRankBuilder(8, 1)) .setTrackTotalHits(false) .setSubSearches( @@ -612,8 +604,7 @@ public void testMultiBM25() { public void testMultiBM25WithAggregation() { for (SearchType searchType : SearchType.CURRENTLY_SUPPORTED) { - SearchResponse response = client().prepareSearch("nrd_index") - .setSearchType(searchType) + SearchResponse response = prepareSearch("nrd_index").setSearchType(searchType) .setRankBuilder(new RRFRankBuilder(8, 1)) .setTrackTotalHits(false) .setSubSearches( @@ -699,8 +690,7 @@ public void testMultiBM25WithAggregation() { public void testMultiBM25AndSingleKnn() { float[] queryVector = { 500.0f }; KnnSearchBuilder knnSearch = new KnnSearchBuilder("vector_asc", queryVector, 101, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(101, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(101, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearch)) .setSubSearches( @@ -756,8 +746,7 @@ public void testMultiBM25AndSingleKnn() { public void testMultiBM25AndSingleKnnWithAggregation() { float[] queryVector = { 500.0f }; KnnSearchBuilder knnSearch = new KnnSearchBuilder("vector_asc", queryVector, 101, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(101, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(101, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearch)) .setSubSearches( @@ -831,8 +820,7 @@ public void testMultiBM25AndMultipleKnn() { float[] queryVectorDesc = { 500.0f }; KnnSearchBuilder knnSearchAsc = new KnnSearchBuilder("vector_asc", queryVectorAsc, 101, 1001, null); KnnSearchBuilder knnSearchDesc = new KnnSearchBuilder("vector_desc", queryVectorDesc, 101, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(101, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(101, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearchAsc, knnSearchDesc)) .setSubSearches( @@ -892,8 +880,7 @@ public void testMultiBM25AndMultipleKnnWithAggregation() { float[] queryVectorDesc = { 500.0f }; KnnSearchBuilder knnSearchAsc = new KnnSearchBuilder("vector_asc", queryVectorAsc, 101, 1001, null); KnnSearchBuilder knnSearchDesc = new KnnSearchBuilder("vector_desc", queryVectorDesc, 101, 1001, null); - SearchResponse response = client().prepareSearch("nrd_index") - .setRankBuilder(new RRFRankBuilder(101, 1)) + SearchResponse response = prepareSearch("nrd_index").setRankBuilder(new RRFRankBuilder(101, 1)) .setTrackTotalHits(false) .setKnnSearch(List.of(knnSearchAsc, knnSearchDesc)) .setSubSearches( diff --git a/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankShardCanMatchIT.java b/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankShardCanMatchIT.java index a7019d76cdcd1..0e51958ea164e 100644 --- a/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankShardCanMatchIT.java +++ b/x-pack/plugin/rank-rrf/src/internalClusterTest/java/org/elasticsearch/xpack/rank/rrf/RRFRankShardCanMatchIT.java @@ -144,8 +144,7 @@ public void testCanMatchShard() throws IOException { indicesAdmin().prepareRefresh("value_index").get(); // match 2 separate shard with no overlap in queries - SearchResponse response = client().prepareSearch("value_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + SearchResponse response = prepareSearch("value_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -164,8 +163,7 @@ public void testCanMatchShard() throws IOException { assertEquals(3, response.getSkippedShards()); // match one shard with one query and do not match the other shard with one query - response = client().prepareSearch("value_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("value_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -184,8 +182,7 @@ public void testCanMatchShard() throws IOException { assertEquals(4, response.getSkippedShards()); // match no shards, but still use one to generate a search response - response = client().prepareSearch("value_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("value_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -204,8 +201,7 @@ public void testCanMatchShard() throws IOException { assertEquals(4, response.getSkippedShards()); // match the same shard for both queries - response = client().prepareSearch("value_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("value_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) @@ -224,8 +220,7 @@ public void testCanMatchShard() throws IOException { assertEquals(4, response.getSkippedShards()); // match one shard with the exact same query - response = client().prepareSearch("value_index") - .setSearchType(SearchType.QUERY_THEN_FETCH) + response = prepareSearch("value_index").setSearchType(SearchType.QUERY_THEN_FETCH) .setPreFilterShardSize(1) .setRankBuilder(new RRFRankBuilder(20, 1)) .setTrackTotalHits(false) diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/BaseSearchableSnapshotsIntegTestCase.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/BaseSearchableSnapshotsIntegTestCase.java index 71c3253b0607b..b07d307f105c0 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/BaseSearchableSnapshotsIntegTestCase.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/BaseSearchableSnapshotsIntegTestCase.java @@ -289,15 +289,10 @@ protected void assertTotalHits(String indexName, TotalHits originalAllHits, Tota } catch (InterruptedException e) { throw new RuntimeException(e); } - allHits.set(t, client().prepareSearch(indexName).setTrackTotalHits(true).get().getHits().getTotalHits()); + allHits.set(t, prepareSearch(indexName).setTrackTotalHits(true).get().getHits().getTotalHits()); barHits.set( t, - client().prepareSearch(indexName) - .setTrackTotalHits(true) - .setQuery(matchQuery("foo", "bar")) - .get() - .getHits() - .getTotalHits() + prepareSearch(indexName).setTrackTotalHits(true).setQuery(matchQuery("foo", "bar")).get().getHits().getTotalHits() ); }); threads[i].start(); diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsIntegTests.java index 8e414002bfa60..81ea36e88628c 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsIntegTests.java @@ -478,7 +478,7 @@ public void testMaxRestoreBytesPerSecIsUsed() throws Exception { assertThat(restore.getRestoreInfo().failedShards(), equalTo(0)); ensureGreen(restoredIndexName); - assertHitCount(client().prepareSearch(restoredIndexName).setSize(0), nbDocs); + assertHitCount(prepareSearch(restoredIndexName).setSize(0), nbDocs); final Index restoredIndex = resolveIndex(restoredIndexName); for (String node : internalCluster().getNodeNames()) { diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRecoverFromSnapshotIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRecoverFromSnapshotIntegTests.java index 1e2d288af2fa7..6f71f7c33bf06 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRecoverFromSnapshotIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRecoverFromSnapshotIntegTests.java @@ -87,7 +87,7 @@ public void testSearchableSnapshotRelocationDoNotUseSnapshotBasedRecoveries() th ensureGreen(restoredIndexName); - assertHitCount(client().prepareSearch(restoredIndexName).setTrackTotalHits(true), totalHits.value); + assertHitCount(prepareSearch(restoredIndexName).setTrackTotalHits(true), totalHits.value); mockAppender.assertAllExpectationsMatched(); Loggers.removeAppender(logger, mockAppender); diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRepositoryIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRepositoryIntegTests.java index d3cdfce0b8175..cb6cf45b641c6 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRepositoryIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsRepositoryIntegTests.java @@ -68,7 +68,7 @@ public void testRepositoryUsedBySearchableSnapshotCanBeUpdatedButNotUnregistered Storage storage = randomFrom(Storage.values()); String restoredIndexName = (storage == Storage.FULL_COPY ? "fully-mounted-" : "partially-mounted-") + indexName + '-' + i; mountSnapshot(repositoryName, snapshotName, indexName, restoredIndexName, Settings.EMPTY, storage); - assertHitCount(client().prepareSearch(restoredIndexName).setTrackTotalHits(true), totalHits.value); + assertHitCount(prepareSearch(restoredIndexName).setTrackTotalHits(true), totalHits.value); mountedIndices[i] = restoredIndexName; } @@ -182,7 +182,7 @@ public void testMountIndexWithDifferentDeletionOfSnapshot() throws Exception { ? equalTo(Boolean.toString(deleteSnapshot)) : nullValue() ); - assertHitCount(client().prepareSearch(mounted).setTrackTotalHits(true), totalHits.value); + assertHitCount(prepareSearch(mounted).setTrackTotalHits(true), totalHits.value); final String mountedAgain = randomValueOtherThan(mounted, () -> randomAlphaOfLength(10).toLowerCase(Locale.ROOT)); final SnapshotRestoreException exception = expectThrows( @@ -207,7 +207,7 @@ public void testMountIndexWithDifferentDeletionOfSnapshot() throws Exception { ? equalTo(Boolean.toString(deleteSnapshot)) : nullValue() ); - assertHitCount(client().prepareSearch(mountedAgain).setTrackTotalHits(true), totalHits.value); + assertHitCount(prepareSearch(mountedAgain).setTrackTotalHits(true), totalHits.value); assertAcked(indicesAdmin().prepareDelete(mountedAgain)); assertAcked(indicesAdmin().prepareDelete(mounted)); @@ -237,7 +237,7 @@ public void testDeletionOfSnapshotSettingCannotBeUpdated() throws Exception { ? equalTo(Boolean.toString(deleteSnapshot)) : nullValue() ); - assertHitCount(client().prepareSearch(mounted).setTrackTotalHits(true), totalHits.value); + assertHitCount(prepareSearch(mounted).setTrackTotalHits(true), totalHits.value); if (randomBoolean()) { assertAcked(indicesAdmin().prepareClose(mounted)); diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java index 7a4a92ac600ac..0a2056c94ded2 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java @@ -225,7 +225,7 @@ public void testBlobStoreCache() throws Exception { final long numberOfCacheWrites = indexingStats != null ? indexingStats.getTotal().getIndexCount() : 0L; logger.info("--> verifying number of documents in index [{}]", restoredIndex); - assertHitCount(client().prepareSearch(restoredIndex).setSize(0).setTrackTotalHits(true), numberOfDocs); + assertHitCount(prepareSearch(restoredIndex).setSize(0).setTrackTotalHits(true), numberOfDocs); for (IndicesService indicesService : internalCluster().getDataNodeInstances(IndicesService.class)) { for (IndexService indexService : indicesService) { @@ -266,7 +266,7 @@ public void testBlobStoreCache() throws Exception { checkNoBlobStoreAccess(); logger.info("--> verifying number of documents in index [{}]", restoredAgainIndex); - assertHitCount(client().prepareSearch(restoredAgainIndex).setSize(0).setTrackTotalHits(true), numberOfDocs); + assertHitCount(prepareSearch(restoredAgainIndex).setSize(0).setTrackTotalHits(true), numberOfDocs); logger.info("--> verifying that no extra cached blobs were indexed [{}]", SNAPSHOT_BLOB_CACHE_INDEX); refreshSystemIndex(); @@ -322,7 +322,7 @@ public Settings onNodeStopped(String nodeName) throws Exception { assertThat(indexingStats != null ? indexingStats.getTotal().getIndexCount() : 0L, equalTo(0L)); logger.info("--> verifying number of documents in index [{}]", restoredAgainIndex); - assertHitCount(client().prepareSearch(restoredAgainIndex).setSize(0).setTrackTotalHits(true), numberOfDocs); + assertHitCount(prepareSearch(restoredAgainIndex).setSize(0).setTrackTotalHits(true), numberOfDocs); logger.info("--> deleting indices, maintenance service should clean up [{}] docs in system index", numberOfCachedBlobs); assertAcked(indicesAdmin().prepareDelete("restored-*")); diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/NodesCachesStatsIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/NodesCachesStatsIntegTests.java index 28a537c5da9ec..0d1bc7eec94bc 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/NodesCachesStatsIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/NodesCachesStatsIntegTests.java @@ -107,14 +107,11 @@ public void testNodesCachesStats() throws Exception { } for (int i = 0; i < 20; i++) { - client().prepareSearch(mountedIndex) - .setQuery( - randomBoolean() - ? QueryBuilders.rangeQuery("id").gte(randomIntBetween(0, 1000)) - : QueryBuilders.termQuery("test", "value" + randomIntBetween(0, 1000)) - ) - .setSize(randomIntBetween(0, 1000)) - .get(); + prepareSearch(mountedIndex).setQuery( + randomBoolean() + ? QueryBuilders.rangeQuery("id").gte(randomIntBetween(0, 1000)) + : QueryBuilders.termQuery("test", "value" + randomIntBetween(0, 1000)) + ).setSize(randomIntBetween(0, 1000)).get(); } assertExecutorIsIdle(SearchableSnapshots.CACHE_FETCH_ASYNC_THREAD_POOL_NAME); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityRandomTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityRandomTests.java index 1aa5c9609f6a1..b5e5183df086d 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityRandomTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityRandomTests.java @@ -108,7 +108,7 @@ public void testDuelWithAliasFilters() throws Exception { SearchResponse searchResponse1 = client().filterWithHeader( Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user" + roleI, USERS_PASSWD)) ).prepareSearch("test").get(); - SearchResponse searchResponse2 = client().prepareSearch("alias" + roleI).get(); + SearchResponse searchResponse2 = prepareSearch("alias" + roleI).get(); assertThat(searchResponse1.getHits().getTotalHits().value, equalTo(searchResponse2.getHits().getTotalHits().value)); for (int hitI = 0; hitI < searchResponse1.getHits().getHits().length; hitI++) { assertThat(searchResponse1.getHits().getAt(hitI).getId(), equalTo(searchResponse2.getHits().getAt(hitI).getId())); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java index be3162ac98b3b..a76b043737375 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java @@ -455,8 +455,8 @@ public void testMSearch() throws Exception { Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD)) ) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -472,8 +472,8 @@ public void testMSearch() throws Exception { response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -490,14 +490,10 @@ public void testMSearch() throws Exception { response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) .prepareMultiSearch() .add( - client().prepareSearch("test1") - .addSort(SortBuilders.fieldSort("id").sortMode(SortMode.MIN)) - .setQuery(QueryBuilders.matchAllQuery()) + prepareSearch("test1").addSort(SortBuilders.fieldSort("id").sortMode(SortMode.MIN)).setQuery(QueryBuilders.matchAllQuery()) ) .add( - client().prepareSearch("test2") - .addSort(SortBuilders.fieldSort("id").sortMode(SortMode.MIN)) - .setQuery(QueryBuilders.matchAllQuery()) + prepareSearch("test2").addSort(SortBuilders.fieldSort("id").sortMode(SortMode.MIN)).setQuery(QueryBuilders.matchAllQuery()) ) .get(); assertFalse(response.getResponses()[0].isFailure()); @@ -925,9 +921,9 @@ public void testGlobalAggregation() throws Exception { client().prepareIndex("test").setId("2").setSource("field2", "value2").setRefreshPolicy(IMMEDIATE).get(); client().prepareIndex("test").setId("3").setSource("field3", "value3").setRefreshPolicy(IMMEDIATE).get(); - SearchResponse response = client().prepareSearch("test") - .addAggregation(AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("field2").field("field2"))) - .get(); + SearchResponse response = prepareSearch("test").addAggregation( + AggregationBuilders.global("global").subAggregation(AggregationBuilders.terms("field2").field("field2")) + ).get(); assertHitCount(response, 3); assertSearchHits(response, "1", "2", "3"); @@ -1023,14 +1019,11 @@ public void testParentChild() throws Exception { } private void verifyParentChild() { - SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) - .get(); + SearchResponse searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.None)).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - searchResponse = client().prepareSearch("test") - .setQuery(hasParentQuery("parent", matchAllQuery(), false)) + searchResponse = prepareSearch("test").setQuery(hasParentQuery("parent", matchAllQuery(), false)) .addSort("id", SortOrder.ASC) .get(); assertHitCount(searchResponse, 3L); @@ -1356,9 +1349,9 @@ public void testSuggesters() throws Exception { ); // Term suggester: - SearchResponse response = client().prepareSearch("test") - .suggest(new SuggestBuilder().setGlobalText("valeu").addSuggestion("_name1", new TermSuggestionBuilder("suggest_field1"))) - .get(); + SearchResponse response = prepareSearch("test").suggest( + new SuggestBuilder().setGlobalText("valeu").addSuggestion("_name1", new TermSuggestionBuilder("suggest_field1")) + ).get(); assertNoFailures(response); TermSuggestion termSuggestion = response.getSuggest().getSuggestion("_name1"); @@ -1381,9 +1374,9 @@ public void testSuggesters() throws Exception { assertThat(e.getMessage(), equalTo("Suggest isn't supported if document level security is enabled")); // Phrase suggester: - response = client().prepareSearch("test") - .suggest(new SuggestBuilder().setGlobalText("valeu").addSuggestion("_name1", new PhraseSuggestionBuilder("suggest_field1"))) - .get(); + response = prepareSearch("test").suggest( + new SuggestBuilder().setGlobalText("valeu").addSuggestion("_name1", new PhraseSuggestionBuilder("suggest_field1")) + ).get(); assertNoFailures(response); PhraseSuggestion phraseSuggestion = response.getSuggest().getSuggestion("_name1"); @@ -1402,9 +1395,9 @@ public void testSuggesters() throws Exception { assertThat(e.getMessage(), equalTo("Suggest isn't supported if document level security is enabled")); // Completion suggester: - response = client().prepareSearch("test") - .suggest(new SuggestBuilder().setGlobalText("valu").addSuggestion("_name1", new CompletionSuggestionBuilder("suggest_field2"))) - .get(); + response = prepareSearch("test").suggest( + new SuggestBuilder().setGlobalText("valu").addSuggestion("_name1", new CompletionSuggestionBuilder("suggest_field2")) + ).get(); assertNoFailures(response); CompletionSuggestion completionSuggestion = response.getSuggest().getSuggestion("_name1"); @@ -1449,10 +1442,7 @@ public void testProfile() throws Exception { .setMapping("field1", "type=text", "other_field", "type=text", "yet_another", "type=text") ); - SearchResponse response = client().prepareSearch("test") - .setProfile(true) - .setQuery(new FuzzyQueryBuilder("other_field", "valeu")) - .get(); + SearchResponse response = prepareSearch("test").setProfile(true).setQuery(new FuzzyQueryBuilder("other_field", "valeu")).get(); assertNoFailures(response); assertThat(response.getProfileResults().size(), equalTo(1)); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityRandomTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityRandomTests.java index 8c69a96648a3a..f9bd893ea3653 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityRandomTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityRandomTests.java @@ -206,8 +206,7 @@ public void testDuel() throws Exception { .should(QueryBuilders.termQuery("field3", "value")) ) .get(); - SearchResponse expected = client().prepareSearch("test") - .addSort("id", SortOrder.ASC) + SearchResponse expected = prepareSearch("test").addSort("id", SortOrder.ASC) .setQuery(QueryBuilders.boolQuery().should(QueryBuilders.termQuery("field1", "value"))) .get(); assertThat(actual.getHits().getTotalHits().value, equalTo(expected.getHits().getTotalHits().value)); @@ -226,8 +225,7 @@ public void testDuel() throws Exception { .should(QueryBuilders.termQuery("field3", "value")) ) .get(); - expected = client().prepareSearch("test") - .addSort("id", SortOrder.ASC) + expected = prepareSearch("test").addSort("id", SortOrder.ASC) .setQuery(QueryBuilders.boolQuery().should(QueryBuilders.termQuery("field2", "value"))) .get(); assertThat(actual.getHits().getTotalHits().value, equalTo(expected.getHits().getTotalHits().value)); @@ -246,8 +244,7 @@ public void testDuel() throws Exception { .should(QueryBuilders.termQuery("field3", "value")) ) .get(); - expected = client().prepareSearch("test") - .addSort("id", SortOrder.ASC) + expected = prepareSearch("test").addSort("id", SortOrder.ASC) .setQuery(QueryBuilders.boolQuery().should(QueryBuilders.termQuery("field3", "value"))) .get(); assertThat(actual.getHits().getTotalHits().value, equalTo(expected.getHits().getTotalHits().value)); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java index 5d7644efccfe5..d5d48440c34ea 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/FieldLevelSecurityTests.java @@ -934,8 +934,8 @@ public void testMSearchApi() throws Exception { Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user1", USERS_PASSWD)) ) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -948,8 +948,8 @@ public void testMSearchApi() throws Exception { // user2 is granted access to field2 only response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user2", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -962,8 +962,8 @@ public void testMSearchApi() throws Exception { // user3 is granted access to field1 and field2 response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user3", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -978,8 +978,8 @@ public void testMSearchApi() throws Exception { // user4 is granted access to no fields, so the search response does say the doc exist, but no fields are returned response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user4", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -990,8 +990,8 @@ public void testMSearchApi() throws Exception { // user5 has no field level security configured, so all fields are returned response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user5", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -1008,8 +1008,8 @@ public void testMSearchApi() throws Exception { // user6 has access to field* response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user6", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -1026,8 +1026,8 @@ public void testMSearchApi() throws Exception { // user7 has roles with field level security and without field level security response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user7", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); @@ -1044,8 +1044,8 @@ public void testMSearchApi() throws Exception { // user8 has roles with field level security with access to field1 and field2 response = client().filterWithHeader(Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("user8", USERS_PASSWD))) .prepareMultiSearch() - .add(client().prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) - .add(client().prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test1").setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch("test2").setQuery(QueryBuilders.matchAllQuery())) .get(); assertFalse(response.getResponses()[0].isFailure()); assertThat(response.getResponses()[0].getResponse().getHits().getTotalHits().value, is(1L)); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/KibanaUserRoleIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/KibanaUserRoleIntegTests.java index fe0dd919d7453..e2cce37789ffb 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/KibanaUserRoleIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/KibanaUserRoleIntegTests.java @@ -102,7 +102,7 @@ public void testSearchAndMSearch() throws Exception { final String field = "foo"; indexRandom(true, client().prepareIndex().setIndex(index).setSource(field, "bar")); - SearchResponse response = client().prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()).get(); + SearchResponse response = prepareSearch(index).setQuery(QueryBuilders.matchAllQuery()).get(); final long hits = response.getHits().getTotalHits().value; assertThat(hits, greaterThan(0L)); response = client().filterWithHeader( @@ -111,13 +111,13 @@ public void testSearchAndMSearch() throws Exception { assertEquals(response.getHits().getTotalHits().value, hits); MultiSearchResponse multiSearchResponse = client().prepareMultiSearch() - .add(client().prepareSearch(index).setQuery(QueryBuilders.matchAllQuery())) + .add(prepareSearch(index).setQuery(QueryBuilders.matchAllQuery())) .get(); final long multiHits = multiSearchResponse.getResponses()[0].getResponse().getHits().getTotalHits().value; assertThat(hits, greaterThan(0L)); multiSearchResponse = client().filterWithHeader( singletonMap("Authorization", UsernamePasswordToken.basicAuthHeaderValue("kibana_user", USERS_PASSWD)) - ).prepareMultiSearch().add(client().prepareSearch(index).setQuery(QueryBuilders.matchAllQuery())).get(); + ).prepareMultiSearch().add(prepareSearch(index).setQuery(QueryBuilders.matchAllQuery())).get(); assertEquals(multiSearchResponse.getResponses()[0].getResponse().getHits().getTotalHits().value, multiHits); } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityCachePermissionTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityCachePermissionTests.java index 49bb1034ee0bc..d2e3907204654 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityCachePermissionTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityCachePermissionTests.java @@ -47,10 +47,9 @@ public void loadData() { } public void testThatTermsFilterQueryDoesntLeakData() { - SearchResponse response = client().prepareSearch("data") - .setQuery(QueryBuilders.constantScoreQuery(QueryBuilders.termsLookupQuery("token", new TermsLookup("tokens", "1", "tokens")))) - .execute() - .actionGet(); + SearchResponse response = prepareSearch("data").setQuery( + QueryBuilders.constantScoreQuery(QueryBuilders.termsLookupQuery("token", new TermsLookup("tokens", "1", "tokens"))) + ).execute().actionGet(); assertThat(response.isTimedOut(), is(false)); assertThat(response.getHits().getHits().length, is(1)); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityClearScrollTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityClearScrollTests.java index 83cda0e51851d..ca826be904771 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityClearScrollTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/SecurityClearScrollTests.java @@ -78,7 +78,7 @@ public void indexRandomDocuments() { MultiSearchRequestBuilder multiSearchRequestBuilder = client().prepareMultiSearch(); int count = randomIntBetween(5, 15); for (int i = 0; i < count; i++) { - multiSearchRequestBuilder.add(client().prepareSearch("index").setScroll("10m").setSize(1)); + multiSearchRequestBuilder.add(prepareSearch("index").setScroll("10m").setSize(1)); } MultiSearchResponse multiSearchResponse = multiSearchRequestBuilder.get(); scrollIds = getScrollIds(multiSearchResponse); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ShrinkIndexWithSecurityTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ShrinkIndexWithSecurityTests.java index 1a663678a738b..c5efabfca13db 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ShrinkIndexWithSecurityTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/ShrinkIndexWithSecurityTests.java @@ -55,9 +55,6 @@ public void testShrinkIndex() throws Exception { // verify all docs ensureGreen(); - assertHitCount( - client().prepareSearch("shrunk_bigindex").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), - randomNumberOfDocs - ); + assertHitCount(prepareSearch("shrunk_bigindex").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), randomNumberOfDocs); } } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java index f48a6eaf2655f..6220fc2ae2c2c 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/ReadActionsTests.java @@ -410,11 +410,11 @@ public void testMultiTermVectors() { } private SearchRequestBuilder trySearch(String... indices) { - return client().prepareSearch(indices); + return prepareSearch(indices); } private SearchRequestBuilder trySearch(IndicesOptions options, String... indices) { - return client().prepareSearch(indices).setIndicesOptions(options); + return prepareSearch(indices).setIndicesOptions(options); } private static T expectThrows(Class expectedType, SearchRequestBuilder searchRequestBuilder) { diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java index 0ead996103256..6a439dd5c8561 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java @@ -46,11 +46,7 @@ public void testScrollIsPerUser() throws Exception { } indexRandom(true, docs); - SearchResponse response = client().prepareSearch("foo") - .setScroll(TimeValue.timeValueSeconds(5L)) - .setQuery(matchAllQuery()) - .setSize(1) - .get(); + SearchResponse response = prepareSearch("foo").setScroll(TimeValue.timeValueSeconds(5L)).setQuery(matchAllQuery()).setSize(1).get(); assertEquals(numDocs, response.getHits().getTotalHits().value); assertEquals(1, response.getHits().getHits().length); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/profile/SecurityDomainIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/profile/SecurityDomainIntegTests.java index 544d86525a971..0892c6f88873f 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/profile/SecurityDomainIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/profile/SecurityDomainIntegTests.java @@ -379,7 +379,7 @@ private void assertAccessToken(CreateTokenResponse createTokenResponse) throws I .prepareHealth() .execute() .actionGet(); - final SearchResponse searchResponse = client().prepareSearch(SecuritySystemIndices.SECURITY_TOKENS_ALIAS).execute().actionGet(); + final SearchResponse searchResponse = prepareSearch(SecuritySystemIndices.SECURITY_TOKENS_ALIAS).execute().actionGet(); final String encodedAuthentication = createTokenResponse.getAuthentication().encode(); for (SearchHit searchHit : searchResponse.getHits().getHits()) { diff --git a/x-pack/plugin/slm/src/internalClusterTest/java/org/elasticsearch/xpack/slm/SLMSnapshotBlockingIntegTests.java b/x-pack/plugin/slm/src/internalClusterTest/java/org/elasticsearch/xpack/slm/SLMSnapshotBlockingIntegTests.java index b17fbb6a0371f..6699097e847a1 100644 --- a/x-pack/plugin/slm/src/internalClusterTest/java/org/elasticsearch/xpack/slm/SLMSnapshotBlockingIntegTests.java +++ b/x-pack/plugin/slm/src/internalClusterTest/java/org/elasticsearch/xpack/slm/SLMSnapshotBlockingIntegTests.java @@ -271,9 +271,9 @@ public void testRetentionWhileSnapshotInProgress() throws Exception { // Assert that the history document has been written for taking the snapshot and deleting it assertBusy(() -> { - SearchResponse resp = client().prepareSearch(".slm-history*") - .setQuery(QueryBuilders.matchQuery("snapshot_name", completedSnapshotName)) - .get(); + SearchResponse resp = prepareSearch(".slm-history*").setQuery( + QueryBuilders.matchQuery("snapshot_name", completedSnapshotName) + ).get(); logger.info( "--> checking history written for {}, got: {}", completedSnapshotName, diff --git a/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java b/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java index e0ea9d4ff076c..3fd9a3a2e9c4d 100644 --- a/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java +++ b/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java @@ -1632,9 +1632,7 @@ private void indexDocs(String indexName, int docIdOffset, int docCount) throws E private void assertDocumentsAreEqual(String indexName, int docCount) { assertDocCount(indexName, docCount); for (int testCase = 0; testCase < 3; testCase++) { - final SearchRequestBuilder searchRequestBuilder = client().prepareSearch(indexName) - .addSort("field", SortOrder.ASC) - .setSize(10_000); + final SearchRequestBuilder searchRequestBuilder = prepareSearch(indexName).addSort("field", SortOrder.ASC).setSize(10_000); SearchResponse searchResponse; switch (testCase) { diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java index e70fc324064ad..46ee89bf8044d 100644 --- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java +++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java @@ -115,7 +115,7 @@ public void testKnownIssueWithCellLeftOfDatelineTouchingPolygonOnRightOfDateline .precision(15) .setGeoBoundingBox(boundingBox) .size(256 * 256); - SearchResponse response = client().prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); + SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); InternalGeoGrid gridPoint = response.getAggregations().get("geometry"); for (InternalGeoGridBucket bucket : gridPoint.getBuckets()) { assertThat(bucket.getDocCount(), Matchers.greaterThan(0L)); @@ -123,7 +123,7 @@ public void testKnownIssueWithCellLeftOfDatelineTouchingPolygonOnRightOfDateline GeoGridQueryBuilder.Grid.GEOHEX, bucket.getKeyAsString() ); - response = client().prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder).get(); + response = prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder).get(); assertThat( "Bucket " + bucket.getKeyAsString(), response.getHits().getTotalHits().value, @@ -166,7 +166,7 @@ public void testKnownIssueWithCellIntersectingPolygonAndBoundingBox() throws IOE .precision(precision) .setGeoBoundingBox(boundingBox) .size(256 * 256); - SearchResponse response = client().prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); + SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); InternalGeoGrid gridPoint = response.getAggregations().get("geometry"); for (InternalGeoGridBucket bucket : gridPoint.getBuckets()) { assertThat(bucket.getDocCount(), Matchers.greaterThan(0L)); @@ -174,7 +174,7 @@ public void testKnownIssueWithCellIntersectingPolygonAndBoundingBox() throws IOE GeoGridQueryBuilder.Grid.GEOHEX, bucket.getKeyAsString() ); - response = client().prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder).get(); + response = prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder).get(); assertThat(response.getHits().getTotalHits().value, Matchers.equalTo(bucket.getDocCount())); } } @@ -269,7 +269,7 @@ private void doTestGrid( for (int i = minPrecision; i <= maxPrecision; i++) { GeoGridAggregationBuilder builderPoint = aggBuilder.apply("geometry").field("geometry").precision(i); - SearchResponse response = client().prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); + SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); InternalGeoGrid gridPoint = response.getAggregations().get("geometry"); assertQuery(gridPoint.getBuckets(), queryBuilder, i); } @@ -297,7 +297,7 @@ private void doTestGrid( .precision(i) .setGeoBoundingBox(boundingBox) .size(256 * 256); - SearchResponse response = client().prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); + SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get(); InternalGeoGrid gridPoint = response.getAggregations().get("geometry"); assertQuery(gridPoint.getBuckets(), queryBuilder, i); } @@ -307,7 +307,7 @@ private void assertQuery(List buckets, BiFunction client().prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("geo_shape")).get() + () -> prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("geo_shape")).get() ); assertThat(e.getCause().getMessage(), containsString("Field [geo_shape] of type [geo_shape] does not support match queries")); - e = expectThrows( - Exception.class, - () -> client().prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("shape")).get() - ); + e = expectThrows(Exception.class, () -> prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("shape")).get()); assertThat(e.getCause().getMessage(), containsString("Field [shape] of type [shape] does not support match queries")); - e = expectThrows( - Exception.class, - () -> client().prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("point")).get() - ); + e = expectThrows(Exception.class, () -> prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("point")).get()); assertThat(e.getCause().getMessage(), containsString("Field [point] of type [point] does not support match queries")); - assertHitCount(client().prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("*shape")), 0L); + assertHitCount(prepareSearch("test").setQuery(queryStringQuery("POINT(0 0)").field("*shape")), 0L); } } diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/WatcherConcreteIndexTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/WatcherConcreteIndexTests.java index 8df698d8150de..1ce21cae4aeeb 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/WatcherConcreteIndexTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/WatcherConcreteIndexTests.java @@ -48,7 +48,7 @@ public void testCanUseAnyConcreteIndexName() throws Exception { assertBusy(() -> timeWarp().trigger("mywatch")); assertBusy(() -> { - SearchResponse searchResult = client().prepareSearch(watchResultsIndex).setTrackTotalHits(true).get(); + SearchResponse searchResult = prepareSearch(watchResultsIndex).setTrackTotalHits(true).get(); assertThat((int) searchResult.getHits().getTotalHits().value, greaterThan(0)); }); } diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/TimeThrottleIntegrationTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/TimeThrottleIntegrationTests.java index cc0490664ec84..4f679742c6862 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/TimeThrottleIntegrationTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/TimeThrottleIntegrationTests.java @@ -96,8 +96,7 @@ private void assertLatestHistoryEntry(String id, String expectedValue) throws Ex assertBusy(() -> { ensureGreen(HistoryStoreField.DATA_STREAM); refresh(HistoryStoreField.DATA_STREAM + "*"); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setSize(1) + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSize(1) .setSource(new SearchSourceBuilder().query(QueryBuilders.boolQuery().must(termQuery("watch_id", id)))) .addSort(SortBuilders.fieldSort("result.execution_time").order(SortOrder.DESC)) .get(); @@ -115,8 +114,7 @@ private void assertTotalHistoryEntries(String id, long expectedCount) throws Exc assertBusy(() -> { // Watcher history is now written asynchronously, so we check this in an assertBusy ensureGreen(HistoryStoreField.DATA_STREAM); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setSize(0) + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSize(0) .setSource(new SearchSourceBuilder().query(QueryBuilders.boolQuery().must(termQuery("watch_id", id)))) .get(); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/email/EmailAttachmentTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/email/EmailAttachmentTests.java index ee0cd616461c4..00c960e5631b7 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/email/EmailAttachmentTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/actions/email/EmailAttachmentTests.java @@ -194,10 +194,9 @@ public void testThatEmailAttachmentsAreSent() throws Exception { assertBusy(() -> { SearchResponse searchResponse; try { - searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setQuery(QueryBuilders.termQuery("watch_id", "_test_id")) - .execute() - .actionGet(); + searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setQuery( + QueryBuilders.termQuery("watch_id", "_test_id") + ).execute().actionGet(); } catch (SearchPhaseExecutionException e) { if (e.getCause() instanceof NoShardAvailableActionException) { // Nothing has created the index yet diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/ArrayCompareConditionSearchTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/ArrayCompareConditionSearchTests.java index cfa70942566ee..2a16bfe0b491e 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/ArrayCompareConditionSearchTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/ArrayCompareConditionSearchTests.java @@ -44,9 +44,9 @@ public void testExecuteWithAggs() throws Exception { refresh(); - SearchResponse response = client().prepareSearch(index) - .addAggregation(AggregationBuilders.terms("top_tweeters").field("user.screen_name.keyword").size(3)) - .get(); + SearchResponse response = prepareSearch(index).addAggregation( + AggregationBuilders.terms("top_tweeters").field("user.screen_name.keyword").size(3) + ).get(); ArrayCompareCondition condition = new ArrayCompareCondition( "ctx.payload.aggregations.top_tweeters.buckets", @@ -80,8 +80,7 @@ public void testExecuteWithAggs() throws Exception { client().prepareIndex(index).setSource(source("fights_for_the_users", "you know, for the users", numberOfDocuments)).get(); refresh(); - response = client().prepareSearch(index) - .addAggregation(AggregationBuilders.terms("top_tweeters").field("user.screen_name.keyword").size(3)) + response = prepareSearch(index).addAggregation(AggregationBuilders.terms("top_tweeters").field("user.screen_name.keyword").size(3)) .get(); ctx = mockExecutionContext("_name", new Payload.XContent(response, ToXContent.EMPTY_PARAMS)); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/CompareConditionSearchTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/CompareConditionSearchTests.java index ac728f59d5d3b..9f957e8dc959e 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/CompareConditionSearchTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/condition/CompareConditionSearchTests.java @@ -40,14 +40,12 @@ public void testExecuteWithAggs() throws Exception { client().prepareIndex("my-index").setSource("@timestamp", "2005-01-01T00:30").get(); refresh(); - SearchResponse response = client().prepareSearch("my-index") - .addAggregation( - AggregationBuilders.dateHistogram("rate") - .field("@timestamp") - .fixedInterval(DateHistogramInterval.HOUR) - .order(BucketOrder.count(false)) - ) - .get(); + SearchResponse response = prepareSearch("my-index").addAggregation( + AggregationBuilders.dateHistogram("rate") + .field("@timestamp") + .fixedInterval(DateHistogramInterval.HOUR) + .order(BucketOrder.count(false)) + ).get(); CompareCondition condition = new CompareCondition( "ctx.payload.aggregations.rate.buckets.0.doc_count", @@ -66,14 +64,12 @@ public void testExecuteWithAggs() throws Exception { client().prepareIndex("my-index").setSource("@timestamp", "2005-01-01T00:40").get(); refresh(); - response = client().prepareSearch("my-index") - .addAggregation( - AggregationBuilders.dateHistogram("rate") - .field("@timestamp") - .fixedInterval(DateHistogramInterval.HOUR) - .order(BucketOrder.count(false)) - ) - .get(); + response = prepareSearch("my-index").addAggregation( + AggregationBuilders.dateHistogram("rate") + .field("@timestamp") + .fixedInterval(DateHistogramInterval.HOUR) + .order(BucketOrder.count(false)) + ).get(); ctx = mockExecutionContext("_name", new Payload.XContent(response, ToXContent.EMPTY_PARAMS)); result = condition.execute(ctx); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateEmailMappingsTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateEmailMappingsTests.java index 6324b9fd9dc58..b0e71ecfa3189 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateEmailMappingsTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateEmailMappingsTests.java @@ -95,15 +95,13 @@ public void testEmailFields() throws Exception { // the action should fail as no email server is available assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1); - SearchResponse response = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setSource( - searchSource().aggregation(terms("from").field("result.actions.email.message.from")) - .aggregation(terms("to").field("result.actions.email.message.to")) - .aggregation(terms("cc").field("result.actions.email.message.cc")) - .aggregation(terms("bcc").field("result.actions.email.message.bcc")) - .aggregation(terms("reply_to").field("result.actions.email.message.reply_to")) - ) - .get(); + SearchResponse response = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSource( + searchSource().aggregation(terms("from").field("result.actions.email.message.from")) + .aggregation(terms("to").field("result.actions.email.message.to")) + .aggregation(terms("cc").field("result.actions.email.message.cc")) + .aggregation(terms("bcc").field("result.actions.email.message.bcc")) + .aggregation(terms("reply_to").field("result.actions.email.message.reply_to")) + ).get(); assertThat(response, notNullValue()); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java index a5b6d8af85d98..36d2cf0239bdc 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java @@ -94,13 +94,11 @@ public void testHttpFields() throws Exception { // the action should fail as no email server is available assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1); - SearchResponse response = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setSource( - searchSource().aggregation(terms("input_result_path").field("result.input.http.request.path")) - .aggregation(terms("input_result_host").field("result.input.http.request.host")) - .aggregation(terms("webhook_path").field("result.actions.webhook.request.path")) - ) - .get(); + SearchResponse response = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSource( + searchSource().aggregation(terms("input_result_path").field("result.input.http.request.path")) + .aggregation(terms("input_result_host").field("result.input.http.request.host")) + .aggregation(terms("webhook_path").field("result.actions.webhook.request.path")) + ).get(); assertThat(response, notNullValue()); assertThat(response.getHits().getTotalHits().value, is(oneOf(1L, 2L))); @@ -166,10 +164,7 @@ public void testExceptionMapping() throws Exception { assertBusy(() -> { // ensure watcher history index has been written with this id flushAndRefresh(HistoryStoreField.INDEX_PREFIX + "*"); - assertHitCount( - client().prepareSearch(HistoryStoreField.INDEX_PREFIX + "*").setQuery(QueryBuilders.termQuery("watch_id", id)), - 1L - ); + assertHitCount(prepareSearch(HistoryStoreField.INDEX_PREFIX + "*").setQuery(QueryBuilders.termQuery("watch_id", id)), 1L); }); // ensure that enabled is set to false diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateIndexActionMappingsTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateIndexActionMappingsTests.java index 7b122d2507853..ecd3424f88139 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateIndexActionMappingsTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateIndexActionMappingsTests.java @@ -48,9 +48,9 @@ public void testIndexActionFields() throws Exception { flush(); refresh(); - SearchResponse response = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setSource(searchSource().aggregation(terms("index_action_indices").field("result.actions.index.response.index"))) - .get(); + SearchResponse response = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSource( + searchSource().aggregation(terms("index_action_indices").field("result.actions.index.response.index")) + ).get(); assertThat(response, notNullValue()); assertThat(response.getHits().getTotalHits().value, is(oneOf(1L, 2L))); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateSearchInputMappingsTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateSearchInputMappingsTests.java index 7cd5bce4372ec..4fba54f7e0208 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateSearchInputMappingsTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateSearchInputMappingsTests.java @@ -64,13 +64,11 @@ public void testHttpFields() throws Exception { // the action should fail as no email server is available assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1); - SearchResponse response = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setSource( - searchSource().aggregation(terms("input_search_type").field("result.input.search.request.search_type")) - .aggregation(terms("input_indices").field("result.input.search.request.indices")) - .aggregation(terms("input_body").field("result.input.search.request.body")) - ) - .get(); + SearchResponse response = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSource( + searchSource().aggregation(terms("input_search_type").field("result.input.search.request.search_type")) + .aggregation(terms("input_indices").field("result.input.search.request.indices")) + .aggregation(terms("input_body").field("result.input.search.request.body")) + ).get(); assertThat(response, notNullValue()); assertThat(response.getHits().getTotalHits().value, is(oneOf(1L, 2L))); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/input/chain/ChainIntegrationTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/input/chain/ChainIntegrationTests.java index 1f0f38ac5be5e..cec68468acf0d 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/input/chain/ChainIntegrationTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/input/chain/ChainIntegrationTests.java @@ -82,7 +82,7 @@ public void testChainedInputsAreWorking() throws Exception { public void assertWatchExecuted() { try { refresh(); - SearchResponse searchResponse = client().prepareSearch("my-index").get(); + SearchResponse searchResponse = prepareSearch("my-index").get(); assertHitCount(searchResponse, 1); assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("the-most-awesome-index-ever")); } catch (IndexNotFoundException e) { diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java index 12ae292f8da2f..33852bca07247 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java @@ -315,12 +315,12 @@ protected long watchRecordCount(QueryBuilder query) { } protected long docCount(String index, SearchSourceBuilder source) { - SearchRequestBuilder builder = client().prepareSearch(index).setSource(source).setSize(0); + SearchRequestBuilder builder = prepareSearch(index).setSource(source).setSize(0); return builder.get().getHits().getTotalHits().value; } protected SearchResponse searchHistory(SearchSourceBuilder builder) { - return client().prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSource(builder).get(); + return prepareSearch(HistoryStoreField.DATA_STREAM + "*").setSource(builder).get(); } protected T getInstanceFromMaster(Class type) { @@ -371,8 +371,9 @@ protected void assertWatchWithMinimumPerformedActionsCount( } refresh(); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setIndicesOptions( + IndicesOptions.lenientExpandOpen() + ) .setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", ExecutionState.EXECUTED.id()))) .get(); lastResponse.set(searchResponse); @@ -403,17 +404,16 @@ protected void assertWatchWithMinimumPerformedActionsCount( } protected SearchResponse searchWatchRecords(Consumer requestBuilderCallback) { - SearchRequestBuilder builder = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*"); + SearchRequestBuilder builder = prepareSearch(HistoryStoreField.DATA_STREAM + "*"); requestBuilderCallback.accept(builder); return builder.get(); } protected long findNumberOfPerformedActions(String watchName) { refresh(); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", ExecutionState.EXECUTED.id()))) - .get(); + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setIndicesOptions( + IndicesOptions.lenientExpandOpen() + ).setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", ExecutionState.EXECUTED.id()))).get(); return searchResponse.getHits().getTotalHits().value; } @@ -438,8 +438,9 @@ protected void assertWatchWithNoActionNeeded(final String watchName, final long assertThat(routingTable.allPrimaryShardsActive(), is(true)); } refresh(); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setIndicesOptions( + IndicesOptions.lenientExpandOpen() + ) .setQuery( boolQuery().must(matchQuery("watch_id", watchName)) .must(matchQuery("state", ExecutionState.EXECUTION_NOT_NEEDED.id())) @@ -477,10 +478,9 @@ protected void assertWatchWithMinimumActionsCount(final String watchName, final } refresh(); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", recordState.id()))) - .get(); + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setIndicesOptions( + IndicesOptions.lenientExpandOpen() + ).setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", recordState.id()))).get(); assertThat( "could not find executed watch record", searchResponse.getHits().getTotalHits().value, diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java index 0acefafb50ca4..2d64fcb7bc833 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java @@ -134,7 +134,7 @@ public void testDeleteWatch() throws Exception { assertThat(deleteWatchResponse.isFound(), is(true)); refresh(); - assertHitCount(client().prepareSearch(Watch.INDEX).setSize(0), 0L); + assertHitCount(prepareSearch(Watch.INDEX).setSize(0), 0L); // Deleting the same watch for the second time deleteWatchResponse = new DeleteWatchRequestBuilder(client()).setId("_name").get(); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BootStrapTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BootStrapTests.java index 6c4a75929733f..c32246e33c571 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BootStrapTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BootStrapTests.java @@ -193,7 +193,7 @@ public void testLoadExistingWatchesUponStartup() throws Exception { ); } bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); - assertHitCount(client().prepareSearch(Watch.INDEX).setSize(0), numWatches); + assertHitCount(prepareSearch(Watch.INDEX).setSize(0), numWatches); startWatcher(); @@ -318,12 +318,12 @@ private void assertSingleExecutionAndCompleteWatchHistory(final long numberOfWat assertThat(maxSize, equalTo(0L)); refresh(); - SearchResponse searchResponse = client().prepareSearch("output").get(); + SearchResponse searchResponse = prepareSearch("output").get(); assertThat(searchResponse.getHits().getTotalHits().value, is(greaterThanOrEqualTo(numberOfWatches))); long successfulWatchExecutions = searchResponse.getHits().getTotalHits().value; // the watch history should contain entries for each triggered watch, which a few have been marked as not executed - SearchResponse historySearchResponse = client().prepareSearch(HistoryStoreField.INDEX_PREFIX + "*").setSize(10000).get(); + SearchResponse historySearchResponse = prepareSearch(HistoryStoreField.INDEX_PREFIX + "*").setSize(10000).get(); assertHitCount(historySearchResponse, expectedWatchHistoryCount); long notExecutedCount = Arrays.stream(historySearchResponse.getHits().getHits()) .filter(hit -> hit.getSourceAsMap().get("state").equals(ExecutionState.NOT_EXECUTED_ALREADY_QUEUED.id())) @@ -402,7 +402,7 @@ public void testWatchRecordSavedTwice() throws Exception { // but even then since the execution of the watch record is async it may take a little bit before // the actual documents are in the output index refresh(); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM).setSize(numRecords).get(); + SearchResponse searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM).setSize(numRecords).get(); assertThat(searchResponse.getHits().getTotalHits().value, Matchers.equalTo((long) numRecords)); for (int i = 0; i < numRecords; i++) { assertThat(searchResponse.getHits().getAt(i).getSourceAsMap().get("state"), is(ExecutionState.EXECUTED.id())); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/HistoryIntegrationTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/HistoryIntegrationTests.java index 3e176f396fabc..2332ef24ff5ef 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/HistoryIntegrationTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/HistoryIntegrationTests.java @@ -73,7 +73,7 @@ public void testThatHistoryIsWrittenWithChainedInput() throws Exception { assertBusy(() -> { flushAndRefresh(".watcher-history-*"); - assertHitCount(client().prepareSearch(".watcher-history-*"), 1); + assertHitCount(prepareSearch(".watcher-history-*"), 1); }); } @@ -106,7 +106,7 @@ public void testFailedInputResultWithDotsInFieldNameGetsStored() throws Exceptio assertBusy(() -> { refresh(".watcher-history*"); - assertHitCount(client().prepareSearch(".watcher-history*").setSize(0), 1); + assertHitCount(prepareSearch(".watcher-history*").setSize(0), 1); }); // as fields with dots are allowed in 5.0 again, the mapping must be checked in addition @@ -151,7 +151,7 @@ public void testPayloadInputWithDotsInFieldNameWorks() throws Exception { assertBusy(() -> { refresh(".watcher-history*"); - assertHitCount(client().prepareSearch(".watcher-history*").setSize(0), 1); + assertHitCount(prepareSearch(".watcher-history*").setSize(0), 1); }); // as fields with dots are allowed in 5.0 again, the mapping must be checked in addition @@ -187,7 +187,7 @@ public void testThatHistoryContainsStatus() throws Exception { assertBusy(() -> { refresh(".watcher-history*"); - SearchResponse searchResponse = client().prepareSearch(".watcher-history*").setSize(1).get(); + SearchResponse searchResponse = prepareSearch(".watcher-history*").setSize(1).get(); assertHitCount(searchResponse, 1); SearchHit hit = searchResponse.getHits().getAt(0); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/RejectedExecutionTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/RejectedExecutionTests.java index 817b8a4d7ff90..5c4039566661a 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/RejectedExecutionTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/RejectedExecutionTests.java @@ -50,7 +50,7 @@ public void testHistoryOnRejection() throws Exception { assertBusy(() -> { flushAndRefresh(".watcher-history-*"); - SearchResponse searchResponse = client().prepareSearch(".watcher-history-*").get(); + SearchResponse searchResponse = prepareSearch(".watcher-history-*").get(); assertThat(searchResponse.getHits().getTotalHits().value, greaterThanOrEqualTo(2L)); }); } diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/SingleNodeTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/SingleNodeTests.java index de53486c235dd..be9b2da6e739c 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/SingleNodeTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/SingleNodeTests.java @@ -67,7 +67,7 @@ public void testThatLoadingWithNonExistingIndexWorks() throws Exception { assertBusy(() -> { RefreshResponse refreshResponse = indicesAdmin().prepareRefresh(".watcher-history*").get(); assertThat(refreshResponse.getStatus(), equalTo(RestStatus.OK)); - SearchResponse searchResponse = client().prepareSearch(".watcher-history*").setSize(0).get(); + SearchResponse searchResponse = prepareSearch(".watcher-history*").setSize(0).get(); assertThat(searchResponse.getHits().getTotalHits().value, is(greaterThanOrEqualTo(1L))); }, 30, TimeUnit.SECONDS); } diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java index 82f302b6fb44d..83a3b175819bd 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java @@ -236,7 +236,7 @@ public void testAckWithRestart() throws Exception { assertThat(ackResponse.getStatus().actionStatus("_id").ackStatus().state(), is(ActionStatus.AckStatus.State.ACKED)); refresh("actions"); - long countAfterAck = client().prepareSearch("actions").setQuery(matchAllQuery()).get().getHits().getTotalHits().value; + long countAfterAck = prepareSearch("actions").setQuery(matchAllQuery()).get().getHits().getTotalHits().value; assertThat(countAfterAck, greaterThanOrEqualTo(1L)); restartWatcherRandomly(); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchMetadataTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchMetadataTests.java index 4f5fabac81973..bb4fa3b12bab4 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchMetadataTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/WatchMetadataTests.java @@ -68,9 +68,7 @@ public void testWatchMetadata() throws Exception { refresh(); SearchResponse searchResponse; try { - searchResponse = client().prepareSearch(HistoryStoreField.DATA_STREAM + "*") - .setQuery(termQuery("metadata.foo", "bar")) - .get(); + searchResponse = prepareSearch(HistoryStoreField.DATA_STREAM + "*").setQuery(termQuery("metadata.foo", "bar")).get(); } catch (SearchPhaseExecutionException e) { if (e.getCause() instanceof NoShardAvailableActionException) { // Nothing has created the index yet diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transform/TransformIntegrationTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transform/TransformIntegrationTests.java index 75489785dfaad..5ea85eb813982 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transform/TransformIntegrationTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transform/TransformIntegrationTests.java @@ -141,13 +141,13 @@ public void testScriptTransform() throws Exception { assertWatchWithMinimumPerformedActionsCount("_id2", 1, false); refresh(); - SearchResponse response = client().prepareSearch("output1").get(); + SearchResponse response = prepareSearch("output1").get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); assertThat(response.getHits().getAt(0).getSourceAsMap().get("key3").toString(), equalTo("20")); - response = client().prepareSearch("output2").get(); + response = prepareSearch("output2").get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); @@ -186,12 +186,12 @@ public void testSearchTransform() throws Exception { assertWatchWithMinimumPerformedActionsCount("_id2", 1, false); refresh(); - SearchResponse response = client().prepareSearch("output1").get(); + SearchResponse response = prepareSearch("output1").get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); assertThat(response.getHits().getAt(0).getSourceAsString(), containsString("mytestresult")); - response = client().prepareSearch("output2").get(); + response = prepareSearch("output2").get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); assertThat(response.getHits().getAt(0).getSourceAsString(), containsString("mytestresult")); @@ -225,13 +225,13 @@ public void testChainTransform() throws Exception { assertWatchWithMinimumPerformedActionsCount("_id2", 1, false); refresh(); - SearchResponse response = client().prepareSearch("output1").get(); + SearchResponse response = prepareSearch("output1").get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); assertThat(response.getHits().getAt(0).getSourceAsMap().get("key4").toString(), equalTo("30")); - response = client().prepareSearch("output2").get(); + response = prepareSearch("output2").get(); assertNoFailures(response); assertThat(response.getHits().getTotalHits().value, greaterThanOrEqualTo(1L)); assertThat(response.getHits().getAt(0).getSourceAsMap().size(), equalTo(1)); diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/delete/DeleteWatchTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/delete/DeleteWatchTests.java index 3cbf38fb8af4e..e48c4efd32b0d 100644 --- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/delete/DeleteWatchTests.java +++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/transport/action/delete/DeleteWatchTests.java @@ -81,9 +81,7 @@ public void testWatchDeletionDuringExecutionWorks() throws Exception { // during execution refresh(HistoryStoreField.INDEX_PREFIX + "*"); - SearchResponse searchResponse = client().prepareSearch(HistoryStoreField.INDEX_PREFIX + "*") - .setQuery(matchAllQuery()) - .get(); + SearchResponse searchResponse = prepareSearch(HistoryStoreField.INDEX_PREFIX + "*").setQuery(matchAllQuery()).get(); assertHitCount(searchResponse, 1); Map source = searchResponse.getHits().getAt(0).getSourceAsMap(); From 1521484d112248c7c411b1a5739cd623a4d0ded9 Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Fri, 20 Oct 2023 13:48:37 +0200 Subject: [PATCH 048/203] Remove more explicit references to SearchResponse in tests (#101092) (#101172) Follow-up to #100966. Add more assertion overloads that consume a requestBuilder as in the other PRs and start using `assertHitCount` in more places that were duplicating what it does. Also add a shortcut for `client().prepareSearch()` to integ tests and bulk-replace some usages of this pattern to avoid these changes from blowing up test code line count further. --- .../datastreams/DataStreamIT.java | 12 +- .../action/admin/HotThreadsIT.java | 3 +- .../action/search/TransportSearchIT.java | 11 +- .../mapper/CopyToMapperIntegrationIT.java | 4 +- .../index/mapper/DynamicMappingIT.java | 42 +- .../mapper/MultiFieldsIntegrationIT.java | 10 +- .../index/shard/IndexShardIT.java | 8 +- .../highlight/HighlighterSearchIT.java | 795 ++++++++++-------- .../search/morelikethis/MoreLikeThisIT.java | 211 ++--- .../search/query/SearchQueryIT.java | 306 +++---- .../search/query/SimpleQueryStringIT.java | 112 +-- .../search/simple/SimpleSearchIT.java | 37 +- .../elasticsearch/test/ESIntegTestCase.java | 5 - .../hamcrest/ElasticsearchAssertions.java | 56 ++ .../xpack/enrich/EnrichMultiNodeIT.java | 10 +- .../ml/integration/AnnotationIndexIT.java | 13 +- 16 files changed, 808 insertions(+), 827 deletions(-) diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java index 896be7b172d6e..69ebbbde2cc35 100644 --- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java +++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java @@ -8,7 +8,6 @@ package org.elasticsearch.datastreams; import org.apache.logging.log4j.core.util.Throwables; -import org.apache.lucene.search.TotalHits; import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionRequestBuilder; @@ -829,9 +828,9 @@ public void testSearchFilteredAndUnfilteredAlias() throws Exception { ); // Searching the filtered and unfiltered aliases should return all results (unfiltered): - assertSearchHits(client().prepareSearch("foo", "bar"), "1", "2"); + assertSearchHits(prepareSearch("foo", "bar"), "1", "2"); // Searching the data stream name and the filtered alias should return all results (unfiltered): - assertSearchHits(client().prepareSearch("foo", dataStreamName), "1", "2"); + assertSearchHits(prepareSearch("foo", dataStreamName), "1", "2"); } public void testRandomDataSteamAliasesUpdate() throws Exception { @@ -1297,8 +1296,7 @@ public void testSearchAllResolvesDataStreams() throws Exception { indexDocs("metrics-foo", numDocsRolledFoo); SearchRequest searchRequest = new SearchRequest("*"); - SearchResponse searchResponse = client().search(searchRequest).actionGet(); - assertThat(searchResponse.getHits().getTotalHits().value, is((long) numDocsBar + numDocsFoo + numDocsRolledFoo)); + assertHitCount(client().search(searchRequest), numDocsBar + numDocsFoo + numDocsRolledFoo); } public void testGetDataStream() throws Exception { @@ -1473,9 +1471,7 @@ public void testQueryDataStreamNameInIndexField() throws Exception { SearchRequest searchRequest = new SearchRequest("*"); searchRequest.source().query(new TermQueryBuilder("_index", "metrics-foo")); - SearchResponse searchResponse = client().search(searchRequest).actionGet(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getTotalHits().relation, equalTo(TotalHits.Relation.EQUAL_TO)); + assertHitCount(client().search(searchRequest), 1); } public void testDataStreamMetadata() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java index 9d745994fd658..6a3a7ccfe221a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/HotThreadsIT.java @@ -107,8 +107,7 @@ public void onFailure(Exception e) { ensureSearchable(); while (latch.getCount() > 0) { assertHitCount( - client().prepareSearch() - .setQuery(matchAllQuery()) + prepareSearch().setQuery(matchAllQuery()) .setPostFilter( boolQuery().must(matchAllQuery()) .mustNot(boolQuery().must(termQuery("field1", "value1")).must(termQuery("field1", "value2"))) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java index 1d320571864fe..93f6c83b16c15 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java @@ -70,8 +70,10 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -167,7 +169,7 @@ public void testLocalClusterAlias() { } } - public void testAbsoluteStartMillis() { + public void testAbsoluteStartMillis() throws ExecutionException, InterruptedException { TaskId parentTaskId = new TaskId("node", randomNonNegativeLong()); { IndexRequest indexRequest = new IndexRequest("test-1970.01.01"); @@ -186,9 +188,7 @@ public void testAbsoluteStartMillis() { assertEquals(RestStatus.CREATED, indexResponse.status()); } { - SearchRequest searchRequest = new SearchRequest(); - SearchResponse searchResponse = client().search(searchRequest).actionGet(); - assertEquals(2, searchResponse.getHits().getTotalHits().value); + assertHitCount(client().search(new SearchRequest()), 2); } { SearchRequest searchRequest = new SearchRequest(""); @@ -205,8 +205,7 @@ public void testAbsoluteStartMillis() { 0, randomBoolean() ); - SearchResponse searchResponse = client().search(searchRequest).actionGet(); - assertEquals(2, searchResponse.getHits().getTotalHits().value); + assertHitCount(client().search(searchRequest), 2); } { SearchRequest searchRequest = SearchRequest.subSearchRequest( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java index 7a93c315a84be..9fd1e788eca8c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/CopyToMapperIntegrationIT.java @@ -21,6 +21,7 @@ import java.io.IOException; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; @@ -66,8 +67,7 @@ public void testDynamicObjectCopyTo() throws Exception { assertAcked(indicesAdmin().prepareCreate("test-idx").setMapping(mapping)); client().prepareIndex("test-idx").setId("1").setSource("foo", "bar").get(); indicesAdmin().prepareRefresh("test-idx").execute().actionGet(); - SearchResponse response = prepareSearch("test-idx").setQuery(QueryBuilders.termQuery("root.top.child", "bar")).get(); - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertHitCount(prepareSearch("test-idx").setQuery(QueryBuilders.termQuery("root.top.child", "bar")), 1L); } private XContentBuilder createDynamicTemplateMapping() throws IOException { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java index 5760b8bebf7c9..d8a94bbb00ee3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java @@ -13,7 +13,6 @@ import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateUpdateTask; @@ -49,6 +48,7 @@ import static org.elasticsearch.index.mapper.MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING; import static org.elasticsearch.index.mapper.vectors.DenseVectorFieldMapper.MIN_DIMS_FOR_DYNAMIC_FLOAT_MAPPING; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -461,18 +461,9 @@ public void testDynamicRuntimeNoConflicts() { BulkResponse bulkItemResponses = client().bulk(bulkRequest).actionGet(); assertFalse(bulkItemResponses.buildFailureMessage(), bulkItemResponses.hasFailures()); - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("one", "one")).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("one.two", 3.5)).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("one.two.three", "1")).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("one", "one")), 1); + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("one.two", 3.5)), 1); + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("one.two.three", "1")), 1); } public void testDynamicRuntimeObjectFields() { @@ -509,22 +500,10 @@ public void testDynamicRuntimeObjectFields() { BulkResponse bulkItemResponses = client().bulk(bulkRequest).actionGet(); assertFalse(bulkItemResponses.buildFailureMessage(), bulkItemResponses.hasFailures()); - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.one", 1)).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("anything", "anything")).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one", "one")).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one.two", "1")).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("obj.one", 1)), 1); + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("anything", "anything")), 1); + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one", "one")), 1); + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.one.two", "1")), 1); Exception exception = expectThrows( DocumentParsingException.class, @@ -568,10 +547,7 @@ public void testDynamicRuntimeObjectFields() { .status() ); - { - SearchResponse searchResponse = prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.dynamic.number", 1)).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); - } + assertHitCount(prepareSearch("test").setQuery(new MatchQueryBuilder("obj.runtime.dynamic.number", 1)), 1); // a doc with the same field but a different type causes a conflict Exception e = expectThrows( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java index 64a4f8d30e600..25c33ee66bad4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/MultiFieldsIntegrationIT.java @@ -26,6 +26,7 @@ import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery; import static org.elasticsearch.index.query.QueryBuilders.matchQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; @@ -47,10 +48,8 @@ public void testMultiFields() throws Exception { client().prepareIndex("my-index").setId("1").setSource("title", "Multi fields").setRefreshPolicy(IMMEDIATE).get(); - SearchResponse searchResponse = prepareSearch("my-index").setQuery(matchQuery("title", "multi")).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = prepareSearch("my-index").setQuery(matchQuery("title.not_analyzed", "Multi fields")).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCount(client().prepareSearch("my-index").setQuery(matchQuery("title", "multi")), 1); + assertHitCount(client().prepareSearch("my-index").setQuery(matchQuery("title.not_analyzed", "Multi fields")), 1); assertAcked(indicesAdmin().preparePutMapping("my-index").setSource(createPutMappingSource())); @@ -68,8 +67,7 @@ public void testMultiFields() throws Exception { client().prepareIndex("my-index").setId("1").setSource("title", "Multi fields").setRefreshPolicy(IMMEDIATE).get(); - searchResponse = prepareSearch("my-index").setQuery(matchQuery("title.uncased", "Multi")).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCount(client().prepareSearch("my-index").setQuery(matchQuery("title.uncased", "Multi")), 1); } @SuppressWarnings("unchecked") diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/IndexShardIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/IndexShardIT.java index 4476bc2ca6ec0..f473015f864db 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/shard/IndexShardIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/shard/IndexShardIT.java @@ -672,7 +672,7 @@ public void testInvalidateIndicesRequestCacheWhenRollbackEngine() throws Excepti } } shard.refresh("test"); - assertThat(client().search(countRequest).actionGet().getHits().getTotalHits().value, equalTo(numDocs)); + assertHitCount(client().search(countRequest), numDocs); assertThat(shard.getLocalCheckpoint(), equalTo(shard.seqNoStats().getMaxSeqNo())); final CountDownLatch engineResetLatch = new CountDownLatch(1); @@ -701,11 +701,7 @@ public void testInvalidateIndicesRequestCacheWhenRollbackEngine() throws Excepti equalTo(numDocs + moreDocs) ); } - assertThat( - "numDocs=" + numDocs + " moreDocs=" + moreDocs, - client().search(countRequest).actionGet().getHits().getTotalHits().value, - equalTo(numDocs + moreDocs) - ); + assertHitCount(client().search(countRequest), numDocs + moreDocs); } public void testShardChangesWithDefaultDocType() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 6564264bcd0f2..1caed3ad45793 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -65,6 +65,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.ExecutionException; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -91,6 +92,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHighlight; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNotHighlighted; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; @@ -137,8 +139,7 @@ public void testHighlightingWithKeywordIgnoreBoundaryScanner() throws IOExceptio refresh(); for (BoundaryScannerType scanner : BoundaryScannerType.values()) { - SearchResponse search = client().prepareSearch() - .addSort(SortBuilders.fieldSort("sort")) + SearchResponse search = prepareSearch().addSort(SortBuilders.fieldSort("sort")) .setQuery(matchQuery("tags", "foo bar")) .highlighter(new HighlightBuilder().field(new Field("tags")).numOfFragments(2).boundaryScannerType(scanner)) .get(); @@ -163,8 +164,7 @@ public void testHighlightingWithStoredKeyword() throws IOException { assertAcked(prepareCreate("test").setMapping(mappings)); client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject().field("text", "foo").endObject()).get(); refresh(); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("text", "foo")) + SearchResponse search = prepareSearch().setQuery(matchQuery("text", "foo")) .highlighter(new HighlightBuilder().field(new Field("text"))) .get(); assertHighlight(search, 0, "text", 0, equalTo("foo")); @@ -189,8 +189,7 @@ public void testHighlightingWithWildcardName() throws IOException { client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject().field("text", "text").endObject()).get(); refresh(); for (String type : ALL_TYPES) { - SearchResponse search = client().prepareSearch() - .setQuery(constantScoreQuery(matchQuery("text", "text"))) + SearchResponse search = prepareSearch().setQuery(constantScoreQuery(matchQuery("text", "text"))) .highlighter(new HighlightBuilder().field(new Field("*").highlighterType(type))) .get(); assertHighlight(search, 0, "text", 0, equalTo("text")); @@ -221,7 +220,7 @@ public void testFieldAlias() throws IOException { for (String type : ALL_TYPES) { HighlightBuilder builder = new HighlightBuilder().field(new Field("alias").highlighterType(type)) .requireFieldMatch(randomBoolean()); - SearchResponse search = client().prepareSearch().setQuery(matchQuery("alias", "foo")).highlighter(builder).get(); + SearchResponse search = prepareSearch().setQuery(matchQuery("alias", "foo")).highlighter(builder).get(); assertHighlight(search, 0, "alias", 0, equalTo("foo")); } } @@ -251,7 +250,7 @@ public void testFieldAliasWithSourceLookup() throws IOException { for (String type : ALL_TYPES) { HighlightBuilder builder = new HighlightBuilder().field(new Field("alias").highlighterType(type)) .requireFieldMatch(randomBoolean()); - SearchResponse search = client().prepareSearch().setQuery(matchQuery("alias", "bar")).highlighter(builder).get(); + SearchResponse search = prepareSearch().setQuery(matchQuery("alias", "bar")).highlighter(builder).get(); assertHighlight(search, 0, "alias", 0, equalTo("foo bar")); } } @@ -276,7 +275,7 @@ public void testFieldAliasWithWildcardField() throws IOException { refresh(); HighlightBuilder builder = new HighlightBuilder().field(new Field("al*")).requireFieldMatch(false); - SearchResponse search = client().prepareSearch().setQuery(matchQuery("alias", "foo")).highlighter(builder).get(); + SearchResponse search = prepareSearch().setQuery(matchQuery("alias", "foo")).highlighter(builder).get(); assertHighlight(search, 0, "alias", 0, equalTo("foo")); } @@ -310,13 +309,11 @@ public void testHighlightingWhenFieldsAreNotStoredThereIsNoSource() throws IOExc .get(); refresh(); for (String type : ALL_TYPES) { - SearchResponse search = client().prepareSearch() - .setQuery(constantScoreQuery(matchQuery("text", "text"))) + SearchResponse search = prepareSearch().setQuery(constantScoreQuery(matchQuery("text", "text"))) .highlighter(new HighlightBuilder().field(new Field("*").highlighterType(type))) .get(); assertHighlight(search, 0, "text", 0, equalTo("text")); - search = client().prepareSearch() - .setQuery(constantScoreQuery(matchQuery("text", "text"))) + search = prepareSearch().setQuery(constantScoreQuery(matchQuery("text", "text"))) .highlighter(new HighlightBuilder().field(new Field("unstored_text"))) .get(); assertNoFailures(search); @@ -333,8 +330,7 @@ public void testHighTermFrequencyDoc() throws IOException { } client().prepareIndex("test").setId("1").setSource("name", builder.toString()).get(); refresh(); - SearchResponse search = client().prepareSearch() - .setQuery(constantScoreQuery(matchQuery("name", "abc"))) + SearchResponse search = prepareSearch().setQuery(constantScoreQuery(matchQuery("name", "abc"))) .highlighter(new HighlightBuilder().field("name")) .get(); assertHighlight(search, 0, "name", 0, startsWith("abc abc abc abc")); @@ -360,24 +356,33 @@ public void testEnsureNoNegativeOffsets() throws Exception { ) .get(); refresh(); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("long_term", "thisisaverylongwordandmakessurethisfails foo highlighed")) - .highlighter(new HighlightBuilder().field("long_term", 18, 1).highlighterType("fvh")) - .get(); - assertHighlight(search, 0, "long_term", 0, 1, equalTo("thisisaverylongwordandmakessurethisfails")); - - search = client().prepareSearch() - .setQuery(matchPhraseQuery("no_long_term", "test foo highlighed").slop(3)) - .highlighter(new HighlightBuilder().field("no_long_term", 18, 1).highlighterType("fvh").postTags("").preTags("")) - .get(); - assertNotHighlighted(search, 0, "no_long_term"); + assertHighlight( + prepareSearch().setQuery(matchQuery("long_term", "thisisaverylongwordandmakessurethisfails foo highlighed")) + .highlighter(new HighlightBuilder().field("long_term", 18, 1).highlighterType("fvh")), + 0, + "long_term", + 0, + 1, + equalTo("thisisaverylongwordandmakessurethisfails") + ); - search = client().prepareSearch() - .setQuery(matchPhraseQuery("no_long_term", "test foo highlighed").slop(3)) - .highlighter(new HighlightBuilder().field("no_long_term", 30, 1).highlighterType("fvh").postTags("").preTags("")) - .get(); + assertNotHighlighted( + prepareSearch().setQuery(matchPhraseQuery("no_long_term", "test foo highlighed").slop(3)) + .highlighter(new HighlightBuilder().field("no_long_term", 18, 1).highlighterType("fvh").postTags("").preTags("")) + .get(), + 0, + "no_long_term" + ); - assertHighlight(search, 0, "no_long_term", 0, 1, equalTo("a test where foo is highlighed and")); + assertHighlight( + prepareSearch().setQuery(matchPhraseQuery("no_long_term", "test foo highlighed").slop(3)) + .highlighter(new HighlightBuilder().field("no_long_term", 30, 1).highlighterType("fvh").postTags("").preTags("")), + 0, + "no_long_term", + 0, + 1, + equalTo("a test where foo is highlighed and") + ); } public void testSourceLookupHighlightingUsingPlainHighlighter() throws Exception { @@ -428,8 +433,7 @@ public void testSourceLookupHighlightingUsingPlainHighlighter() throws Exception } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "bug")) .highlighter(new HighlightBuilder().field("title", -1, 0)) .get(); @@ -437,8 +441,7 @@ public void testSourceLookupHighlightingUsingPlainHighlighter() throws Exception assertHighlight(search, i, "title", 0, equalTo("This is a test on the highlighting bug present in elasticsearch")); } - search = client().prepareSearch() - .setQuery(matchQuery("attachments.body", "attachment")) + search = prepareSearch().setQuery(matchQuery("attachments.body", "attachment")) .highlighter(new HighlightBuilder().field("attachments.body", -1, 0)) .get(); @@ -497,8 +500,7 @@ public void testSourceLookupHighlightingUsingFastVectorHighlighter() throws Exce } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "bug")) .highlighter(new HighlightBuilder().field("title", -1, 0)) .get(); @@ -506,8 +508,7 @@ public void testSourceLookupHighlightingUsingFastVectorHighlighter() throws Exce assertHighlight(search, i, "title", 0, equalTo("This is a test on the highlighting bug present in elasticsearch")); } - search = client().prepareSearch() - .setQuery(matchQuery("attachments.body", "attachment")) + search = prepareSearch().setQuery(matchQuery("attachments.body", "attachment")) .highlighter(new HighlightBuilder().field("attachments.body", -1, 2)) .execute() .get(); @@ -570,8 +571,7 @@ public void testSourceLookupHighlightingUsingPostingsHighlighter() throws Except } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "bug")) // asking for the whole field to be highlighted .highlighter(new HighlightBuilder().field("title", -1, 0)) .get(); @@ -587,8 +587,7 @@ public void testSourceLookupHighlightingUsingPostingsHighlighter() throws Except assertHighlight(search, i, "title", 1, 2, equalTo("This is the second bug to perform highlighting on.")); } - search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + search = prepareSearch().setQuery(matchQuery("title", "bug")) // sentences will be generated out of each value .highlighter(new HighlightBuilder().field("title")) .get(); @@ -604,8 +603,7 @@ public void testSourceLookupHighlightingUsingPostingsHighlighter() throws Except assertHighlight(search, i, "title", 1, 2, equalTo("This is the second bug to perform highlighting on.")); } - search = client().prepareSearch() - .setQuery(matchQuery("attachments.body", "attachment")) + search = prepareSearch().setQuery(matchQuery("attachments.body", "attachment")) .highlighter(new HighlightBuilder().field("attachments.body", -1, 2)) .get(); @@ -633,8 +631,7 @@ public void testHighlightIssue1994() throws Exception { client().prepareIndex("test").setId("2").setSource("titleTV", new String[] { "some text to highlight", "highlight other text" }) ); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "bug")) .highlighter(new HighlightBuilder().field("title", -1, 2).field("titleTV", -1, 2).requireFieldMatch(false)) .get(); @@ -643,8 +640,7 @@ public void testHighlightIssue1994() throws Exception { assertHighlight(search, 0, "titleTV", 0, equalTo("This is a test on the highlighting bug present in elasticsearch")); assertHighlight(search, 0, "titleTV", 1, 2, equalTo("The bug is bugging us")); - search = client().prepareSearch() - .setQuery(matchQuery("titleTV", "highlight")) + search = prepareSearch().setQuery(matchQuery("titleTV", "highlight")) .highlighter(new HighlightBuilder().field("titleTV", -1, 2)) .get(); @@ -744,10 +740,7 @@ public void testPlainHighlighter() throws Exception { SearchSourceBuilder source = searchSource().query(termQuery("field1", "test")) .highlighter(highlight().highlighterType("plain").field("field1").order("score").preTags("").postTags("")); - - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a test")); + assertHighlight(prepareSearch("test").setSource(source), 0, "field1", 0, 1, equalTo("this is a test")); } public void testPlainHighlighterOrder() throws Exception { @@ -809,25 +802,19 @@ public void testFastVectorHighlighter() throws Exception { SearchSourceBuilder source = searchSource().query(termQuery("field1", "test")) .highlighter(highlight().field("field1", 100, 0).order("score").preTags("").postTags("")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a test")); + assertHighlight(prepareSearch("test").setSource(source), 0, "field1", 0, 1, equalTo("this is a test")); logger.info("--> searching with boundary characters"); source = searchSource().query(matchQuery("field2", "quick")) .highlighter(highlight().field("field2", 30, 1).boundaryChars(new char[] { ' ' })); - searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over")); + assertHighlight(prepareSearch("test").setSource(source), 0, "field2", 0, 1, equalTo("The quick brown fox jumps over")); logger.info("--> searching with boundary characters on the field"); source = searchSource().query(matchQuery("field2", "quick")) .highlighter(highlight().field(new Field("field2").fragmentSize(30).numOfFragments(1).boundaryChars(new char[] { ' ' }))); - searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over")); + assertHighlight(prepareSearch("test").setSource(source), 0, "field2", 0, 1, equalTo("The quick brown fox jumps over")); } public void testHighlighterWithSentenceBoundaryScanner() throws Exception { @@ -931,10 +918,8 @@ public void testHighlighterWithWordBoundaryScanner() throws Exception { .boundaryScannerType(BoundaryScannerType.WORD) ); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field1", 0, @@ -961,10 +946,8 @@ public void testHighlighterWithWordBoundaryScannerAndLocale() throws Exception { .boundaryScannerLocale(Locale.ENGLISH.toLanguageTag()) ); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field1", 0, @@ -1225,8 +1208,7 @@ public void testFastVectorHighlighterManyDocs() throws Exception { indexRandom(true, indexRequestBuilders); logger.info("--> searching explicitly on field1 and highlighting on it"); - SearchResponse searchResponse = client().prepareSearch() - .setSize(COUNT) + SearchResponse searchResponse = prepareSearch().setSize(COUNT) .setQuery(termQuery("field1", "test")) .highlighter(new HighlightBuilder().field("field1", 100, 0)) .get(); @@ -1266,8 +1248,7 @@ public void testSameContent() throws Exception { } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "bug")) .highlighter(new HighlightBuilder().field("title", -1, 0)) .get(); @@ -1287,8 +1268,7 @@ public void testFastVectorHighlighterOffsetParameter() throws Exception { } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "bug")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "bug")) .highlighter(new HighlightBuilder().field("title", 30, 1, 10).highlighterType("fvh")) .get(); @@ -1309,8 +1289,7 @@ public void testEscapeHtml() throws Exception { } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "test")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "test")) .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1, 10)) .get(); @@ -1330,8 +1309,7 @@ public void testEscapeHtmlVector() throws Exception { } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "test")) + SearchResponse search = prepareSearch().setQuery(matchQuery("title", "test")) .highlighter(new HighlightBuilder().encoder("html").field("title", 30, 1, 10).highlighterType("plain")) .get(); @@ -1370,20 +1348,26 @@ public void testMultiMapperVectorWithStore() throws Exception { refresh(); // simple search on body with standard analyzer with a simple field query - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)) - .get(); - - assertHighlight(search, 0, "title", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)), + 0, + "title", + 0, + 1, + equalTo("this is a test") + ); // search on title.key and highlight on title - search = client().prepareSearch() - .setQuery(matchQuery("title.key", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)) - .get(); - - assertHighlight(search, 0, "title.key", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title.key", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)), + 0, + "title.key", + 0, + 1, + equalTo("this is a test") + ); } public void testMultiMapperVectorFromSource() throws Exception { @@ -1417,20 +1401,26 @@ public void testMultiMapperVectorFromSource() throws Exception { refresh(); // simple search on body with standard analyzer with a simple field query - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)) - .get(); - - assertHighlight(search, 0, "title", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)), + 0, + "title", + 0, + 1, + equalTo("this is a test") + ); // search on title.key and highlight on title.key - search = client().prepareSearch() - .setQuery(matchQuery("title.key", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)) - .get(); - - assertHighlight(search, 0, "title.key", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title.key", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)), + 0, + "title.key", + 0, + 1, + equalTo("this is a test") + ); } public void testMultiMapperNoVectorWithStore() throws Exception { @@ -1464,20 +1454,26 @@ public void testMultiMapperNoVectorWithStore() throws Exception { refresh(); // simple search on body with standard analyzer with a simple field query - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)) - .get(); - - assertHighlight(search, 0, "title", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)), + 0, + "title", + 0, + 1, + equalTo("this is a test") + ); // search on title.key and highlight on title - search = client().prepareSearch() - .setQuery(matchQuery("title.key", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)) - .get(); - - assertHighlight(search, 0, "title.key", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title.key", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)), + 0, + "title.key", + 0, + 1, + equalTo("this is a test") + ); } public void testMultiMapperNoVectorFromSource() throws Exception { @@ -1510,20 +1506,26 @@ public void testMultiMapperNoVectorFromSource() throws Exception { refresh(); // simple search on body with standard analyzer with a simple field query - SearchResponse search = client().prepareSearch() - .setQuery(matchQuery("title", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)) - .get(); - - assertHighlight(search, 0, "title", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title", 50, 1)), + 0, + "title", + 0, + 1, + equalTo("this is a test") + ); // search on title.key and highlight on title.key - search = client().prepareSearch() - .setQuery(matchQuery("title.key", "this is a test")) - .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)) - .get(); - - assertHighlight(search, 0, "title.key", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title.key", "this is a test")) + .highlighter(new HighlightBuilder().encoder("html").field("title.key", 50, 1)), + 0, + "title.key", + 0, + 1, + equalTo("this is a test") + ); } public void testFastVectorHighlighterShouldFailIfNoTermVectors() throws Exception { @@ -1539,14 +1541,12 @@ public void testFastVectorHighlighterShouldFailIfNoTermVectors() throws Exceptio indexRandom(true, indexRequestBuilders); assertNoFailures( - client().prepareSearch() - .setQuery(matchPhraseQuery("title", "this is a test")) + prepareSearch().setQuery(matchPhraseQuery("title", "this is a test")) .highlighter(new HighlightBuilder().field("title", 50, 1, 10)) ); assertFailures( - client().prepareSearch() - .setQuery(matchPhraseQuery("title", "this is a test")) + prepareSearch().setQuery(matchPhraseQuery("title", "this is a test")) .highlighter(new HighlightBuilder().field("title", 50, 1, 10).highlighterType("fvh")), RestStatus.BAD_REQUEST, containsString( @@ -1556,8 +1556,7 @@ public void testFastVectorHighlighterShouldFailIfNoTermVectors() throws Exceptio // should not fail if there is a wildcard assertNoFailures( - client().prepareSearch() - .setQuery(matchPhraseQuery("title", "this is a test")) + prepareSearch().setQuery(matchPhraseQuery("title", "this is a test")) .highlighter(new HighlightBuilder().field("tit*", 50, 1, 10).highlighterType("fvh")) ); } @@ -1574,8 +1573,7 @@ public void testDisableFastVectorHighlighter() throws Exception { } indexRandom(true, indexRequestBuilders); - SearchResponse search = client().prepareSearch() - .setQuery(matchPhraseQuery("title", "test for the workaround")) + SearchResponse search = prepareSearch().setQuery(matchPhraseQuery("title", "test for the workaround")) .highlighter(new HighlightBuilder().field("title", 50, 1, 10).highlighterType("fvh")) .get(); @@ -1585,8 +1583,7 @@ public void testDisableFastVectorHighlighter() throws Exception { } // Using plain highlighter instead of FVH - search = client().prepareSearch() - .setQuery(matchPhraseQuery("title", "test for the workaround")) + search = prepareSearch().setQuery(matchPhraseQuery("title", "test for the workaround")) .highlighter(new HighlightBuilder().field("title", 50, 1, 10).highlighterType("plain")) .get(); @@ -1602,8 +1599,7 @@ public void testDisableFastVectorHighlighter() throws Exception { } // Using plain highlighter instead of FVH on the field level - search = client().prepareSearch() - .setQuery(matchPhraseQuery("title", "test for the workaround")) + search = prepareSearch().setQuery(matchPhraseQuery("title", "test for the workaround")) .highlighter( new HighlightBuilder().field(new HighlightBuilder.Field("title").highlighterType("plain")).highlighterType("plain") ) @@ -1660,10 +1656,14 @@ public void testBoostingQuery() { SearchSourceBuilder source = searchSource().query( boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f) ).highlighter(highlight().field("field2").order("score").preTags("").postTags("")); - - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); + assertHighlight( + prepareSearch("test").setSource(source), + 0, + "field2", + 0, + 1, + equalTo("The quick brown fox jumps over the lazy dog") + ); } public void testBoostingQueryTermVector() throws IOException { @@ -1676,10 +1676,14 @@ public void testBoostingQueryTermVector() throws IOException { SearchSourceBuilder source = searchSource().query( boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f) ).highlighter(highlight().field("field2").order("score").preTags("").postTags("")); - - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); + assertHighlight( + prepareSearch("test").setSource(source), + 0, + "field2", + 0, + 1, + equalTo("The quick brown fox jumps over the lazy dog") + ); } public void testPlainHighlightDifferentFragmenter() throws Exception { @@ -1928,99 +1932,120 @@ public void testHighlightNoMatchSize() throws IOException { // When you don't set noMatchSize you don't get any results if there isn't anything to highlight. HighlightBuilder.Field field = new HighlightBuilder.Field("text").fragmentSize(21).numOfFragments(1).highlighterType("plain"); - SearchResponse response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); // When noMatchSize is set to 0 you also shouldn't get any field.highlighterType("plain").noMatchSize(0); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); // When noMatchSize is between 0 and the size of the string field.highlighterType("plain").noMatchSize(21); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so") + ); // The FVH also works but the fragment is longer than the plain highlighter because of boundary_max_scan field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so some") + ); // Unified hl also works but the fragment is longer than the plain highlighter because of the boundary is the word field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so some") + ); // We can also ask for a fragment longer than the input string and get the whole string for (String type : new String[] { "plain", "unified" }) { field.highlighterType(type).noMatchSize(text.length() * 2).numOfFragments(0); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo(text)); + assertHighlight(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text", 0, 1, equalTo(text)); } field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo(text)); + assertHighlight(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text", 0, 1, equalTo(text)); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo(text)); + assertHighlight(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text", 0, 1, equalTo(text)); // We can also ask for a fragment exactly the size of the input field and get the whole field field.highlighterType("plain").noMatchSize(text.length()); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo(text)); + assertHighlight(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text", 0, 1, equalTo(text)); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo(text)); + assertHighlight(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text", 0, 1, equalTo(text)); // unified hl returns the first sentence as the noMatchSize does not cross sentence boundary. field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo(text)); + assertHighlight(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text", 0, 1, equalTo(text)); // You can set noMatchSize globally in the highlighter as well field.highlighterType("plain").noMatchSize(null); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so") + ); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so some") + ); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field).noMatchSize(21)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so some") + ); // We don't break if noMatchSize is less than zero though field.highlighterType("plain").noMatchSize(randomIntBetween(Integer.MIN_VALUE, -1)); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); } public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { @@ -2042,16 +2067,34 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { .numOfFragments(1) .highlighterType("plain") .noMatchSize(21); - SearchResponse response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so") + ); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so some") + ); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am pretty long so some")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am pretty long so some") + ); // And noMatchSize returns nothing when the first entry is empty string! indexDoc("test", "2", "text", new String[] { "", text2 }); @@ -2059,33 +2102,34 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds("2"); field.highlighterType("plain"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); // except for the unified highlighter which starts from the first string with actual content field.highlighterType("unified"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("I am short")); + assertHighlight( + prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("I am short") + ); // But if the field was actually empty then you should get no highlighting field indexDoc("test", "3", "text", new String[] {}); refresh(); idsQueryBuilder = QueryBuilders.idsQuery().addIds("3"); field.highlighterType("plain"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("unified"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); // Same for if the field doesn't even exist on the document indexDoc("test", "4"); @@ -2093,29 +2137,27 @@ public void testHighlightNoMatchSizeWithMultivaluedFields() throws IOException { idsQueryBuilder = QueryBuilders.idsQuery().addIds("4"); field.highlighterType("plain"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("unified"); - response = prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "postings"); + assertNotHighlighted( + prepareSearch("test").setQuery(idsQueryBuilder).highlighter(new HighlightBuilder().field(field)), + 0, + "postings" + ); // Again same if the field isn't mapped field = new HighlightBuilder.Field("unmapped").highlighterType("plain").noMatchSize(21); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertNotHighlighted(response, 0, "text"); + assertNotHighlighted(prepareSearch("test").highlighter(new HighlightBuilder().field(field)), 0, "text"); } public void testHighlightNoMatchSizeNumberOfFragments() { @@ -2138,21 +2180,39 @@ public void testHighlightNoMatchSizeNumberOfFragments() { .numOfFragments(0) .highlighterType("plain") .noMatchSize(20); - SearchResponse response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("This is the first")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("This is the first") + ); field.highlighterType("fvh"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("This is the first sentence")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("This is the first sentence") + ); field.highlighterType("unified"); - response = prepareSearch("test").highlighter(new HighlightBuilder().field(field)).get(); - assertHighlight(response, 0, "text", 0, 1, equalTo("This is the first sentence")); + assertHighlight( + prepareSearch("test").highlighter(new HighlightBuilder().field(field)), + 0, + "text", + 0, + 1, + equalTo("This is the first sentence") + ); // if there's a match we only return the values with matches (whole value as number_of_fragments == 0) MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("text", "third fifth"); field.highlighterType("plain"); - response = prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); + SearchResponse response = prepareSearch("test").setQuery(queryBuilder).highlighter(new HighlightBuilder().field(field)).get(); assertHighlight(response, 0, "text", 0, 2, equalTo("This is the third sentence. This is the fourth sentence.")); assertHighlight(response, 0, "text", 1, 2, equalTo("This is the fifth sentence")); @@ -2179,26 +2239,18 @@ public void testPostingsHighlighter() throws Exception { logger.info("--> highlighting and searching on field1"); SearchSourceBuilder source = searchSource().query(termQuery("field1", "test")) .highlighter(highlight().field("field1").preTags("").postTags("")); - SearchResponse searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a test")); + assertHighlight(client().search(new SearchRequest("test").source(source)), 0, "field1", 0, 1, equalTo("this is a test")); logger.info("--> searching on field1, highlighting on field1"); source = searchSource().query(termQuery("field1", "test")) .highlighter(highlight().field("field1").preTags("").postTags("")); - - searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a test")); + assertHighlight(client().search(new SearchRequest("test").source(source)), 0, "field1", 0, 1, equalTo("this is a test")); logger.info("--> searching on field2, highlighting on field2"); source = searchSource().query(termQuery("field2", "quick")) .highlighter(highlight().field("field2").order("score").preTags("").postTags("")); - - searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - assertHighlight( - searchResponse, + client().search(new SearchRequest("test").source(source)), 0, "field2", 0, @@ -2209,20 +2261,21 @@ public void testPostingsHighlighter() throws Exception { logger.info("--> searching on field2, highlighting on field2"); source = searchSource().query(matchPhraseQuery("field2", "quick brown")) .highlighter(highlight().field("field2").preTags("").postTags("")); - - searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy quick dog")); + assertHighlight( + client().search(new SearchRequest("test").source(source)), + 0, + "field2", + 0, + 1, + equalTo("The quick brown fox jumps over the lazy quick dog") + ); // lets fall back to the standard highlighter then, what people would do to highlight query matches logger.info("--> searching on field2, highlighting on field2, falling back to the plain highlighter"); source = searchSource().query(matchPhraseQuery("field2", "quick brown")) .highlighter(highlight().field("field2").preTags("").postTags("").highlighterType("plain").requireFieldMatch(false)); - - searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - assertHighlight( - searchResponse, + client().search(new SearchRequest("test").source(source)), 0, "field2", 0, @@ -2244,11 +2297,15 @@ public void testPostingsHighlighterMultipleFields() throws Exception { "The slow brown fox. Second sentence." ); refresh(); - - SearchResponse response = prepareSearch("test").setQuery(QueryBuilders.matchQuery("field1", "fox")) - .highlighter(new HighlightBuilder().field(new Field("field1").preTags("<1>").postTags("").requireFieldMatch(true))) - .get(); - assertHighlight(response, 0, "field1", 0, 1, equalTo("The quick brown <1>fox. Second sentence.")); + assertHighlight( + prepareSearch("test").setQuery(QueryBuilders.matchQuery("field1", "fox")) + .highlighter(new HighlightBuilder().field(new Field("field1").preTags("<1>").postTags("").requireFieldMatch(true))), + 0, + "field1", + 0, + 1, + equalTo("The quick brown <1>fox. Second sentence.") + ); } public void testPostingsHighlighterNumberOfFragments() throws Exception { @@ -2493,8 +2550,7 @@ public void testPostingsHighlighterEscapeHtml() throws Exception { } indexRandom(true, indexRequestBuilders); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(matchQuery("title", "test")) + SearchResponse searchResponse = prepareSearch().setQuery(matchQuery("title", "test")) .highlighter(new HighlightBuilder().field("title").encoder("html")) .get(); @@ -2540,7 +2596,7 @@ public void testPostingsHighlighterMultiMapperWithStore() throws Exception { refresh(); // simple search on body with standard analyzer with a simple field query - SearchResponse searchResponse = client().prepareSearch() + SearchResponse searchResponse = prepareSearch() // lets make sure we analyze the query and we highlight the resulting terms .setQuery(matchQuery("title", "This is a Test")) .highlighter(new HighlightBuilder().field("title")) @@ -2552,8 +2608,7 @@ public void testPostingsHighlighterMultiMapperWithStore() throws Exception { assertHighlight(hit, "title", 0, 1, equalTo("this is a test . Second sentence.")); // search on title.key and highlight on title - searchResponse = client().prepareSearch() - .setQuery(matchQuery("title.key", "this is a test")) + searchResponse = prepareSearch().setQuery(matchQuery("title.key", "this is a test")) .highlighter(new HighlightBuilder().field("title.key")) .get(); assertHitCount(searchResponse, 1L); @@ -2600,20 +2655,24 @@ public void testPostingsHighlighterMultiMapperFromSource() throws Exception { refresh(); // simple search on body with standard analyzer with a simple field query - SearchResponse searchResponse = client().prepareSearch() - .setQuery(matchQuery("title", "this is a test")) - .highlighter(new HighlightBuilder().field("title")) - .get(); - - assertHighlight(searchResponse, 0, "title", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title", "this is a test")).highlighter(new HighlightBuilder().field("title")), + 0, + "title", + 0, + 1, + equalTo("this is a test") + ); // search on title.key and highlight on title.key - searchResponse = client().prepareSearch() - .setQuery(matchQuery("title.key", "this is a test")) - .highlighter(new HighlightBuilder().field("title.key")) - .get(); - - assertHighlight(searchResponse, 0, "title.key", 0, 1, equalTo("this is a test")); + assertHighlight( + prepareSearch().setQuery(matchQuery("title.key", "this is a test")).highlighter(new HighlightBuilder().field("title.key")), + 0, + "title.key", + 0, + 1, + equalTo("this is a test") + ); } public void testPostingsHighlighterShouldFailIfNoOffsets() throws Exception { @@ -2643,11 +2702,11 @@ public void testPostingsHighlighterShouldFailIfNoOffsets() throws Exception { indexRandom(true, indexRequestBuilders); assertNoFailures( - client().prepareSearch().setQuery(matchQuery("title", "this is a test")).highlighter(new HighlightBuilder().field("title")) + prepareSearch().setQuery(matchQuery("title", "this is a test")).highlighter(new HighlightBuilder().field("title")) ); } - public void testPostingsHighlighterBoostingQuery() throws IOException { + public void testPostingsHighlighterBoostingQuery() throws IOException, ExecutionException, InterruptedException { assertAcked(prepareCreate("test").setMapping(type1PostingsffsetsMapping())); ensureGreen(); client().prepareIndex("test") @@ -2659,9 +2718,14 @@ public void testPostingsHighlighterBoostingQuery() throws IOException { SearchSourceBuilder source = searchSource().query( boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f) ).highlighter(highlight().field("field2").preTags("").postTags("")); - SearchResponse searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy dog! Second sentence.")); + assertHighlight( + client().search(new SearchRequest("test").source(source)), + 0, + "field2", + 0, + 1, + equalTo("The quick brown fox jumps over the lazy dog! Second sentence.") + ); } private static XContentBuilder type1PostingsffsetsMapping() throws IOException { @@ -2693,9 +2757,8 @@ public void testPostingsHighlighterPrefixQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(prefixQuery("field2", "qui")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field2", 0, @@ -2715,10 +2778,8 @@ public void testPostingsHighlighterFuzzyQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(fuzzyQuery("field2", "quck")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field2", 0, @@ -2738,10 +2799,8 @@ public void testPostingsHighlighterRegexpQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(regexpQuery("field2", "qu[a-l]+k")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field2", 0, @@ -2761,10 +2820,8 @@ public void testPostingsHighlighterWildcardQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(wildcardQuery("field2", "qui*")).highlighter(highlight().field("field2")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field2", 0, @@ -2773,7 +2830,7 @@ public void testPostingsHighlighterWildcardQuery() throws Exception { ); source = searchSource().query(wildcardQuery("field2", "qu*k")).highlighter(highlight().field("field2")); - searchResponse = prepareSearch("test").setSource(source).get(); + SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHitCount(searchResponse, 1L); assertHighlight( @@ -2796,9 +2853,7 @@ public void testPostingsHighlighterTermRangeQuery() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(rangeQuery("field2").gte("aaaa").lt("zzzz")) .highlighter(highlight().field("field2")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - - assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("aaab")); + assertHighlight(prepareSearch("test").setSource(source), 0, "field2", 0, 1, equalTo("aaab")); } public void testPostingsHighlighterQueryString() throws Exception { @@ -2813,9 +2868,8 @@ public void testPostingsHighlighterQueryString() throws Exception { logger.info("--> highlighting and searching on field2"); SearchSourceBuilder source = searchSource().query(queryStringQuery("qui*").defaultField("field2")) .highlighter(highlight().field("field2")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); assertHighlight( - searchResponse, + prepareSearch("test").setSource(source), 0, "field2", 0, @@ -2834,8 +2888,14 @@ public void testPostingsHighlighterRegexpQueryWithinConstantScoreQuery() throws logger.info("--> highlighting and searching on field1"); SearchSourceBuilder source = searchSource().query(constantScoreQuery(regexpQuery("field1", "pho[a-z]+"))) .highlighter(highlight().field("field1")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); + assertHighlight( + prepareSearch("test").setSource(source), + 0, + "field1", + 0, + 1, + equalTo("The photography word will get highlighted") + ); } public void testPostingsHighlighterMultiTermQueryMultipleLevels() throws Exception { @@ -2851,8 +2911,14 @@ public void testPostingsHighlighterMultiTermQueryMultipleLevels() throws Excepti .should(matchQuery("field1", "test")) .should(constantScoreQuery(queryStringQuery("field1:photo*"))) ).highlighter(highlight().field("field1")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); + assertHighlight( + prepareSearch("test").setSource(source), + 0, + "field1", + 0, + 1, + equalTo("The photography word will get highlighted") + ); } public void testPostingsHighlighterPrefixQueryWithinBooleanQuery() throws Exception { @@ -2866,8 +2932,14 @@ public void testPostingsHighlighterPrefixQueryWithinBooleanQuery() throws Except SearchSourceBuilder source = searchSource().query( boolQuery().must(prefixQuery("field1", "photo")).should(matchQuery("field1", "test").minimumShouldMatch("0")) ).highlighter(highlight().field("field1")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); + assertHighlight( + prepareSearch("test").setSource(source), + 0, + "field1", + 0, + 1, + equalTo("The photography word will get highlighted") + ); } public void testPostingsHighlighterQueryStringWithinFilteredQuery() throws Exception { @@ -2881,8 +2953,14 @@ public void testPostingsHighlighterQueryStringWithinFilteredQuery() throws Excep SearchSourceBuilder source = searchSource().query( boolQuery().must(queryStringQuery("field1:photo*")).mustNot(existsQuery("field_null")) ).highlighter(highlight().field("field1")); - SearchResponse searchResponse = prepareSearch("test").setSource(source).get(); - assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("The photography word will get highlighted")); + assertHighlight( + prepareSearch("test").setSource(source), + 0, + "field1", + 0, + 1, + equalTo("The photography word will get highlighted") + ); } public void testPostingsHighlighterManyDocs() throws Exception { @@ -2906,8 +2984,7 @@ public void testPostingsHighlighterManyDocs() throws Exception { indexRandom(true, indexRequestBuilders); logger.info("--> searching explicitly on field1 and highlighting on it"); - SearchRequestBuilder searchRequestBuilder = client().prepareSearch() - .setSize(COUNT) + SearchRequestBuilder searchRequestBuilder = prepareSearch().setSize(COUNT) .setQuery(termQuery("field1", "test")) .highlighter(new HighlightBuilder().field("field1")); SearchResponse searchResponse = searchRequestBuilder.get(); @@ -2938,10 +3015,15 @@ public void testDoesNotHighlightTypeName() throws Exception { indexRandom(true, client().prepareIndex("test").setSource("foo", "test typename")); for (String highlighter : ALL_TYPES) { - SearchResponse response = prepareSearch("test").setQuery(matchQuery("foo", "test")) - .highlighter(new HighlightBuilder().field("foo").highlighterType(highlighter).requireFieldMatch(false)) - .get(); - assertHighlight(response, 0, "foo", 0, 1, equalTo("test typename")); + assertHighlight( + prepareSearch("test").setQuery(matchQuery("foo", "test")) + .highlighter(new HighlightBuilder().field("foo").highlighterType(highlighter).requireFieldMatch(false)), + 0, + "foo", + 0, + 1, + equalTo("test typename") + ); } } @@ -2965,10 +3047,15 @@ public void testDoesNotHighlightAliasFilters() throws Exception { indexRandom(true, client().prepareIndex("test").setSource("foo", "test japanese")); for (String highlighter : ALL_TYPES) { - SearchResponse response = prepareSearch("filtered_alias").setQuery(matchQuery("foo", "test")) - .highlighter(new HighlightBuilder().field("foo").highlighterType(highlighter).requireFieldMatch(false)) - .get(); - assertHighlight(response, 0, "foo", 0, 1, equalTo("test japanese")); + assertHighlight( + prepareSearch("filtered_alias").setQuery(matchQuery("foo", "test")) + .highlighter(new HighlightBuilder().field("foo").highlighterType(highlighter).requireFieldMatch(false)), + 0, + "foo", + 0, + 1, + equalTo("test japanese") + ); } } @@ -3051,15 +3138,19 @@ private

> void phraseBoostTestCaseForClauses( // Try with a bool query phrase.boost(boost); - SearchResponse response = search.setQuery(boolQuery().must(terms).should(phrase)).get(); - assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher); + assertHighlight(search.setQuery(boolQuery().must(terms).should(phrase)), 0, "field1", 0, 1, highlightedMatcher); phrase.boost(1); // Try with a boosting query - response = search.setQuery(boostingQuery(phrase, terms).boost(boost).negativeBoost(1)).get(); - assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher); + assertHighlight(search.setQuery(boostingQuery(phrase, terms).boost(boost).negativeBoost(1)), 0, "field1", 0, 1, highlightedMatcher); // Try with a boosting query using a negative boost - response = search.setQuery(boostingQuery(phrase, terms).boost(1).negativeBoost(1 / boost)).get(); - assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher); + assertHighlight( + search.setQuery(boostingQuery(phrase, terms).boost(1).negativeBoost(1 / boost)), + 0, + "field1", + 0, + 1, + highlightedMatcher + ); } public void testGeoFieldHighlightingWithDifferentHighlighters() throws IOException { @@ -3095,11 +3186,9 @@ public void testGeoFieldHighlightingWithDifferentHighlighters() throws IOExcepti .setCorners(61.10078883158897, -170.15625, -64.92354174306496, 118.47656249999999) ) .should(QueryBuilders.termQuery("text", "failure")); - SearchResponse search = client().prepareSearch() - .setSource( - new SearchSourceBuilder().query(query).highlighter(new HighlightBuilder().field("*").highlighterType(highlighterType)) - ) - .get(); + SearchResponse search = prepareSearch().setSource( + new SearchSourceBuilder().query(query).highlighter(new HighlightBuilder().field("*").highlighterType(highlighterType)) + ).get(); assertNoFailures(search); assertThat(search.getHits().getTotalHits().value, equalTo(1L)); assertThat(search.getHits().getAt(0).getHighlightFields().get("text").fragments().length, equalTo(1)); @@ -3142,11 +3231,12 @@ public void testGeoFieldHighlightingWhenQueryGetsRewritten() throws IOException .setCorners(new GeoPoint(48.934059, 41.610741), new GeoPoint(-23.065941, 113.610741)) ) ); - SearchResponse search = client().prepareSearch() - .setSource(new SearchSourceBuilder().query(query).highlighter(new HighlightBuilder().highlighterType("plain").field("jd"))) - .get(); - assertNoFailures(search); - assertThat(search.getHits().getTotalHits().value, equalTo(1L)); + assertHitCountAndNoFailures( + prepareSearch().setSource( + new SearchSourceBuilder().query(query).highlighter(new HighlightBuilder().highlighterType("plain").field("jd")) + ), + 1 + ); } public void testKeywordFieldHighlighting() throws IOException { @@ -3168,12 +3258,10 @@ public void testKeywordFieldHighlighting() throws IOException { .setSource(jsonBuilder().startObject().field("keyword_field", "some text").endObject()) .get(); refresh(); - SearchResponse search = client().prepareSearch() - .setSource( - new SearchSourceBuilder().query(QueryBuilders.matchQuery("keyword_field", "some text")) - .highlighter(new HighlightBuilder().field("*")) - ) - .get(); + SearchResponse search = prepareSearch().setSource( + new SearchSourceBuilder().query(QueryBuilders.matchQuery("keyword_field", "some text")) + .highlighter(new HighlightBuilder().field("*")) + ).get(); assertNoFailures(search); assertThat(search.getHits().getTotalHits().value, equalTo(1L)); assertThat( @@ -3200,8 +3288,7 @@ public void testCopyToFields() throws Exception { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); - SearchResponse response = client().prepareSearch() - .setQuery(matchQuery("foo_copy", "brown")) + SearchResponse response = prepareSearch().setQuery(matchQuery("foo_copy", "brown")) .highlighter(new HighlightBuilder().field(new Field("foo_copy"))) .get(); @@ -3251,8 +3338,7 @@ public void testACopyFieldWithNestedQuery() throws Exception { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(nestedQuery("foo", matchQuery("foo.text", "brown cow"), ScoreMode.None)) + SearchResponse searchResponse = prepareSearch().setQuery(nestedQuery("foo", matchQuery("foo.text", "brown cow"), ScoreMode.None)) .highlighter(new HighlightBuilder().field(new Field("foo_text").highlighterType("fvh")).requireFieldMatch(false)) .get(); assertHitCount(searchResponse, 1); @@ -3269,8 +3355,7 @@ public void testFunctionScoreQueryHighlight() throws Exception { .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(new FunctionScoreQueryBuilder(QueryBuilders.prefixQuery("text", "bro"))) + SearchResponse searchResponse = prepareSearch().setQuery(new FunctionScoreQueryBuilder(QueryBuilders.prefixQuery("text", "bro"))) .highlighter(new HighlightBuilder().field(new Field("text"))) .get(); assertHitCount(searchResponse, 1); @@ -3290,15 +3375,12 @@ public void testFiltersFunctionScoreQueryHighlight() throws Exception { new RandomScoreFunctionBuilder() ); - SearchResponse searchResponse = client().prepareSearch() - .setQuery( - new FunctionScoreQueryBuilder( - QueryBuilders.prefixQuery("text", "bro"), - new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { filterBuilder } - ) + SearchResponse searchResponse = prepareSearch().setQuery( + new FunctionScoreQueryBuilder( + QueryBuilders.prefixQuery("text", "bro"), + new FunctionScoreQueryBuilder.FilterFunctionBuilder[] { filterBuilder } ) - .highlighter(new HighlightBuilder().field(new Field("text"))) - .get(); + ).highlighter(new HighlightBuilder().field(new Field("text"))).get(); assertHitCount(searchResponse, 1); HighlightField field = searchResponse.getHits().getAt(0).getHighlightFields().get("text"); assertThat(field.getFragments().length, equalTo(1)); @@ -3379,18 +3461,16 @@ public void testWithNestedQuery() throws Exception { .get(); for (String type : new String[] { "unified", "plain" }) { - SearchResponse searchResponse = client().prepareSearch() - .setQuery(nestedQuery("foo", matchQuery("foo.text", "brown cow"), ScoreMode.None)) - .highlighter(new HighlightBuilder().field(new Field("foo.text").highlighterType(type))) - .get(); + SearchResponse searchResponse = prepareSearch().setQuery( + nestedQuery("foo", matchQuery("foo.text", "brown cow"), ScoreMode.None) + ).highlighter(new HighlightBuilder().field(new Field("foo.text").highlighterType(type))).get(); assertHitCount(searchResponse, 1); HighlightField field = searchResponse.getHits().getAt(0).getHighlightFields().get("foo.text"); assertThat(field.getFragments().length, equalTo(2)); assertThat(field.getFragments()[0].string(), equalTo("brown shoes")); assertThat(field.getFragments()[1].string(), equalTo("cow")); - searchResponse = client().prepareSearch() - .setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None)) + searchResponse = prepareSearch().setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None)) .highlighter(new HighlightBuilder().field(new Field("foo.text").highlighterType(type))) .get(); assertHitCount(searchResponse, 1); @@ -3398,8 +3478,7 @@ public void testWithNestedQuery() throws Exception { assertThat(field.getFragments().length, equalTo(1)); assertThat(field.getFragments()[0].string(), equalTo("brown shoes")); - searchResponse = client().prepareSearch() - .setQuery(nestedQuery("foo", matchPhraseQuery("foo.text", "brown shoes"), ScoreMode.None)) + searchResponse = prepareSearch().setQuery(nestedQuery("foo", matchPhraseQuery("foo.text", "brown shoes"), ScoreMode.None)) .highlighter(new HighlightBuilder().field(new Field("foo.text").highlighterType(type))) .get(); assertHitCount(searchResponse, 1); @@ -3407,8 +3486,7 @@ public void testWithNestedQuery() throws Exception { assertThat(field.getFragments().length, equalTo(1)); assertThat(field.getFragments()[0].string(), equalTo("brown shoes")); - searchResponse = client().prepareSearch() - .setQuery(nestedQuery("foo", matchPhrasePrefixQuery("foo.text", "bro"), ScoreMode.None)) + searchResponse = prepareSearch().setQuery(nestedQuery("foo", matchPhrasePrefixQuery("foo.text", "bro"), ScoreMode.None)) .highlighter(new HighlightBuilder().field(new Field("foo.text").highlighterType(type))) .get(); assertHitCount(searchResponse, 1); @@ -3421,8 +3499,7 @@ public void testWithNestedQuery() throws Exception { // but we highlight the root text field since nested documents cannot be highlighted with postings nor term vectors // directly. for (String type : ALL_TYPES) { - SearchResponse searchResponse = client().prepareSearch() - .setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None)) + SearchResponse searchResponse = prepareSearch().setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None)) .highlighter(new HighlightBuilder().field(new Field("text").highlighterType(type).requireFieldMatch(false))) .get(); assertHitCount(searchResponse, 1); @@ -3445,8 +3522,7 @@ public void testWithNormalizer() throws Exception { .get(); for (String highlighterType : new String[] { "unified", "plain" }) { - SearchResponse searchResponse = client().prepareSearch() - .setQuery(matchQuery("keyword", "hello world")) + SearchResponse searchResponse = prepareSearch().setQuery(matchQuery("keyword", "hello world")) .highlighter(new HighlightBuilder().field(new Field("keyword").highlighterType(highlighterType))) .get(); assertHitCount(searchResponse, 1); @@ -3467,10 +3543,9 @@ public void testDisableHighlightIdField() throws Exception { .get(); for (String highlighterType : new String[] { "plain", "unified" }) { - SearchResponse searchResponse = client().prepareSearch() - .setQuery(matchQuery("_id", "d33f85bf1e51e84d9ab38948db9f3a068e1fe5294f1d8603914ac8c7bcc39ca1")) - .highlighter(new HighlightBuilder().field(new Field("*").highlighterType(highlighterType).requireFieldMatch(false))) - .get(); + SearchResponse searchResponse = prepareSearch().setQuery( + matchQuery("_id", "d33f85bf1e51e84d9ab38948db9f3a068e1fe5294f1d8603914ac8c7bcc39ca1") + ).highlighter(new HighlightBuilder().field(new Field("*").highlighterType(highlighterType).requireFieldMatch(false))).get(); assertHitCount(searchResponse, 1); assertNull(searchResponse.getHits().getAt(0).getHighlightFields().get("_id")); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java index 32d00a4a925ac..415de06030938 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/morelikethis/MoreLikeThisIT.java @@ -39,6 +39,7 @@ import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertOrderedSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertRequestBuilderThrows; @@ -82,8 +83,7 @@ public void testSimpleMoreLikeThis() throws Exception { logger.info("Running moreLikeThis"); assertHitCount( - client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), 1L ); } @@ -116,8 +116,7 @@ public void testSimpleMoreLikeThisWithTypes() throws Exception { logger.info("Running moreLikeThis"); assertHitCount( - client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), 1L ); } @@ -153,11 +152,10 @@ public void testMoreLikeThisForZeroTokensInOneOfTheAnalyzedFields() throws Excep indicesAdmin().refresh(new RefreshRequest()).actionGet(); assertHitCount( - client().prepareSearch() - .setQuery( - moreLikeThisQuery(new String[] { "myField", "empty" }, null, new Item[] { new Item("test", "1") }).minTermFreq(1) - .minDocFreq(1) - ), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "myField", "empty" }, null, new Item[] { new Item("test", "1") }).minTermFreq(1) + .minDocFreq(1) + ), 1L ); } @@ -179,8 +177,7 @@ public void testSimpleMoreLikeOnLongField() throws Exception { logger.info("Running moreLikeThis"); assertHitCount( - client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), 0L ); } @@ -223,8 +220,7 @@ public void testMoreLikeThisWithAliases() throws Exception { logger.info("Running moreLikeThis on index"); assertHitCount( - client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), 2L ); @@ -271,9 +267,9 @@ public void testMoreLikeThisWithAliasesInLikeDocuments() throws Exception { ).actionGet(); refresh(indexName); - SearchResponse response = client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item(aliasName, "1") }).minTermFreq(1).minDocFreq(1)) - .get(); + SearchResponse response = prepareSearch().setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item(aliasName, "1") }).minTermFreq(1).minDocFreq(1) + ).get(); assertHitCount(response, 2L); assertThat(response.getHits().getAt(0).getId(), equalTo("3")); } @@ -287,8 +283,8 @@ public void testMoreLikeThisIssue2197() throws Exception { indicesAdmin().prepareRefresh("foo").get(); assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN)); - assertNoFailures(client().prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1") }))); - assertNoFailures(client().prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1") }))); + assertNoFailures(prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1") }))); + assertNoFailures(prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1") }))); } // Issue #2489 @@ -303,9 +299,7 @@ public void testMoreLikeWithCustomRouting() throws Exception { .get(); indicesAdmin().prepareRefresh("foo").get(); - assertNoFailures( - client().prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1").routing("2") })) - ); + assertNoFailures(prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1").routing("2") }))); } // Issue #3039 @@ -319,9 +313,7 @@ public void testMoreLikeThisIssueRoutingNotSerialized() throws Exception { .setRouting("4000") .get(); indicesAdmin().prepareRefresh("foo").get(); - assertNoFailures( - client().prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1").routing("4000") })) - ); + assertNoFailures(prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("foo", "1").routing("4000") }))); } // Issue #3252 @@ -355,90 +347,83 @@ public void testNumericField() throws Exception { // Implicit list of fields -> ignore numeric fields assertHitCount( - client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)), 1L ); // Explicit list of fields including numeric fields -> fail assertRequestBuilderThrows( - client().prepareSearch() - .setQuery( - new MoreLikeThisQueryBuilder(new String[] { "string_value", "int_value" }, null, new Item[] { new Item("test", "1") }) - .minTermFreq(1) - .minDocFreq(1) - ), + prepareSearch().setQuery( + new MoreLikeThisQueryBuilder(new String[] { "string_value", "int_value" }, null, new Item[] { new Item("test", "1") }) + .minTermFreq(1) + .minDocFreq(1) + ), SearchPhaseExecutionException.class ); // mlt query with no field -> exception because _all is not enabled) assertRequestBuilderThrows( - client().prepareSearch().setQuery(moreLikeThisQuery(new String[] { "index" }).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery(moreLikeThisQuery(new String[] { "index" }).minTermFreq(1).minDocFreq(1)), SearchPhaseExecutionException.class ); // mlt query with string fields assertHitCount( - client().prepareSearch() - .setQuery(moreLikeThisQuery(new String[] { "string_value" }, new String[] { "index" }, null).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "string_value" }, new String[] { "index" }, null).minTermFreq(1).minDocFreq(1) + ), 2L ); // mlt query with at least a numeric field -> fail by default assertRequestBuilderThrows( - client().prepareSearch() - .setQuery(moreLikeThisQuery(new String[] { "string_value", "int_value" }, new String[] { "index" }, null)), + prepareSearch().setQuery(moreLikeThisQuery(new String[] { "string_value", "int_value" }, new String[] { "index" }, null)), SearchPhaseExecutionException.class ); // mlt query with at least a numeric field -> fail by command assertRequestBuilderThrows( - client().prepareSearch() - .setQuery( - moreLikeThisQuery(new String[] { "string_value", "int_value" }, new String[] { "index" }, null).failOnUnsupportedField( - true - ) - ), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "string_value", "int_value" }, new String[] { "index" }, null).failOnUnsupportedField(true) + ), SearchPhaseExecutionException.class ); // mlt query with at least a numeric field but fail_on_unsupported_field set to false assertHitCount( - client().prepareSearch() - .setQuery( - moreLikeThisQuery(new String[] { "string_value", "int_value" }, new String[] { "index" }, null).minTermFreq(1) - .minDocFreq(1) - .failOnUnsupportedField(false) - ), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "string_value", "int_value" }, new String[] { "index" }, null).minTermFreq(1) + .minDocFreq(1) + .failOnUnsupportedField(false) + ), 2L ); // mlt field query on a numeric field -> failure by default assertRequestBuilderThrows( - client().prepareSearch() - .setQuery(moreLikeThisQuery(new String[] { "int_value" }, new String[] { "42" }, null).minTermFreq(1).minDocFreq(1)), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "int_value" }, new String[] { "42" }, null).minTermFreq(1).minDocFreq(1) + ), SearchPhaseExecutionException.class ); // mlt field query on a numeric field -> failure by command assertRequestBuilderThrows( - client().prepareSearch() - .setQuery( - moreLikeThisQuery(new String[] { "int_value" }, new String[] { "42" }, null).minTermFreq(1) - .minDocFreq(1) - .failOnUnsupportedField(true) - ), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "int_value" }, new String[] { "42" }, null).minTermFreq(1) + .minDocFreq(1) + .failOnUnsupportedField(true) + ), SearchPhaseExecutionException.class ); // mlt field query on a numeric field but fail_on_unsupported_field set to false assertHitCount( - client().prepareSearch() - .setQuery( - moreLikeThisQuery(new String[] { "int_value" }, new String[] { "42" }, null).minTermFreq(1) - .minDocFreq(1) - .failOnUnsupportedField(false) - ), + prepareSearch().setQuery( + moreLikeThisQuery(new String[] { "int_value" }, new String[] { "42" }, null).minTermFreq(1) + .minDocFreq(1) + .failOnUnsupportedField(false) + ), 0L ); } @@ -470,7 +455,7 @@ public void testMoreLikeThisWithFieldAlias() throws Exception { QueryBuilder query = QueryBuilders.moreLikeThisQuery(new String[] { "alias" }, null, new Item[] { item }) .minTermFreq(1) .minDocFreq(1); - assertHitCount(client().prepareSearch().setQuery(query), 1L); + assertHitCount(prepareSearch().setQuery(query), 1L); } public void testSimpleMoreLikeInclude() throws Exception { @@ -509,37 +494,34 @@ public void testSimpleMoreLikeInclude() throws Exception { logger.info("Running More Like This with include true"); assertOrderedSearchHits( - client().prepareSearch() - .setQuery( - new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1) - .minDocFreq(1) - .include(true) - .minimumShouldMatch("0%") - ), + prepareSearch().setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1) + .minDocFreq(1) + .include(true) + .minimumShouldMatch("0%") + ), "1", "2" ); assertOrderedSearchHits( - client().prepareSearch() - .setQuery( - new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "2") }).minTermFreq(1) - .minDocFreq(1) - .include(true) - .minimumShouldMatch("0%") - ), + prepareSearch().setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "2") }).minTermFreq(1) + .minDocFreq(1) + .include(true) + .minimumShouldMatch("0%") + ), "2", "1" ); logger.info("Running More Like This with include false"); assertSearchHits( - client().prepareSearch() - .setQuery( - new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1) - .minDocFreq(1) - .minimumShouldMatch("0%") - ), + prepareSearch().setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1) + .minDocFreq(1) + .minimumShouldMatch("0%") + ), "2" ); } @@ -576,7 +558,7 @@ public void testSimpleMoreLikeThisIds() throws Exception { .include(true) .minTermFreq(1) .minDocFreq(1); - assertHitCount(client().prepareSearch().setQuery(queryBuilder), 3L); + assertHitCount(prepareSearch().setQuery(queryBuilder), 3L); } public void testMoreLikeThisMultiValueFields() throws Exception { @@ -607,9 +589,7 @@ public void testMoreLikeThisMultiValueFields() throws Exception { .minDocFreq(1) .maxQueryTerms(max_query_terms) .minimumShouldMatch("0%"); - SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, max_query_terms); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), max_query_terms); } } @@ -670,9 +650,7 @@ public void testMoreLikeThisArtificialDocs() throws Exception { .minDocFreq(0) .maxQueryTerms(100) .minimumShouldMatch("100%"); // strict all terms must match! - SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), 1); } public void testMoreLikeThisMalformedArtificialDocs() throws Exception { @@ -696,16 +674,12 @@ public void testMoreLikeThisMalformedArtificialDocs() throws Exception { MoreLikeThisQueryBuilder mltQuery = moreLikeThisQuery(new Item[] { new Item("test", malformedFieldDoc) }).minTermFreq(0) .minDocFreq(0) .minimumShouldMatch("0%"); - SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, 0); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), 0); logger.info("Checking with an empty document ..."); XContentBuilder emptyDoc = jsonBuilder().startObject().endObject(); mltQuery = moreLikeThisQuery(null, new Item[] { new Item("test", emptyDoc) }).minTermFreq(0).minDocFreq(0).minimumShouldMatch("0%"); - response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, 0); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), 0); logger.info("Checking the document matches otherwise ..."); XContentBuilder normalDoc = jsonBuilder().startObject() @@ -715,12 +689,10 @@ public void testMoreLikeThisMalformedArtificialDocs() throws Exception { mltQuery = moreLikeThisQuery(null, new Item[] { new Item("test", normalDoc) }).minTermFreq(0) .minDocFreq(0) .minimumShouldMatch("100%"); // strict all terms must match but date is ignored - response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), 1); } - public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedException, IOException { + public void testMoreLikeThisUnlike() throws InterruptedException, IOException { createIndex("test"); ensureGreen(); int numFields = randomIntBetween(5, 10); @@ -744,9 +716,7 @@ public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedExcep .minDocFreq(0) .maxQueryTerms(100) .minimumShouldMatch("0%"); - SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, numFields); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), numFields); logger.info("Now check like this doc, but ignore one doc in the index, then two and so on..."); List docs = new ArrayList<>(numFields); @@ -758,10 +728,7 @@ public void testMoreLikeThisUnlike() throws ExecutionException, InterruptedExcep .maxQueryTerms(100) .include(true) .minimumShouldMatch("0%"); - - response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, numFields - (i + 1)); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), numFields - (i + 1)); } } @@ -783,17 +750,13 @@ public void testSelectFields() throws IOException, ExecutionException, Interrupt .minDocFreq(0) .include(true) .minimumShouldMatch("1%"); - SearchResponse response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, 2); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), 2); mltQuery = moreLikeThisQuery(new String[] { "text" }, null, new Item[] { new Item("test", "1") }).minTermFreq(0) .minDocFreq(0) .include(true) .minimumShouldMatch("1%"); - response = prepareSearch("test").setQuery(mltQuery).get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(mltQuery), 1); } public void testWithRouting() throws IOException { @@ -841,9 +804,9 @@ public void testWithMissingRouting() throws IOException { logger.info("Running moreLikeThis with one item without routing attribute"); SearchPhaseExecutionException exception = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch() - .setQuery(new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1)) - .get() + () -> prepareSearch().setQuery( + new MoreLikeThisQueryBuilder(null, new Item[] { new Item("test", "1") }).minTermFreq(1).minDocFreq(1) + ).get() ); Throwable cause = exception.getCause(); @@ -855,14 +818,12 @@ public void testWithMissingRouting() throws IOException { logger.info("Running moreLikeThis with one item with routing attribute and two items without routing attribute"); SearchPhaseExecutionException exception = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch() - .setQuery( - new MoreLikeThisQueryBuilder( - null, - new Item[] { new Item("test", "1").routing("1"), new Item("test", "2"), new Item("test", "3") } - ).minTermFreq(1).minDocFreq(1) - ) - .get() + () -> prepareSearch().setQuery( + new MoreLikeThisQueryBuilder( + null, + new Item[] { new Item("test", "1").routing("1"), new Item("test", "2"), new Item("test", "3") } + ).minTermFreq(1).minDocFreq(1) + ).get() ); Throwable cause = exception.getCause(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java index 199f730b65aea..918746021f381 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -146,8 +146,8 @@ public void testEmptyQueryString() throws ExecutionException, InterruptedExcepti client().prepareIndex("test").setId("3").setSource("field1", "quick") ); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("quick")), 3L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("")), 0L); // return no docs + assertHitCount(prepareSearch().setQuery(queryStringQuery("quick")), 3L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("")), 0L); // return no docs } // see https://github.com/elastic/elasticsearch/issues/3177 @@ -161,8 +161,7 @@ public void testIssue3177() { forceMerge(); refresh(); assertHitCount( - client().prepareSearch() - .setQuery(matchAllQuery()) + prepareSearch().setQuery(matchAllQuery()) .setPostFilter( boolQuery().must(matchAllQuery()) .must(boolQuery().mustNot(boolQuery().must(termQuery("field1", "value1")).must(termQuery("field1", "value2")))) @@ -170,20 +169,16 @@ public void testIssue3177() { 3L ); assertHitCount( - client().prepareSearch() - .setQuery( - boolQuery().must( - boolQuery().should(termQuery("field1", "value1")) - .should(termQuery("field1", "value2")) - .should(termQuery("field1", "value3")) - ).filter(boolQuery().mustNot(boolQuery().must(termQuery("field1", "value1")).must(termQuery("field1", "value2")))) - ), + prepareSearch().setQuery( + boolQuery().must( + boolQuery().should(termQuery("field1", "value1")) + .should(termQuery("field1", "value2")) + .should(termQuery("field1", "value3")) + ).filter(boolQuery().mustNot(boolQuery().must(termQuery("field1", "value1")).must(termQuery("field1", "value2")))) + ), 3L ); - assertHitCount( - client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(boolQuery().mustNot(termQuery("field1", "value3"))), - 2L - ); + assertHitCount(prepareSearch().setQuery(matchAllQuery()).setPostFilter(boolQuery().mustNot(termQuery("field1", "value3"))), 2L); } public void testIndexOptions() throws Exception { @@ -194,10 +189,10 @@ public void testIndexOptions() throws Exception { client().prepareIndex("test").setId("2").setSource("field1", "quick lazy huge brown fox", "field2", "quick lazy huge brown fox") ); - assertHitCount(client().prepareSearch().setQuery(matchPhraseQuery("field2", "quick brown").slop(0)), 1L); + assertHitCount(prepareSearch().setQuery(matchPhraseQuery("field2", "quick brown").slop(0)), 1L); assertFailures( - client().prepareSearch().setQuery(matchPhraseQuery("field1", "quick brown").slop(0)), + prepareSearch().setQuery(matchPhraseQuery("field1", "quick brown").slop(0)), RestStatus.BAD_REQUEST, containsString("field:[field1] was indexed without position data; cannot run PhraseQuery") ); @@ -213,7 +208,7 @@ public void testConstantScoreQuery() throws Exception { client().prepareIndex("test").setId("2").setSource("field1", "quick lazy huge brown fox", "field2", "quick lazy huge brown fox") ); - SearchResponse searchResponse = client().prepareSearch().setQuery(constantScoreQuery(matchQuery("field1", "quick"))).get(); + SearchResponse searchResponse = prepareSearch().setQuery(constantScoreQuery(matchQuery("field1", "quick"))).get(); assertHitCount(searchResponse, 2L); for (SearchHit searchHit : searchResponse.getHits().getHits()) { assertThat(searchHit, hasScore(1.0f)); @@ -303,11 +298,11 @@ public void testQueryStringAnalyzedWildcard() throws Exception { client().prepareIndex("test").setId("1").setSource("field1", "value_1", "field2", "value_2").get(); refresh(); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("value*")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("*ue*")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("*ue_1")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("val*e_1")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("v?l*e?1")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("value*")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("*ue*")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("*ue_1")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("val*e_1")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("v?l*e?1")), 1L); } public void testLowercaseExpandedTerms() { @@ -316,10 +311,10 @@ public void testLowercaseExpandedTerms() { client().prepareIndex("test").setId("1").setSource("field1", "value_1", "field2", "value_2").get(); refresh(); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("VALUE_3~1")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("ValUE_*")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("vAl*E_1")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("[VALUE_1 TO VALUE_3]")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("VALUE_3~1")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("ValUE_*")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("vAl*E_1")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("[VALUE_1 TO VALUE_3]")), 1L); } // Issue #3540 @@ -334,12 +329,12 @@ public void testDateRangeInQueryString() { client().prepareIndex("test").setId("1").setSource("past", aMonthAgo, "future", aMonthFromNow).get(); refresh(); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("past:[now-2M/d TO now/d]")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("future:[now/d TO now+2M/d]")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("past:[now-2M/d TO now/d]")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("future:[now/d TO now+2M/d]")), 1L); SearchPhaseExecutionException e = expectThrows( SearchPhaseExecutionException.class, - () -> client().prepareSearch().setQuery(queryStringQuery("future:[now/D TO now+2M/d]").lenient(false)).get() + () -> prepareSearch().setQuery(queryStringQuery("future:[now/D TO now+2M/d]").lenient(false)).get() ); assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); assertThat(e.toString(), containsString("unit [D] not supported for date math")); @@ -357,7 +352,7 @@ public void testDateRangeInQueryStringWithTimeZone_7880() { client().prepareIndex("test").setId("1").setSource("past", now).get(); refresh(); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("past:[now-1m/m TO now+1m/m]").timeZone(timeZone.getId())), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("past:[now-1m/m TO now+1m/m]").timeZone(timeZone.getId())), 1L); } // Issue #10477 @@ -371,27 +366,20 @@ public void testDateRangeInQueryStringWithTimeZone_10477() { refresh(); // Timezone set with dates - assertHitCount( - client().prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00+0200 TO 2015-04-06T23:00:00+0200]")), - 2L - ); + assertHitCount(prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00+0200 TO 2015-04-06T23:00:00+0200]")), 2L); // Same timezone set with time_zone assertHitCount( - client().prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00 TO 2015-04-06T23:00:00]").timeZone("+0200")), + prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00 TO 2015-04-06T23:00:00]").timeZone("+0200")), 2L ); // We set a timezone which will give no result - assertHitCount( - client().prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00-0200 TO 2015-04-06T23:00:00-0200]")), - 0L - ); + assertHitCount(prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00-0200 TO 2015-04-06T23:00:00-0200]")), 0L); // Same timezone set with time_zone but another timezone is set directly within dates which has the precedence assertHitCount( - client().prepareSearch() - .setQuery(queryStringQuery("past:[2015-04-06T00:00:00-0200 TO 2015-04-06T23:00:00-0200]").timeZone("+0200")), + prepareSearch().setQuery(queryStringQuery("past:[2015-04-06T00:00:00-0200 TO 2015-04-06T23:00:00-0200]").timeZone("+0200")), 0L ); } @@ -406,13 +394,13 @@ public void testIdsQueryTestsIdIndexed() throws Exception { client().prepareIndex("test").setId("3").setSource("field1", "value3") ); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(constantScoreQuery(idsQuery().addIds("1", "3"))), "1", "3"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(idsQuery().addIds("1", "3")), "1", "3"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(constantScoreQuery(idsQuery().addIds("1", "3"))), "1", "3"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(idsQuery().addIds("1", "3")), "1", "3"); - assertHitCount(client().prepareSearch().setQuery(idsQuery().addIds("7", "10")), 0L); + assertHitCount(prepareSearch().setQuery(idsQuery().addIds("7", "10")), 0L); // repeat..., with terms - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_id", "1", "3"))), "1", "3"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(constantScoreQuery(termsQuery("_id", "1", "3"))), "1", "3"); } public void testTermIndexQuery() throws Exception { @@ -425,28 +413,16 @@ public void testTermIndexQuery() throws Exception { } for (String indexName : indexNames) { - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(constantScoreQuery(termQuery("_index", indexName))), - indexName + "1" - ); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(constantScoreQuery(termQuery("_index", indexName))), indexName + "1"); } for (String indexName : indexNames) { - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexName))), - indexName + "1" - ); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexName))), indexName + "1"); } for (String indexName : indexNames) { - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(constantScoreQuery(matchQuery("_index", indexName))), - indexName + "1" - ); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(constantScoreQuery(matchQuery("_index", indexName))), indexName + "1"); } { - assertHitCountAndNoFailures( - client().prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexNames))), - indexNames.length - ); + assertHitCountAndNoFailures(prepareSearch().setQuery(constantScoreQuery(termsQuery("_index", indexNames))), indexNames.length); } } @@ -502,15 +478,15 @@ public void testFilterExistsMissing() throws Exception { ) ); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("field1")), "1", "2"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(constantScoreQuery(existsQuery("field1"))), "1", "2"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(queryStringQuery("_exists_:field1")), "1", "2"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("field2")), "1", "3"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("field3")), "4"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(existsQuery("field1")), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(constantScoreQuery(existsQuery("field1"))), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(queryStringQuery("_exists_:field1")), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(existsQuery("field2")), "1", "3"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(existsQuery("field3")), "4"); // wildcard check - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("x*")), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(existsQuery("x*")), "1", "2"); // object check - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(existsQuery("obj1")), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(existsQuery("obj1")), "1", "2"); } public void testPassQueryOrFilterAsJSONString() throws Exception { @@ -519,13 +495,13 @@ public void testPassQueryOrFilterAsJSONString() throws Exception { client().prepareIndex("test").setId("1").setSource("field1", "value1_1", "field2", "value2_1").setRefreshPolicy(IMMEDIATE).get(); WrapperQueryBuilder wrapper = new WrapperQueryBuilder("{ \"term\" : { \"field1\" : \"value1_1\" } }"); - assertHitCount(client().prepareSearch().setQuery(wrapper), 1L); + assertHitCount(prepareSearch().setQuery(wrapper), 1L); BoolQueryBuilder bool = boolQuery().must(wrapper).must(new TermQueryBuilder("field2", "value2_1")); - assertHitCount(client().prepareSearch().setQuery(bool), 1L); + assertHitCount(prepareSearch().setQuery(bool), 1L); WrapperQueryBuilder wrapperFilter = wrapperQuery("{ \"term\" : { \"field1\" : \"value1_1\" } }"); - assertHitCount(client().prepareSearch().setPostFilter(wrapperFilter), 1L); + assertHitCount(prepareSearch().setPostFilter(wrapperFilter), 1L); } public void testFiltersWithCustomCacheKey() throws Exception { @@ -549,14 +525,14 @@ public void testMatchQueryNumeric() throws Exception { client().prepareIndex("test").setId("3").setSource("long", 3L, "double", 3.0d) ); - SearchResponse searchResponse = client().prepareSearch().setQuery(matchQuery("long", "1")).get(); + SearchResponse searchResponse = prepareSearch().setQuery(matchQuery("long", "1")).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); - searchResponse = client().prepareSearch().setQuery(matchQuery("double", "2")).get(); + searchResponse = prepareSearch().setQuery(matchQuery("double", "2")).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("2")); - expectThrows(SearchPhaseExecutionException.class, () -> client().prepareSearch().setQuery(matchQuery("double", "2 3 4")).get()); + expectThrows(SearchPhaseExecutionException.class, () -> prepareSearch().setQuery(matchQuery("double", "2 3 4")).get()); } public void testMatchQueryFuzzy() throws Exception { @@ -567,22 +543,18 @@ public void testMatchQueryFuzzy() throws Exception { client().prepareIndex("test").setId("1").setSource("text", "Unit"), client().prepareIndex("test").setId("2").setSource("text", "Unity") ); - assertHitCount(client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(0))), 0L); + assertHitCount(prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(0))), 0L); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(1))), "1", "2"); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(1))), - "1", - "2" - ); - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO"))), + prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO"))), "1", "2" ); - assertHitCount(client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO:5,7"))), 0L); + assertHitCount(prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO:5,7"))), 0L); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness(Fuzziness.fromString("AUTO:5,7"))), + prepareSearch().setQuery(matchQuery("text", "unify").fuzziness(Fuzziness.fromString("AUTO:5,7"))), "2" ); } @@ -600,29 +572,29 @@ public void testMultiMatchQuery() throws Exception { MultiMatchQueryBuilder builder = multiMatchQuery("value1 value2 value4", "field1", "field2"); // this uses dismax so scores are equal and the order can be arbitrary assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(builder).addAggregation(AggregationBuilders.terms("field1").field("field1.keyword")), + prepareSearch().setQuery(builder).addAggregation(AggregationBuilders.terms("field1").field("field1.keyword")), "1", "2" ); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(builder), "1", "2"); indicesAdmin().prepareRefresh("test").get(); builder = multiMatchQuery("value1", "field1", "field2").operator(Operator.AND); // Operator only applies on terms inside a field! // Fields are always OR-ed together. - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(builder), "1"); refresh(); builder = multiMatchQuery("value1", "field1").field("field3", 1.5f).operator(Operator.AND); // Operator only applies on terms inside // a field! Fields are always OR-ed // together. - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "3", "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(builder), "3", "1"); indicesAdmin().prepareRefresh("test").get(); builder = multiMatchQuery("value1").field("field1").field("field3", 1.5f).operator(Operator.AND); // Operator only applies on terms // inside a field! Fields are // always OR-ed together. - SearchResponse searchResponse = client().prepareSearch().setQuery(builder).get(); + SearchResponse searchResponse = prepareSearch().setQuery(builder).get(); assertHitCount(searchResponse, 2L); assertSearchHits(searchResponse, "3", "1"); @@ -637,7 +609,7 @@ public void testMultiMatchQuery() throws Exception { Matcher reasonMatcher = containsString("NumberFormatException: For input string: \"value1\""); ShardSearchFailure[] shardFailures; try { - client().prepareSearch().setQuery(builder).get(); + prepareSearch().setQuery(builder).get(); shardFailures = searchResponse.getShardFailures(); assertThat("Expected shard failures, got none", shardFailures, not(emptyArray())); } catch (SearchPhaseExecutionException e) { @@ -651,7 +623,7 @@ public void testMultiMatchQuery() throws Exception { } builder.lenient(true); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(builder), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(builder), "1"); } public void testMatchQueryZeroTermsQuery() { @@ -662,14 +634,14 @@ public void testMatchQueryZeroTermsQuery() { BoolQueryBuilder boolQuery = boolQuery().must(matchQuery("field1", "a").zeroTermsQuery(ZeroTermsQueryOption.NONE)) .must(matchQuery("field1", "value1").zeroTermsQuery(ZeroTermsQueryOption.NONE)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 0L); + assertHitCount(prepareSearch().setQuery(boolQuery), 0L); boolQuery = boolQuery().must(matchQuery("field1", "a").zeroTermsQuery(ZeroTermsQueryOption.ALL)) .must(matchQuery("field1", "value1").zeroTermsQuery(ZeroTermsQueryOption.ALL)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 1L); + assertHitCount(prepareSearch().setQuery(boolQuery), 1L); boolQuery = boolQuery().must(matchQuery("field1", "a").zeroTermsQuery(ZeroTermsQueryOption.ALL)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 2L); + assertHitCount(prepareSearch().setQuery(boolQuery), 2L); } public void testMultiMatchQueryZeroTermsQuery() { @@ -681,14 +653,14 @@ public void testMultiMatchQueryZeroTermsQuery() { BoolQueryBuilder boolQuery = boolQuery().must(multiMatchQuery("a", "field1", "field2").zeroTermsQuery(ZeroTermsQueryOption.NONE)) // Fields are ORed together .must(multiMatchQuery("value1", "field1", "field2").zeroTermsQuery(ZeroTermsQueryOption.NONE)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 0L); + assertHitCount(prepareSearch().setQuery(boolQuery), 0L); boolQuery = boolQuery().must(multiMatchQuery("a", "field1", "field2").zeroTermsQuery(ZeroTermsQueryOption.ALL)) .must(multiMatchQuery("value4", "field1", "field2").zeroTermsQuery(ZeroTermsQueryOption.ALL)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 1L); + assertHitCount(prepareSearch().setQuery(boolQuery), 1L); boolQuery = boolQuery().must(multiMatchQuery("a", "field1").zeroTermsQuery(ZeroTermsQueryOption.ALL)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 2L); + assertHitCount(prepareSearch().setQuery(boolQuery), 2L); } public void testMultiMatchQueryMinShouldMatch() { @@ -700,39 +672,39 @@ public void testMultiMatchQueryMinShouldMatch() { MultiMatchQueryBuilder multiMatchQuery = multiMatchQuery("value1 value2 foo", "field1", "field2"); multiMatchQuery.minimumShouldMatch("70%"); - SearchResponse searchResponse = client().prepareSearch().setQuery(multiMatchQuery).get(); + SearchResponse searchResponse = prepareSearch().setQuery(multiMatchQuery).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); multiMatchQuery.minimumShouldMatch("30%"); - searchResponse = client().prepareSearch().setQuery(multiMatchQuery).get(); + searchResponse = prepareSearch().setQuery(multiMatchQuery).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("1")); assertSecondHit(searchResponse, hasId("2")); multiMatchQuery.minimumShouldMatch("70%"); - searchResponse = client().prepareSearch().setQuery(multiMatchQuery).get(); + searchResponse = prepareSearch().setQuery(multiMatchQuery).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); multiMatchQuery.minimumShouldMatch("30%"); - searchResponse = client().prepareSearch().setQuery(multiMatchQuery).get(); + searchResponse = prepareSearch().setQuery(multiMatchQuery).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("1")); assertSecondHit(searchResponse, hasId("2")); multiMatchQuery = multiMatchQuery("value1 value2 bar", "field1"); multiMatchQuery.minimumShouldMatch("100%"); - assertHitCount(client().prepareSearch().setQuery(multiMatchQuery), 0L); + assertHitCount(prepareSearch().setQuery(multiMatchQuery), 0L); multiMatchQuery.minimumShouldMatch("70%"); - searchResponse = client().prepareSearch().setQuery(multiMatchQuery).get(); + searchResponse = prepareSearch().setQuery(multiMatchQuery).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); // Min should match > # optional clauses returns no docs. multiMatchQuery = multiMatchQuery("value1 value2 value3", "field1", "field2"); multiMatchQuery.minimumShouldMatch("4"); - assertHitCount(client().prepareSearch().setQuery(multiMatchQuery), 0L); + assertHitCount(prepareSearch().setQuery(multiMatchQuery), 0L); } public void testBoolQueryMinShouldMatchBiggerThanNumberOfShouldClauses() throws IOException { @@ -743,7 +715,7 @@ public void testBoolQueryMinShouldMatchBiggerThanNumberOfShouldClauses() throws BoolQueryBuilder boolQuery = boolQuery().must(termQuery("field1", "value1")) .should(boolQuery().should(termQuery("field1", "value1")).should(termQuery("field1", "value2")).minimumShouldMatch(3)); - SearchResponse searchResponse = client().prepareSearch().setQuery(boolQuery).get(); + SearchResponse searchResponse = prepareSearch().setQuery(boolQuery).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); @@ -751,18 +723,18 @@ public void testBoolQueryMinShouldMatchBiggerThanNumberOfShouldClauses() throws .should(boolQuery().should(termQuery("field1", "value1")).should(termQuery("field1", "value2")).minimumShouldMatch(1)) // Only one should clause is defined, returns no docs. .minimumShouldMatch(2); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 0L); + assertHitCount(prepareSearch().setQuery(boolQuery), 0L); boolQuery = boolQuery().should(termQuery("field1", "value1")) .should(boolQuery().should(termQuery("field1", "value1")).should(termQuery("field1", "value2")).minimumShouldMatch(3)) .minimumShouldMatch(1); - searchResponse = client().prepareSearch().setQuery(boolQuery).get(); + searchResponse = prepareSearch().setQuery(boolQuery).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); boolQuery = boolQuery().must(termQuery("field1", "value1")) .must(boolQuery().should(termQuery("field1", "value1")).should(termQuery("field1", "value2")).minimumShouldMatch(3)); - assertHitCount(client().prepareSearch().setQuery(boolQuery), 0L); + assertHitCount(prepareSearch().setQuery(boolQuery), 0L); } public void testFuzzyQueryString() { @@ -771,7 +743,7 @@ public void testFuzzyQueryString() { client().prepareIndex("test").setId("2").setSource("str", "shay", "date", "2012-02-05", "num", 20).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch().setQuery(queryStringQuery("str:kimcy~1")).get(); + SearchResponse searchResponse = prepareSearch().setQuery(queryStringQuery("str:kimcy~1")).get(); assertNoFailures(searchResponse); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("1")); @@ -792,9 +764,9 @@ public void testQuotedQueryStringWithBoost() throws InterruptedException { client().prepareIndex("test").setId("2").setSource("important", "nothing important", "less_important", "phrase match") ); - SearchResponse searchResponse = client().prepareSearch() - .setQuery(queryStringQuery("\"phrase match\"").field("important", boost).field("less_important")) - .get(); + SearchResponse searchResponse = prepareSearch().setQuery( + queryStringQuery("\"phrase match\"").field("important", boost).field("less_important") + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("1")); assertSecondHit(searchResponse, hasId("2")); @@ -810,20 +782,20 @@ public void testSpecialRangeSyntaxInQueryString() { client().prepareIndex("test").setId("2").setSource("str", "shay", "date", "2012-02-05", "num", 20).get(); refresh(); - SearchResponse searchResponse = client().prepareSearch().setQuery(queryStringQuery("num:>19")).get(); + SearchResponse searchResponse = prepareSearch().setQuery(queryStringQuery("num:>19")).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("2")); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("num:>20")), 0L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("num:>20")), 0L); - searchResponse = client().prepareSearch().setQuery(queryStringQuery("num:>=20")).get(); + searchResponse = prepareSearch().setQuery(queryStringQuery("num:>=20")).get(); assertHitCount(searchResponse, 1L); assertFirstHit(searchResponse, hasId("2")); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("num:>11")), 2L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("num:<20")), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("num:<=20")), 2L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("+num:>11 +num:<20")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("num:>11")), 2L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("num:<20")), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("num:<=20")), 2L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("+num:>11 +num:<20")), 1L); } public void testEmptytermsQuery() throws Exception { @@ -1024,23 +996,23 @@ public void testBasicQueryById() throws Exception { client().prepareIndex("test").setId("3").setSource("field1", "value3").get(); refresh(); - SearchResponse searchResponse = client().prepareSearch().setQuery(idsQuery().addIds("1", "2")).get(); + SearchResponse searchResponse = prepareSearch().setQuery(idsQuery().addIds("1", "2")).get(); assertHitCount(searchResponse, 2L); assertThat(searchResponse.getHits().getHits().length, equalTo(2)); - searchResponse = client().prepareSearch().setQuery(idsQuery().addIds("1")).get(); + searchResponse = prepareSearch().setQuery(idsQuery().addIds("1")).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); - searchResponse = client().prepareSearch().setQuery(idsQuery().addIds("1", "2")).get(); + searchResponse = prepareSearch().setQuery(idsQuery().addIds("1", "2")).get(); assertHitCount(searchResponse, 2L); assertThat(searchResponse.getHits().getHits().length, equalTo(2)); - searchResponse = client().prepareSearch().setQuery(idsQuery().addIds("1")).get(); + searchResponse = prepareSearch().setQuery(idsQuery().addIds("1")).get(); assertHitCount(searchResponse, 1L); assertThat(searchResponse.getHits().getHits().length, equalTo(1)); - searchResponse = client().prepareSearch().setQuery(idsQuery().addIds("1", "2", "3", "4")).get(); + searchResponse = prepareSearch().setQuery(idsQuery().addIds("1", "2", "3", "4")).get(); assertHitCount(searchResponse, 3L); assertThat(searchResponse.getHits().getHits().length, equalTo(3)); } @@ -1185,28 +1157,34 @@ public void testNumericRangeFilter_2826() throws Exception { client().prepareIndex("test").setId("4").setSource("field1", "test2", "num_long", 4).get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setPostFilter( - boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4)) - ).get(); - assertHitCount(searchResponse, 4L); + assertHitCount( + prepareSearch("test").setPostFilter( + boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4)) + ), + 4L + ); // This made 2826 fail! (only with bit based filters) - searchResponse = prepareSearch("test").setPostFilter( - boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4)) - ).get(); - assertHitCount(searchResponse, 4L); + assertHitCount( + prepareSearch("test").setPostFilter( + boolQuery().should(rangeQuery("num_long").from(1).to(2)).should(rangeQuery("num_long").from(3).to(4)) + ), + 4L + ); // This made #2979 fail! - searchResponse = prepareSearch("test").setPostFilter( - boolQuery().must(termQuery("field1", "test1")) - .should(rangeQuery("num_long").from(1).to(2)) - .should(rangeQuery("num_long").from(3).to(4)) - ).get(); - assertHitCount(searchResponse, 2L); + assertHitCount( + prepareSearch("test").setPostFilter( + boolQuery().must(termQuery("field1", "test1")) + .should(rangeQuery("num_long").from(1).to(2)) + .should(rangeQuery("num_long").from(3).to(4)) + ), + 2L + ); } // see #2926 - public void testMustNot() throws IOException, ExecutionException, InterruptedException { + public void testMustNot() throws InterruptedException { assertAcked( prepareCreate("test") // issue manifested only with shards>=2 @@ -1221,15 +1199,12 @@ public void testMustNot() throws IOException, ExecutionException, InterruptedExc client().prepareIndex("test").setId("4").setSource("description", "foo") ); - SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .get(); - assertHitCount(searchResponse, 4L); - - searchResponse = prepareSearch("test").setQuery(boolQuery().mustNot(matchQuery("description", "anything"))) - .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) - .get(); - assertHitCount(searchResponse, 2L); + assertHitCount(prepareSearch("test").setQuery(matchAllQuery()).setSearchType(SearchType.DFS_QUERY_THEN_FETCH), 4L); + assertHitCount( + prepareSearch("test").setQuery(boolQuery().mustNot(matchQuery("description", "anything"))) + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH), + 2L + ); } public void testIntervals() throws InterruptedException { @@ -1642,7 +1617,7 @@ public void testSearchEmptyDoc() { client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).get(); refresh(); - assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 1L); + assertHitCount(prepareSearch().setQuery(matchAllQuery()), 1L); } public void testMatchPhrasePrefixQuery() throws ExecutionException, InterruptedException { @@ -1653,12 +1628,9 @@ public void testMatchPhrasePrefixQuery() throws ExecutionException, InterruptedE client().prepareIndex("test1").setId("2").setSource("field", "trying out Elasticsearch") ); - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "Johnnie la").slop(between(2, 5))), - "1" - ); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "trying")), "2"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(matchPhrasePrefixQuery("field", "try")), "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(matchPhrasePrefixQuery("field", "Johnnie la").slop(between(2, 5))), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(matchPhrasePrefixQuery("field", "trying")), "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(matchPhrasePrefixQuery("field", "try")), "2"); } public void testQueryStringParserCache() throws Exception { @@ -1760,8 +1732,7 @@ public void testFieldAliasesForMetaFields() throws Exception { indexRandom(true, false, indexRequest); updateClusterSettings(Settings.builder().put(IndicesService.INDICES_ID_FIELD_DATA_ENABLED_SETTING.getKey(), true)); try { - SearchResponse searchResponse = client().prepareSearch() - .setQuery(termQuery("routing-alias", "custom")) + SearchResponse searchResponse = prepareSearch().setQuery(termQuery("routing-alias", "custom")) .addDocValueField("id-alias") .get(); assertHitCount(searchResponse, 1L); @@ -1796,10 +1767,10 @@ public void testWildcardQueryNormalizationOnKeywordField() { { WildcardQueryBuilder wildCardQuery = wildcardQuery("field1", "Bb*"); - assertHitCount(client().prepareSearch().setQuery(wildCardQuery), 1L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 1L); wildCardQuery = wildcardQuery("field1", "bb*"); - assertHitCount(client().prepareSearch().setQuery(wildCardQuery), 1L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 1L); } } @@ -1821,17 +1792,14 @@ public void testWildcardQueryNormalizationOnTextField() { { WildcardQueryBuilder wildCardQuery = wildcardQuery("field1", "Bb*"); - SearchResponse searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); - assertHitCount(searchResponse, 0L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 0L); // the following works not because of normalization but because of the `case_insensitive` parameter wildCardQuery = wildcardQuery("field1", "Bb*").caseInsensitive(true); - searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); - assertHitCount(searchResponse, 1L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 1L); wildCardQuery = wildcardQuery("field1", "bb*"); - searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); - assertHitCount(searchResponse, 1L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 1L); } } @@ -1853,12 +1821,10 @@ public void testWildcardQueryNormalizationKeywordSpecialCharacters() { refresh(); WildcardQueryBuilder wildCardQuery = wildcardQuery("field", "la*"); - SearchResponse searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); - assertHitCount(searchResponse, 1L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 1L); wildCardQuery = wildcardQuery("field", "la*el-?"); - searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); - assertHitCount(searchResponse, 1L); + assertHitCount(prepareSearch().setQuery(wildCardQuery), 1L); } public static class MockAnalysisPlugin extends Plugin implements AnalysisPlugin { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java index c41aa2067b9c4..78d98b76b9bc8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/query/SimpleQueryStringIT.java @@ -85,40 +85,30 @@ public void testSimpleQueryString() throws ExecutionException, InterruptedExcept client().prepareIndex("test").setId("6").setSource("otherbody", "spaghetti") ); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar")), "1", "2", "3"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar")), "1", "2", "3"); // Tests boost value setting. In this case doc 1 should always be ranked above the other // two matches. - SearchResponse searchResponse = client().prepareSearch() - .setQuery(boolQuery().should(simpleQueryStringQuery("\"foo bar\"").boost(10.0f)).should(termQuery("body", "eggplant"))) - .get(); + SearchResponse searchResponse = prepareSearch().setQuery( + boolQuery().should(simpleQueryStringQuery("\"foo bar\"").boost(10.0f)).should(termQuery("body", "eggplant")) + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("3")); - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").defaultOperator(Operator.AND)), - "3" - ); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar").defaultOperator(Operator.AND)), "3"); - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("\"quux baz\" +(eggplant | spaghetti)")), - "4", - "5" - ); - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("eggplants").analyzer("mock_snowball")), - "4" - ); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("\"quux baz\" +(eggplant | spaghetti)")), "4", "5"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("eggplants").analyzer("mock_snowball")), "4"); - searchResponse = client().prepareSearch() - .setQuery(simpleQueryStringQuery("spaghetti").field("body", 1000.0f).field("otherbody", 2.0f).queryName("myquery")) - .get(); + searchResponse = prepareSearch().setQuery( + simpleQueryStringQuery("spaghetti").field("body", 1000.0f).field("otherbody", 2.0f).queryName("myquery") + ).get(); assertHitCount(searchResponse, 2L); assertFirstHit(searchResponse, hasId("5")); assertSearchHits(searchResponse, "5", "6"); assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("myquery")); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("spaghetti").field("*body")), "5", "6"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("spaghetti").field("*body")), "5", "6"); } public void testSimpleQueryStringMinimumShouldMatch() throws Exception { @@ -134,15 +124,11 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { ); logger.info("--> query 1"); - assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")), - "3", - "4" - ); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")), "3", "4"); logger.info("--> query 2"); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")), + prepareSearch().setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")), "3", "4" ); @@ -150,8 +136,7 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { // test case from #13884 logger.info("--> query 3"); assertSearchHitsWithoutFailures( - client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo").field("body").field("body2").field("body3").minimumShouldMatch("-50%")), + prepareSearch().setQuery(simpleQueryStringQuery("foo").field("body").field("body2").field("body3").minimumShouldMatch("-50%")), "1", "3", "4" @@ -159,7 +144,7 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { logger.info("--> query 4"); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body").field("body2").minimumShouldMatch("70%")), + prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body").field("body2").minimumShouldMatch("70%")), "3", "4" ); @@ -175,7 +160,7 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { logger.info("--> query 5"); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")), + prepareSearch().setQuery(simpleQueryStringQuery("foo bar").field("body").field("body2").minimumShouldMatch("2")), "3", "4", "7", @@ -184,7 +169,7 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { logger.info("--> query 6"); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")), + prepareSearch().setQuery(simpleQueryStringQuery("foo bar").minimumShouldMatch("2")), "3", "4", "6", @@ -194,8 +179,7 @@ public void testSimpleQueryStringMinimumShouldMatch() throws Exception { logger.info("--> query 7"); assertSearchHitsWithoutFailures( - client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo bar baz").field("body2").field("other").minimumShouldMatch("70%")), + prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body2").field("other").minimumShouldMatch("70%")), "6", "7", "8" @@ -224,10 +208,10 @@ public void testNestedFieldSimpleQueryString() throws IOException { client().prepareIndex("test").setId("1").setSource("body", "foo bar baz").get(); refresh(); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")), "1"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")), "1"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")), "1"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body")), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo bar baz").field("body.sub")), "1"); } public void testSimpleQueryStringFlags() throws ExecutionException, InterruptedException { @@ -243,52 +227,47 @@ public void testSimpleQueryStringFlags() throws ExecutionException, InterruptedE ); assertSearchHitsWithoutFailures( - client().prepareSearch().setQuery(simpleQueryStringQuery("foo bar").flags(SimpleQueryStringFlag.ALL)), + prepareSearch().setQuery(simpleQueryStringQuery("foo bar").flags(SimpleQueryStringFlag.ALL)), "1", "2", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.OR)), + prepareSearch().setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.OR)), "1", "2", "3" ); assertSearchHitsWithoutFailures( - client().prepareSearch() - .setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE)), + prepareSearch().setQuery(simpleQueryStringQuery("foo | bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE)), "3" ); assertHitCount( - client().prepareSearch() - .setQuery(simpleQueryStringQuery("baz | egg*").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE)), + prepareSearch().setQuery(simpleQueryStringQuery("baz | egg*").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE)), 0L ); assertHitCount( - client().prepareSearch() - .setSource( - new SearchSourceBuilder().query( - QueryBuilders.simpleQueryStringQuery("foo|bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE) - ) - ), + prepareSearch().setSource( + new SearchSourceBuilder().query( + QueryBuilders.simpleQueryStringQuery("foo|bar").defaultOperator(Operator.AND).flags(SimpleQueryStringFlag.NONE) + ) + ), 1L ); assertSearchHitsWithoutFailures( - client().prepareSearch() - .setQuery( - simpleQueryStringQuery("quuz~1 + egg*").flags( - SimpleQueryStringFlag.WHITESPACE, - SimpleQueryStringFlag.AND, - SimpleQueryStringFlag.FUZZY, - SimpleQueryStringFlag.PREFIX - ) - ), + prepareSearch().setQuery( + simpleQueryStringQuery("quuz~1 + egg*").flags( + SimpleQueryStringFlag.WHITESPACE, + SimpleQueryStringFlag.AND, + SimpleQueryStringFlag.FUZZY, + SimpleQueryStringFlag.PREFIX + ) + ), "4" ); } @@ -302,15 +281,14 @@ public void testSimpleQueryStringLenient() throws ExecutionException, Interrupte ); refresh(); - SearchResponse searchResponse = client().prepareSearch() - .setAllowPartialSearchResults(true) + SearchResponse searchResponse = prepareSearch().setAllowPartialSearchResults(true) .setQuery(simpleQueryStringQuery("foo").field("field")) .get(); assertFailures(searchResponse); assertHitCount(searchResponse, 1L); assertSearchHits(searchResponse, "1"); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("foo").field("field").lenient(true)), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("foo").field("field").lenient(true)), "1"); } // Issue #7967 @@ -345,21 +323,21 @@ public void testSimpleQueryStringAnalyzeWildcard() throws ExecutionException, In indexRandom(true, client().prepareIndex("test1").setId("1").setSource("location", "Köln")); refresh(); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("Köln*").field("location")), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("Köln*").field("location")), "1"); } public void testSimpleQueryStringUsesFieldAnalyzer() throws Exception { client().prepareIndex("test").setId("1").setSource("foo", 123, "bar", "abc").get(); client().prepareIndex("test").setId("2").setSource("foo", 234, "bar", "bcd").get(); refresh(); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("123").field("foo").field("bar")), "1"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("123").field("foo").field("bar")), "1"); } public void testSimpleQueryStringOnIndexMetaField() throws Exception { client().prepareIndex("test").setId("1").setSource("foo", 123, "bar", "abc").get(); client().prepareIndex("test").setId("2").setSource("foo", 234, "bar", "bcd").get(); refresh(); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("test").field("_index")), "1", "2"); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("test").field("_index")), "1", "2"); } public void testEmptySimpleQueryStringWithAnalysis() throws Exception { @@ -381,7 +359,7 @@ public void testEmptySimpleQueryStringWithAnalysis() throws Exception { indexRandom(true, client().prepareIndex("test1").setId("1").setSource("body", "Some Text")); refresh(); - assertSearchHitsWithoutFailures(client().prepareSearch().setQuery(simpleQueryStringQuery("the*").field("body"))); + assertSearchHitsWithoutFailures(prepareSearch().setQuery(simpleQueryStringQuery("the*").field("body"))); } public void testBasicAllQuery() throws Exception { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java index 45a64491de693..61490cac43e45 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java @@ -92,7 +92,7 @@ public void testSearchRandomPreference() throws InterruptedException, ExecutionE randomPreference = randomUnicodeOfLengthBetween(0, 4); } // id is not indexed, but lets see that we automatically convert to - assertHitCount(client().prepareSearch().setQuery(QueryBuilders.matchAllQuery()).setPreference(randomPreference), 6L); + assertHitCount(prepareSearch().setQuery(QueryBuilders.matchAllQuery()).setPreference(randomPreference), 6L); } } @@ -119,8 +119,7 @@ public void testSimpleIp() throws Exception { client().prepareIndex("test").setId("1").setSource("from", "192.168.0.5", "to", "192.168.0.10").setRefreshPolicy(IMMEDIATE).get(); assertHitCount( - client().prepareSearch() - .setQuery(boolQuery().must(rangeQuery("from").lte("192.168.0.7")).must(rangeQuery("to").gte("192.168.0.7"))), + prepareSearch().setQuery(boolQuery().must(rangeQuery("from").lte("192.168.0.7")).must(rangeQuery("to").gte("192.168.0.7"))), 1L ); } @@ -151,22 +150,19 @@ public void testIpCidr() throws Exception { client().prepareIndex("test").setId("5").setSource("ip", "2001:db8::ff00:42:8329").get(); refresh(); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.0.1"))), 1L); - assertHitCount(client().prepareSearch().setQuery(queryStringQuery("ip: 192.168.0.1")), 1L); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.0.1/32"))), 1L); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.0.0/24"))), 3L); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.0.0.0/8"))), 4L); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "0.0.0.0/0"))), 4L); - assertHitCount( - client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "2001:db8::ff00:42:8329/128"))), - 1L - ); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "2001:db8::/64"))), 1L); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "::/0"))), 5L); - assertHitCount(client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.1.5/32"))), 0L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.0.1"))), 1L); + assertHitCount(prepareSearch().setQuery(queryStringQuery("ip: 192.168.0.1")), 1L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.0.1/32"))), 1L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.0.0/24"))), 3L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.0.0.0/8"))), 4L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "0.0.0.0/0"))), 4L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "2001:db8::ff00:42:8329/128"))), 1L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "2001:db8::/64"))), 1L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "::/0"))), 5L); + assertHitCount(prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "192.168.1.5/32"))), 0L); assertFailures( - client().prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "0/0/0/0/0"))), + prepareSearch().setQuery(boolQuery().must(QueryBuilders.termQuery("ip", "0/0/0/0/0"))), RestStatus.BAD_REQUEST, containsString("Expected [ip/prefix] but was [0/0/0/0/0]") ); @@ -177,8 +173,8 @@ public void testSimpleId() { client().prepareIndex("test").setId("XXX1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get(); // id is not indexed, but lets see that we automatically convert to - assertHitCount(client().prepareSearch().setQuery(QueryBuilders.termQuery("_id", "XXX1")), 1L); - assertHitCount(client().prepareSearch().setQuery(QueryBuilders.queryStringQuery("_id:XXX1")), 1L); + assertHitCount(prepareSearch().setQuery(QueryBuilders.termQuery("_id", "XXX1")), 1L); + assertHitCount(prepareSearch().setQuery(QueryBuilders.queryStringQuery("_id:XXX1")), 1L); } public void testSimpleDateRange() throws Exception { @@ -460,8 +456,7 @@ public void testTermQueryBigInt() throws Exception { XContentParser parser = createParser(JsonXContent.jsonXContent, queryJson); parser.nextToken(); TermQueryBuilder query = TermQueryBuilder.fromXContent(parser); - SearchResponse searchResponse = prepareSearch("idx").setQuery(query).get(); - assertEquals(1, searchResponse.getHits().getTotalHits().value); + assertHitCount(prepareSearch("idx").setQuery(query), 1); } public void testTooLongRegexInRegexpQuery() throws Exception { diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index b145dc28e34a0..78ea21f117114 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -21,7 +21,6 @@ import org.apache.lucene.search.TotalHits; import org.apache.lucene.tests.util.LuceneTestCase; import org.elasticsearch.ExceptionsHelper; -import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; @@ -1410,10 +1409,6 @@ protected final DocWriteResponse index(String index, String id, Map startIndex(String index, String id, BytesReference source, XContentType type) { - return client().prepareIndex(index).setId(id).setSource(source, type).execute(); - } - /** * Syntactic sugar for: *

diff --git a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
index 3379599c381e5..03f197ced56df 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
@@ -56,6 +56,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
 import static org.apache.lucene.tests.util.LuceneTestCase.expectThrows;
@@ -305,6 +306,20 @@ public static void assertHitCount(SearchRequestBuilder searchRequestBuilder, lon
         }
     }
 
+    public static void assertHitCount(ActionFuture responseFuture, long expectedHitCount) {
+        SearchResponse res;
+        try {
+            res = responseFuture.get();
+        } catch (ExecutionException | InterruptedException ex) {
+            throw new AssertionError(ex);
+        }
+        try {
+            assertHitCount(res, expectedHitCount);
+        } finally {
+            res.decRef();
+        }
+    }
+
     public static void assertHitCount(SearchResponse countResponse, long expectedHitCount) {
         final TotalHits totalHits = countResponse.getHits().getTotalHits();
         if (totalHits.relation != TotalHits.Relation.EQUAL_TO || totalHits.value != expectedHitCount) {
@@ -422,6 +437,38 @@ public static void assertHighlight(SearchResponse resp, int hit, String field, i
         assertHighlight(resp, hit, field, fragment, greaterThan(fragment), matcher);
     }
 
+    public static void assertHighlight(
+        SearchRequestBuilder searchRequestBuilder,
+        int hit,
+        String field,
+        int fragment,
+        int totalFragments,
+        Matcher matcher
+    ) {
+        var resp = searchRequestBuilder.get();
+        try {
+            assertHighlight(resp, hit, field, fragment, equalTo(totalFragments), matcher);
+        } finally {
+            resp.decRef();
+        }
+    }
+
+    public static void assertHighlight(
+        ActionFuture responseFuture,
+        int hit,
+        String field,
+        int fragment,
+        int totalFragments,
+        Matcher matcher
+    ) throws ExecutionException, InterruptedException {
+        var resp = responseFuture.get();
+        try {
+            assertHighlight(resp, hit, field, fragment, equalTo(totalFragments), matcher);
+        } finally {
+            resp.decRef();
+        }
+    }
+
     public static void assertHighlight(
         SearchResponse resp,
         int hit,
@@ -466,6 +513,15 @@ private static void assertHighlight(
         assertThat(hit.getHighlightFields().get(field).fragments()[fragment].string(), matcher);
     }
 
+    public static void assertNotHighlighted(SearchRequestBuilder searchRequestBuilder, int hit, String field) {
+        var resp = searchRequestBuilder.get();
+        try {
+            assertNotHighlighted(resp, hit, field);
+        } finally {
+            resp.decRef();
+        }
+    }
+
     public static void assertNotHighlighted(SearchResponse resp, int hit, String field) {
         assertNoFailures(resp);
         assertThat("not enough hits", resp.getHits().getHits(), arrayWithSize(greaterThan(hit)));
diff --git a/x-pack/plugin/enrich/src/internalClusterTest/java/org/elasticsearch/xpack/enrich/EnrichMultiNodeIT.java b/x-pack/plugin/enrich/src/internalClusterTest/java/org/elasticsearch/xpack/enrich/EnrichMultiNodeIT.java
index 389ccd380a599..5416741c8743d 100644
--- a/x-pack/plugin/enrich/src/internalClusterTest/java/org/elasticsearch/xpack/enrich/EnrichMultiNodeIT.java
+++ b/x-pack/plugin/enrich/src/internalClusterTest/java/org/elasticsearch/xpack/enrich/EnrichMultiNodeIT.java
@@ -6,7 +6,6 @@
  */
 package org.elasticsearch.xpack.enrich;
 
-import org.apache.lucene.search.TotalHits;
 import org.elasticsearch.ResourceNotFoundException;
 import org.elasticsearch.action.ActionFuture;
 import org.elasticsearch.action.admin.cluster.node.tasks.get.GetTaskRequest;
@@ -21,7 +20,6 @@
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.ingest.PutPipelineRequest;
 import org.elasticsearch.action.search.SearchRequest;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.settings.Settings;
@@ -46,12 +44,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 
 import static org.elasticsearch.test.NodeRoles.ingestOnlyNode;
 import static org.elasticsearch.test.NodeRoles.masterOnlyNode;
 import static org.elasticsearch.test.NodeRoles.nonIngestNode;
 import static org.elasticsearch.test.NodeRoles.nonMasterNode;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
@@ -85,7 +85,7 @@ protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) {
             .build();
     }
 
-    public void testEnrichAPIs() {
+    public void testEnrichAPIs() throws ExecutionException, InterruptedException {
         final int numPolicies = randomIntBetween(2, 4);
         internalCluster().startNodes(randomIntBetween(2, 3));
         int numDocsInSourceIndex = randomIntBetween(8, 32);
@@ -111,9 +111,7 @@ public void testEnrichAPIs() {
             assertThat(result, equalTo(new EnrichPolicy.NamedPolicy(policyName, enrichPolicy)));
             String enrichIndexPrefix = EnrichPolicy.getBaseName(policyName) + "*";
             refresh(enrichIndexPrefix);
-            SearchResponse searchResponse = client().search(new SearchRequest(enrichIndexPrefix)).actionGet();
-            assertThat(searchResponse.getHits().getTotalHits().relation, equalTo(TotalHits.Relation.EQUAL_TO));
-            assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) numDocsInSourceIndex));
+            assertHitCount(client().search(new SearchRequest(enrichIndexPrefix)), numDocsInSourceIndex);
         }
 
         GetEnrichPolicyAction.Response response = client().execute(GetEnrichPolicyAction.INSTANCE, new GetEnrichPolicyAction.Request())
diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/AnnotationIndexIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/AnnotationIndexIT.java
index 5980e04acbf9c..3f2ca0703bbdc 100644
--- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/AnnotationIndexIT.java
+++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/AnnotationIndexIT.java
@@ -14,9 +14,7 @@
 import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
 import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
 import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.search.SearchPhaseExecutionException;
 import org.elasticsearch.action.search.SearchRequest;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.cluster.metadata.AliasMetadata;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
@@ -38,6 +36,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.is;
 
@@ -283,12 +282,7 @@ public void testNotCreatedWhenAfterOtherMlIndexAndUpgradeInProgress() throws Exc
 
                 try {
                     assertBusy(() -> {
-                        try {
-                            SearchResponse response = client().search(new SearchRequest(".ml-notifications*")).actionGet();
-                            assertEquals(1, response.getHits().getHits().length);
-                        } catch (SearchPhaseExecutionException e) {
-                            throw new AssertionError("Notifications index exists but shards not yet ready - continuing busy wait", e);
-                        }
+                        assertHitCount(client().search(new SearchRequest(".ml-notifications*")), 1);
                         assertFalse(annotationsIndexExists(AnnotationIndex.LATEST_INDEX_NAME));
                         assertEquals(0, numberOfAnnotationsAliases());
                     });
@@ -316,8 +310,7 @@ public void testNotCreatedWhenAfterOtherMlIndexAndResetInProgress() throws Excep
             // to be created, but in this case it shouldn't as we're doing a reset
 
             assertBusy(() -> {
-                SearchResponse response = client().search(new SearchRequest(".ml-state")).actionGet();
-                assertEquals(1, response.getHits().getHits().length);
+                assertHitCount(client().search(new SearchRequest(".ml-state")), 1);
                 assertFalse(annotationsIndexExists(AnnotationIndex.LATEST_INDEX_NAME));
                 assertEquals(0, numberOfAnnotationsAliases());
             });

From 9794c6e205e6070f1930725f7833e3e8fa18fc6a Mon Sep 17 00:00:00 2001
From: David Turner 
Date: Fri, 20 Oct 2023 18:33:00 +0100
Subject: [PATCH 049/203] Use ESIntegTestCase#prepareSearch more (#101179)

The refactoring in #101175 only covered all the one-arg call sites. This
PR does the rest.
---
 .../pipeline/DateDerivativeIT.java            |  14 +-
 .../common/HighlighterWithAnalyzersTests.java |  27 +-
 .../script/expression/MoreExpressionIT.java   |  10 +-
 .../script/expression/StoredExpressionIT.java |   9 +-
 .../join/query/ChildQuerySearchIT.java        |  22 +-
 .../elasticsearch/join/query/InnerHitsIT.java |  13 +-
 .../percolator/PercolatorQuerySearchIT.java   | 326 ++++++++----------
 .../reindex/DeleteByQueryBasicTests.java      |  10 +-
 .../indices/delete/DeleteIndexBlocksIT.java   |   8 +-
 .../action/bulk/BulkWithUpdatesIT.java        |   2 +-
 .../action/search/PointInTimeIT.java          |  42 +--
 .../action/search/TransportSearchIT.java      |   2 +-
 .../elasticsearch/aliases/IndexAliasesIT.java |  77 ++---
 .../cluster/MinimumMasterNodesIT.java         |  16 +-
 .../allocation/FilteringAllocationIT.java     |  24 +-
 .../cluster/routing/PrimaryAllocationIT.java  |   4 +-
 .../cluster/routing/ShardRoutingRoleIT.java   |   3 +-
 .../ClusterDisruptionCleanSettingsIT.java     |   2 +-
 .../elasticsearch/env/NodeEnvironmentIT.java  |   2 +-
 .../gateway/GatewayIndexStateIT.java          |   8 +-
 .../gateway/QuorumGatewayIT.java              |   4 +-
 .../gateway/RecoveryFromGatewayIT.java        |  46 +--
 .../index/store/CorruptedFileIT.java          |  14 +-
 ...DateMathIndexExpressionsIntegrationIT.java |   4 +-
 .../indices/IndicesOptionsIntegrationIT.java  |  14 +-
 .../RandomExceptionCircuitBreakerIT.java      |   2 +-
 .../indices/recovery/IndexRecoveryIT.java     |   2 +-
 .../settings/UpdateNumberOfReplicasIT.java    |   6 +-
 .../indices/state/OpenCloseIndexIT.java       |   2 +-
 .../indices/stats/IndexStatsIT.java           |  20 +-
 .../recovery/FullRollingRestartIT.java        |   6 +-
 .../recovery/RecoveryWhileUnderLoadIT.java    |   9 +-
 .../elasticsearch/recovery/RelocationIT.java  |   2 +-
 .../recovery/TruncatedRecoveryIT.java         |   4 +-
 .../routing/AliasResolveRoutingIT.java        |   5 +-
 .../elasticsearch/routing/AliasRoutingIT.java |  72 ++--
 .../routing/PartitionedRoutingIT.java         |   6 +-
 .../routing/SimpleRoutingIT.java              |  48 +--
 .../search/SearchWithRejectionsIT.java        |   2 +-
 .../aggregations/bucket/DateHistogramIT.java  |   6 +-
 .../aggregations/bucket/DateRangeIT.java      |  11 +-
 .../bucket/DiversifiedSamplerIT.java          |   6 +-
 .../aggregations/bucket/DoubleTermsIT.java    |  20 +-
 .../aggregations/bucket/GeoDistanceIT.java    |  16 +-
 .../aggregations/bucket/GeoHashGridIT.java    |   6 +-
 .../aggregations/bucket/HistogramIT.java      |  18 +-
 .../search/aggregations/bucket/IpRangeIT.java |  16 +-
 .../aggregations/bucket/LongTermsIT.java      |  20 +-
 .../search/aggregations/bucket/RangeIT.java   |  20 +-
 .../search/aggregations/bucket/SamplerIT.java |   3 +-
 .../bucket/TermsDocCountErrorIT.java          |  36 +-
 .../bucket/terms/StringTermsIT.java           |  24 +-
 .../aggregations/metrics/ExtendedStatsIT.java |   6 +-
 .../metrics/HDRPercentileRanksIT.java         |   3 +-
 .../metrics/HDRPercentilesIT.java             |   3 +-
 .../metrics/MedianAbsoluteDeviationIT.java    |   3 +-
 .../search/aggregations/metrics/SumIT.java    |  10 +-
 .../metrics/TDigestPercentileRanksIT.java     |   3 +-
 .../metrics/TDigestPercentilesIT.java         |   3 +-
 .../aggregations/metrics/ValueCountIT.java    |   3 +-
 .../aggregations/pipeline/BucketScriptIT.java |  82 ++---
 .../search/basic/SearchRedStateIndexIT.java   |  11 +-
 .../search/basic/SearchWhileRelocatingIT.java |   6 +-
 .../basic/SearchWithRandomExceptionsIT.java   |   6 +-
 .../basic/SearchWithRandomIOExceptionsIT.java |   8 +-
 .../search/fetch/FetchSubPhasePluginIT.java   |   6 +-
 .../search/fetch/subphase/InnerHitsIT.java    |  44 +--
 .../fetch/subphase/MatchedQueriesIT.java      |  78 ++---
 .../search/fields/SearchFieldsIT.java         |  61 ++--
 .../search/functionscore/QueryRescorerIT.java |  78 ++---
 .../functionscore/RandomScoreFunctionIT.java  |  18 +-
 .../search/geo/GeoDistanceIT.java             |  15 +-
 .../search/nested/SimpleNestedIT.java         |  51 +--
 .../search/profile/dfs/DfsProfilerIT.java     |   3 +-
 .../search/profile/query/QueryProfilerIT.java |  22 +-
 .../search/routing/SearchPreferenceIT.java    |  12 +-
 .../scriptfilter/ScriptQuerySearchIT.java     |  22 +-
 .../search/scroll/SearchScrollIT.java         |  70 ++--
 .../SearchScrollWithFailingNodesIT.java       |   5 +-
 .../search/slice/SearchSliceIT.java           |   7 +-
 .../search/sort/FieldSortIT.java              | 238 +++++--------
 .../search/sort/GeoDistanceIT.java            |   9 +-
 .../search/sort/GeoDistanceSortBuilderIT.java |  64 ++--
 .../search/sort/SimpleSortIT.java             |  36 +-
 .../search/stats/FieldUsageStatsIT.java       |   9 +-
 .../search/stats/SearchStatsIT.java           |   3 +-
 .../search/suggest/SuggestSearchIT.java       |  25 +-
 .../similarity/SimilarityIT.java              |  10 +-
 .../versioning/SimpleVersioningIT.java        |   7 +-
 .../metrics/CentroidAggregationTestBase.java  |   6 +-
 .../SpatialBoundsAggregationTestBase.java     |   3 +-
 .../search/geo/BaseShapeIntegTestCase.java    |  38 +-
 .../geo/GeoBoundingBoxQueryIntegTestCase.java | 240 ++++++-------
 .../search/geo/GeoShapeIntegTestCase.java     |   5 +-
 .../index/engine/frozen/FrozenIndexIT.java    |  18 +-
 .../xpack/unsignedlong/UnsignedLongTests.java |   6 +-
 .../integration/JobStorageDeletionTaskIT.java |   3 +-
 .../integration/MlDistributedFailureIT.java   |   3 +-
 .../PinnedQueryBuilderIT.java                 |  36 +-
 .../RetrySearchIntegTests.java                |   6 +-
 .../security/authz/SecurityScrollTests.java   |   3 +-
 .../search/GeoShapeWithDocValuesIT.java       |   3 +-
 102 files changed, 974 insertions(+), 1527 deletions(-)

diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java
index 5c4a7d5ef5875..f375d341c5ae3 100644
--- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java
+++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java
@@ -555,14 +555,12 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                dateHistogram("histo").field("date")
-                    .calendarInterval(DateHistogramInterval.MONTH)
-                    .minDocCount(0)
-                    .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count"))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            dateHistogram("histo").field("date")
+                .calendarInterval(DateHistogramInterval.MONTH)
+                .minDocCount(0)
+                .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count"))
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java
index 6798aba477f97..e7e9aa32b1684 100644
--- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java
+++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java
@@ -158,26 +158,21 @@ public void testMultiPhraseCutoff() throws IOException {
             )
             .get();
         refresh();
-        SearchResponse search = client().prepareSearch()
-            .setQuery(matchPhraseQuery("body", "Test: http://www.facebook.com "))
+        SearchResponse search = prepareSearch().setQuery(matchPhraseQuery("body", "Test: http://www.facebook.com "))
             .highlighter(new HighlightBuilder().field("body").highlighterType("fvh"))
             .get();
         assertHighlight(search, 0, "body", 0, startsWith("Test: http://www.facebook.com"));
-        search = client().prepareSearch()
-            .setQuery(
-                matchPhraseQuery(
-                    "body",
-                    "Test: http://www.facebook.com "
-                        + "http://elasticsearch.org http://xing.com http://cnn.com "
-                        + "http://quora.com http://twitter.com this is a test for highlighting "
-                        + "feature Test: http://www.facebook.com http://elasticsearch.org "
-                        + "http://xing.com http://cnn.com http://quora.com http://twitter.com this "
-                        + "is a test for highlighting feature"
-                )
+        search = prepareSearch().setQuery(
+            matchPhraseQuery(
+                "body",
+                "Test: http://www.facebook.com "
+                    + "http://elasticsearch.org http://xing.com http://cnn.com "
+                    + "http://quora.com http://twitter.com this is a test for highlighting "
+                    + "feature Test: http://www.facebook.com http://elasticsearch.org "
+                    + "http://xing.com http://cnn.com http://quora.com http://twitter.com this "
+                    + "is a test for highlighting feature"
             )
-            .highlighter(new HighlightBuilder().field("body").highlighterType("fvh"))
-            .execute()
-            .actionGet();
+        ).highlighter(new HighlightBuilder().field("body").highlighterType("fvh")).execute().actionGet();
         assertHighlight(
             search,
             0,
diff --git a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java
index 6c428556c7344..f71a55f4f6be0 100644
--- a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java
+++ b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/MoreExpressionIT.java
@@ -66,7 +66,7 @@ private SearchRequestBuilder buildRequest(String script, Object... params) {
             paramsMap.put(params[i].toString(), params[i + 1]);
         }
 
-        SearchRequestBuilder req = client().prepareSearch().setIndices("test");
+        SearchRequestBuilder req = prepareSearch().setIndices("test");
         req.setQuery(QueryBuilders.matchAllQuery())
             .addSort(SortBuilders.fieldSort("id").order(SortOrder.ASC).unmappedType("long"))
             .addScriptField("foo", new Script(ScriptType.INLINE, "expression", script, paramsMap));
@@ -113,7 +113,7 @@ public void testScore() throws Exception {
         ScriptScoreFunctionBuilder score = ScoreFunctionBuilders.scriptFunction(
             new Script(ScriptType.INLINE, "expression", "1 / _score", Collections.emptyMap())
         );
-        SearchRequestBuilder req = client().prepareSearch().setIndices("test");
+        SearchRequestBuilder req = prepareSearch().setIndices("test");
         req.setQuery(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("text", "hello"), score).boostMode(CombineFunction.REPLACE));
         req.setSearchType(SearchType.DFS_QUERY_THEN_FETCH); // make sure DF is consistent
         SearchResponse rsp = req.get();
@@ -124,7 +124,7 @@ public void testScore() throws Exception {
         assertEquals("3", hits.getAt(1).getId());
         assertEquals("2", hits.getAt(2).getId());
 
-        req = client().prepareSearch().setIndices("test");
+        req = prepareSearch().setIndices("test");
         req.setQuery(QueryBuilders.functionScoreQuery(QueryBuilders.termQuery("text", "hello"), score).boostMode(CombineFunction.REPLACE));
         score = ScoreFunctionBuilders.scriptFunction(new Script(ScriptType.INLINE, "expression", "1 / _score", Collections.emptyMap()));
         req.addAggregation(AggregationBuilders.max("max_score").script((score).getScript()));
@@ -466,7 +466,7 @@ public void testSpecialValueVariable() throws Exception {
             client().prepareIndex("test").setId("3").setSource("x", 13, "y", 1.8)
         );
 
-        SearchRequestBuilder req = client().prepareSearch().setIndices("test");
+        SearchRequestBuilder req = prepareSearch().setIndices("test");
         req.setQuery(QueryBuilders.matchAllQuery())
             .addAggregation(
                 AggregationBuilders.stats("int_agg")
@@ -512,7 +512,7 @@ public void testStringSpecialValueVariable() throws Exception {
             client().prepareIndex("test").setId("3").setSource("text", "hello")
         );
 
-        SearchRequestBuilder req = client().prepareSearch().setIndices("test");
+        SearchRequestBuilder req = prepareSearch().setIndices("test");
         req.setQuery(QueryBuilders.matchAllQuery())
             .addAggregation(
                 AggregationBuilders.terms("term_agg")
diff --git a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java
index 77e90d42ad94c..dcf380d338c14 100644
--- a/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java
+++ b/modules/lang-expression/src/internalClusterTest/java/org/elasticsearch/script/expression/StoredExpressionIT.java
@@ -50,12 +50,9 @@ public void testAllOpsDisabledIndexedScripts() throws IOException {
             assertThat(e.getCause().getMessage(), containsString("Failed to compile stored script [script1] using lang [expression]"));
         }
         try {
-            client().prepareSearch()
-                .setSource(
-                    new SearchSourceBuilder().scriptField("test1", new Script(ScriptType.STORED, null, "script1", Collections.emptyMap()))
-                )
-                .setIndices("test")
-                .get();
+            prepareSearch().setSource(
+                new SearchSourceBuilder().scriptField("test1", new Script(ScriptType.STORED, null, "script1", Collections.emptyMap()))
+            ).setIndices("test").get();
             fail("search script should have been rejected");
         } catch (Exception e) {
             assertThat(e.toString(), containsString("cannot execute scripts using [field] context"));
diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java
index a5b9e5532a960..1915df55a7836 100644
--- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java
+++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java
@@ -1259,26 +1259,20 @@ public void testParentChildCaching() throws Exception {
         indicesAdmin().prepareRefresh("test").get();
 
         for (int i = 0; i < 2; i++) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(
-                    boolQuery().must(matchAllQuery())
-                        .filter(
-                            boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery())
-                        )
-                )
-                .get();
+            SearchResponse searchResponse = prepareSearch().setQuery(
+                boolQuery().must(matchAllQuery())
+                    .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery()))
+            ).get();
             assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
         }
 
         createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get();
         indicesAdmin().prepareRefresh("test").get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(matchAllQuery())
-                    .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery()))
-            )
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            boolQuery().must(matchAllQuery())
+                .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery()))
+        ).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
     }
diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java
index e64d5594fa77b..a857316319a63 100644
--- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java
+++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java
@@ -590,13 +590,12 @@ public void testInnerHitsWithIgnoreUnmapped() {
         client().prepareIndex("index2").setId("3").setSource("key", "value").get();
         refresh();
         assertSearchHitsWithoutFailures(
-            client().prepareSearch("index1", "index2")
-                .setQuery(
-                    boolQuery().should(
-                        hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true)
-                            .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true))
-                    ).should(termQuery("key", "value"))
-                ),
+            prepareSearch("index1", "index2").setQuery(
+                boolQuery().should(
+                    hasChildQuery("child_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true)
+                        .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true))
+                ).should(termQuery("key", "value"))
+            ),
             "1",
             "3"
         );
diff --git a/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java
index 39b42b25ca65d..9362080c9cb33 100644
--- a/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java
+++ b/modules/percolator/src/internalClusterTest/java/org/elasticsearch/percolator/PercolatorQuerySearchIT.java
@@ -98,14 +98,13 @@ public void testPercolatorQuery() throws Exception {
 
         BytesReference source = BytesReference.bytes(jsonBuilder().startObject().endObject());
         logger.info("percolating empty doc");
-        SearchResponse response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
 
         source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject());
         logger.info("percolating doc with 1 field");
-        response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 2);
@@ -116,8 +115,7 @@ public void testPercolatorQuery() throws Exception {
 
         source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").field("field2", "value").endObject());
         logger.info("percolating doc with 2 fields");
-        response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 3);
@@ -129,19 +127,16 @@ public void testPercolatorQuery() throws Exception {
         assertThat(response.getHits().getAt(2).getFields().get("_percolator_document_slot").getValue(), equalTo(0));
 
         logger.info("percolating doc with 2 fields");
-        response = client().prepareSearch()
-            .setQuery(
-                new PercolateQueryBuilder(
-                    "query",
-                    Arrays.asList(
-                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()),
-                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").field("field2", "value").endObject())
-                    ),
-                    XContentType.JSON
-                )
+        response = prepareSearch().setQuery(
+            new PercolateQueryBuilder(
+                "query",
+                Arrays.asList(
+                    BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()),
+                    BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").field("field2", "value").endObject())
+                ),
+                XContentType.JSON
             )
-            .addSort("id", SortOrder.ASC)
-            .get();
+        ).addSort("id", SortOrder.ASC).get();
         assertHitCount(response, 3);
         assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
         assertThat(response.getHits().getAt(0).getFields().get("_percolator_document_slot").getValues(), equalTo(Arrays.asList(0, 1)));
@@ -238,44 +233,44 @@ public void testPercolatorRangeQueries() throws Exception {
 
         // Test long range:
         BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field1", 12).endObject());
-        SearchResponse response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         logger.info("response={}", response);
         assertHitCount(response, 2);
         assertThat(response.getHits().getAt(0).getId(), equalTo("3"));
         assertThat(response.getHits().getAt(1).getId(), equalTo("1"));
 
         source = BytesReference.bytes(jsonBuilder().startObject().field("field1", 11).endObject());
-        response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
 
         // Test double range:
         source = BytesReference.bytes(jsonBuilder().startObject().field("field2", 12).endObject());
-        response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 2);
         assertThat(response.getHits().getAt(0).getId(), equalTo("6"));
         assertThat(response.getHits().getAt(1).getId(), equalTo("4"));
 
         source = BytesReference.bytes(jsonBuilder().startObject().field("field2", 11).endObject());
-        response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("4"));
 
         // Test IP range:
         source = BytesReference.bytes(jsonBuilder().startObject().field("field3", "192.168.1.5").endObject());
-        response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 2);
         assertThat(response.getHits().getAt(0).getId(), equalTo("9"));
         assertThat(response.getHits().getAt(1).getId(), equalTo("7"));
 
         source = BytesReference.bytes(jsonBuilder().startObject().field("field3", "192.168.1.4").endObject());
-        response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("7"));
 
         // Test date range:
         source = BytesReference.bytes(jsonBuilder().startObject().field("field4", "2016-05-15").endObject());
-        response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("10"));
     }
@@ -325,8 +320,7 @@ public void testPercolatorGeoQueries() throws Exception {
         BytesReference source = BytesReference.bytes(
             jsonBuilder().startObject().startObject("field1").field("lat", 52.20).field("lon", 4.51).endObject().endObject()
         );
-        SearchResponse response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 3);
@@ -365,15 +359,12 @@ public void testPercolatorQueryExistingDocument() throws Exception {
         indicesAdmin().prepareRefresh().get();
 
         logger.info("percolating empty doc");
-        SearchResponse response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", "test", "1", null, null, null))
-            .get();
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "1", null, null, null)).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
 
         logger.info("percolating doc with 1 field");
-        response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", "test", "5", null, null, null))
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "5", null, null, null))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 2);
@@ -381,8 +372,7 @@ public void testPercolatorQueryExistingDocument() throws Exception {
         assertThat(response.getHits().getAt(1).getId(), equalTo("2"));
 
         logger.info("percolating doc with 2 fields");
-        response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", "test", "6", null, null, null))
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "6", null, null, null))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 3);
@@ -404,7 +394,7 @@ public void testPercolatorQueryExistingDocumentSourceDisabled() throws Exception
 
         logger.info("percolating empty doc with source disabled");
         IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> {
-            client().prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "1", null, null, null)).get();
+            prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "1", null, null, null)).get();
         });
         assertThat(e.getMessage(), containsString("source disabled"));
     }
@@ -488,8 +478,7 @@ public void testPercolatorSpecificQueries() throws Exception {
                 .field("field2", "the quick brown fox falls down into the well")
                 .endObject()
         );
-        SearchResponse response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 3);
@@ -542,8 +531,7 @@ public void testPercolatorQueryWithHighlighting() throws Exception {
         BytesReference document = BytesReference.bytes(
             jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject()
         );
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", document, XContentType.JSON))
+        SearchResponse searchResponse = prepareSearch().setQuery(new PercolateQueryBuilder("query", document, XContentType.JSON))
             .highlighter(new HighlightBuilder().field("field1"))
             .addSort("id", SortOrder.ASC)
             .get();
@@ -574,14 +562,10 @@ public void testPercolatorQueryWithHighlighting() throws Exception {
             jsonBuilder().startObject().field("field1", "The quick brown fox jumps").endObject()
         );
         BytesReference document2 = BytesReference.bytes(jsonBuilder().startObject().field("field1", "over the lazy dog").endObject());
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().should(new PercolateQueryBuilder("query", document1, XContentType.JSON).setName("query1"))
-                    .should(new PercolateQueryBuilder("query", document2, XContentType.JSON).setName("query2"))
-            )
-            .highlighter(new HighlightBuilder().field("field1"))
-            .addSort("id", SortOrder.ASC)
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().should(new PercolateQueryBuilder("query", document1, XContentType.JSON).setName("query1"))
+                .should(new PercolateQueryBuilder("query", document2, XContentType.JSON).setName("query2"))
+        ).highlighter(new HighlightBuilder().field("field1")).addSort("id", SortOrder.ASC).get();
         logger.info("searchResponse={}", searchResponse);
         assertHitCount(searchResponse, 5);
 
@@ -606,22 +590,18 @@ public void testPercolatorQueryWithHighlighting() throws Exception {
             equalTo("The quick brown fox jumps")
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                new PercolateQueryBuilder(
-                    "query",
-                    Arrays.asList(
-                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "dog").endObject()),
-                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "fox").endObject()),
-                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "jumps").endObject()),
-                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "brown fox").endObject())
-                    ),
-                    XContentType.JSON
-                )
+        searchResponse = prepareSearch().setQuery(
+            new PercolateQueryBuilder(
+                "query",
+                Arrays.asList(
+                    BytesReference.bytes(jsonBuilder().startObject().field("field1", "dog").endObject()),
+                    BytesReference.bytes(jsonBuilder().startObject().field("field1", "fox").endObject()),
+                    BytesReference.bytes(jsonBuilder().startObject().field("field1", "jumps").endObject()),
+                    BytesReference.bytes(jsonBuilder().startObject().field("field1", "brown fox").endObject())
+                ),
+                XContentType.JSON
             )
-            .highlighter(new HighlightBuilder().field("field1"))
-            .addSort("id", SortOrder.ASC)
-            .get();
+        ).highlighter(new HighlightBuilder().field("field1")).addSort("id", SortOrder.ASC).get();
         assertHitCount(searchResponse, 5);
         assertThat(
             searchResponse.getHits().getAt(0).getFields().get("_percolator_document_slot").getValues(),
@@ -660,32 +640,28 @@ public void testPercolatorQueryWithHighlighting() throws Exception {
             equalTo("brown fox")
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().should(
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().should(
+                new PercolateQueryBuilder(
+                    "query",
+                    Arrays.asList(
+                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "dog").endObject()),
+                        BytesReference.bytes(jsonBuilder().startObject().field("field1", "fox").endObject())
+                    ),
+                    XContentType.JSON
+                ).setName("query1")
+            )
+                .should(
                     new PercolateQueryBuilder(
                         "query",
                         Arrays.asList(
-                            BytesReference.bytes(jsonBuilder().startObject().field("field1", "dog").endObject()),
-                            BytesReference.bytes(jsonBuilder().startObject().field("field1", "fox").endObject())
+                            BytesReference.bytes(jsonBuilder().startObject().field("field1", "jumps").endObject()),
+                            BytesReference.bytes(jsonBuilder().startObject().field("field1", "brown fox").endObject())
                         ),
                         XContentType.JSON
-                    ).setName("query1")
+                    ).setName("query2")
                 )
-                    .should(
-                        new PercolateQueryBuilder(
-                            "query",
-                            Arrays.asList(
-                                BytesReference.bytes(jsonBuilder().startObject().field("field1", "jumps").endObject()),
-                                BytesReference.bytes(jsonBuilder().startObject().field("field1", "brown fox").endObject())
-                            ),
-                            XContentType.JSON
-                        ).setName("query2")
-                    )
-            )
-            .highlighter(new HighlightBuilder().field("field1"))
-            .addSort("id", SortOrder.ASC)
-            .get();
+        ).highlighter(new HighlightBuilder().field("field1")).addSort("id", SortOrder.ASC).get();
         logger.info("searchResponse={}", searchResponse);
         assertHitCount(searchResponse, 5);
         assertThat(
@@ -764,9 +740,9 @@ public void testTakePositionOffsetGapIntoAccount() throws Exception {
             .get();
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", new BytesArray("{\"field\" : [\"brown\", \"fox\"]}"), XContentType.JSON))
-            .get();
+        SearchResponse response = prepareSearch().setQuery(
+            new PercolateQueryBuilder("query", new BytesArray("{\"field\" : [\"brown\", \"fox\"]}"), XContentType.JSON)
+        ).get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("2"));
     }
@@ -846,16 +822,14 @@ public void testWithMultiplePercolatorFields() throws Exception {
         indicesAdmin().prepareRefresh().get();
 
         BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field", "value").endObject());
-        SearchResponse response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder(queryFieldName, source, XContentType.JSON))
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder(queryFieldName, source, XContentType.JSON))
             .setIndices("test1")
             .get();
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
         assertThat(response.getHits().getAt(0).getIndex(), equalTo("test1"));
 
-        response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("object_field." + queryFieldName, source, XContentType.JSON))
+        response = prepareSearch().setQuery(new PercolateQueryBuilder("object_field." + queryFieldName, source, XContentType.JSON))
             .setIndices("test2")
             .get();
         assertHitCount(response, 1);
@@ -942,10 +916,67 @@ public void testPercolateQueryWithNestedDocuments() throws Exception {
             .get();
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(
-                new PercolateQueryBuilder(
-                    "query",
+        SearchResponse response = prepareSearch().setQuery(
+            new PercolateQueryBuilder(
+                "query",
+                BytesReference.bytes(
+                    XContentFactory.jsonBuilder()
+                        .startObject()
+                        .field("companyname", "stark")
+                        .startArray("employee")
+                        .startObject()
+                        .field("name", "virginia potts")
+                        .endObject()
+                        .startObject()
+                        .field("name", "tony stark")
+                        .endObject()
+                        .endArray()
+                        .endObject()
+                ),
+                XContentType.JSON
+            )
+        ).addSort("id", SortOrder.ASC).get();
+        assertHitCount(response, 2);
+        assertThat(response.getHits().getAt(0).getId(), equalTo("q1"));
+        assertThat(response.getHits().getAt(1).getId(), equalTo("q3"));
+
+        response = prepareSearch().setQuery(
+            new PercolateQueryBuilder(
+                "query",
+                BytesReference.bytes(
+                    XContentFactory.jsonBuilder()
+                        .startObject()
+                        .field("companyname", "notstark")
+                        .startArray("employee")
+                        .startObject()
+                        .field("name", "virginia stark")
+                        .endObject()
+                        .startObject()
+                        .field("name", "tony stark")
+                        .endObject()
+                        .endArray()
+                        .endObject()
+                ),
+                XContentType.JSON
+            )
+        ).addSort("id", SortOrder.ASC).get();
+        assertHitCount(response, 1);
+        assertThat(response.getHits().getAt(0).getId(), equalTo("q3"));
+
+        response = prepareSearch().setQuery(
+            new PercolateQueryBuilder(
+                "query",
+                BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("companyname", "notstark").endObject()),
+                XContentType.JSON
+            )
+        ).addSort("id", SortOrder.ASC).get();
+        assertHitCount(response, 1);
+        assertThat(response.getHits().getAt(0).getId(), equalTo("q3"));
+
+        response = prepareSearch().setQuery(
+            new PercolateQueryBuilder(
+                "query",
+                Arrays.asList(
                     BytesReference.bytes(
                         XContentFactory.jsonBuilder()
                             .startObject()
@@ -960,104 +991,35 @@ public void testPercolateQueryWithNestedDocuments() throws Exception {
                             .endArray()
                             .endObject()
                     ),
-                    XContentType.JSON
-                )
-            )
-            .addSort("id", SortOrder.ASC)
-            .get();
-        assertHitCount(response, 2);
-        assertThat(response.getHits().getAt(0).getId(), equalTo("q1"));
-        assertThat(response.getHits().getAt(1).getId(), equalTo("q3"));
-
-        response = client().prepareSearch()
-            .setQuery(
-                new PercolateQueryBuilder(
-                    "query",
                     BytesReference.bytes(
                         XContentFactory.jsonBuilder()
                             .startObject()
-                            .field("companyname", "notstark")
+                            .field("companyname", "stark")
                             .startArray("employee")
                             .startObject()
-                            .field("name", "virginia stark")
+                            .field("name", "peter parker")
                             .endObject()
                             .startObject()
-                            .field("name", "tony stark")
+                            .field("name", "virginia potts")
                             .endObject()
                             .endArray()
                             .endObject()
                     ),
-                    XContentType.JSON
-                )
-            )
-            .addSort("id", SortOrder.ASC)
-            .get();
-        assertHitCount(response, 1);
-        assertThat(response.getHits().getAt(0).getId(), equalTo("q3"));
-
-        response = client().prepareSearch()
-            .setQuery(
-                new PercolateQueryBuilder(
-                    "query",
-                    BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("companyname", "notstark").endObject()),
-                    XContentType.JSON
-                )
-            )
-            .addSort("id", SortOrder.ASC)
-            .get();
-        assertHitCount(response, 1);
-        assertThat(response.getHits().getAt(0).getId(), equalTo("q3"));
-
-        response = client().prepareSearch()
-            .setQuery(
-                new PercolateQueryBuilder(
-                    "query",
-                    Arrays.asList(
-                        BytesReference.bytes(
-                            XContentFactory.jsonBuilder()
-                                .startObject()
-                                .field("companyname", "stark")
-                                .startArray("employee")
-                                .startObject()
-                                .field("name", "virginia potts")
-                                .endObject()
-                                .startObject()
-                                .field("name", "tony stark")
-                                .endObject()
-                                .endArray()
-                                .endObject()
-                        ),
-                        BytesReference.bytes(
-                            XContentFactory.jsonBuilder()
-                                .startObject()
-                                .field("companyname", "stark")
-                                .startArray("employee")
-                                .startObject()
-                                .field("name", "peter parker")
-                                .endObject()
-                                .startObject()
-                                .field("name", "virginia potts")
-                                .endObject()
-                                .endArray()
-                                .endObject()
-                        ),
-                        BytesReference.bytes(
-                            XContentFactory.jsonBuilder()
-                                .startObject()
-                                .field("companyname", "stark")
-                                .startArray("employee")
-                                .startObject()
-                                .field("name", "peter parker")
-                                .endObject()
-                                .endArray()
-                                .endObject()
-                        )
-                    ),
-                    XContentType.JSON
-                )
+                    BytesReference.bytes(
+                        XContentFactory.jsonBuilder()
+                            .startObject()
+                            .field("companyname", "stark")
+                            .startArray("employee")
+                            .startObject()
+                            .field("name", "peter parker")
+                            .endObject()
+                            .endArray()
+                            .endObject()
+                    )
+                ),
+                XContentType.JSON
             )
-            .addSort("id", SortOrder.ASC)
-            .get();
+        ).addSort("id", SortOrder.ASC).get();
         assertHitCount(response, 2);
         assertThat(response.getHits().getAt(0).getId(), equalTo("q1"));
         assertThat(response.getHits().getAt(0).getFields().get("_percolator_document_slot").getValues(), equalTo(Arrays.asList(0, 1)));
@@ -1188,9 +1150,7 @@ public void testDisallowExpensiveQueries() throws IOException {
 
             // Execute with search.allow_expensive_queries = null => default value = false => success
             BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject());
-            SearchResponse response = client().prepareSearch()
-                .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
-                .get();
+            SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
             assertHitCount(response, 1);
             assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
             assertThat(response.getHits().getAt(0).getFields().get("_percolator_document_slot").getValue(), equalTo(0));
@@ -1200,7 +1160,7 @@ public void testDisallowExpensiveQueries() throws IOException {
 
             ElasticsearchException e = expectThrows(
                 ElasticsearchException.class,
-                () -> client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get()
+                () -> prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get()
             );
             assertEquals(
                 "[percolate] queries cannot be executed when 'search.allow_expensive_queries' is set to false.",
@@ -1210,7 +1170,7 @@ public void testDisallowExpensiveQueries() throws IOException {
             // Set search.allow_expensive_queries setting to "true" ==> success
             updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", true));
 
-            response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
+            response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get();
             assertHitCount(response, 1);
             assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
             assertThat(response.getHits().getAt(0).getFields().get("_percolator_document_slot").getValue(), equalTo(0));
diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java
index d3b401ab3541d..2f2248e304989 100644
--- a/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java
+++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/DeleteByQueryBasicTests.java
@@ -125,12 +125,12 @@ public void testDeleteByQueryWithMultipleIndices() throws Exception {
             assertHitCount(prepareSearch("test-" + i).setSize(0), remaining);
         }
 
-        assertHitCount(client().prepareSearch().setSize(0), (indices * docs) - deletions);
+        assertHitCount(prepareSearch().setSize(0), (indices * docs) - deletions);
     }
 
     public void testDeleteByQueryWithMissingIndex() throws Exception {
         indexRandom(true, client().prepareIndex("test").setId("1").setSource("foo", "a"));
-        assertHitCount(client().prepareSearch().setSize(0), 1);
+        assertHitCount(prepareSearch().setSize(0), 1);
 
         try {
             deleteByQuery().source("missing").filter(QueryBuilders.matchAllQuery()).get();
@@ -154,19 +154,19 @@ public void testDeleteByQueryWithRouting() throws Exception {
         indexRandom(true, true, true, builders);
 
         logger.info("--> counting documents with no routing, should be equal to [{}]", docs);
-        assertHitCount(client().prepareSearch().setSize(0), docs);
+        assertHitCount(prepareSearch().setSize(0), docs);
 
         String routing = String.valueOf(randomIntBetween(2, docs));
 
         logger.info("--> counting documents with routing [{}]", routing);
-        long expected = client().prepareSearch().setSize(0).setRouting(routing).get().getHits().getTotalHits().value;
+        long expected = prepareSearch().setSize(0).setRouting(routing).get().getHits().getTotalHits().value;
 
         logger.info("--> delete all documents with routing [{}] with a delete-by-query", routing);
         DeleteByQueryRequestBuilder delete = deleteByQuery().source("test").filter(QueryBuilders.matchAllQuery());
         delete.source().setRouting(routing);
         assertThat(delete.refresh(true).get(), matcher().deleted(expected));
 
-        assertHitCount(client().prepareSearch().setSize(0), docs - expected);
+        assertHitCount(prepareSearch().setSize(0), docs - expected);
     }
 
     public void testDeleteByMatchQuery() throws Exception {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java
index b44c49982e31b..7af46dd994fb2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java
@@ -48,12 +48,12 @@ public void testDeleteIndexOnIndexReadOnlyAllowDeleteSetting() {
         refresh();
         try {
             updateIndexSettings(Settings.builder().put(IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE, true), "test");
-            assertSearchHits(client().prepareSearch(), "1");
+            assertSearchHits(prepareSearch(), "1");
             assertBlocked(
                 client().prepareIndex().setIndex("test").setId("2").setSource("foo", "bar"),
                 IndexMetadata.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK
             );
-            assertSearchHits(client().prepareSearch(), "1");
+            assertSearchHits(prepareSearch(), "1");
             assertAcked(indicesAdmin().prepareDelete("test"));
         } finally {
             Settings settings = Settings.builder().putNull(IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE).build();
@@ -92,7 +92,7 @@ public void testDeleteIndexOnClusterReadOnlyAllowDeleteSetting() {
         refresh();
         try {
             updateClusterSettings(Settings.builder().put(Metadata.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey(), true));
-            assertSearchHits(client().prepareSearch(), "1");
+            assertSearchHits(prepareSearch(), "1");
             assertBlocked(
                 client().prepareIndex().setIndex("test").setId("2").setSource("foo", "bar"),
                 Metadata.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK
@@ -101,7 +101,7 @@ public void testDeleteIndexOnClusterReadOnlyAllowDeleteSetting() {
                 indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.number_of_replicas", 2)),
                 Metadata.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK
             );
-            assertSearchHits(client().prepareSearch(), "1");
+            assertSearchHits(prepareSearch(), "1");
             assertAcked(indicesAdmin().prepareDelete("test"));
         } finally {
             updateClusterSettings(Settings.builder().putNull(Metadata.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey()));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java
index 0f12959519322..9433f93d91f58 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkWithUpdatesIT.java
@@ -514,7 +514,7 @@ public void testBulkIndexingWhileInitializing() throws Exception {
 
         refresh();
 
-        assertHitCount(client().prepareSearch().setSize(0), numDocs);
+        assertHitCount(prepareSearch().setSize(0), numDocs);
     }
 
     public void testFailingVersionedUpdatedOnBulk() throws Exception {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java
index 2f81992973142..e931cd0da822d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java
@@ -83,7 +83,7 @@ public void testBasic() {
         }
         refresh("test");
         String pitId = openPointInTime(new String[] { "test" }, TimeValue.timeValueMinutes(2));
-        SearchResponse resp1 = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+        SearchResponse resp1 = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
         assertThat(resp1.pointInTimeId(), equalTo(pitId));
         assertHitCount(resp1, numDocs);
         int deletedDocs = 0;
@@ -101,8 +101,7 @@ public void testBasic() {
             assertHitCount(resp2, numDocs - deletedDocs);
         }
         try {
-            SearchResponse resp3 = client().prepareSearch()
-                .setPreference(null)
+            SearchResponse resp3 = prepareSearch().setPreference(null)
                 .setQuery(new MatchAllQueryBuilder())
                 .setPointInTime(new PointInTimeBuilder(pitId))
                 .get();
@@ -128,7 +127,7 @@ public void testMultipleIndices() {
         refresh();
         String pitId = openPointInTime(new String[] { "*" }, TimeValue.timeValueMinutes(2));
         try {
-            SearchResponse resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+            SearchResponse resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs);
             assertNotNull(resp.pointInTimeId());
@@ -140,11 +139,11 @@ public void testMultipleIndices() {
                 client().prepareIndex(index).setId(id).setSource("value", i).get();
             }
             refresh();
-            resp = client().prepareSearch().get();
+            resp = prepareSearch().get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs + moreDocs);
 
-            resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+            resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs);
             assertNotNull(resp.pointInTimeId());
@@ -165,7 +164,7 @@ public void testRelocation() throws Exception {
         refresh();
         String pitId = openPointInTime(new String[] { "test" }, TimeValue.timeValueMinutes(2));
         try {
-            SearchResponse resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+            SearchResponse resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs);
             assertThat(resp.pointInTimeId(), equalTo(pitId));
@@ -185,7 +184,7 @@ public void testRelocation() throws Exception {
                 }
                 refresh();
             }
-            resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+            resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs);
             assertThat(resp.pointInTimeId(), equalTo(pitId));
@@ -198,7 +197,7 @@ public void testRelocation() throws Exception {
                     .collect(Collectors.toSet());
                 assertThat(assignedNodes, everyItem(not(in(excludedNodes))));
             }, 30, TimeUnit.SECONDS);
-            resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+            resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs);
             assertThat(resp.pointInTimeId(), equalTo(pitId));
@@ -216,7 +215,7 @@ public void testPointInTimeNotFound() throws Exception {
         }
         refresh();
         String pit = openPointInTime(new String[] { "index" }, TimeValue.timeValueSeconds(5));
-        SearchResponse resp1 = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pit)).get();
+        SearchResponse resp1 = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pit)).get();
         assertNoFailures(resp1);
         assertHitCount(resp1, index1);
         if (rarely()) {
@@ -229,7 +228,7 @@ public void testPointInTimeNotFound() throws Exception {
         }
         SearchPhaseExecutionException e = expectThrows(
             SearchPhaseExecutionException.class,
-            () -> client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pit)).get()
+            () -> prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pit)).get()
         );
         for (ShardSearchFailure failure : e.shardFailures()) {
             assertThat(ExceptionsHelper.unwrapCause(failure.getCause()), instanceOf(SearchContextMissingException.class));
@@ -254,7 +253,7 @@ public void testIndexNotFound() {
         refresh();
         String pit = openPointInTime(new String[] { "index-*" }, TimeValue.timeValueMinutes(2));
         try {
-            SearchResponse resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pit)).get();
+            SearchResponse resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pit)).get();
             assertNoFailures(resp);
             assertHitCount(resp, index1 + index2);
             indicesAdmin().prepareDelete("index-1").get();
@@ -265,19 +264,14 @@ public void testIndexNotFound() {
             }
 
             // Allow partial search result
-            resp = client().prepareSearch()
-                .setPreference(null)
-                .setAllowPartialSearchResults(true)
-                .setPointInTime(new PointInTimeBuilder(pit))
-                .get();
+            resp = prepareSearch().setPreference(null).setAllowPartialSearchResults(true).setPointInTime(new PointInTimeBuilder(pit)).get();
             assertFailures(resp);
             assertHitCount(resp, index2);
 
             // Do not allow partial search result
             expectThrows(
                 ElasticsearchException.class,
-                () -> client().prepareSearch()
-                    .setPreference(null)
+                () -> prepareSearch().setPreference(null)
                     .setAllowPartialSearchResults(false)
                     .setPointInTime(new PointInTimeBuilder(pit))
                     .get()
@@ -313,8 +307,7 @@ public void testCanMatch() throws Exception {
                 }
             }
             client().prepareIndex("test").setId("1").setSource("created_date", "2020-01-01").get();
-            SearchResponse resp = client().prepareSearch()
-                .setQuery(new RangeQueryBuilder("created_date").gte("2020-01-02").lte("2020-01-03"))
+            SearchResponse resp = prepareSearch().setQuery(new RangeQueryBuilder("created_date").gte("2020-01-02").lte("2020-01-03"))
                 .setSearchType(SearchType.QUERY_THEN_FETCH)
                 .setPreference(null)
                 .setPreFilterShardSize(randomIntBetween(2, 3))
@@ -373,14 +366,13 @@ public void testPartialResults() throws Exception {
         refresh();
         String pitId = openPointInTime(new String[] { "test-*" }, TimeValue.timeValueMinutes(2));
         try {
-            SearchResponse resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+            SearchResponse resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
             assertNoFailures(resp);
             assertHitCount(resp, numDocs1 + numDocs2);
             assertThat(resp.pointInTimeId(), equalTo(pitId));
 
             internalCluster().restartNode(assignedNodeForIndex1);
-            resp = client().prepareSearch()
-                .setPreference(null)
+            resp = prepareSearch().setPreference(null)
                 .setAllowPartialSearchResults(true)
                 .setPointInTime(new PointInTimeBuilder(pitId))
                 .get();
@@ -491,7 +483,7 @@ public void testOpenPITConcurrentShardRequests() throws Exception {
     @SuppressWarnings({ "rawtypes", "unchecked" })
     private void assertPagination(PointInTimeBuilder pit, int expectedNumDocs, int size, SortBuilder... sorts) throws Exception {
         Set seen = new HashSet<>();
-        SearchRequestBuilder builder = client().prepareSearch().setSize(size).setPointInTime(pit);
+        SearchRequestBuilder builder = prepareSearch().setSize(size).setPointInTime(pit);
         for (SortBuilder sort : sorts) {
             builder.addSort(sort);
         }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java
index 93f6c83b16c15..d84d4270af24c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/TransportSearchIT.java
@@ -388,7 +388,7 @@ public void testShardCountLimit() throws Exception {
             // no exception
             prepareSearch("test1").get();
 
-            e = expectThrows(IllegalArgumentException.class, () -> client().prepareSearch("test1", "test2").get());
+            e = expectThrows(IllegalArgumentException.class, () -> prepareSearch("test1", "test2").get());
             assertThat(
                 e.getMessage(),
                 containsString(
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java
index 978b9dda888f0..c1ca4c60f176e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/aliases/IndexAliasesIT.java
@@ -311,7 +311,7 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception {
         terms = searchResponse.getAggregations().get("test");
         assertThat(terms.getBuckets().size(), equalTo(2));
 
-        searchResponse = client().prepareSearch("foos", "bars").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("foos", "bars").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "1", "2");
 
         logger.info("--> checking single non-filtering alias search");
@@ -319,15 +319,15 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception {
         assertHits(searchResponse.getHits(), "1", "2", "3", "4");
 
         logger.info("--> checking non-filtering alias and filtering alias search");
-        searchResponse = client().prepareSearch("alias1", "foos").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("alias1", "foos").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "1", "2", "3", "4");
 
         logger.info("--> checking index and filtering alias search");
-        searchResponse = client().prepareSearch("test", "foos").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("test", "foos").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "1", "2", "3", "4");
 
         logger.info("--> checking index and alias wildcard search");
-        searchResponse = client().prepareSearch("te*", "fo*").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("te*", "fo*").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "1", "2", "3", "4");
     }
 
@@ -389,45 +389,34 @@ public void testSearchingFilteringAliasesTwoIndices() throws Exception {
         );
 
         logger.info("--> checking filtering alias for two indices and one complete index");
-        searchResponse = client().prepareSearch("foos", "test1").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("foos", "test1").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "1", "2", "3", "4", "5");
         assertThat(
-            client().prepareSearch("foos", "test1").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
+            prepareSearch("foos", "test1").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
             equalTo(5L)
         );
 
         logger.info("--> checking filtering alias for two indices and non-filtering alias for one index");
-        searchResponse = client().prepareSearch("foos", "aliasToTest1").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("foos", "aliasToTest1").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "1", "2", "3", "4", "5");
         assertThat(
-            client().prepareSearch("foos", "aliasToTest1")
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .get()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch("foos", "aliasToTest1").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
             equalTo(5L)
         );
 
         logger.info("--> checking filtering alias for two indices and non-filtering alias for both indices");
-        searchResponse = client().prepareSearch("foos", "aliasToTests").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("foos", "aliasToTests").setQuery(QueryBuilders.matchAllQuery()).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(8L));
         assertThat(
-            client().prepareSearch("foos", "aliasToTests")
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .get()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch("foos", "aliasToTests").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
             equalTo(8L)
         );
 
         logger.info("--> checking filtering alias for two indices and non-filtering alias for both indices");
-        searchResponse = client().prepareSearch("foos", "aliasToTests").setQuery(QueryBuilders.termQuery("name", "something")).get();
+        searchResponse = prepareSearch("foos", "aliasToTests").setQuery(QueryBuilders.termQuery("name", "something")).get();
         assertHits(searchResponse.getHits(), "4", "8");
         assertThat(
-            client().prepareSearch("foos", "aliasToTests")
-                .setSize(0)
+            prepareSearch("foos", "aliasToTests").setSize(0)
                 .setQuery(QueryBuilders.termQuery("name", "something"))
                 .get()
                 .getHits()
@@ -488,47 +477,31 @@ public void testSearchingFilteringAliasesMultipleIndices() throws Exception {
         refresh();
 
         logger.info("--> checking filtering alias for multiple indices");
-        SearchResponse searchResponse = client().prepareSearch("filter23", "filter13").setQuery(QueryBuilders.matchAllQuery()).get();
+        SearchResponse searchResponse = prepareSearch("filter23", "filter13").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "21", "31", "13", "33");
         assertThat(
-            client().prepareSearch("filter23", "filter13")
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .get()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch("filter23", "filter13").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
             equalTo(4L)
         );
 
-        searchResponse = client().prepareSearch("filter23", "filter1").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("filter23", "filter1").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "21", "31", "11", "12", "13");
         assertThat(
-            client().prepareSearch("filter23", "filter1")
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .get()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch("filter23", "filter1").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
             equalTo(5L)
         );
 
-        searchResponse = client().prepareSearch("filter13", "filter1").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("filter13", "filter1").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "11", "12", "13", "33");
         assertThat(
-            client().prepareSearch("filter13", "filter1")
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .get()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch("filter13", "filter1").setSize(0).setQuery(QueryBuilders.matchAllQuery()).get().getHits().getTotalHits().value,
             equalTo(4L)
         );
 
-        searchResponse = client().prepareSearch("filter13", "filter1", "filter23").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("filter13", "filter1", "filter23").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "11", "12", "13", "21", "31", "33");
         assertThat(
-            client().prepareSearch("filter13", "filter1", "filter23")
-                .setSize(0)
+            prepareSearch("filter13", "filter1", "filter23").setSize(0)
                 .setQuery(QueryBuilders.matchAllQuery())
                 .get()
                 .getHits()
@@ -536,11 +509,10 @@ public void testSearchingFilteringAliasesMultipleIndices() throws Exception {
             equalTo(6L)
         );
 
-        searchResponse = client().prepareSearch("filter23", "filter13", "test2").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("filter23", "filter13", "test2").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "21", "22", "23", "31", "13", "33");
         assertThat(
-            client().prepareSearch("filter23", "filter13", "test2")
-                .setSize(0)
+            prepareSearch("filter23", "filter13", "test2").setSize(0)
                 .setQuery(QueryBuilders.matchAllQuery())
                 .get()
                 .getHits()
@@ -548,11 +520,10 @@ public void testSearchingFilteringAliasesMultipleIndices() throws Exception {
             equalTo(6L)
         );
 
-        searchResponse = client().prepareSearch("filter23", "filter13", "test1", "test2").setQuery(QueryBuilders.matchAllQuery()).get();
+        searchResponse = prepareSearch("filter23", "filter13", "test1", "test2").setQuery(QueryBuilders.matchAllQuery()).get();
         assertHits(searchResponse.getHits(), "11", "12", "13", "21", "22", "23", "31", "33");
         assertThat(
-            client().prepareSearch("filter23", "filter13", "test1", "test2")
-                .setSize(0)
+            prepareSearch("filter23", "filter13", "test1", "test2").setSize(0)
                 .setQuery(QueryBuilders.matchAllQuery())
                 .get()
                 .getHits()
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java
index c0bd061e3e963..c437f2b5a4c8c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/MinimumMasterNodesIT.java
@@ -107,13 +107,7 @@ public void testTwoNodesNoMasterBlock() throws Exception {
         logger.info("--> verify we get the data back");
         for (int i = 0; i < 10; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
-                    .setQuery(QueryBuilders.matchAllQuery())
-                    .execute()
-                    .actionGet()
-                    .getHits()
-                    .getTotalHits().value,
+                prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(100L)
             );
         }
@@ -161,7 +155,7 @@ public void testTwoNodesNoMasterBlock() throws Exception {
 
         logger.info("--> verify we get the data back after cluster reform");
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
+            assertHitCount(prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
         }
 
         logger.info("--> clearing voting config exclusions");
@@ -208,7 +202,7 @@ public void testTwoNodesNoMasterBlock() throws Exception {
 
         logger.info("--> verify we the data back");
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
+            assertHitCount(prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
         }
     }
 
@@ -261,7 +255,7 @@ public void testThreeNodesNoMasterBlock() throws Exception {
         refresh();
         logger.info("--> verify we get the data back");
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
+            assertHitCount(prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
         }
 
         List nonMasterNodes = new ArrayList<>(
@@ -290,7 +284,7 @@ public void testThreeNodesNoMasterBlock() throws Exception {
 
         logger.info("--> verify we the data back");
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
+            assertHitCount(prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()), 100);
         }
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/allocation/FilteringAllocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/allocation/FilteringAllocationIT.java
index b91fbb1c9b79f..f2fb19825371f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/allocation/FilteringAllocationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/allocation/FilteringAllocationIT.java
@@ -52,13 +52,7 @@ public void testDecommissionNodeNoReplicas() {
         }
         indicesAdmin().prepareRefresh().execute().actionGet();
         assertThat(
-            client().prepareSearch()
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .execute()
-                .actionGet()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
             equalTo(100L)
         );
 
@@ -89,13 +83,7 @@ public void testDecommissionNodeNoReplicas() {
 
         indicesAdmin().prepareRefresh().execute().actionGet();
         assertThat(
-            client().prepareSearch()
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .execute()
-                .actionGet()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
             equalTo(100L)
         );
     }
@@ -151,13 +139,7 @@ public void testDisablingAllocationFiltering() {
         }
         indicesAdmin().prepareRefresh().execute().actionGet();
         assertThat(
-            client().prepareSearch()
-                .setSize(0)
-                .setQuery(QueryBuilders.matchAllQuery())
-                .execute()
-                .actionGet()
-                .getHits()
-                .getTotalHits().value,
+            prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
             equalTo(100L)
         );
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java
index a086f3e0b7777..73ea91155a087 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java
@@ -177,7 +177,7 @@ public void testDoNotAllowStaleReplicasToBePromotedToPrimary() throws Exception
 
         logger.info("--> check that the up-to-date primary shard gets promoted and that documents are available");
         ensureYellow("test");
-        assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2L);
+        assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2L);
     }
 
     public void testFailedAllocationOfStalePrimaryToDataNodeWithNoData() throws Exception {
@@ -475,7 +475,7 @@ public void testNotWaitForQuorumCopies() throws Exception {
         internalCluster().restartRandomDataNode();
         logger.info("--> checking that index still gets allocated with only 1 shard copy being available");
         ensureYellow("test");
-        assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 1L);
+        assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 1L);
     }
 
     /**
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java
index 0f42b5999920a..4d451971a656e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java
@@ -526,8 +526,7 @@ public void testSearchRouting() throws Exception {
                 }
                 String pitId = client().execute(OpenPointInTimeAction.INSTANCE, openRequest).actionGet().getPointInTimeId();
                 try {
-                    final var profileResults = client().prepareSearch()
-                        .setPointInTime(new PointInTimeBuilder(pitId))
+                    final var profileResults = prepareSearch().setPointInTime(new PointInTimeBuilder(pitId))
                         .setProfile(true)
                         .get()
                         .getProfileResults();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionCleanSettingsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionCleanSettingsIT.java
index 848f16b7cd5d0..2561799b475ad 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionCleanSettingsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionCleanSettingsIT.java
@@ -63,6 +63,6 @@ public void testSearchWithRelocationAndSlowClusterStateProcessing() throws Excep
 
         IndicesStoreIntegrationIT.relocateAndBlockCompletion(logger, "test", 0, node_1, node_2);
         // now search for the documents and see if we get a reply
-        assertThat(client().prepareSearch().setSize(0).get().getHits().getTotalHits().value, equalTo(100L));
+        assertThat(prepareSearch().setSize(0).get().getHits().getTotalHits().value, equalTo(100L));
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java b/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java
index f791f527862dd..30940c1e154b0 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/env/NodeEnvironmentIT.java
@@ -238,7 +238,7 @@ public void testUpgradeDataFolder() throws IOException, InterruptedException {
         assertEquals(nodeId, clusterAdmin().prepareState().get().getState().nodes().getMasterNodeId());
         assertTrue(indexExists("test"));
         ensureYellow("test");
-        assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 1L);
+        assertHitCount(prepareSearch().setQuery(matchAllQuery()), 1L);
     }
 
     public void testFailsToStartOnDataPathsFromMultipleNodes() throws IOException {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java
index ff47be7ee4e01..e995d815af0f1 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/gateway/GatewayIndexStateIT.java
@@ -283,7 +283,7 @@ public void testTwoNodesSingleDoc() throws Exception {
 
         logger.info("--> verify 1 doc in the index");
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 1L);
+            assertHitCount(prepareSearch().setQuery(matchAllQuery()), 1L);
         }
 
         logger.info("--> closing test index...");
@@ -306,9 +306,9 @@ public void testTwoNodesSingleDoc() throws Exception {
         assertThat(health.isTimedOut(), equalTo(false));
 
         logger.info("--> verify 1 doc in the index");
-        assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 1L);
+        assertHitCount(prepareSearch().setQuery(matchAllQuery()), 1L);
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 1L);
+            assertHitCount(prepareSearch().setQuery(matchAllQuery()), 1L);
         }
     }
 
@@ -548,7 +548,7 @@ public void testArchiveBrokenClusterSettings() throws Exception {
         assertNull(
             state.metadata().persistentSettings().get("archived." + ShardLimitValidator.SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey())
         );
-        assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 1L);
+        assertHitCount(prepareSearch().setQuery(matchAllQuery()), 1L);
     }
 
     public void testHalfDeletedIndexImport() throws Exception {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/gateway/QuorumGatewayIT.java b/server/src/internalClusterTest/java/org/elasticsearch/gateway/QuorumGatewayIT.java
index 1e2b03d775ee7..a77201e1e141a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/gateway/QuorumGatewayIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/gateway/QuorumGatewayIT.java
@@ -50,7 +50,7 @@ public void testQuorumRecovery() throws Exception {
         refresh();
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2L);
         }
         logger.info("--> restart all nodes");
         internalCluster().fullRestart(new RestartCallback() {
@@ -90,7 +90,7 @@ public void doAfterNodes(int numNodes, final Client activeClient) throws Excepti
         ensureGreen();
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 3L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 3L);
         }
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java b/server/src/internalClusterTest/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java
index 6d923bf5821b4..81149efb1596f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/gateway/RecoveryFromGatewayIT.java
@@ -123,7 +123,7 @@ public void testOneNodeRecoverFromGateway() throws Exception {
             .actionGet();
 
         refresh();
-        assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("appAccountIds", 179)), 2);
+        assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("appAccountIds", 179)), 2);
         ensureYellow("test"); // wait for primary allocations here otherwise if we have a lot of shards we might have a
         // shard that is still in post recovery when we restart and the ensureYellow() below will timeout
 
@@ -135,7 +135,7 @@ public void testOneNodeRecoverFromGateway() throws Exception {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         indicesAdmin().prepareRefresh().execute().actionGet();
-        assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("appAccountIds", 179)), 2);
+        assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("appAccountIds", 179)), 2);
 
         internalCluster().fullRestart();
 
@@ -144,7 +144,7 @@ public void testOneNodeRecoverFromGateway() throws Exception {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         indicesAdmin().prepareRefresh().execute().actionGet();
-        assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("appAccountIds", 179)), 2);
+        assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("appAccountIds", 179)), 2);
     }
 
     private Map assertAndCapturePrimaryTerms(Map previousTerms) {
@@ -233,10 +233,10 @@ public void testSingleNodeNoFlush() throws Exception {
         refresh();
 
         for (int i = 0; i <= randomInt(10); i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), value1Docs + value2Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("field", "value1")), value1Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("field", "value2")), value2Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("num", 179)), value1Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), value1Docs + value2Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("field", "value1")), value1Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("field", "value2")), value2Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("num", 179)), value1Docs);
         }
         if (indexToAllShards == false) {
             // we have to verify primaries are started for them to be restored
@@ -253,10 +253,10 @@ public void testSingleNodeNoFlush() throws Exception {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         for (int i = 0; i <= randomInt(10); i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), value1Docs + value2Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("field", "value1")), value1Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("field", "value2")), value2Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("num", 179)), value1Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), value1Docs + value2Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("field", "value1")), value1Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("field", "value2")), value2Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("num", 179)), value1Docs);
         }
 
         internalCluster().fullRestart();
@@ -266,10 +266,10 @@ public void testSingleNodeNoFlush() throws Exception {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         for (int i = 0; i <= randomInt(10); i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), value1Docs + value2Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("field", "value1")), value1Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("field", "value2")), value2Docs);
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(termQuery("num", 179)), value1Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), value1Docs + value2Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("field", "value1")), value1Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("field", "value2")), value2Docs);
+            assertHitCount(prepareSearch().setSize(0).setQuery(termQuery("num", 179)), value1Docs);
         }
     }
 
@@ -288,7 +288,7 @@ public void testSingleNodeWithFlush() throws Exception {
             .actionGet();
         refresh();
 
-        assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
+        assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
 
         ensureYellow("test"); // wait for primary allocations here otherwise if we have a lot of shards we might have a
         // shard that is still in post recovery when we restart and the ensureYellow() below will timeout
@@ -302,7 +302,7 @@ public void testSingleNodeWithFlush() throws Exception {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
         }
 
         internalCluster().fullRestart();
@@ -312,7 +312,7 @@ public void testSingleNodeWithFlush() throws Exception {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
         }
     }
 
@@ -337,7 +337,7 @@ public void testTwoNodeFirstNodeCleared() throws Exception {
         ensureGreen();
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
         }
 
         Map primaryTerms = assertAndCapturePrimaryTerms(null);
@@ -365,7 +365,7 @@ public boolean clearData(String nodeName) {
         primaryTerms = assertAndCapturePrimaryTerms(primaryTerms);
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
         }
 
         client().execute(ClearVotingConfigExclusionsAction.INSTANCE, new ClearVotingConfigExclusionsRequest()).get();
@@ -395,7 +395,7 @@ public void testLatestVersionLoaded() throws Exception {
         ensureGreen();
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2);
         }
 
         String metadataUuid = clusterAdmin().prepareState().execute().get().getState().getMetadata().clusterUUID();
@@ -418,7 +418,7 @@ public void testLatestVersionLoaded() throws Exception {
 
         logger.info("--> checking if documents exist, there should be 3");
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 3);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 3);
         }
 
         logger.info("--> add some metadata and additional template");
@@ -462,7 +462,7 @@ public void testLatestVersionLoaded() throws Exception {
         assertThat(clusterAdmin().prepareState().execute().get().getState().getMetadata().clusterUUID(), equalTo(metadataUuid));
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 3);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 3);
         }
 
         ClusterState state = clusterAdmin().prepareState().execute().actionGet().getState();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java
index 29475583dc44d..9219b732ddb02 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java
@@ -166,7 +166,7 @@ public void testCorruptFileAndRecover() throws InterruptedException, IOException
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).get());
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).get());
         // we have to flush at least once here since we don't corrupt the translog
-        assertHitCount(client().prepareSearch().setSize(0), numDocs);
+        assertHitCount(prepareSearch().setSize(0), numDocs);
 
         final int numShards = numShards("test");
         ShardRouting corruptedShardRouting = corruptRandomPrimaryFile();
@@ -192,7 +192,7 @@ public void testCorruptFileAndRecover() throws InterruptedException, IOException
         assertThat(health.getStatus(), equalTo(ClusterHealthStatus.GREEN));
         final int numIterations = scaledRandomIntBetween(5, 20);
         for (int i = 0; i < numIterations; i++) {
-            assertHitCount(client().prepareSearch().setSize(numDocs), numDocs);
+            assertHitCount(prepareSearch().setSize(numDocs), numDocs);
         }
 
         /*
@@ -277,7 +277,7 @@ public void testCorruptPrimaryNoReplica() throws ExecutionException, Interrupted
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).get());
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).get());
         // we have to flush at least once here since we don't corrupt the translog
-        assertHitCount(client().prepareSearch().setSize(0), numDocs);
+        assertHitCount(prepareSearch().setSize(0), numDocs);
 
         ShardRouting shardRouting = corruptRandomPrimaryFile();
         /*
@@ -411,7 +411,7 @@ public void testCorruptionOnNetworkLayer() throws InterruptedException {
         ensureGreen();
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).execute().actionGet());
         // we have to flush at least once here since we don't corrupt the translog
-        assertHitCount(client().prepareSearch().setSize(0), numDocs);
+        assertHitCount(prepareSearch().setSize(0), numDocs);
 
         var source = (MockTransportService) internalCluster().getInstance(TransportService.class, primariesNode.getName());
         var target = internalCluster().getInstance(TransportService.class, unluckyNode.getName());
@@ -509,7 +509,7 @@ public void onTimeout(TimeValue timeout) {
 
         final int numIterations = scaledRandomIntBetween(5, 20);
         for (int i = 0; i < numIterations; i++) {
-            assertHitCount(client().prepareSearch().setSize(numDocs), numDocs);
+            assertHitCount(prepareSearch().setSize(numDocs), numDocs);
         }
     }
 
@@ -550,7 +550,7 @@ public void testCorruptFileThenSnapshotAndRestore() throws InterruptedException,
         ensureGreen();
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).execute().actionGet());
         // we have to flush at least once here since we don't corrupt the translog
-        assertHitCount(client().prepareSearch().setSize(0), numDocs);
+        assertHitCount(prepareSearch().setSize(0), numDocs);
 
         ShardRouting shardRouting = corruptRandomPrimaryFile(false);
         logger.info("--> shard {} has a corrupted file", shardRouting);
@@ -617,7 +617,7 @@ public void testReplicaCorruption() throws Exception {
         ensureGreen();
         assertAllSuccessful(indicesAdmin().prepareFlush().setForce(true).execute().actionGet());
         // we have to flush at least once here since we don't corrupt the translog
-        assertHitCount(client().prepareSearch().setSize(0), numDocs);
+        assertHitCount(prepareSearch().setSize(0), numDocs);
 
         // disable allocations of replicas post restart (the restart will change replicas to primaries, so we have
         // to capture replicas post restart)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationIT.java
index cbe7af4d05d98..211e34c99ec23 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationIT.java
@@ -87,7 +87,7 @@ public void testIndexNameDateMathExpressions() {
         client().prepareIndex(dateMathExp3).setId("3").setSource("{}", XContentType.JSON).get();
         refresh();
 
-        SearchResponse searchResponse = dateSensitiveGet(client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3));
+        SearchResponse searchResponse = dateSensitiveGet(prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3));
         assertHitCount(searchResponse, 3);
         assertSearchHits(searchResponse, "1", "2", "3");
 
@@ -144,7 +144,7 @@ public void testAutoCreateIndexWithDateMathExpression() {
         client().prepareIndex(dateMathExp3).setId("3").setSource("{}", XContentType.JSON).get();
         refresh();
 
-        SearchResponse searchResponse = dateSensitiveGet(client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3));
+        SearchResponse searchResponse = dateSensitiveGet(prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3));
         assertHitCount(searchResponse, 3);
         assertSearchHits(searchResponse, "1", "2", "3");
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java
index e465643143265..7bedd163c2530 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesOptionsIntegrationIT.java
@@ -396,25 +396,19 @@ public void testAllMissingLenient() throws Exception {
         createIndex("test1");
         client().prepareIndex("test1").setId("1").setSource("k", "v").setRefreshPolicy(IMMEDIATE).get();
         assertHitCount(prepareSearch("test2").setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(matchAllQuery()), 0L);
-        assertHitCount(
-            client().prepareSearch("test2", "test3").setQuery(matchAllQuery()).setIndicesOptions(IndicesOptions.lenientExpandOpen()),
-            0L
-        );
+        assertHitCount(prepareSearch("test2", "test3").setQuery(matchAllQuery()).setIndicesOptions(IndicesOptions.lenientExpandOpen()), 0L);
         // you should still be able to run empty searches without things blowing up
-        assertHitCount(client().prepareSearch().setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(matchAllQuery()), 1L);
+        assertHitCount(prepareSearch().setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(matchAllQuery()), 1L);
     }
 
     public void testAllMissingStrict() throws Exception {
         createIndex("test1");
         expectThrows(IndexNotFoundException.class, () -> prepareSearch("test2").setQuery(matchAllQuery()).execute().actionGet());
 
-        expectThrows(
-            IndexNotFoundException.class,
-            () -> client().prepareSearch("test2", "test3").setQuery(matchAllQuery()).execute().actionGet()
-        );
+        expectThrows(IndexNotFoundException.class, () -> prepareSearch("test2", "test3").setQuery(matchAllQuery()).execute().actionGet());
 
         // you should still be able to run empty searches without things blowing up
-        client().prepareSearch().setQuery(matchAllQuery()).execute().actionGet();
+        prepareSearch().setQuery(matchAllQuery()).execute().actionGet();
     }
 
     // For now don't handle closed indices
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java
index 1e8f5b7f1f5e5..5958f1ad57932 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java
@@ -152,7 +152,7 @@ public void testBreakerWithRandomExceptions() throws IOException, InterruptedExc
         }
 
         for (int i = 0; i < numSearches; i++) {
-            SearchRequestBuilder searchRequestBuilder = client().prepareSearch().setQuery(QueryBuilders.matchAllQuery());
+            SearchRequestBuilder searchRequestBuilder = prepareSearch().setQuery(QueryBuilders.matchAllQuery());
             if (random().nextBoolean()) {
                 searchRequestBuilder.addSort("test-str", SortOrder.ASC);
             }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java
index 7ab3cecb95d86..60be1f11371d6 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java
@@ -1076,7 +1076,7 @@ public void testDoNotInfinitelyWaitForMapping() {
         }
 
         indicesAdmin().prepareRefresh("test").get();
-        assertHitCount(client().prepareSearch(), numDocs);
+        assertHitCount(prepareSearch(), numDocs);
     }
 
     /** Makes sure the new master does not repeatedly fetch index metadata from recovering replicas */
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java
index 4631a4fb7cf41..0a8c7ffc9d293 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java
@@ -62,7 +62,7 @@ public void testSimpleUpdateNumberOfReplicas() throws Exception {
         refresh();
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 10L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 10L);
         }
 
         final long settingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
@@ -118,7 +118,7 @@ public void testSimpleUpdateNumberOfReplicas() throws Exception {
         assertThat(clusterHealth.getIndices().get("test").getActiveShards(), equalTo(numShards.numPrimaries * 3));
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 10L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 10L);
         }
 
         logger.info("Decreasing number of replicas from 2 to 0");
@@ -141,7 +141,7 @@ public void testSimpleUpdateNumberOfReplicas() throws Exception {
         assertThat(clusterHealth.getIndices().get("test").getActiveShards(), equalTo(numShards.numPrimaries));
 
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setQuery(matchAllQuery()), 10);
+            assertHitCount(prepareSearch().setQuery(matchAllQuery()), 10);
         }
 
         final long afterReplicaDecreaseSettingsVersion = clusterAdmin().prepareState()
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java
index 70ef73862016a..1ce0c0985b704 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/OpenCloseIndexIT.java
@@ -296,7 +296,7 @@ public void testOpenCloseWithDocs() throws IOException, ExecutionException, Inte
         // check the index still contains the records that we indexed
         indicesAdmin().prepareOpen("test").execute().get();
         ensureGreen();
-        SearchResponse searchResponse = client().prepareSearch().setQuery(QueryBuilders.matchQuery("test", "init")).get();
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("test", "init")).get();
         assertNoFailures(searchResponse);
         assertHitCount(searchResponse, docs);
     }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java
index d4d3596349b93..73f3dd88c9cec 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java
@@ -151,8 +151,8 @@ public void testFieldDataStats() {
         assertThat(indicesStats.getTotal().getFieldData().getMemorySizeInBytes(), equalTo(0L));
 
         // sort to load it to field data...
-        client().prepareSearch().addSort("field", SortOrder.ASC).execute().actionGet();
-        client().prepareSearch().addSort("field", SortOrder.ASC).execute().actionGet();
+        prepareSearch().addSort("field", SortOrder.ASC).execute().actionGet();
+        prepareSearch().addSort("field", SortOrder.ASC).execute().actionGet();
 
         nodesStats = clusterAdmin().prepareNodesStats("data:true").setIndices(true).execute().actionGet();
         assertThat(
@@ -167,8 +167,8 @@ public void testFieldDataStats() {
         assertThat(indicesStats.getTotal().getFieldData().getMemorySizeInBytes(), greaterThan(0L));
 
         // sort to load it to field data...
-        client().prepareSearch().addSort("field2", SortOrder.ASC).execute().actionGet();
-        client().prepareSearch().addSort("field2", SortOrder.ASC).execute().actionGet();
+        prepareSearch().addSort("field2", SortOrder.ASC).execute().actionGet();
+        prepareSearch().addSort("field2", SortOrder.ASC).execute().actionGet();
 
         // now check the per field stats
         nodesStats = clusterAdmin().prepareNodesStats("data:true")
@@ -272,16 +272,8 @@ public void testClearAllCaches() throws Exception {
         assertThat(indicesStats.getTotal().getQueryCache().getMemorySizeInBytes(), equalTo(0L));
 
         // sort to load it to field data and filter to load filter cache
-        client().prepareSearch()
-            .setPostFilter(QueryBuilders.termQuery("field", "value1"))
-            .addSort("field", SortOrder.ASC)
-            .execute()
-            .actionGet();
-        client().prepareSearch()
-            .setPostFilter(QueryBuilders.termQuery("field", "value2"))
-            .addSort("field", SortOrder.ASC)
-            .execute()
-            .actionGet();
+        prepareSearch().setPostFilter(QueryBuilders.termQuery("field", "value1")).addSort("field", SortOrder.ASC).execute().actionGet();
+        prepareSearch().setPostFilter(QueryBuilders.termQuery("field", "value2")).addSort("field", SortOrder.ASC).execute().actionGet();
 
         nodesStats = clusterAdmin().prepareNodesStats("data:true").setIndices(true).execute().actionGet();
         assertThat(
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/FullRollingRestartIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/FullRollingRestartIT.java
index fa8b782ff9305..f4aa261b09625 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/FullRollingRestartIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/FullRollingRestartIT.java
@@ -95,7 +95,7 @@ public void testFullRollingRestart() throws Exception {
         logger.info("--> refreshing and checking data");
         refresh();
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2000L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2000L);
         }
 
         // now start shutting nodes down
@@ -124,7 +124,7 @@ public void testFullRollingRestart() throws Exception {
         logger.info("--> stopped two nodes, verifying data");
         refresh();
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2000L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2000L);
         }
 
         // closing the 3rd node
@@ -154,7 +154,7 @@ public void testFullRollingRestart() throws Exception {
         logger.info("--> one node left, verifying data");
         refresh();
         for (int i = 0; i < 10; i++) {
-            assertHitCount(client().prepareSearch().setSize(0).setQuery(matchAllQuery()), 2000L);
+            assertHitCount(prepareSearch().setSize(0).setQuery(matchAllQuery()), 2000L);
         }
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RecoveryWhileUnderLoadIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RecoveryWhileUnderLoadIT.java
index a05d9bffa48fd..bfd16adaa405b 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RecoveryWhileUnderLoadIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RecoveryWhileUnderLoadIT.java
@@ -319,8 +319,7 @@ private void iterateAssertCount(final int numberOfShards, final int iterations,
         SearchResponse[] iterationResults = new SearchResponse[iterations];
         boolean error = false;
         for (int i = 0; i < iterations; i++) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setSize((int) numberOfDocs)
+            SearchResponse searchResponse = prepareSearch().setSize((int) numberOfDocs)
                 .setQuery(matchAllQuery())
                 .setTrackTotalHits(true)
                 .addSort("id", SortOrder.ASC)
@@ -370,11 +369,7 @@ private void iterateAssertCount(final int numberOfShards, final int iterations,
             assertBusy(() -> {
                 boolean errorOccurred = false;
                 for (int i = 0; i < iterations; i++) {
-                    SearchResponse searchResponse = client().prepareSearch()
-                        .setTrackTotalHits(true)
-                        .setSize(0)
-                        .setQuery(matchAllQuery())
-                        .get();
+                    SearchResponse searchResponse = prepareSearch().setTrackTotalHits(true).setSize(0).setQuery(matchAllQuery()).get();
                     if (searchResponse.getHits().getTotalHits().value != numberOfDocs) {
                         errorOccurred = true;
                     }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java
index c221fbf78ccca..ea392e14104fa 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java
@@ -522,7 +522,7 @@ public void testIndexSearchAndRelocateConcurrently() throws Exception {
         final int numIters = randomIntBetween(10, 20);
         for (int i = 0; i < numIters; i++) {
             logger.info(" --> checking iteration {}", i);
-            assertSearchHitsWithoutFailures(client().prepareSearch().setSize(ids.size()), ids.toArray(Strings.EMPTY_ARRAY));
+            assertSearchHitsWithoutFailures(prepareSearch().setSize(ids.size()), ids.toArray(Strings.EMPTY_ARRAY));
         }
         stopped.set(true);
         for (Thread searchThread : searchThreads) {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java
index ba845d3525df6..74b0b5a1ef864 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java
@@ -93,7 +93,7 @@ public void testCancelRecoveryAndResume() throws Exception {
         indexRandom(true, builder);
         for (int i = 0; i < numDocs; i++) {
             String id = Integer.toString(i);
-            assertHitCount(client().prepareSearch().setQuery(QueryBuilders.termQuery("the_id", id)), 1);
+            assertHitCount(prepareSearch().setQuery(QueryBuilders.termQuery("the_id", id)), 1);
         }
         ensureGreen();
         // ensure we have flushed segments and make them a big one via optimize
@@ -143,7 +143,7 @@ public void testCancelRecoveryAndResume() throws Exception {
         ensureGreen("test");
         for (int i = 0; i < numDocs; i++) {
             String id = Integer.toString(i);
-            assertHitCount(client().prepareSearch().setQuery(QueryBuilders.termQuery("the_id", id)), 1);
+            assertHitCount(prepareSearch().setQuery(QueryBuilders.termQuery("the_id", id)), 1);
         }
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasResolveRoutingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasResolveRoutingIT.java
index 70c306a08d600..478cae8746f86 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasResolveRoutingIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasResolveRoutingIT.java
@@ -43,10 +43,7 @@ public void testSearchClosedWildcardIndex() throws ExecutionException, Interrupt
         );
         refresh("test-*");
         assertHitCount(
-            client().prepareSearch()
-                .setIndices("alias-*")
-                .setIndicesOptions(IndicesOptions.lenientExpandOpen())
-                .setQuery(queryStringQuery("quick")),
+            prepareSearch().setIndices("alias-*").setIndicesOptions(IndicesOptions.lenientExpandOpen()).setQuery(queryStringQuery("quick")),
             3L
         );
     }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java
index 7acd0e8b62ff9..7ee081ffd433e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/routing/AliasRoutingIT.java
@@ -121,7 +121,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with no routing, should fine one");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
+                prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(1L)
             );
         }
@@ -129,8 +129,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with wrong routing, should not find");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting("1")
+                prepareSearch().setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -140,8 +139,7 @@ public void testAliasSearchRouting() throws Exception {
             );
 
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -171,8 +169,7 @@ public void testAliasSearchRouting() throws Exception {
         for (int i = 0; i < 5; i++) {
 
             assertThat(
-                client().prepareSearch()
-                    .setRouting("0")
+                prepareSearch().setRouting("0")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -181,8 +178,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(1L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting("0")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -212,17 +208,11 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with no routing, should fine two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
+                prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
-                    .setQuery(QueryBuilders.matchAllQuery())
-                    .execute()
-                    .actionGet()
-                    .getHits()
-                    .getTotalHits().value,
+                prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(2L)
             );
         }
@@ -230,8 +220,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with 0 routing, should find one");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting("0")
+                prepareSearch().setRouting("0")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -240,8 +229,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(1L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting("0")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -268,8 +256,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with 1 routing, should find one");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting("1")
+                prepareSearch().setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -278,8 +265,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(1L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -306,8 +292,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with 0,1 indexRoutings , should find two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting("0", "1")
+                prepareSearch().setRouting("0", "1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -316,8 +301,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting("0", "1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -344,8 +328,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with two routing aliases , should find two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch("alias0", "alias1")
-                    .setQuery(QueryBuilders.matchAllQuery())
+                prepareSearch("alias0", "alias1").setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
                     .getHits()
@@ -353,8 +336,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch("alias0", "alias1")
-                    .setSize(0)
+                prepareSearch("alias0", "alias1").setSize(0)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -367,8 +349,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with alias0, alias1 and alias01, should find two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch("alias0", "alias1", "alias01")
-                    .setQuery(QueryBuilders.matchAllQuery())
+                prepareSearch("alias0", "alias1", "alias01").setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
                     .getHits()
@@ -376,8 +357,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch("alias0", "alias1", "alias01")
-                    .setSize(0)
+                prepareSearch("alias0", "alias1", "alias01").setSize(0)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -390,8 +370,7 @@ public void testAliasSearchRouting() throws Exception {
         logger.info("--> search with test, alias0 and alias1, should find two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch("test", "alias0", "alias1")
-                    .setQuery(QueryBuilders.matchAllQuery())
+                prepareSearch("test", "alias0", "alias1").setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
                     .getHits()
@@ -399,8 +378,7 @@ public void testAliasSearchRouting() throws Exception {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch("test", "alias0", "alias1")
-                    .setSize(0)
+                prepareSearch("test", "alias0", "alias1").setSize(0)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -458,8 +436,7 @@ public void testAliasSearchRoutingWithTwoIndices() throws Exception {
         logger.info("--> search with alias-a1,alias-b0, should not find");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch("alias-a1", "alias-b0")
-                    .setQuery(QueryBuilders.matchAllQuery())
+                prepareSearch("alias-a1", "alias-b0").setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
                     .getHits()
@@ -467,8 +444,7 @@ public void testAliasSearchRoutingWithTwoIndices() throws Exception {
                 equalTo(0L)
             );
             assertThat(
-                client().prepareSearch("alias-a1", "alias-b0")
-                    .setSize(0)
+                prepareSearch("alias-a1", "alias-b0").setSize(0)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -498,8 +474,7 @@ public void testAliasSearchRoutingWithTwoIndices() throws Exception {
         logger.info("--> search with alias-a0,alias-b1 should find two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch("alias-a0", "alias-b1")
-                    .setQuery(QueryBuilders.matchAllQuery())
+                prepareSearch("alias-a0", "alias-b1").setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
                     .getHits()
@@ -507,8 +482,7 @@ public void testAliasSearchRoutingWithTwoIndices() throws Exception {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch("alias-a0", "alias-b1")
-                    .setSize(0)
+                prepareSearch("alias-a0", "alias-b1").setSize(0)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/routing/PartitionedRoutingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/routing/PartitionedRoutingIT.java
index 72729172d6c52..4b685ca2699be 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/routing/PartitionedRoutingIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/routing/PartitionedRoutingIT.java
@@ -145,8 +145,7 @@ private void verifyRoutedSearches(String index, Map> routing
             String routing = routingEntry.getKey();
             int expectedDocuments = routingEntry.getValue().size();
 
-            SearchResponse response = client().prepareSearch()
-                .setQuery(QueryBuilders.termQuery("_routing", routing))
+            SearchResponse response = prepareSearch().setQuery(QueryBuilders.termQuery("_routing", routing))
                 .setRouting(routing)
                 .setIndices(index)
                 .setSize(100)
@@ -183,8 +182,7 @@ private void verifyBroadSearches(String index, Map> routingT
             String routing = routingEntry.getKey();
             int expectedDocuments = routingEntry.getValue().size();
 
-            SearchResponse response = client().prepareSearch()
-                .setQuery(QueryBuilders.termQuery("_routing", routing))
+            SearchResponse response = prepareSearch().setQuery(QueryBuilders.termQuery("_routing", routing))
                 .setIndices(index)
                 .setSize(100)
                 .execute()
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/routing/SimpleRoutingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/routing/SimpleRoutingIT.java
index f1417d2f230ad..93b1ac68be6a5 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/routing/SimpleRoutingIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/routing/SimpleRoutingIT.java
@@ -138,7 +138,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with no routing, should fine one");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
+                prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(1L)
             );
         }
@@ -146,8 +146,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with wrong routing, should not find");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting("1")
+                prepareSearch().setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -156,8 +155,7 @@ public void testSimpleSearchRouting() {
                 equalTo(0L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -171,8 +169,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with correct routing, should find");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting(routingValue)
+                prepareSearch().setRouting(routingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -181,8 +178,7 @@ public void testSimpleSearchRouting() {
                 equalTo(1L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting(routingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -205,17 +201,11 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with no routing, should fine two");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
+                prepareSearch().setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
-                    .setQuery(QueryBuilders.matchAllQuery())
-                    .execute()
-                    .actionGet()
-                    .getHits()
-                    .getTotalHits().value,
+                prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet().getHits().getTotalHits().value,
                 equalTo(2L)
             );
         }
@@ -223,8 +213,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with {} routing, should find one", routingValue);
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting(routingValue)
+                prepareSearch().setRouting(routingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -233,8 +222,7 @@ public void testSimpleSearchRouting() {
                 equalTo(1L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting(routingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -248,8 +236,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with {} routing, should find one", secondRoutingValue);
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting("1")
+                prepareSearch().setRouting("1")
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -258,8 +245,7 @@ public void testSimpleSearchRouting() {
                 equalTo(1L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting(secondRoutingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -273,8 +259,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with {},{} indexRoutings , should find two", routingValue, "1");
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting(routingValue, secondRoutingValue)
+                prepareSearch().setRouting(routingValue, secondRoutingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -283,8 +268,7 @@ public void testSimpleSearchRouting() {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting(routingValue, secondRoutingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
@@ -298,8 +282,7 @@ public void testSimpleSearchRouting() {
         logger.info("--> search with {},{},{} indexRoutings , should find two", routingValue, secondRoutingValue, routingValue);
         for (int i = 0; i < 5; i++) {
             assertThat(
-                client().prepareSearch()
-                    .setRouting(routingValue, secondRoutingValue, routingValue)
+                prepareSearch().setRouting(routingValue, secondRoutingValue, routingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
                     .actionGet()
@@ -308,8 +291,7 @@ public void testSimpleSearchRouting() {
                 equalTo(2L)
             );
             assertThat(
-                client().prepareSearch()
-                    .setSize(0)
+                prepareSearch().setSize(0)
                     .setRouting(routingValue, secondRoutingValue, routingValue)
                     .setQuery(QueryBuilders.matchAllQuery())
                     .execute()
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchWithRejectionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchWithRejectionsIT.java
index b452b4da6e2d4..3202037c8486f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchWithRejectionsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchWithRejectionsIT.java
@@ -48,7 +48,7 @@ public void testOpenContextsAfterRejections() throws Exception {
         SearchType searchType = randomFrom(SearchType.DEFAULT, SearchType.QUERY_THEN_FETCH, SearchType.DFS_QUERY_THEN_FETCH);
         logger.info("search type is {}", searchType);
         for (int i = 0; i < numSearches; i++) {
-            responses[i] = client().prepareSearch().setQuery(matchAllQuery()).setSearchType(searchType).execute();
+            responses[i] = prepareSearch().setQuery(matchAllQuery()).setSearchType(searchType).execute();
         }
         for (int i = 0; i < numSearches; i++) {
             try {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java
index 8031c467a47ae..d0a4fa4865694 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java
@@ -900,9 +900,9 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH))
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            dateHistogram("histo").field("date").calendarInterval(DateHistogramInterval.MONTH)
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java
index 79b9b0b3628d5..f67a7461d3bae 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java
@@ -510,14 +510,9 @@ public void testMultiValuedField() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                dateRange("range").field("date")
-                    .addUnboundedTo(date(2, 15))
-                    .addRange(date(2, 15), date(3, 15))
-                    .addUnboundedFrom(date(3, 15))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            dateRange("range").field("date").addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15)).addUnboundedFrom(date(3, 15))
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java
index 49e27ff401740..612b4bf006aa2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DiversifiedSamplerIT.java
@@ -205,8 +205,7 @@ public void testPartiallyUnmappedDiversifyField() throws Exception {
             .field("author")
             .maxDocsPerValue(1);
         sampleAgg.subAggregation(terms("authors").field("author"));
-        SearchResponse response = client().prepareSearch("idx_unmapped_author", "test")
-            .setSearchType(SearchType.QUERY_THEN_FETCH)
+        SearchResponse response = prepareSearch("idx_unmapped_author", "test").setSearchType(SearchType.QUERY_THEN_FETCH)
             .setQuery(new TermQueryBuilder("genre", "fantasy"))
             .setFrom(0)
             .setSize(60)
@@ -225,8 +224,7 @@ public void testWhollyUnmappedDiversifyField() throws Exception {
         DiversifiedAggregationBuilder sampleAgg = new DiversifiedAggregationBuilder("sample").shardSize(100);
         sampleAgg.field("author").maxDocsPerValue(MAX_DOCS_PER_AUTHOR).executionHint(randomExecutionHint());
         sampleAgg.subAggregation(terms("authors").field("author"));
-        SearchResponse response = client().prepareSearch("idx_unmapped", "idx_unmapped_author")
-            .setSearchType(SearchType.QUERY_THEN_FETCH)
+        SearchResponse response = prepareSearch("idx_unmapped", "idx_unmapped_author").setSearchType(SearchType.QUERY_THEN_FETCH)
             .setQuery(new TermQueryBuilder("genre", "fantasy"))
             .setFrom(0)
             .setSize(60)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java
index 7f18e532d7d7a..0c0209df7d5a3 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java
@@ -453,11 +453,9 @@ public void testScriptMultiValued() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx_unmapped", "idx")
-            .addAggregation(
-                new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values()))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx_unmapped", "idx").addAggregation(
+            new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values()))
+        ).get();
 
         assertNoFailures(response);
 
@@ -476,13 +474,11 @@ public void testPartiallyUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmappedWithFormat() throws Exception {
-        SearchResponse response = client().prepareSearch("idx_unmapped", "idx")
-            .addAggregation(
-                new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME)
-                    .collectMode(randomFrom(SubAggCollectionMode.values()))
-                    .format("0000.00")
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx_unmapped", "idx").addAggregation(
+            new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME)
+                .collectMode(randomFrom(SubAggCollectionMode.values()))
+                .format("0000.00")
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java
index 64441466c7954..7639445f1f5ac 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoDistanceIT.java
@@ -273,15 +273,13 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location")
-                    .unit(DistanceUnit.KILOMETERS)
-                    .addUnboundedTo(500)
-                    .addRange(500, 1000)
-                    .addUnboundedFrom(1000)
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            geoDistance("amsterdam_rings", new GeoPoint(52.3760, 4.894)).field("location")
+                .unit(DistanceUnit.KILOMETERS)
+                .addUnboundedTo(500)
+                .addRange(500, 1000)
+                .addUnboundedFrom(1000)
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java
index 2767a1878c11a..1cd8d5bc2fc3d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/GeoHashGridIT.java
@@ -218,9 +218,9 @@ public void testUnmapped() throws Exception {
 
     public void testPartiallyUnmapped() throws Exception {
         for (int precision = 1; precision <= PRECISION; precision++) {
-            SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-                .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision))
-                .get();
+            SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+                geohashGrid("geohashgrid").field("location").precision(precision)
+            ).get();
 
             assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java
index f644c89ddbc58..cea3872b9a7c8 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java
@@ -825,9 +825,9 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval))
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            histogram("histo").field(SINGLE_VALUED_FIELD_NAME).interval(interval)
+        ).get();
 
         assertNoFailures(response);
 
@@ -846,13 +846,11 @@ public void testPartiallyUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmappedWithExtendedBounds() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                histogram("histo").field(SINGLE_VALUED_FIELD_NAME)
-                    .interval(interval)
-                    .extendedBounds(-1 * 2 * interval, valueCounts.length * interval)
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            histogram("histo").field(SINGLE_VALUED_FIELD_NAME)
+                .interval(interval)
+                .extendedBounds(-1 * 2 * interval, valueCounts.length * interval)
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java
index 0a076721c2e0c..8e4c503b89bb5 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/IpRangeIT.java
@@ -151,15 +151,13 @@ public void testIpMask() {
     }
 
     public void testPartiallyUnmapped() {
-        SearchResponse rsp = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                AggregationBuilders.ipRange("my_range")
-                    .field("ip")
-                    .addUnboundedTo("192.168.1.0")
-                    .addRange("192.168.1.0", "192.168.1.10")
-                    .addUnboundedFrom("192.168.1.10")
-            )
-            .get();
+        SearchResponse rsp = prepareSearch("idx", "idx_unmapped").addAggregation(
+            AggregationBuilders.ipRange("my_range")
+                .field("ip")
+                .addUnboundedTo("192.168.1.0")
+                .addRange("192.168.1.0", "192.168.1.10")
+                .addUnboundedFrom("192.168.1.10")
+        ).get();
         assertNoFailures(rsp);
         Range range = rsp.getAggregations().get("my_range");
         assertEquals(3, range.getBuckets().size());
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java
index 67f244eabbd3b..6feee8e36701b 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java
@@ -450,11 +450,9 @@ public void testScriptMultiValued() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx_unmapped", "idx")
-            .addAggregation(
-                new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values()))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx_unmapped", "idx").addAggregation(
+            new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME).collectMode(randomFrom(SubAggCollectionMode.values()))
+        ).get();
 
         assertNoFailures(response);
 
@@ -473,13 +471,11 @@ public void testPartiallyUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmappedWithFormat() throws Exception {
-        SearchResponse response = client().prepareSearch("idx_unmapped", "idx")
-            .addAggregation(
-                new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME)
-                    .collectMode(randomFrom(SubAggCollectionMode.values()))
-                    .format("0000")
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx_unmapped", "idx").addAggregation(
+            new TermsAggregationBuilder("terms").field(SINGLE_VALUED_FIELD_NAME)
+                .collectMode(randomFrom(SubAggCollectionMode.values()))
+                .format("0000")
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java
index 2b5b4d5b1d791..cfe3a6708341a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java
@@ -759,9 +759,9 @@ public void testUnmapped() throws Exception {
     public void testPartiallyUnmapped() throws Exception {
         clusterAdmin().prepareHealth("idx_unmapped").setWaitForYellowStatus().get();
 
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6))
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            range("range").field(SINGLE_VALUED_FIELD_NAME).addUnboundedTo(3).addRange(3, 6).addUnboundedFrom(6)
+        ).get();
 
         assertNoFailures(response);
 
@@ -961,9 +961,9 @@ public void testScriptCaching() throws Exception {
     }
 
     public void testFieldAlias() {
-        SearchResponse response = client().prepareSearch("old_index", "new_index")
-            .addAggregation(range("range").field("route_length_miles").addUnboundedTo(50.0).addRange(50.0, 150.0).addUnboundedFrom(150.0))
-            .get();
+        SearchResponse response = prepareSearch("old_index", "new_index").addAggregation(
+            range("range").field("route_length_miles").addUnboundedTo(50.0).addRange(50.0, 150.0).addUnboundedFrom(150.0)
+        ).get();
 
         assertNoFailures(response);
 
@@ -990,11 +990,9 @@ public void testFieldAlias() {
     }
 
     public void testFieldAliasWithMissingValue() {
-        SearchResponse response = client().prepareSearch("old_index", "new_index")
-            .addAggregation(
-                range("range").field("route_length_miles").missing(0.0).addUnboundedTo(50.0).addRange(50.0, 150.0).addUnboundedFrom(150.0)
-            )
-            .get();
+        SearchResponse response = prepareSearch("old_index", "new_index").addAggregation(
+            range("range").field("route_length_miles").missing(0.0).addUnboundedTo(50.0).addRange(50.0, 150.0).addUnboundedFrom(150.0)
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java
index 2e1bd1812ea4b..f6d7d37a29136 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java
@@ -158,8 +158,7 @@ public void testUnmappedChildAggNoDiversity() throws Exception {
     public void testPartiallyUnmappedChildAggNoDiversity() throws Exception {
         SamplerAggregationBuilder sampleAgg = sampler("sample").shardSize(100);
         sampleAgg.subAggregation(terms("authors").field("author"));
-        SearchResponse response = client().prepareSearch("idx_unmapped", "test")
-            .setSearchType(SearchType.QUERY_THEN_FETCH)
+        SearchResponse response = prepareSearch("idx_unmapped", "test").setSearchType(SearchType.QUERY_THEN_FETCH)
             .setQuery(new TermQueryBuilder("genre", "fantasy"))
             .setFrom(0)
             .setSize(60)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java
index 109c2ec356f19..58609df7ae8fe 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java
@@ -960,16 +960,14 @@ public void testDoubleValueFieldSubAggDesc() throws Exception {
      * 3 one-shard indices.
      */
     public void testFixedDocs() throws Exception {
-        SearchResponse response = client().prepareSearch("idx_fixed_docs_0", "idx_fixed_docs_1", "idx_fixed_docs_2")
-            .addAggregation(
-                terms("terms").executionHint(randomExecutionHint())
-                    .field(STRING_FIELD_NAME)
-                    .showTermDocCountError(true)
-                    .size(5)
-                    .shardSize(5)
-                    .collectMode(randomFrom(SubAggCollectionMode.values()))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx_fixed_docs_0", "idx_fixed_docs_1", "idx_fixed_docs_2").addAggregation(
+            terms("terms").executionHint(randomExecutionHint())
+                .field(STRING_FIELD_NAME)
+                .showTermDocCountError(true)
+                .size(5)
+                .shardSize(5)
+                .collectMode(randomFrom(SubAggCollectionMode.values()))
+        ).get();
         assertNoFailures(response);
 
         Terms terms = response.getAggregations().get("terms");
@@ -1015,16 +1013,14 @@ public void testFixedDocs() throws Exception {
      * See https://github.com/elastic/elasticsearch/issues/40005 for more details
      */
     public void testIncrementalReduction() {
-        SearchResponse response = client().prepareSearch("idx_fixed_docs_3", "idx_fixed_docs_4", "idx_fixed_docs_5")
-            .addAggregation(
-                terms("terms").executionHint(randomExecutionHint())
-                    .field(STRING_FIELD_NAME)
-                    .showTermDocCountError(true)
-                    .size(5)
-                    .shardSize(5)
-                    .collectMode(randomFrom(SubAggCollectionMode.values()))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx_fixed_docs_3", "idx_fixed_docs_4", "idx_fixed_docs_5").addAggregation(
+            terms("terms").executionHint(randomExecutionHint())
+                .field(STRING_FIELD_NAME)
+                .showTermDocCountError(true)
+                .size(5)
+                .shardSize(5)
+                .collectMode(randomFrom(SubAggCollectionMode.values()))
+        ).get();
         assertNoFailures(response);
         Terms terms = response.getAggregations().get("terms");
         assertThat(terms.getDocCountError(), equalTo(0L));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java
index 58976326a8e9c..5d337500ea14d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java
@@ -533,13 +533,11 @@ public void testScriptMultiValued() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                new TermsAggregationBuilder("terms").executionHint(randomExecutionHint())
-                    .field(SINGLE_VALUED_FIELD_NAME)
-                    .collectMode(randomFrom(SubAggCollectionMode.values()))
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            new TermsAggregationBuilder("terms").executionHint(randomExecutionHint())
+                .field(SINGLE_VALUED_FIELD_NAME)
+                .collectMode(randomFrom(SubAggCollectionMode.values()))
+        ).get();
 
         assertNoFailures(response);
 
@@ -1125,13 +1123,11 @@ private void assertMultiSortResponse(String[] expectedKeys, BucketOrder... order
     }
 
     public void testIndexMetaField() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "empty_bucket_idx")
-            .addAggregation(
-                new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values()))
-                    .executionHint(randomExecutionHint())
-                    .field(IndexFieldMapper.NAME)
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "empty_bucket_idx").addAggregation(
+            new TermsAggregationBuilder("terms").collectMode(randomFrom(SubAggCollectionMode.values()))
+                .executionHint(randomExecutionHint())
+                .field(IndexFieldMapper.NAME)
+        ).get();
 
         assertNoFailures(response);
         StringTerms terms = response.getAggregations().get("terms");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java
index ce3140891871e..ff94a9f5cad82 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java
@@ -160,8 +160,7 @@ public void testPartiallyUnmapped() {
             .get()
             .getAggregations()
             .get("stats");
-        ExtendedStats s2 = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(extendedStats("stats").field("value").sigma(sigma))
+        ExtendedStats s2 = prepareSearch("idx", "idx_unmapped").addAggregation(extendedStats("stats").field("value").sigma(sigma))
             .get()
             .getAggregations()
             .get("stats");
@@ -343,8 +342,7 @@ public void testSingleValuedFieldGetProperty() throws Exception {
     @Override
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
         double sigma = randomDouble() * randomIntBetween(1, 10);
-        SearchResponse searchResponse = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(extendedStats("stats").field("value").sigma(sigma))
             .get();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java
index 29592bb1d5d04..841419a04424d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java
@@ -248,8 +248,7 @@ public void testSingleValuedFieldOutsideRange() throws Exception {
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
         int sigDigits = randomSignificantDigits();
         final double[] pcts = randomPercents(minValue, maxValue);
-        SearchResponse searchResponse = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(
                 percentileRanks("percentile_ranks", pcts).method(PercentilesMethod.HDR)
                     .numberOfSignificantValueDigits(sigDigits)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java
index d0c173f8f14b6..fe48fe4fe48b2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java
@@ -206,8 +206,7 @@ public void testSingleValuedFieldGetProperty() throws Exception {
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
         final double[] pcts = randomPercentiles();
         int sigDigits = randomSignificantDigits();
-        SearchResponse searchResponse = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(
                 percentiles("percentiles").numberOfSignificantValueDigits(sigDigits)
                     .method(PercentilesMethod.HDR)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java
index 0b15180e39447..9e150e4d8b769 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java
@@ -195,8 +195,7 @@ public void testSingleValuedFieldGetProperty() throws Exception {
 
     @Override
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
-        final SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        final SearchResponse response = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(randomBuilder().field("value"))
             .get();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java
index 5504dd9148e87..b100e7eb9f955 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java
@@ -270,9 +270,7 @@ public void testScriptCaching() throws Exception {
     }
 
     public void testFieldAlias() {
-        SearchResponse response = client().prepareSearch("old_index", "new_index")
-            .addAggregation(sum("sum").field("route_length_miles"))
-            .get();
+        SearchResponse response = prepareSearch("old_index", "new_index").addAggregation(sum("sum").field("route_length_miles")).get();
 
         assertNoFailures(response);
 
@@ -283,9 +281,9 @@ public void testFieldAlias() {
     }
 
     public void testFieldAliasInSubAggregation() {
-        SearchResponse response = client().prepareSearch("old_index", "new_index")
-            .addAggregation(terms("terms").field("transit_mode").subAggregation(sum("sum").field("route_length_miles")))
-            .get();
+        SearchResponse response = prepareSearch("old_index", "new_index").addAggregation(
+            terms("terms").field("transit_mode").subAggregation(sum("sum").field("route_length_miles"))
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java
index edcb65f169bcd..cdede393bd4f5 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java
@@ -207,8 +207,7 @@ public void testSingleValuedFieldOutsideRange() throws Exception {
     @Override
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
         final double[] pcts = randomPercents(minValue, maxValue);
-        SearchResponse searchResponse = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(randomCompression(percentileRanks("percentile_ranks", pcts)).field("value"))
             .get();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java
index 3e7f4f9780327..28b1911c9441a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java
@@ -179,8 +179,7 @@ public void testSingleValuedFieldGetProperty() throws Exception {
     @Override
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
         final double[] pcts = randomPercentiles();
-        SearchResponse searchResponse = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(randomCompression(percentiles("percentiles")).field("value").percentiles(pcts))
             .get();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java
index 4d5c06d8acdd0..719a5831375df 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java
@@ -114,8 +114,7 @@ public void testSingleValuedFieldGetProperty() throws Exception {
     }
 
     public void testSingleValuedFieldPartiallyUnmapped() throws Exception {
-        SearchResponse searchResponse = client().prepareSearch("idx", "idx_unmapped")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("idx", "idx_unmapped").setQuery(matchAllQuery())
             .addAggregation(count("count").field("value"))
             .get();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java
index c53f6c276ceaa..9a9dc44b71ef2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/pipeline/BucketScriptIT.java
@@ -588,24 +588,22 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                histogram("histo").field(FIELD_1_NAME)
-                    .interval(interval)
-                    .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
-                    .subAggregation(sum("field3Sum").field(FIELD_3_NAME))
-                    .subAggregation(sum("field4Sum").field(FIELD_4_NAME))
-                    .subAggregation(
-                        bucketScript(
-                            "seriesArithmetic",
-                            new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()),
-                            "field2Sum",
-                            "field3Sum",
-                            "field4Sum"
-                        )
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            histogram("histo").field(FIELD_1_NAME)
+                .interval(interval)
+                .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
+                .subAggregation(sum("field3Sum").field(FIELD_3_NAME))
+                .subAggregation(sum("field4Sum").field(FIELD_4_NAME))
+                .subAggregation(
+                    bucketScript(
+                        "seriesArithmetic",
+                        new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_value0 + _value1 + _value2", Collections.emptyMap()),
+                        "field2Sum",
+                        "field3Sum",
+                        "field4Sum"
                     )
-            )
-            .get();
+                )
+        ).get();
 
         assertNoFailures(response);
 
@@ -651,14 +649,12 @@ public void testSingleBucketPathAgg() throws Exception {
             "seriesArithmetic"
         );
 
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                histogram("histo").field(FIELD_1_NAME)
-                    .interval(interval)
-                    .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
-                    .subAggregation(bucketScriptAgg)
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            histogram("histo").field(FIELD_1_NAME)
+                .interval(interval)
+                .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
+                .subAggregation(bucketScriptAgg)
+        ).get();
 
         assertNoFailures(response);
 
@@ -698,16 +694,14 @@ public void testArrayBucketPathAgg() throws Exception {
             "seriesArithmetic"
         );
 
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                histogram("histo").field(FIELD_1_NAME)
-                    .interval(interval)
-                    .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
-                    .subAggregation(sum("field3Sum").field(FIELD_3_NAME))
-                    .subAggregation(sum("field4Sum").field(FIELD_4_NAME))
-                    .subAggregation(bucketScriptAgg)
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            histogram("histo").field(FIELD_1_NAME)
+                .interval(interval)
+                .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
+                .subAggregation(sum("field3Sum").field(FIELD_3_NAME))
+                .subAggregation(sum("field4Sum").field(FIELD_4_NAME))
+                .subAggregation(bucketScriptAgg)
+        ).get();
 
         assertNoFailures(response);
 
@@ -757,16 +751,14 @@ public void testObjectBucketPathAgg() throws Exception {
             "seriesArithmetic"
         );
 
-        SearchResponse response = client().prepareSearch("idx", "idx_unmapped")
-            .addAggregation(
-                histogram("histo").field(FIELD_1_NAME)
-                    .interval(interval)
-                    .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
-                    .subAggregation(sum("field3Sum").field(FIELD_3_NAME))
-                    .subAggregation(sum("field4Sum").field(FIELD_4_NAME))
-                    .subAggregation(bucketScriptAgg)
-            )
-            .get();
+        SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation(
+            histogram("histo").field(FIELD_1_NAME)
+                .interval(interval)
+                .subAggregation(sum("field2Sum").field(FIELD_2_NAME))
+                .subAggregation(sum("field3Sum").field(FIELD_3_NAME))
+                .subAggregation(sum("field4Sum").field(FIELD_4_NAME))
+                .subAggregation(bucketScriptAgg)
+        ).get();
 
         assertNoFailures(response);
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchRedStateIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchRedStateIndexIT.java
index a79c09a72f7df..74acaf95bd24a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchRedStateIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchRedStateIndexIT.java
@@ -43,7 +43,7 @@ public void testAllowPartialsWithRedState() throws Exception {
         final int numShards = cluster().numDataNodes() + 2;
         buildRedIndex(numShards);
 
-        SearchResponse searchResponse = client().prepareSearch().setSize(0).setAllowPartialSearchResults(true).get();
+        SearchResponse searchResponse = prepareSearch().setSize(0).setAllowPartialSearchResults(true).get();
         assertThat(RestStatus.OK, equalTo(searchResponse.status()));
         assertThat("Expect some shards failed", searchResponse.getFailedShards(), allOf(greaterThan(0), lessThanOrEqualTo(numShards)));
         assertThat("Expect no shards skipped", searchResponse.getSkippedShards(), equalTo(0));
@@ -60,7 +60,7 @@ public void testClusterAllowPartialsWithRedState() throws Exception {
 
         setClusterDefaultAllowPartialResults(true);
 
-        SearchResponse searchResponse = client().prepareSearch().setSize(0).get();
+        SearchResponse searchResponse = prepareSearch().setSize(0).get();
         assertThat(RestStatus.OK, equalTo(searchResponse.status()));
         assertThat("Expect some shards failed", searchResponse.getFailedShards(), allOf(greaterThan(0), lessThanOrEqualTo(numShards)));
         assertThat("Expect no shards skipped", searchResponse.getSkippedShards(), equalTo(0));
@@ -79,7 +79,7 @@ public void testDisallowPartialsWithRedState() throws Exception {
 
         SearchPhaseExecutionException ex = expectThrows(
             SearchPhaseExecutionException.class,
-            () -> client().prepareSearch().setSize(0).setAllowPartialSearchResults(false).get()
+            () -> prepareSearch().setSize(0).setAllowPartialSearchResults(false).get()
         );
         assertThat(ex.getDetailedMessage(), containsString("Search rejected due to missing shard"));
     }
@@ -88,10 +88,7 @@ public void testClusterDisallowPartialsWithRedState() throws Exception {
         buildRedIndex(cluster().numDataNodes() + 2);
 
         setClusterDefaultAllowPartialResults(false);
-        SearchPhaseExecutionException ex = expectThrows(
-            SearchPhaseExecutionException.class,
-            () -> client().prepareSearch().setSize(0).get()
-        );
+        SearchPhaseExecutionException ex = expectThrows(SearchPhaseExecutionException.class, () -> prepareSearch().setSize(0).get());
         assertThat(ex.getDetailedMessage(), containsString("Search rejected due to missing shard"));
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileRelocatingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileRelocatingIT.java
index a4a5765926a3c..24df07217a5a2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileRelocatingIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWhileRelocatingIT.java
@@ -61,7 +61,7 @@ private void testSearchAndRelocateConcurrently(final int numberOfReplicas) throw
             );
         }
         indexRandom(true, indexBuilders.toArray(new IndexRequestBuilder[indexBuilders.size()]));
-        assertHitCount(client().prepareSearch(), (numDocs));
+        assertHitCount(prepareSearch(), (numDocs));
         final int numIters = scaledRandomIntBetween(5, 20);
         for (int i = 0; i < numIters; i++) {
             final AtomicBoolean stop = new AtomicBoolean(false);
@@ -74,7 +74,7 @@ private void testSearchAndRelocateConcurrently(final int numberOfReplicas) throw
                     public void run() {
                         try {
                             while (stop.get() == false) {
-                                SearchResponse sr = client().prepareSearch().setSize(numDocs).get();
+                                SearchResponse sr = prepareSearch().setSize(numDocs).get();
                                 if (sr.getHits().getTotalHits().value != numDocs) {
                                     // if we did not search all shards but had no serious failures that is potentially fine
                                     // if only the hit-count is wrong. this can happen if the cluster-state is behind when the
@@ -134,7 +134,7 @@ public void run() {
             if (nonCriticalExceptions.isEmpty() == false) {
                 logger.info("non-critical exceptions: {}", nonCriticalExceptions);
                 for (int j = 0; j < 10; j++) {
-                    assertHitCount(client().prepareSearch(), numDocs);
+                    assertHitCount(prepareSearch(), numDocs);
                 }
             }
         }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomExceptionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomExceptionsIT.java
index 519e839c5d322..6f701e956788b 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomExceptionsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomExceptionsIT.java
@@ -131,8 +131,7 @@ public void testRandomExceptions() throws IOException, InterruptedException, Exe
                 int docToQuery = between(0, numDocs - 1);
                 int expectedResults = added[docToQuery] ? 1 : 0;
                 logger.info("Searching for [test:{}]", English.intToEnglish(docToQuery));
-                SearchResponse searchResponse = client().prepareSearch()
-                    .setQuery(QueryBuilders.matchQuery("test", English.intToEnglish(docToQuery)))
+                SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("test", English.intToEnglish(docToQuery)))
                     .setSize(expectedResults)
                     .get();
                 logger.info("Successful shards: [{}]  numShards: [{}]", searchResponse.getSuccessfulShards(), test.numPrimaries);
@@ -140,8 +139,7 @@ public void testRandomExceptions() throws IOException, InterruptedException, Exe
                     assertResultsAndLogOnFailure(expectedResults, searchResponse);
                 }
                 // check match all
-                searchResponse = client().prepareSearch()
-                    .setQuery(QueryBuilders.matchAllQuery())
+                searchResponse = prepareSearch().setQuery(QueryBuilders.matchAllQuery())
                     .setSize(numCreated)
                     .addSort("_id", SortOrder.ASC)
                     .get();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java
index 6279450e99f49..54ad0cd7e0cff 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/basic/SearchWithRandomIOExceptionsIT.java
@@ -154,8 +154,7 @@ public void testRandomDirectoryIOExceptions() throws IOException, InterruptedExc
                 int docToQuery = between(0, numDocs - 1);
                 int expectedResults = added[docToQuery] ? 1 : 0;
                 logger.info("Searching for [test:{}]", English.intToEnglish(docToQuery));
-                SearchResponse searchResponse = client().prepareSearch()
-                    .setQuery(QueryBuilders.matchQuery("test", English.intToEnglish(docToQuery)))
+                SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("test", English.intToEnglish(docToQuery)))
                     .setSize(expectedResults)
                     .get();
                 logger.info("Successful shards: [{}]  numShards: [{}]", searchResponse.getSuccessfulShards(), numShards.numPrimaries);
@@ -163,8 +162,7 @@ public void testRandomDirectoryIOExceptions() throws IOException, InterruptedExc
                     assertResultsAndLogOnFailure(expectedResults, searchResponse);
                 }
                 // check match all
-                searchResponse = client().prepareSearch()
-                    .setQuery(QueryBuilders.matchAllQuery())
+                searchResponse = prepareSearch().setQuery(QueryBuilders.matchAllQuery())
                     .setSize(numCreated + numInitialDocs)
                     .addSort("_uid", SortOrder.ASC)
                     .get();
@@ -192,7 +190,7 @@ public void testRandomDirectoryIOExceptions() throws IOException, InterruptedExc
             indicesAdmin().prepareClose("test").execute().get();
             indicesAdmin().prepareOpen("test").execute().get();
             ensureGreen();
-            assertHitCountAndNoFailures(client().prepareSearch().setQuery(QueryBuilders.matchQuery("test", "init")), numInitialDocs);
+            assertHitCountAndNoFailures(prepareSearch().setQuery(QueryBuilders.matchQuery("test", "init")), numInitialDocs);
         }
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java
index fff6386b643f6..b600098d82b33 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java
@@ -72,9 +72,9 @@ public void testPlugin() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse response = client().prepareSearch()
-            .setSource(new SearchSourceBuilder().ext(Collections.singletonList(new TermVectorsFetchBuilder("test"))))
-            .get();
+        SearchResponse response = prepareSearch().setSource(
+            new SearchSourceBuilder().ext(Collections.singletonList(new TermVectorsFetchBuilder("test")))
+        ).get();
         assertNoFailures(response);
         assertThat(
             ((Map) response.getHits().getAt(0).field("term_vectors_fetch").getValues().get(0)).get("i"),
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java
index 626417d1cf77a..00c5342577231 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java
@@ -812,13 +812,11 @@ public void testNestedSource() throws Exception {
 
         // the field name (comments.message) used for source filtering should be the same as when using that field for
         // other features (like in the query dsl or aggs) in order for consistency:
-        SearchResponse response = client().prepareSearch()
-            .setQuery(
-                nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.None).innerHit(
-                    new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.of(true, new String[] { "comments.message" }, null))
-                )
+        SearchResponse response = prepareSearch().setQuery(
+            nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.None).innerHit(
+                new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.of(true, new String[] { "comments.message" }, null))
             )
-            .get();
+        ).get();
         assertNoFailures(response);
         assertHitCount(response, 1);
 
@@ -834,9 +832,9 @@ public void testNestedSource() throws Exception {
             equalTo("fox ate rabbit x y z")
         );
 
-        response = client().prepareSearch()
-            .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder()))
-            .get();
+        response = prepareSearch().setQuery(
+            nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder())
+        ).get();
         assertNoFailures(response);
         assertHitCount(response, 1);
 
@@ -854,23 +852,18 @@ public void testNestedSource() throws Exception {
 
         // Source filter on a field that does not exist inside the nested document and just check that we do not fail and
         // return an empty _source:
-        response = client().prepareSearch()
-            .setQuery(
-                nestedQuery("comments", matchQuery("comments.message", "away"), ScoreMode.None).innerHit(
-                    new InnerHitBuilder().setFetchSourceContext(
-                        FetchSourceContext.of(true, new String[] { "comments.missing_field" }, null)
-                    )
-                )
+        response = prepareSearch().setQuery(
+            nestedQuery("comments", matchQuery("comments.message", "away"), ScoreMode.None).innerHit(
+                new InnerHitBuilder().setFetchSourceContext(FetchSourceContext.of(true, new String[] { "comments.missing_field" }, null))
             )
-            .get();
+        ).get();
         assertNoFailures(response);
         assertHitCount(response, 1);
         assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getTotalHits().value, equalTo(1L));
         assertThat(response.getHits().getAt(0).getInnerHits().get("comments").getAt(0).getSourceAsMap().size(), equalTo(0));
 
         // Check that inner hits contain _source even when it's disabled on the root request.
-        response = client().prepareSearch()
-            .setFetchSource(false)
+        response = prepareSearch().setFetchSource(false)
             .setQuery(nestedQuery("comments", matchQuery("comments.message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder()))
             .get();
         assertNoFailures(response);
@@ -887,13 +880,12 @@ public void testInnerHitsWithIgnoreUnmapped() throws Exception {
         refresh();
 
         assertSearchHitsWithoutFailures(
-            client().prepareSearch("index1", "index2")
-                .setQuery(
-                    boolQuery().should(
-                        nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true)
-                            .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true))
-                    ).should(termQuery("key", "value"))
-                ),
+            prepareSearch("index1", "index2").setQuery(
+                boolQuery().should(
+                    nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).ignoreUnmapped(true)
+                        .innerHit(new InnerHitBuilder().setIgnoreUnmapped(true))
+                ).should(termQuery("key", "value"))
+            ),
             "1",
             "3"
         );
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesIT.java
index c0bbbb48c09a2..d7347ef21328f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesIT.java
@@ -46,15 +46,12 @@ public void testSimpleMatchedQueryFromFilteredQuery() throws Exception {
         client().prepareIndex("test").setId("3").setSource("name", "test3", "number", 3).get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(matchAllQuery())
-                    .filter(
-                        boolQuery().should(rangeQuery("number").lt(2).queryName("test1"))
-                            .should(rangeQuery("number").gte(2).queryName("test2"))
-                    )
-            )
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            boolQuery().must(matchAllQuery())
+                .filter(
+                    boolQuery().should(rangeQuery("number").lt(2).queryName("test1")).should(rangeQuery("number").gte(2).queryName("test2"))
+                )
+        ).get();
         assertHitCount(searchResponse, 3L);
         for (SearchHit hit : searchResponse.getHits()) {
             if (hit.getId().equals("3") || hit.getId().equals("2")) {
@@ -70,11 +67,9 @@ public void testSimpleMatchedQueryFromFilteredQuery() throws Exception {
             }
         }
 
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().should(rangeQuery("number").lte(2).queryName("test1")).should(rangeQuery("number").gt(2).queryName("test2"))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().should(rangeQuery("number").lte(2).queryName("test1")).should(rangeQuery("number").gt(2).queryName("test2"))
+        ).get();
         assertHitCount(searchResponse, 3L);
         for (SearchHit hit : searchResponse.getHits()) {
             if (hit.getId().equals("1") || hit.getId().equals("2")) {
@@ -100,8 +95,7 @@ public void testSimpleMatchedQueryFromTopLevelFilter() throws Exception {
         client().prepareIndex("test").setId("3").setSource("name", "test").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setPostFilter(
                 boolQuery().should(termQuery("name", "test").queryName("name")).should(termQuery("title", "title1").queryName("title"))
             )
@@ -123,8 +117,7 @@ public void testSimpleMatchedQueryFromTopLevelFilter() throws Exception {
             }
         }
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setPostFilter(
                 boolQuery().should(termQuery("name", "test").queryName("name")).should(termQuery("title", "title1").queryName("title"))
             )
@@ -157,10 +150,9 @@ public void testSimpleMatchedQueryFromTopLevelFilterAndFilteredQuery() throws Ex
         client().prepareIndex("test").setId("3").setSource("name", "test", "title", "title3").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(boolQuery().must(matchAllQuery()).filter(termsQuery("title", "title1", "title2", "title3").queryName("title")))
-            .setPostFilter(termQuery("name", "test").queryName("name"))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            boolQuery().must(matchAllQuery()).filter(termsQuery("title", "title1", "title2", "title3").queryName("title"))
+        ).setPostFilter(termQuery("name", "test").queryName("name")).get();
         assertHitCount(searchResponse, 3L);
         for (SearchHit hit : searchResponse.getHits()) {
             if (hit.getId().equals("1") || hit.getId().equals("2") || hit.getId().equals("3")) {
@@ -174,8 +166,7 @@ public void testSimpleMatchedQueryFromTopLevelFilterAndFilteredQuery() throws Ex
             }
         }
 
-        searchResponse = client().prepareSearch()
-            .setQuery(termsQuery("title", "title1", "title2", "title3").queryName("title"))
+        searchResponse = prepareSearch().setQuery(termsQuery("title", "title1", "title2", "title3").queryName("title"))
             .setPostFilter(matchQuery("name", "test").queryName("name"))
             .get();
         assertHitCount(searchResponse, 3L);
@@ -199,9 +190,7 @@ public void testRegExpQuerySupportsName() {
         client().prepareIndex("test1").setId("1").setSource("title", "title1").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.regexpQuery("title", "title1").queryName("regex"))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.regexpQuery("title", "title1").queryName("regex")).get();
         assertHitCount(searchResponse, 1L);
 
         for (SearchHit hit : searchResponse.getHits()) {
@@ -222,9 +211,7 @@ public void testPrefixQuerySupportsName() {
         client().prepareIndex("test1").setId("1").setSource("title", "title1").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.prefixQuery("title", "title").queryName("prefix"))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.prefixQuery("title", "title").queryName("prefix")).get();
         assertHitCount(searchResponse, 1L);
 
         for (SearchHit hit : searchResponse.getHits()) {
@@ -245,9 +232,7 @@ public void testFuzzyQuerySupportsName() {
         client().prepareIndex("test1").setId("1").setSource("title", "title1").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.fuzzyQuery("title", "titel1").queryName("fuzzy"))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.fuzzyQuery("title", "titel1").queryName("fuzzy")).get();
         assertHitCount(searchResponse, 1L);
 
         for (SearchHit hit : searchResponse.getHits()) {
@@ -268,9 +253,7 @@ public void testWildcardQuerySupportsName() {
         client().prepareIndex("test1").setId("1").setSource("title", "title1").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.wildcardQuery("title", "titl*").queryName("wildcard"))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.wildcardQuery("title", "titl*").queryName("wildcard")).get();
         assertHitCount(searchResponse, 1L);
 
         for (SearchHit hit : searchResponse.getHits()) {
@@ -291,9 +274,9 @@ public void testSpanFirstQuerySupportsName() {
         client().prepareIndex("test1").setId("1").setSource("title", "title1 title2").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.spanFirstQuery(QueryBuilders.spanTermQuery("title", "title1"), 10).queryName("span"))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            QueryBuilders.spanFirstQuery(QueryBuilders.spanTermQuery("title", "title1"), 10).queryName("span")
+        ).get();
         assertHitCount(searchResponse, 1L);
 
         for (SearchHit hit : searchResponse.getHits()) {
@@ -321,13 +304,11 @@ public void testMatchedWithShould() throws Exception {
         // Execute search at least two times to load it in cache
         int iter = scaledRandomIntBetween(2, 10);
         for (int i = 0; i < iter; i++) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(
-                    boolQuery().minimumShouldMatch(1)
-                        .should(queryStringQuery("dolor").queryName("dolor"))
-                        .should(queryStringQuery("elit").queryName("elit"))
-                )
-                .get();
+            SearchResponse searchResponse = prepareSearch().setQuery(
+                boolQuery().minimumShouldMatch(1)
+                    .should(queryStringQuery("dolor").queryName("dolor"))
+                    .should(queryStringQuery("elit").queryName("elit"))
+            ).get();
 
             assertHitCount(searchResponse, 2L);
             for (SearchHit hit : searchResponse.getHits()) {
@@ -359,7 +340,7 @@ public void testMatchedWithWrapperQuery() throws Exception {
         BytesReference termBytes = XContentHelper.toXContent(termQueryBuilder, XContentType.JSON, false);
         QueryBuilder[] queries = new QueryBuilder[] { wrapperQuery(matchBytes), constantScoreQuery(wrapperQuery(termBytes)) };
         for (QueryBuilder query : queries) {
-            SearchResponse searchResponse = client().prepareSearch().setQuery(query).get();
+            SearchResponse searchResponse = prepareSearch().setQuery(query).get();
             assertHitCount(searchResponse, 1L);
             SearchHit hit = searchResponse.getHits().getAt(0);
             assertThat(hit.getMatchedQueriesAndScores().size(), equalTo(1));
@@ -376,8 +357,7 @@ public void testMatchedWithRescoreQuery() throws Exception {
         client().prepareIndex("test").setId("2").setSource("content", "hello you").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(new MatchAllQueryBuilder().queryName("all"))
+        SearchResponse searchResponse = prepareSearch().setQuery(new MatchAllQueryBuilder().queryName("all"))
             .setRescorer(
                 new QueryRescorerBuilder(new MatchPhraseQueryBuilder("content", "hello you").boost(10).queryName("rescore_phrase"))
             )
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java
index dc1b14cd5bfd1..89d001df58d12 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java
@@ -189,33 +189,32 @@ public void testStoredFields() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("field1").get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("field1").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().size(), equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field1").getValue().toString(), equalTo("value1"));
 
         // field2 is not stored, check that it is not extracted from source.
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("field2").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("field2").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().size(), equalTo(0));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field2"), nullValue());
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("field3").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("field3").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().size(), equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field3").getValue().toString(), equalTo("value3"));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("*3").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("*3").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().size(), equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field3").getValue().toString(), equalTo("value3"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addStoredField("*3")
             .addStoredField("field1")
             .addStoredField("field2")
@@ -226,20 +225,20 @@ public void testStoredFields() throws Exception {
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field3").getValue().toString(), equalTo("value3"));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field1").getValue().toString(), equalTo("value1"));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("field*").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("field*").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().size(), equalTo(2));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field3").getValue().toString(), equalTo("value3"));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field1").getValue().toString(), equalTo("value1"));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("f*3").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("f*3").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().size(), equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field3").getValue().toString(), equalTo("value3"));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("*").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("*").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getSourceAsMap(), nullValue());
@@ -247,7 +246,7 @@ public void testStoredFields() throws Exception {
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field1").getValue().toString(), equalTo("value1"));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("field3").getValue().toString(), equalTo("value3"));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("*").addStoredField("_source").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("*").addStoredField("_source").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
         assertThat(searchResponse.getHits().getAt(0).getSourceAsMap(), notNullValue());
@@ -298,8 +297,7 @@ public void testScriptDocAndFields() throws Exception {
         indicesAdmin().refresh(new RefreshRequest()).actionGet();
 
         logger.info("running doc['num1'].value");
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .addSort("num1", SortOrder.ASC)
             .addScriptField("sNum1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value", Collections.emptyMap()))
             .addScriptField(
@@ -336,8 +334,7 @@ public void testScriptDocAndFields() throws Exception {
         assertThat(response.getHits().getAt(2).getFields().get("date1").getValues().get(0), equalTo(120000L));
 
         logger.info("running doc['num1'].value * factor");
-        response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        response = prepareSearch().setQuery(matchAllQuery())
             .addSort("num1", SortOrder.ASC)
             .addScriptField(
                 "sNum1",
@@ -387,8 +384,7 @@ public void testScriptFieldWithNanos() throws Exception {
             client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject().field("date", date).endObject())
         );
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .addSort("date", SortOrder.ASC)
             .addScriptField(
                 "date1",
@@ -428,8 +424,7 @@ public void testIdBasedScriptFields() throws Exception {
         }
         indexRandom(true, indexRequestBuilders);
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .addSort("num1", SortOrder.ASC)
             .setSize(numDocs)
             .addScriptField("id", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_fields._id.value", Collections.emptyMap()))
@@ -472,8 +467,7 @@ public void testScriptFieldUsingSource() throws Exception {
             .get();
         indicesAdmin().refresh(new RefreshRequest()).actionGet();
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("s_obj1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_source.obj1", Collections.emptyMap()))
             .addScriptField(
                 "s_obj1_test",
@@ -513,8 +507,7 @@ public void testScriptFieldUsingSource() throws Exception {
     public void testScriptFieldsForNullReturn() throws Exception {
         client().prepareIndex("test").setId("1").setSource("foo", "bar").setRefreshPolicy("true").get();
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("test_script_1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "return null", Collections.emptyMap()))
             .get();
 
@@ -631,8 +624,7 @@ public void testStoredFieldsWithoutSource() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addStoredField("byte_field")
             .addStoredField("short_field")
             .addStoredField("integer_field")
@@ -852,8 +844,7 @@ public void testDocValueFields() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchRequestBuilder builder = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchRequestBuilder builder = prepareSearch().setQuery(matchAllQuery())
             .addDocValueField("text_field")
             .addDocValueField("keyword_field")
             .addDocValueField("byte_field")
@@ -910,7 +901,7 @@ public void testDocValueFields() throws Exception {
         assertThat(searchResponse.getHits().getAt(0).getFields().get("binary_field").getValues(), equalTo(List.of("KmQ=")));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("ip_field").getValues(), equalTo(List.of("::1")));
 
-        builder = client().prepareSearch().setQuery(matchAllQuery()).addDocValueField("*field");
+        builder = prepareSearch().setQuery(matchAllQuery()).addDocValueField("*field");
         searchResponse = builder.get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
@@ -952,8 +943,7 @@ public void testDocValueFields() throws Exception {
         assertThat(searchResponse.getHits().getAt(0).getFields().get("binary_field").getValues(), equalTo(List.of("KmQ=")));
         assertThat(searchResponse.getHits().getAt(0).getFields().get("ip_field").getValues(), equalTo(List.of("::1")));
 
-        builder = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        builder = prepareSearch().setQuery(matchAllQuery())
             .addDocValueField("byte_field", "#.0")
             .addDocValueField("short_field", "#.0")
             .addDocValueField("integer_field", "#.0")
@@ -1081,8 +1071,7 @@ public void testDocValueFieldsWithFieldAlias() throws Exception {
         indexDoc("test", "1", "text_field", "foo", "date_field", formatter.format(date));
         refresh("test");
 
-        SearchRequestBuilder builder = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchRequestBuilder builder = prepareSearch().setQuery(matchAllQuery())
             .addDocValueField("text_field_alias")
             .addDocValueField("date_field_alias")
             .addDocValueField("date_field");
@@ -1144,10 +1133,7 @@ public void testWildcardDocValueFieldsWithFieldAlias() throws Exception {
         indexDoc("test", "1", "text_field", "foo", "date_field", formatter.format(date));
         refresh("test");
 
-        SearchRequestBuilder builder = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .addDocValueField("*alias")
-            .addDocValueField("date_field");
+        SearchRequestBuilder builder = prepareSearch().setQuery(matchAllQuery()).addDocValueField("*alias").addDocValueField("date_field");
         SearchResponse searchResponse = builder.get();
 
         assertNoFailures(searchResponse);
@@ -1199,8 +1185,7 @@ public void testStoredFieldsWithFieldAlias() throws Exception {
         indexDoc("test", "1", "field1", "value1", "field2", "value2");
         refresh("test");
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addStoredField("field1-alias")
             .addStoredField("field2-alias")
             .get();
@@ -1243,7 +1228,7 @@ public void testWildcardStoredFieldsWithFieldAlias() throws Exception {
         indexDoc("test", "1", "field1", "value1", "field2", "value2");
         refresh("test");
 
-        SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addStoredField("field*").get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).addStoredField("field*").get();
         assertHitCount(searchResponse, 1L);
 
         SearchHit hit = searchResponse.getHits().getAt(0);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java
index ecb4be2685ab0..14df03bb86e8d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/QueryRescorerIT.java
@@ -72,8 +72,7 @@ public void testEnforceWindowSize() {
 
         int numShards = getNumShards("test").numPrimaries;
         for (int j = 0; j < iters; j++) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(QueryBuilders.matchAllQuery())
+            SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.matchAllQuery())
                 .setRescorer(
                     new QueryRescorerBuilder(
                         functionScoreQuery(matchAllQuery(), ScoreFunctionBuilders.weightFactorFunction(100)).boostMode(
@@ -122,8 +121,9 @@ public void testRescorePhrase() throws Exception {
             .setSource("field1", "quick huge brown", "field2", "the quick lazy huge brown fox jumps over the tree")
             .get();
         refresh();
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR)
+        )
             .setRescorer(
                 new QueryRescorerBuilder(matchPhraseQuery("field1", "quick brown").slop(2).boost(4.0f)).setRescoreQueryWeight(2),
                 5
@@ -136,8 +136,7 @@ public void testRescorePhrase() throws Exception {
         assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("3"));
         assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("2"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
+        searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
             .setRescorer(new QueryRescorerBuilder(matchPhraseQuery("field1", "the quick brown").slop(3)), 5)
             .get();
 
@@ -146,8 +145,7 @@ public void testRescorePhrase() throws Exception {
         assertSecondHit(searchResponse, hasId("2"));
         assertThirdHit(searchResponse, hasId("3"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
+        searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
             .setRescorer(new QueryRescorerBuilder(matchPhraseQuery("field1", "the quick brown")), 5)
             .get();
 
@@ -191,8 +189,9 @@ public void testMoreDocs() throws Exception {
         client().prepareIndex("test").setId("11").setSource("field1", "2st street boston massachusetts").get();
         client().prepareIndex("test").setId("12").setSource("field1", "3st street boston massachusetts").get();
         indicesAdmin().prepareRefresh("test").get();
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "lexington avenue massachusetts").operator(Operator.OR))
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            QueryBuilders.matchQuery("field1", "lexington avenue massachusetts").operator(Operator.OR)
+        )
             .setFrom(0)
             .setSize(5)
             .setRescorer(
@@ -208,8 +207,9 @@ public void testMoreDocs() throws Exception {
         assertSecondHit(searchResponse, hasId("6"));
         assertThirdHit(searchResponse, hasId("3"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "lexington avenue massachusetts").operator(Operator.OR))
+        searchResponse = prepareSearch().setQuery(
+            QueryBuilders.matchQuery("field1", "lexington avenue massachusetts").operator(Operator.OR)
+        )
             .setFrom(0)
             .setSize(5)
             .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
@@ -228,8 +228,9 @@ public void testMoreDocs() throws Exception {
         assertThirdHit(searchResponse, hasId("3"));
 
         // Make sure non-zero from works:
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "lexington avenue massachusetts").operator(Operator.OR))
+        searchResponse = prepareSearch().setQuery(
+            QueryBuilders.matchQuery("field1", "lexington avenue massachusetts").operator(Operator.OR)
+        )
             .setFrom(2)
             .setSize(5)
             .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
@@ -271,8 +272,7 @@ public void testSmallRescoreWindow() throws Exception {
         client().prepareIndex("test").setId("2").setSource("field1", "lexington avenue boston massachusetts road").get();
         indicesAdmin().prepareRefresh("test").get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "massachusetts"))
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "massachusetts"))
             .setFrom(0)
             .setSize(5)
             .get();
@@ -285,8 +285,7 @@ public void testSmallRescoreWindow() throws Exception {
         assertFourthHit(searchResponse, hasId("2"));
 
         // Now, rescore only top 2 hits w/ proximity:
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "massachusetts"))
+        searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "massachusetts"))
             .setFrom(0)
             .setSize(5)
             .setRescorer(
@@ -305,8 +304,7 @@ public void testSmallRescoreWindow() throws Exception {
         assertFourthHit(searchResponse, hasId("2"));
 
         // Now, rescore only top 3 hits w/ proximity:
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "massachusetts"))
+        searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "massachusetts"))
             .setFrom(0)
             .setSize(5)
             .setRescorer(
@@ -351,8 +349,7 @@ public void testRescorerMadeScoresWorse() throws Exception {
         client().prepareIndex("test").setId("2").setSource("field1", "lexington avenue boston massachusetts road").get();
         indicesAdmin().prepareRefresh("test").get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "massachusetts").operator(Operator.OR))
+        SearchResponse searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "massachusetts").operator(Operator.OR))
             .setFrom(0)
             .setSize(5)
             .get();
@@ -365,8 +362,7 @@ public void testRescorerMadeScoresWorse() throws Exception {
         assertFourthHit(searchResponse, hasId("2"));
 
         // Now, penalizing rescore (nothing matches the rescore query):
-        searchResponse = client().prepareSearch()
-            .setQuery(QueryBuilders.matchQuery("field1", "massachusetts").operator(Operator.OR))
+        searchResponse = prepareSearch().setQuery(QueryBuilders.matchQuery("field1", "massachusetts").operator(Operator.OR))
             .setFrom(0)
             .setSize(5)
             .setRescorer(
@@ -434,8 +430,7 @@ public void testEquivalence() throws Exception {
             int rescoreWindow = between(1, 3) * resultSize;
             String intToEnglish = English.intToEnglish(between(0, numDocs - 1));
             String query = intToEnglish.split(" ")[0];
-            SearchResponse rescored = client().prepareSearch()
-                .setSearchType(SearchType.QUERY_THEN_FETCH)
+            SearchResponse rescored = prepareSearch().setSearchType(SearchType.QUERY_THEN_FETCH)
                 .setPreference("test") // ensure we hit the same shards for tie-breaking
                 .setQuery(QueryBuilders.matchQuery("field1", query).operator(Operator.OR))
                 .setFrom(0)
@@ -448,8 +443,7 @@ public void testEquivalence() throws Exception {
                 )
                 .get();
 
-            SearchResponse plain = client().prepareSearch()
-                .setSearchType(SearchType.QUERY_THEN_FETCH)
+            SearchResponse plain = prepareSearch().setSearchType(SearchType.QUERY_THEN_FETCH)
                 .setPreference("test") // ensure we hit the same shards for tie-breaking
                 .setQuery(QueryBuilders.matchQuery("field1", query).operator(Operator.OR))
                 .setFrom(0)
@@ -459,8 +453,7 @@ public void testEquivalence() throws Exception {
             // check equivalence
             assertEquivalent(query, plain, rescored);
 
-            rescored = client().prepareSearch()
-                .setSearchType(SearchType.QUERY_THEN_FETCH)
+            rescored = prepareSearch().setSearchType(SearchType.QUERY_THEN_FETCH)
                 .setPreference("test") // ensure we hit the same shards for tie-breaking
                 .setQuery(QueryBuilders.matchQuery("field1", query).operator(Operator.OR))
                 .setFrom(0)
@@ -502,8 +495,7 @@ public void testExplain() throws Exception {
         refresh();
 
         {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
+            SearchResponse searchResponse = prepareSearch().setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                 .setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
                 .setRescorer(
                     new QueryRescorerBuilder(matchPhraseQuery("field1", "the quick brown").slop(2).boost(4.0f)).setQueryWeight(0.5f)
@@ -549,8 +541,7 @@ public void testExplain() throws Exception {
                 innerRescoreQuery.setScoreMode(QueryRescoreMode.fromString(scoreModes[innerMode]));
             }
 
-            SearchResponse searchResponse = client().prepareSearch()
-                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
+            SearchResponse searchResponse = prepareSearch().setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                 .setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
                 .setRescorer(innerRescoreQuery, 5)
                 .setExplain(true)
@@ -573,8 +564,7 @@ public void testExplain() throws Exception {
                     outerRescoreQuery.setScoreMode(QueryRescoreMode.fromString(scoreModes[outerMode]));
                 }
 
-                searchResponse = client().prepareSearch()
-                    .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
+                searchResponse = prepareSearch().setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                     .setQuery(QueryBuilders.matchQuery("field1", "the quick brown").operator(Operator.OR))
                     .addRescorer(innerRescoreQuery, 5)
                     .addRescorer(outerRescoreQuery.windowSize(10))
@@ -628,8 +618,7 @@ public void testScoring() throws Exception {
                     rescoreQuery.setScoreMode(QueryRescoreMode.fromString(scoreMode));
                 }
 
-                SearchResponse rescored = client().prepareSearch()
-                    .setPreference("test") // ensure we hit the same shards for tie-breaking
+                SearchResponse rescored = prepareSearch().setPreference("test") // ensure we hit the same shards for tie-breaking
                     .setFrom(0)
                     .setSize(10)
                     .setQuery(query)
@@ -697,7 +686,7 @@ public void testMultipleRescores() throws Exception {
         ).setScoreMode(QueryRescoreMode.Total);
 
         // First set the rescore window large enough that both rescores take effect
-        SearchRequestBuilder request = client().prepareSearch();
+        SearchRequestBuilder request = prepareSearch();
         request.addRescorer(eightIsGreat, numDocs).addRescorer(sevenIsBetter, numDocs);
         SearchResponse response = request.get();
         assertFirstHit(response, hasId("7"));
@@ -771,7 +760,7 @@ public void testFromSize() throws Exception {
         }
         refresh();
 
-        SearchRequestBuilder request = client().prepareSearch();
+        SearchRequestBuilder request = prepareSearch();
         request.setQuery(QueryBuilders.termQuery("text", "hello"));
         request.setFrom(1);
         request.setSize(4);
@@ -789,8 +778,7 @@ public void testRescorePhaseWithInvalidSort() throws Exception {
 
         Exception exc = expectThrows(
             Exception.class,
-            () -> client().prepareSearch()
-                .addSort(SortBuilders.fieldSort("number"))
+            () -> prepareSearch().addSort(SortBuilders.fieldSort("number"))
                 .setTrackScores(true)
                 .addRescorer(new QueryRescorerBuilder(matchAllQuery()), 50)
                 .get()
@@ -800,8 +788,7 @@ public void testRescorePhaseWithInvalidSort() throws Exception {
 
         exc = expectThrows(
             Exception.class,
-            () -> client().prepareSearch()
-                .addSort(SortBuilders.fieldSort("number"))
+            () -> prepareSearch().addSort(SortBuilders.fieldSort("number"))
                 .addSort(SortBuilders.scoreSort())
                 .setTrackScores(true)
                 .addRescorer(new QueryRescorerBuilder(matchAllQuery()), 50)
@@ -810,8 +797,7 @@ public void testRescorePhaseWithInvalidSort() throws Exception {
         assertNotNull(exc.getCause());
         assertThat(exc.getCause().getMessage(), containsString("Cannot use [sort] option in conjunction with [rescore]."));
 
-        SearchResponse resp = client().prepareSearch()
-            .addSort(SortBuilders.scoreSort())
+        SearchResponse resp = prepareSearch().addSort(SortBuilders.scoreSort())
             .setTrackScores(true)
             .addRescorer(new QueryRescorerBuilder(matchAllQuery()).setRescoreQueryWeight(100.0f), 50)
             .get();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java
index a9ab85bf79173..ef8ffcf0d806a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/RandomScoreFunctionIT.java
@@ -99,8 +99,7 @@ public void testConsistentHitsWithSameSeed() throws Exception {
             int innerIters = scaledRandomIntBetween(2, 5);
             SearchHit[] hits = null;
             for (int i = 0; i < innerIters; i++) {
-                SearchResponse searchResponse = client().prepareSearch()
-                    .setSize(docCount) // get all docs otherwise we are prone to tie-breaking
+                SearchResponse searchResponse = prepareSearch().setSize(docCount) // get all docs otherwise we are prone to tie-breaking
                     .setPreference(preference)
                     .setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(seed).setField("foo")))
                     .get();
@@ -281,8 +280,7 @@ public void testScoreRange() throws Exception {
         refresh();
         int iters = scaledRandomIntBetween(10, 20);
         for (int i = 0; i < iters; ++i) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(functionScoreQuery(matchAllQuery(), randomFunction()))
+            SearchResponse searchResponse = prepareSearch().setQuery(functionScoreQuery(matchAllQuery(), randomFunction()))
                 .setSize(docCount)
                 .get();
 
@@ -303,20 +301,17 @@ public void testSeeds() throws Exception {
         flushAndRefresh();
 
         assertNoFailures(
-            client().prepareSearch()
-                .setSize(docCount) // get all docs otherwise we are prone to tie-breaking
+            prepareSearch().setSize(docCount) // get all docs otherwise we are prone to tie-breaking
                 .setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(randomInt()).setField(SeqNoFieldMapper.NAME)))
         );
 
         assertNoFailures(
-            client().prepareSearch()
-                .setSize(docCount) // get all docs otherwise we are prone to tie-breaking
+            prepareSearch().setSize(docCount) // get all docs otherwise we are prone to tie-breaking
                 .setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(randomLong()).setField(SeqNoFieldMapper.NAME)))
         );
 
         assertNoFailures(
-            client().prepareSearch()
-                .setSize(docCount) // get all docs otherwise we are prone to tie-breaking
+            prepareSearch().setSize(docCount) // get all docs otherwise we are prone to tie-breaking
                 .setQuery(
                     functionScoreQuery(
                         matchAllQuery(),
@@ -343,8 +338,7 @@ public void checkDistribution() throws Exception {
 
         for (int i = 0; i < count; i++) {
 
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(functionScoreQuery(matchAllQuery(), new RandomScoreFunctionBuilder()))
+            SearchResponse searchResponse = prepareSearch().setQuery(functionScoreQuery(matchAllQuery(), new RandomScoreFunctionBuilder()))
                 .get();
 
             matrix[Integer.valueOf(searchResponse.getHits().getAt(0).getId())]++;
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java
index 7d3e8c7be03c4..d5bbf767d1046 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/geo/GeoDistanceIT.java
@@ -126,24 +126,21 @@ public void testDistanceScript() throws Exception {
         refresh();
 
         // Test doc['location'].arcDistance(lat, lon)
-        SearchResponse searchResponse1 = client().prepareSearch()
-            .addStoredField("_source")
+        SearchResponse searchResponse1 = prepareSearch().addStoredField("_source")
             .addScriptField("distance", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "arcDistance", Collections.emptyMap()))
             .get();
         Double resultDistance1 = searchResponse1.getHits().getHits()[0].getFields().get("distance").getValue();
         assertThat(resultDistance1, closeTo(GeoUtils.arcDistance(src_lat, src_lon, tgt_lat, tgt_lon), 0.01d));
 
         // Test doc['location'].planeDistance(lat, lon)
-        SearchResponse searchResponse2 = client().prepareSearch()
-            .addStoredField("_source")
+        SearchResponse searchResponse2 = prepareSearch().addStoredField("_source")
             .addScriptField("distance", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "planeDistance", Collections.emptyMap()))
             .get();
         Double resultDistance2 = searchResponse2.getHits().getHits()[0].getFields().get("distance").getValue();
         assertThat(resultDistance2, closeTo(GeoUtils.planeDistance(src_lat, src_lon, tgt_lat, tgt_lon), 0.01d));
 
         // Test doc['location'].geohashDistance(lat, lon)
-        SearchResponse searchResponse4 = client().prepareSearch()
-            .addStoredField("_source")
+        SearchResponse searchResponse4 = prepareSearch().addStoredField("_source")
             .addScriptField("distance", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "geohashDistance", Collections.emptyMap()))
             .get();
         Double resultDistance4 = searchResponse4.getHits().getHits()[0].getFields().get("distance").getValue();
@@ -156,8 +153,7 @@ public void testDistanceScript() throws Exception {
         );
 
         // Test doc['location'].arcDistance(lat, lon + 360)/1000d
-        SearchResponse searchResponse5 = client().prepareSearch()
-            .addStoredField("_source")
+        SearchResponse searchResponse5 = prepareSearch().addStoredField("_source")
             .addScriptField(
                 "distance",
                 new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "arcDistance(lat, lon + 360)/1000d", Collections.emptyMap())
@@ -167,8 +163,7 @@ public void testDistanceScript() throws Exception {
         assertThat(resultArcDistance5, closeTo(GeoUtils.arcDistance(src_lat, src_lon, tgt_lat, tgt_lon) / 1000d, 0.01d));
 
         // Test doc['location'].arcDistance(lat + 360, lon)/1000d
-        SearchResponse searchResponse6 = client().prepareSearch()
-            .addStoredField("_source")
+        SearchResponse searchResponse6 = prepareSearch().addStoredField("_source")
             .addScriptField(
                 "distance",
                 new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "arcDistance(lat + 360, lon)/1000d", Collections.emptyMap())
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java
index 2deedccec4192..736796d73f164 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/nested/SimpleNestedIT.java
@@ -788,8 +788,7 @@ public void testNestedSortWithMultiLevelFiltering() throws Exception {
         refresh();
 
         // access id = 1, read, max value, asc, should use matt and shay
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("acl.operation.user.username")
                     .setNestedSort(
@@ -812,8 +811,7 @@ public void testNestedSortWithMultiLevelFiltering() throws Exception {
         assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("shay"));
 
         // access id = 1, read, min value, asc, should now use adrien and luca
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("acl.operation.user.username")
                     .setNestedSort(
@@ -836,8 +834,7 @@ public void testNestedSortWithMultiLevelFiltering() throws Exception {
         assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("luca"));
 
         // execute, by matt or luca, by user id, sort missing first
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("acl.operation.user.id")
                     .setNestedSort(
@@ -863,8 +860,7 @@ public void testNestedSortWithMultiLevelFiltering() throws Exception {
         assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("1"));
 
         // execute, by matt or luca, by username, sort missing last (default)
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("acl.operation.user.username")
                     .setNestedSort(
@@ -948,8 +944,7 @@ public void testLeakingSortValues() throws Exception {
 
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(termQuery("_id", 2))
+        SearchResponse searchResponse = prepareSearch().setQuery(termQuery("_id", 2))
             .addSort(
                 SortBuilders.fieldSort("nested1.nested2.sortVal")
                     .setNestedSort(
@@ -1131,8 +1126,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         refresh();
 
         // Without nested filter
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child"))
@@ -1151,8 +1145,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         // With nested filter
         NestedSortBuilder nestedSort = new NestedSortBuilder("parent.child");
         nestedSort.setFilter(QueryBuilders.termQuery("parent.child.filter", true));
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedSort(nestedSort).order(SortOrder.ASC))
             .get();
         assertHitCount(searchResponse, 3);
@@ -1165,8 +1158,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
 
         // Nested path should be automatically detected, expect same results as above search request
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedSort(nestedSort).order(SortOrder.ASC))
             .get();
 
@@ -1180,8 +1172,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
 
         nestedSort.setFilter(QueryBuilders.termQuery("parent.filter", false));
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("parent.parent_values").setNestedSort(nestedSort).order(SortOrder.ASC))
             .get();
 
@@ -1194,8 +1185,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(
@@ -1217,8 +1207,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("6"));
 
         // Check if closest nested type is resolved
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_obj.value")
                     .setNestedSort(new NestedSortBuilder("parent.child").setFilter(QueryBuilders.termQuery("parent.child.filter", true)))
@@ -1236,8 +1225,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
 
         // Sort mode: sum
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child"))
@@ -1255,8 +1243,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1"));
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("11"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child"))
@@ -1275,8 +1262,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("2"));
 
         // Sort mode: sum with filter
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child").setFilter(QueryBuilders.termQuery("parent.child.filter", true)))
@@ -1295,8 +1281,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
 
         // Sort mode: avg
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child"))
@@ -1314,8 +1299,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1"));
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child"))
@@ -1334,8 +1318,7 @@ public void testSortNestedWithNestedFilter() throws Exception {
         assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("1"));
 
         // Sort mode: avg with filter
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("parent.child.child_values")
                     .setNestedSort(new NestedSortBuilder("parent.child").setFilter(QueryBuilders.termQuery("parent.child.filter", true)))
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/dfs/DfsProfilerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/dfs/DfsProfilerIT.java
index fce19a316b34a..f7b2b0f4443d3 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/dfs/DfsProfilerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/dfs/DfsProfilerIT.java
@@ -67,8 +67,7 @@ public void testProfileDfs() throws Exception {
         for (int i = 0; i < iters; i++) {
             QueryBuilder q = randomQueryBuilder(List.of(textField), List.of(numericField), numDocs, 3);
             logger.info("Query: {}", q);
-            SearchResponse resp = client().prepareSearch()
-                .setQuery(q)
+            SearchResponse resp = prepareSearch().setQuery(q)
                 .setTrackTotalHits(true)
                 .setProfile(true)
                 .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java
index 32334deb268b4..e7b02faede9b1 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/query/QueryProfilerIT.java
@@ -63,8 +63,7 @@ public void testProfileQuery() throws Exception {
             QueryBuilder q = randomQueryBuilder(stringFields, numericFields, numDocs, 3);
             logger.info("Query: {}", q);
 
-            SearchResponse resp = client().prepareSearch()
-                .setQuery(q)
+            SearchResponse resp = prepareSearch().setQuery(q)
                 .setTrackTotalHits(true)
                 .setProfile(true)
                 .setSearchType(SearchType.QUERY_THEN_FETCH)
@@ -186,7 +185,7 @@ public void testSimpleMatch() throws Exception {
 
         QueryBuilder q = QueryBuilders.matchQuery("field1", "one");
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         Map p = resp.getProfileResults();
         assertNotNull(p);
@@ -227,7 +226,7 @@ public void testBool() throws Exception {
             .must(QueryBuilders.matchQuery("field1", "one"))
             .must(QueryBuilders.matchQuery("field1", "two"));
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         Map p = resp.getProfileResults();
         assertNotNull(p);
@@ -288,7 +287,7 @@ public void testEmptyBool() throws Exception {
         QueryBuilder q = QueryBuilders.boolQuery();
         logger.info("Query: {}", q);
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         assertNotNull("Profile response element should not be null", resp.getProfileResults());
         assertThat("Profile response should not be an empty array", resp.getProfileResults().size(), not(0));
@@ -333,7 +332,7 @@ public void testCollapsingBool() throws Exception {
 
         logger.info("Query: {}", q);
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         assertNotNull("Profile response element should not be null", resp.getProfileResults());
         assertThat("Profile response should not be an empty array", resp.getProfileResults().size(), not(0));
@@ -373,7 +372,7 @@ public void testBoosting() throws Exception {
             .negativeBoost(randomFloat());
         logger.info("Query: {}", q);
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         assertNotNull("Profile response element should not be null", resp.getProfileResults());
         assertThat("Profile response should not be an empty array", resp.getProfileResults().size(), not(0));
@@ -413,7 +412,7 @@ public void testDisMaxRange() throws Exception {
             .add(QueryBuilders.rangeQuery("field2").from(null).to(73).includeLower(true).includeUpper(true));
         logger.info("Query: {}", q);
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         assertNotNull("Profile response element should not be null", resp.getProfileResults());
         assertThat("Profile response should not be an empty array", resp.getProfileResults().size(), not(0));
@@ -452,7 +451,7 @@ public void testRange() throws Exception {
 
         logger.info("Query: {}", q.toString());
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(true).setSearchType(SearchType.QUERY_THEN_FETCH).get();
 
         assertNotNull("Profile response element should not be null", resp.getProfileResults());
         assertThat("Profile response should not be an empty array", resp.getProfileResults().size(), not(0));
@@ -493,8 +492,7 @@ public void testPhrase() throws Exception {
 
         logger.info("Query: {}", q);
 
-        SearchResponse resp = client().prepareSearch()
-            .setQuery(q)
+        SearchResponse resp = prepareSearch().setQuery(q)
             .setIndices("test")
             .setProfile(true)
             .setSearchType(SearchType.QUERY_THEN_FETCH)
@@ -545,7 +543,7 @@ public void testNoProfile() throws Exception {
 
         logger.info("Query: {}", q);
 
-        SearchResponse resp = client().prepareSearch().setQuery(q).setProfile(false).get();
+        SearchResponse resp = prepareSearch().setQuery(q).setProfile(false).get();
         assertThat("Profile response element should be an empty map", resp.getProfileResults().size(), equalTo(0));
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java
index 3b19d55efef71..1d13bea9e0639 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/routing/SearchPreferenceIT.java
@@ -67,10 +67,10 @@ public void testStopOneNodePreferenceWithRedState() throws IOException {
             "_prefer_nodes:somenode,server2" };
         for (String pref : preferences) {
             logger.info("--> Testing out preference={}", pref);
-            SearchResponse searchResponse = client().prepareSearch().setSize(0).setPreference(pref).get();
+            SearchResponse searchResponse = prepareSearch().setSize(0).setPreference(pref).get();
             assertThat(RestStatus.OK, equalTo(searchResponse.status()));
             assertThat(pref, searchResponse.getFailedShards(), greaterThanOrEqualTo(0));
-            searchResponse = client().prepareSearch().setPreference(pref).get();
+            searchResponse = prepareSearch().setPreference(pref).get();
             assertThat(RestStatus.OK, equalTo(searchResponse.status()));
             assertThat(pref, searchResponse.getFailedShards(), greaterThanOrEqualTo(0));
         }
@@ -112,13 +112,13 @@ public void testSimplePreference() {
         client().prepareIndex("test").setSource("field1", "value1").get();
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_local").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setPreference("_local").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("1234").get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setPreference("1234").get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
     }
 
@@ -127,7 +127,7 @@ public void testThatSpecifyingNonExistingNodesReturnsUsefulError() {
         ensureGreen();
 
         try {
-            client().prepareSearch().setQuery(matchAllQuery()).setPreference("_only_nodes:DOES-NOT-EXIST").get();
+            prepareSearch().setQuery(matchAllQuery()).setPreference("_only_nodes:DOES-NOT-EXIST").get();
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException e) {
             assertThat(e, hasToString(containsString("no data nodes with criteria [DOES-NOT-EXIST] found for shard: [test][")));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java
index 294768a319b09..dc460468db605 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scriptfilter/ScriptQuerySearchIT.java
@@ -114,10 +114,9 @@ public void testCustomScriptBinaryField() throws Exception {
         flush();
         refresh();
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(
-                scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['binaryData'].get(0).length > 15", emptyMap()))
-            )
+        SearchResponse response = prepareSearch().setQuery(
+            scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['binaryData'].get(0).length > 15", emptyMap()))
+        )
             .addScriptField(
                 "sbinaryData",
                 new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['binaryData'].get(0).length", emptyMap())
@@ -169,8 +168,9 @@ public void testCustomScriptBoost() throws Exception {
         refresh();
 
         logger.info("running doc['num1'].value > 1");
-        SearchResponse response = client().prepareSearch()
-            .setQuery(scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > 1", Collections.emptyMap())))
+        SearchResponse response = prepareSearch().setQuery(
+            scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > 1", Collections.emptyMap()))
+        )
             .addSort("num1", SortOrder.ASC)
             .addScriptField("sNum1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value", Collections.emptyMap()))
             .get();
@@ -185,8 +185,9 @@ public void testCustomScriptBoost() throws Exception {
         params.put("param1", 2);
 
         logger.info("running doc['num1'].value > param1");
-        response = client().prepareSearch()
-            .setQuery(scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > param1", params)))
+        response = prepareSearch().setQuery(
+            scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > param1", params))
+        )
             .addSort("num1", SortOrder.ASC)
             .addScriptField("sNum1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value", Collections.emptyMap()))
             .get();
@@ -198,8 +199,9 @@ public void testCustomScriptBoost() throws Exception {
         params = new HashMap<>();
         params.put("param1", -1);
         logger.info("running doc['num1'].value > param1");
-        response = client().prepareSearch()
-            .setQuery(scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > param1", params)))
+        response = prepareSearch().setQuery(
+            scriptQuery(new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value > param1", params))
+        )
             .addSort("num1", SortOrder.ASC)
             .addScriptField("sNum1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['num1'].value", Collections.emptyMap()))
             .get();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java
index 6fb19890a183f..f94e59cbe1ab4 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollIT.java
@@ -81,8 +81,7 @@ public void testSimpleScrollQueryThenFetch() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .addSort("field", SortOrder.ASC)
@@ -134,8 +133,7 @@ public void testSimpleScrollQueryThenFetchSmallSizeUnevenDistribution() throws E
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setSearchType(SearchType.QUERY_THEN_FETCH)
+        SearchResponse searchResponse = prepareSearch().setSearchType(SearchType.QUERY_THEN_FETCH)
             .setQuery(matchAllQuery())
             .setSize(3)
             .setScroll(TimeValue.timeValueMinutes(2))
@@ -202,26 +200,13 @@ public void testScrollAndUpdateIndex() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        assertThat(client().prepareSearch().setSize(0).setQuery(matchAllQuery()).get().getHits().getTotalHits().value, equalTo(500L));
-        assertThat(
-            client().prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value,
-            equalTo(500L)
-        );
-        assertThat(
-            client().prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value,
-            equalTo(500L)
-        );
-        assertThat(
-            client().prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value,
-            equalTo(0L)
-        );
-        assertThat(
-            client().prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value,
-            equalTo(0L)
-        );
+        assertThat(prepareSearch().setSize(0).setQuery(matchAllQuery()).get().getHits().getTotalHits().value, equalTo(500L));
+        assertThat(prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value, equalTo(500L));
+        assertThat(prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value, equalTo(500L));
+        assertThat(prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value, equalTo(0L));
+        assertThat(prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value, equalTo(0L));
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(queryStringQuery("user:kimchy"))
+        SearchResponse searchResponse = prepareSearch().setQuery(queryStringQuery("user:kimchy"))
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .addSort("postDate", SortOrder.ASC)
@@ -237,21 +222,15 @@ public void testScrollAndUpdateIndex() throws Exception {
             } while (searchResponse.getHits().getHits().length > 0);
 
             indicesAdmin().prepareRefresh().get();
-            assertThat(client().prepareSearch().setSize(0).setQuery(matchAllQuery()).get().getHits().getTotalHits().value, equalTo(500L));
-            assertThat(
-                client().prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value,
-                equalTo(0L)
-            );
-            assertThat(
-                client().prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value,
-                equalTo(0L)
-            );
+            assertThat(prepareSearch().setSize(0).setQuery(matchAllQuery()).get().getHits().getTotalHits().value, equalTo(500L));
+            assertThat(prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value, equalTo(0L));
+            assertThat(prepareSearch().setSize(0).setQuery(termQuery("message", "test")).get().getHits().getTotalHits().value, equalTo(0L));
             assertThat(
-                client().prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value,
+                prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value,
                 equalTo(500L)
             );
             assertThat(
-                client().prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value,
+                prepareSearch().setSize(0).setQuery(termQuery("message", "update")).get().getHits().getTotalHits().value,
                 equalTo(500L)
             );
         } finally {
@@ -274,16 +253,14 @@ public void testSimpleScrollQueryThenFetch_clearScrollIds() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse searchResponse1 = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse1 = prepareSearch().setQuery(matchAllQuery())
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .setSearchType(SearchType.QUERY_THEN_FETCH)
             .addSort("field", SortOrder.ASC)
             .get();
 
-        SearchResponse searchResponse2 = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse2 = prepareSearch().setQuery(matchAllQuery())
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .setSearchType(SearchType.QUERY_THEN_FETCH)
@@ -394,16 +371,14 @@ public void testSimpleScrollQueryThenFetchClearAllScrollIds() throws Exception {
 
         indicesAdmin().prepareRefresh().get();
 
-        SearchResponse searchResponse1 = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse1 = prepareSearch().setQuery(matchAllQuery())
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .setSearchType(SearchType.QUERY_THEN_FETCH)
             .addSort("field", SortOrder.ASC)
             .get();
 
-        SearchResponse searchResponse2 = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse2 = prepareSearch().setQuery(matchAllQuery())
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .setSearchType(SearchType.QUERY_THEN_FETCH)
@@ -538,8 +513,7 @@ public void testCloseAndReopenOrDeleteWithActiveScroll() {
             client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", i).get();
         }
         refresh();
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(35)
             .setScroll(TimeValue.timeValueMinutes(2))
             .addSort("field", SortOrder.ASC)
@@ -602,7 +576,7 @@ public void testInvalidScrollKeepAlive() throws IOException {
 
         Exception exc = expectThrows(
             Exception.class,
-            () -> client().prepareSearch().setQuery(matchAllQuery()).setSize(1).setScroll(TimeValue.timeValueHours(2)).get()
+            () -> prepareSearch().setQuery(matchAllQuery()).setSize(1).setScroll(TimeValue.timeValueHours(2)).get()
         );
         IllegalArgumentException illegalArgumentException = (IllegalArgumentException) ExceptionsHelper.unwrap(
             exc,
@@ -611,11 +585,7 @@ public void testInvalidScrollKeepAlive() throws IOException {
         assertNotNull(illegalArgumentException);
         assertThat(illegalArgumentException.getMessage(), containsString("Keep alive for request (2h) is too large"));
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .setSize(1)
-            .setScroll(TimeValue.timeValueMinutes(5))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(1).setScroll(TimeValue.timeValueMinutes(5)).get();
         assertNotNull(searchResponse.getScrollId());
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollWithFailingNodesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollWithFailingNodesIT.java
index 64597a3520118..96c007e05e414 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollWithFailingNodesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/scroll/SearchScrollWithFailingNodesIT.java
@@ -58,8 +58,7 @@ public void testScanScrollWithShardExceptions() throws Exception {
         indexRandom(false, writes);
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(10)
             .setScroll(TimeValue.timeValueMinutes(1))
             .get();
@@ -75,7 +74,7 @@ public void testScanScrollWithShardExceptions() throws Exception {
 
         internalCluster().stopRandomNonMasterNode();
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).setScroll(TimeValue.timeValueMinutes(1)).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).setScroll(TimeValue.timeValueMinutes(1)).get();
         assertThat(searchResponse.getSuccessfulShards(), lessThan(searchResponse.getTotalShards()));
         numHits = 0;
         int numberOfSuccessfulShards = searchResponse.getSuccessfulShards();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java
index a1e860ba848bb..7edc0ff139a1e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java
@@ -143,12 +143,11 @@ public void testWithPreferenceAndRoutings() throws Exception {
                     .addAliasAction(IndicesAliasesRequest.AliasActions.add().index("test").alias("alias3").routing("baz"))
                     .get()
             );
-            SearchResponse sr = client().prepareSearch("alias1", "alias3").setQuery(matchAllQuery()).setSize(0).get();
+            SearchResponse sr = prepareSearch("alias1", "alias3").setQuery(matchAllQuery()).setSize(0).get();
             int numDocs = (int) sr.getHits().getTotalHits().value;
             int max = randomIntBetween(2, numShards * 3);
             int fetchSize = randomIntBetween(10, 100);
-            SearchRequestBuilder request = client().prepareSearch("alias1", "alias3")
-                .setQuery(matchAllQuery())
+            SearchRequestBuilder request = prepareSearch("alias1", "alias3").setQuery(matchAllQuery())
                 .setScroll(new Scroll(TimeValue.timeValueSeconds(10)))
                 .setSize(fetchSize)
                 .addSort(SortBuilders.fieldSort("_doc"));
@@ -277,7 +276,7 @@ public void testInvalidQuery() throws Exception {
         setupIndex(0, 1);
         SearchPhaseExecutionException exc = expectThrows(
             SearchPhaseExecutionException.class,
-            () -> client().prepareSearch().setQuery(matchAllQuery()).slice(new SliceBuilder("invalid_random_int", 0, 10)).get()
+            () -> prepareSearch().setQuery(matchAllQuery()).slice(new SliceBuilder("invalid_random_int", 0, 10)).get()
         );
         Throwable rootCause = findRootCause(exc);
         assertThat(rootCause.getClass(), equalTo(SearchException.class));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
index 2d3af1f555d4f..87d87f2260734 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
@@ -120,10 +120,9 @@ public void testIssue8226() {
         }
         refresh();
         // sort DESC
-        SearchResponse searchResponse = client().prepareSearch()
-            .addSort(new FieldSortBuilder("entry").order(SortOrder.DESC).unmappedType(useMapping ? null : "long"))
-            .setSize(10)
-            .get();
+        SearchResponse searchResponse = prepareSearch().addSort(
+            new FieldSortBuilder("entry").order(SortOrder.DESC).unmappedType(useMapping ? null : "long")
+        ).setSize(10).get();
         logClusterState();
         assertNoFailures(searchResponse);
 
@@ -134,10 +133,9 @@ public void testIssue8226() {
         }
 
         // sort ASC
-        searchResponse = client().prepareSearch()
-            .addSort(new FieldSortBuilder("entry").order(SortOrder.ASC).unmappedType(useMapping ? null : "long"))
-            .setSize(10)
-            .get();
+        searchResponse = prepareSearch().addSort(
+            new FieldSortBuilder("entry").order(SortOrder.ASC).unmappedType(useMapping ? null : "long")
+        ).setSize(10).get();
         logClusterState();
         assertNoFailures(searchResponse);
 
@@ -174,25 +172,20 @@ public void testIssue6614() throws ExecutionException, InterruptedException {
             docs += builders.size();
             builders.clear();
         }
-        SearchResponse allDocsResponse = client().prepareSearch()
-            .setQuery(
-                QueryBuilders.boolQuery()
-                    .must(QueryBuilders.termQuery("foo", "bar"))
-                    .must(QueryBuilders.rangeQuery("timeUpdated").gte("2014/0" + randomIntBetween(1, 7) + "/01"))
-            )
-            .addSort(new FieldSortBuilder("timeUpdated").order(SortOrder.ASC).unmappedType("date"))
-            .setSize(docs)
-            .get();
+        SearchResponse allDocsResponse = prepareSearch().setQuery(
+            QueryBuilders.boolQuery()
+                .must(QueryBuilders.termQuery("foo", "bar"))
+                .must(QueryBuilders.rangeQuery("timeUpdated").gte("2014/0" + randomIntBetween(1, 7) + "/01"))
+        ).addSort(new FieldSortBuilder("timeUpdated").order(SortOrder.ASC).unmappedType("date")).setSize(docs).get();
         assertNoFailures(allDocsResponse);
 
         final int numiters = randomIntBetween(1, 20);
         for (int i = 0; i < numiters; i++) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(
-                    QueryBuilders.boolQuery()
-                        .must(QueryBuilders.termQuery("foo", "bar"))
-                        .must(QueryBuilders.rangeQuery("timeUpdated").gte("2014/" + Strings.format("%02d", randomIntBetween(1, 7)) + "/01"))
-                )
+            SearchResponse searchResponse = prepareSearch().setQuery(
+                QueryBuilders.boolQuery()
+                    .must(QueryBuilders.termQuery("foo", "bar"))
+                    .must(QueryBuilders.rangeQuery("timeUpdated").gte("2014/" + Strings.format("%02d", randomIntBetween(1, 7)) + "/01"))
+            )
                 .addSort(new FieldSortBuilder("timeUpdated").order(SortOrder.ASC).unmappedType("date"))
                 .setSize(scaledRandomIntBetween(1, docs))
                 .get();
@@ -221,7 +214,7 @@ public void testTrackScores() throws Exception {
         );
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort("svalue", SortOrder.ASC).get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).addSort("svalue", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getMaxScore(), equalTo(Float.NaN));
         for (SearchHit hit : searchResponse.getHits()) {
@@ -229,7 +222,7 @@ public void testTrackScores() throws Exception {
         }
 
         // now check with score tracking
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort("svalue", SortOrder.ASC).setTrackScores(true).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addSort("svalue", SortOrder.ASC).setTrackScores(true).get();
 
         assertThat(searchResponse.getHits().getMaxScore(), not(equalTo(Float.NaN)));
         for (SearchHit hit : searchResponse.getHits()) {
@@ -298,8 +291,7 @@ public void testRandomSorting() throws IOException, InterruptedException, Execut
         }
         if (sparseBytes.isEmpty() == false) {
             int size = between(1, sparseBytes.size());
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
                 .setPostFilter(QueryBuilders.existsQuery("sparse_bytes"))
                 .setSize(size)
                 .addSort("sparse_bytes", SortOrder.ASC)
@@ -575,11 +567,7 @@ public void testSimpleSorts() throws Exception {
         // STRING
         int size = 1 + random.nextInt(10);
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .setSize(size)
-            .addSort("str_value", SortOrder.ASC)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("str_value", SortOrder.ASC).get();
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
         for (int i = 0; i < size; i++) {
@@ -590,7 +578,7 @@ public void testSimpleSorts() throws Exception {
             );
         }
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("str_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("str_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -606,7 +594,7 @@ public void testSimpleSorts() throws Exception {
 
         // BYTE
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("byte_value", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("byte_value", SortOrder.ASC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -615,7 +603,7 @@ public void testSimpleSorts() throws Exception {
             assertThat(((Number) searchResponse.getHits().getAt(i).getSortValues()[0]).byteValue(), equalTo((byte) i));
         }
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("byte_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("byte_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -628,7 +616,7 @@ public void testSimpleSorts() throws Exception {
 
         // SHORT
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("short_value", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("short_value", SortOrder.ASC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -637,7 +625,7 @@ public void testSimpleSorts() throws Exception {
             assertThat(((Number) searchResponse.getHits().getAt(i).getSortValues()[0]).shortValue(), equalTo((short) i));
         }
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("short_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("short_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -650,7 +638,7 @@ public void testSimpleSorts() throws Exception {
 
         // INTEGER
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("integer_value", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("integer_value", SortOrder.ASC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -661,7 +649,7 @@ public void testSimpleSorts() throws Exception {
 
         assertThat(searchResponse.toString(), not(containsString("error")));
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("integer_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("integer_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -674,7 +662,7 @@ public void testSimpleSorts() throws Exception {
 
         // LONG
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("long_value", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("long_value", SortOrder.ASC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -685,7 +673,7 @@ public void testSimpleSorts() throws Exception {
 
         assertThat(searchResponse.toString(), not(containsString("error")));
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("long_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("long_value", SortOrder.DESC).get();
         assertHitCount(searchResponse, 10L);
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -698,7 +686,7 @@ public void testSimpleSorts() throws Exception {
 
         // FLOAT
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("float_value", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("float_value", SortOrder.ASC).get();
 
         assertHitCount(searchResponse, 10L);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -709,7 +697,7 @@ public void testSimpleSorts() throws Exception {
 
         assertThat(searchResponse.toString(), not(containsString("error")));
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("float_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("float_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -722,7 +710,7 @@ public void testSimpleSorts() throws Exception {
 
         // DOUBLE
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("double_value", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("double_value", SortOrder.ASC).get();
 
         assertHitCount(searchResponse, 10L);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -733,7 +721,7 @@ public void testSimpleSorts() throws Exception {
 
         assertThat(searchResponse.toString(), not(containsString("error")));
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("double_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("double_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10L);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -780,8 +768,7 @@ public void testSortMissingNumbers() throws Exception {
         refresh();
 
         logger.info("--> sort with no missing (same as missing _last)");
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("i_value").order(SortOrder.ASC))
             .get();
         assertNoFailures(searchResponse);
@@ -792,8 +779,7 @@ public void testSortMissingNumbers() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2"));
 
         logger.info("--> sort with missing _last");
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("i_value").order(SortOrder.ASC).missing("_last"))
             .get();
         assertNoFailures(searchResponse);
@@ -804,8 +790,7 @@ public void testSortMissingNumbers() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2"));
 
         logger.info("--> sort with missing _first");
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("i_value").order(SortOrder.ASC).missing("_first"))
             .get();
         assertNoFailures(searchResponse);
@@ -855,8 +840,7 @@ public void testSortMissingStrings() throws IOException {
         }
 
         logger.info("--> sort with no missing (same as missing _last)");
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("value").order(SortOrder.ASC))
             .get();
         assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
@@ -867,8 +851,7 @@ public void testSortMissingStrings() throws IOException {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2"));
 
         logger.info("--> sort with missing _last");
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("value").order(SortOrder.ASC).missing("_last"))
             .get();
         assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
@@ -879,8 +862,7 @@ public void testSortMissingStrings() throws IOException {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2"));
 
         logger.info("--> sort with missing _first");
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("value").order(SortOrder.ASC).missing("_first"))
             .get();
         assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
@@ -891,8 +873,7 @@ public void testSortMissingStrings() throws IOException {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3"));
 
         logger.info("--> sort with missing b");
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("value").order(SortOrder.ASC).missing("b"))
             .get();
         assertThat(Arrays.toString(searchResponse.getShardFailures()), searchResponse.getFailedShards(), equalTo(0));
@@ -1036,7 +1017,7 @@ public void testIgnoreUnmapped() throws Exception {
 
         logger.info("--> sort with an unmapped field, verify it fails");
         try {
-            SearchResponse result = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("kkk")).get();
+            SearchResponse result = prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("kkk")).get();
             assertThat("Expected exception but returned with", result, nullValue());
         } catch (SearchPhaseExecutionException e) {
             // we check that it's a parse failure rather than a different shard failure
@@ -1045,12 +1026,11 @@ public void testIgnoreUnmapped() throws Exception {
             }
         }
 
-        assertNoFailures(client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("kkk").unmappedType("keyword")));
+        assertNoFailures(prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("kkk").unmappedType("keyword")));
 
         // nested field
         assertNoFailures(
-            client().prepareSearch()
-                .setQuery(matchAllQuery())
+            prepareSearch().setQuery(matchAllQuery())
                 .addSort(
                     SortBuilders.fieldSort("nested.foo")
                         .unmappedType("keyword")
@@ -1060,8 +1040,7 @@ public void testIgnoreUnmapped() throws Exception {
 
         // nestedQuery
         assertNoFailures(
-            client().prepareSearch()
-                .setQuery(matchAllQuery())
+            prepareSearch().setQuery(matchAllQuery())
                 .addSort(
                     SortBuilders.fieldSort("nested.foo")
                         .unmappedType("keyword")
@@ -1150,11 +1129,7 @@ public void testSortMVField() throws Exception {
 
         refresh();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .setSize(10)
-            .addSort("long_values", SortOrder.ASC)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("long_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1168,7 +1143,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).longValue(), equalTo(7L));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("long_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("long_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1182,8 +1157,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).longValue(), equalTo(3L));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(10)
             .addSort(SortBuilders.fieldSort("long_values").order(SortOrder.DESC).sortMode(SortMode.SUM))
             .get();
@@ -1200,8 +1174,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).longValue(), equalTo(2L));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(10)
             .addSort(SortBuilders.fieldSort("long_values").order(SortOrder.DESC).sortMode(SortMode.AVG))
             .get();
@@ -1218,8 +1191,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).longValue(), equalTo(1L));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(10)
             .addSort(SortBuilders.fieldSort("long_values").order(SortOrder.DESC).sortMode(SortMode.MEDIAN))
             .get();
@@ -1236,7 +1208,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).longValue(), equalTo(2L));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("int_values", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("int_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1250,7 +1222,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).intValue(), equalTo(7));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("int_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("int_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1264,7 +1236,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).intValue(), equalTo(3));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("short_values", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("short_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1278,7 +1250,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).intValue(), equalTo(7));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("short_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("short_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1292,7 +1264,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).intValue(), equalTo(3));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("byte_values", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("byte_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1306,7 +1278,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).intValue(), equalTo(7));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("byte_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("byte_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1320,7 +1292,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).intValue(), equalTo(3));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("float_values", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("float_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1334,7 +1306,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).floatValue(), equalTo(7f));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("float_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("float_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1348,7 +1320,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).floatValue(), equalTo(3f));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("double_values", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("double_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1362,7 +1334,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).doubleValue(), equalTo(7d));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("double_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("double_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1376,7 +1348,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(3)));
         assertThat(((Number) searchResponse.getHits().getAt(2).getSortValues()[0]).doubleValue(), equalTo(3d));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("string_values", SortOrder.ASC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("string_values", SortOrder.ASC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1390,7 +1362,7 @@ public void testSortMVField() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).getId(), equalTo(Integer.toString(2)));
         assertThat(searchResponse.getHits().getAt(2).getSortValues()[0], equalTo("07"));
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("string_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(10).addSort("string_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L));
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
@@ -1427,11 +1399,7 @@ public void testSortOnRareField() throws IOException {
             .get();
 
         refresh();
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .setSize(3)
-            .addSort("string_values", SortOrder.DESC)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(3).addSort("string_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getHits().length, equalTo(1));
 
@@ -1450,7 +1418,7 @@ public void testSortOnRareField() throws IOException {
         }
         refresh();
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(2).addSort("string_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(2).addSort("string_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getHits().length, equalTo(2));
 
@@ -1472,7 +1440,7 @@ public void testSortOnRareField() throws IOException {
         }
         refresh();
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(3).addSort("string_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(3).addSort("string_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
 
@@ -1493,7 +1461,7 @@ public void testSortOnRareField() throws IOException {
             refresh();
         }
 
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(3).addSort("string_values", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(3).addSort("string_values", SortOrder.DESC).get();
 
         assertThat(searchResponse.getHits().getHits().length, equalTo(3));
 
@@ -1520,8 +1488,7 @@ public void testSortMetaField() throws Exception {
             indexRandom(true, indexReqs);
 
             SortOrder order = randomFrom(SortOrder.values());
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
                 .setSize(randomIntBetween(1, numDocs + 5))
                 .addSort("_id", order)
                 .get();
@@ -1621,8 +1588,7 @@ public void testNestedSort() throws IOException, InterruptedException, Execution
 
         // We sort on nested field
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("nested.foo").setNestedSort(new NestedSortBuilder("nested")).order(SortOrder.DESC))
             .get();
         assertNoFailures(searchResponse);
@@ -1634,8 +1600,7 @@ public void testNestedSort() throws IOException, InterruptedException, Execution
         assertThat(hits[1].getSortValues()[0], is("bar"));
 
         // We sort on nested fields with max_children limit
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(
                 SortBuilders.fieldSort("nested.foo").setNestedSort(new NestedSortBuilder("nested").setMaxChildren(1)).order(SortOrder.DESC)
             )
@@ -1651,8 +1616,7 @@ public void testNestedSort() throws IOException, InterruptedException, Execution
         {
             SearchPhaseExecutionException exc = expectThrows(
                 SearchPhaseExecutionException.class,
-                () -> client().prepareSearch()
-                    .setQuery(matchAllQuery())
+                () -> prepareSearch().setQuery(matchAllQuery())
                     .addSort(
                         SortBuilders.fieldSort("nested.bar.foo")
                             .setNestedSort(
@@ -1666,8 +1630,7 @@ public void testNestedSort() throws IOException, InterruptedException, Execution
         }
 
         // We sort on nested sub field
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(SortBuilders.fieldSort("nested.foo.sub").setNestedSort(new NestedSortBuilder("nested")).order(SortOrder.DESC))
             .get();
         assertNoFailures(searchResponse);
@@ -1681,7 +1644,7 @@ public void testNestedSort() throws IOException, InterruptedException, Execution
         // missing nested path
         SearchPhaseExecutionException exc = expectThrows(
             SearchPhaseExecutionException.class,
-            () -> client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("nested.foo")).get()
+            () -> prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("nested.foo")).get()
         );
         assertThat(exc.toString(), containsString("it is mandatory to set the [nested] context"));
     }
@@ -1766,8 +1729,7 @@ public void testScriptFieldSort() throws Exception {
 
         {
             Script script = new Script(ScriptType.INLINE, NAME, "doc['number'].value", Collections.emptyMap());
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
                 .setSize(randomIntBetween(1, numDocs + 5))
                 .addSort(SortBuilders.scriptSort(script, ScriptSortBuilder.ScriptSortType.NUMBER))
                 .addSort(SortBuilders.scoreSort())
@@ -1783,8 +1745,7 @@ public void testScriptFieldSort() throws Exception {
 
         {
             Script script = new Script(ScriptType.INLINE, NAME, "doc['keyword'].value", Collections.emptyMap());
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
                 .setSize(randomIntBetween(1, numDocs + 5))
                 .addSort(SortBuilders.scriptSort(script, ScriptSortBuilder.ScriptSortType.STRING))
                 .addSort(SortBuilders.scoreSort())
@@ -1812,8 +1773,7 @@ public void testFieldAlias() throws Exception {
         builders.add(client().prepareIndex("new_index").setSource("route_length_miles", 100.2));
         indexRandom(true, true, builders);
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .setSize(builders.size())
             .addSort(SortBuilders.fieldSort("route_length_miles"))
             .get();
@@ -1838,8 +1798,7 @@ public void testFieldAliasesWithMissingValues() throws Exception {
         builders.add(client().prepareIndex("new_index").setSource("route_length_miles", 100.2));
         indexRandom(true, true, builders);
 
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .setSize(builders.size())
             .addSort(SortBuilders.fieldSort("route_length_miles").missing(120.3))
             .get();
@@ -1864,8 +1823,7 @@ public void testCastNumericType() throws Exception {
         indexRandom(true, true, builders);
 
         {
-            SearchResponse response = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse response = prepareSearch().setQuery(matchAllQuery())
                 .setSize(builders.size())
                 .addSort(SortBuilders.fieldSort("field").setNumericType("long"))
                 .get();
@@ -1881,8 +1839,7 @@ public void testCastNumericType() throws Exception {
         }
 
         {
-            SearchResponse response = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse response = prepareSearch().setQuery(matchAllQuery())
                 .setSize(builders.size())
                 .addSort(SortBuilders.fieldSort("field").setNumericType("double"))
                 .get();
@@ -1908,8 +1865,7 @@ public void testCastDate() throws Exception {
         indexRandom(true, true, builders);
 
         {
-            SearchResponse response = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse response = prepareSearch().setQuery(matchAllQuery())
                 .setSize(2)
                 .addSort(SortBuilders.fieldSort("field").setNumericType("date"))
                 .get();
@@ -1922,8 +1878,7 @@ public void testCastDate() throws Exception {
             assertEquals(1712879236854L, hits.getAt(0).getSortValues()[0]);
             assertEquals(1712879237000L, hits.getAt(1).getSortValues()[0]);
 
-            response = client().prepareSearch()
-                .setMaxConcurrentShardRequests(1)
+            response = prepareSearch().setMaxConcurrentShardRequests(1)
                 .setQuery(matchAllQuery())
                 .setSize(1)
                 .addSort(SortBuilders.fieldSort("field").setNumericType("date"))
@@ -1934,8 +1889,7 @@ public void testCastDate() throws Exception {
             assertThat(hits.getAt(0).getSortValues()[0].getClass(), equalTo(Long.class));
             assertEquals(1712879236854L, hits.getAt(0).getSortValues()[0]);
 
-            response = client().prepareSearch()
-                .setMaxConcurrentShardRequests(1)
+            response = prepareSearch().setMaxConcurrentShardRequests(1)
                 .setQuery(matchAllQuery())
                 .setSize(1)
                 .addSort(SortBuilders.fieldSort("field").order(SortOrder.DESC).setNumericType("date"))
@@ -1948,8 +1902,7 @@ public void testCastDate() throws Exception {
         }
 
         {
-            SearchResponse response = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse response = prepareSearch().setQuery(matchAllQuery())
                 .setSize(2)
                 .addSort(SortBuilders.fieldSort("field").setNumericType("date_nanos"))
                 .get();
@@ -1961,8 +1914,7 @@ public void testCastDate() throws Exception {
             assertEquals(1712879236854775807L, hits.getAt(0).getSortValues()[0]);
             assertEquals(1712879237000000000L, hits.getAt(1).getSortValues()[0]);
 
-            response = client().prepareSearch()
-                .setMaxConcurrentShardRequests(1)
+            response = prepareSearch().setMaxConcurrentShardRequests(1)
                 .setQuery(matchAllQuery())
                 .setSize(1)
                 .addSort(SortBuilders.fieldSort("field").setNumericType("date_nanos"))
@@ -1972,8 +1924,7 @@ public void testCastDate() throws Exception {
             assertThat(hits.getAt(0).getSortValues()[0].getClass(), equalTo(Long.class));
             assertEquals(1712879236854775807L, hits.getAt(0).getSortValues()[0]);
 
-            response = client().prepareSearch()
-                .setMaxConcurrentShardRequests(1)
+            response = prepareSearch().setMaxConcurrentShardRequests(1)
                 .setQuery(matchAllQuery())
                 .setSize(1)
                 .addSort(SortBuilders.fieldSort("field").order(SortOrder.DESC).setNumericType("date_nanos"))
@@ -1988,8 +1939,7 @@ public void testCastDate() throws Exception {
             builders.clear();
             builders.add(client().prepareIndex("index_date").setSource("field", "1905-04-11T23:47:17"));
             indexRandom(true, true, builders);
-            SearchResponse response = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse response = prepareSearch().setQuery(matchAllQuery())
                 .setSize(1)
                 .addSort(SortBuilders.fieldSort("field").setNumericType("date_nanos"))
                 .get();
@@ -2002,8 +1952,7 @@ public void testCastDate() throws Exception {
             builders.clear();
             builders.add(client().prepareIndex("index_date").setSource("field", "2346-04-11T23:47:17"));
             indexRandom(true, true, builders);
-            SearchResponse response = client().prepareSearch()
-                .setQuery(QueryBuilders.rangeQuery("field").gt("1970-01-01"))
+            SearchResponse response = prepareSearch().setQuery(QueryBuilders.rangeQuery("field").gt("1970-01-01"))
                 .setSize(10)
                 .addSort(SortBuilders.fieldSort("field").setNumericType("date_nanos"))
                 .get();
@@ -2020,8 +1969,7 @@ public void testCastNumericTypeExceptions() throws Exception {
             for (String numericType : new String[] { "long", "double", "date", "date_nanos" }) {
                 ElasticsearchException exc = expectThrows(
                     ElasticsearchException.class,
-                    () -> client().prepareSearch()
-                        .setQuery(matchAllQuery())
+                    () -> prepareSearch().setQuery(matchAllQuery())
                         .addSort(SortBuilders.fieldSort(invalidField).setNumericType(numericType))
                         .get()
                 );
@@ -2050,10 +1998,7 @@ public void testLongSortOptimizationCorrectResults() {
         refresh();
 
         // *** 1. sort DESC on long_field
-        SearchResponse searchResponse = client().prepareSearch()
-            .addSort(new FieldSortBuilder("long_field").order(SortOrder.DESC))
-            .setSize(10)
-            .get();
+        SearchResponse searchResponse = prepareSearch().addSort(new FieldSortBuilder("long_field").order(SortOrder.DESC)).setSize(10).get();
         assertNoFailures(searchResponse);
         long previousLong = Long.MAX_VALUE;
         for (int i = 0; i < searchResponse.getHits().getHits().length; i++) {
@@ -2065,7 +2010,7 @@ public void testLongSortOptimizationCorrectResults() {
         }
 
         // *** 2. sort ASC on long_field
-        searchResponse = client().prepareSearch().addSort(new FieldSortBuilder("long_field").order(SortOrder.ASC)).setSize(10).get();
+        searchResponse = prepareSearch().addSort(new FieldSortBuilder("long_field").order(SortOrder.ASC)).setSize(10).get();
         assertNoFailures(searchResponse);
         previousLong = Long.MIN_VALUE;
         for (int i = 0; i < searchResponse.getHits().getHits().length; i++) {
@@ -2090,8 +2035,7 @@ public void testSortMixedFieldTypes() {
         refresh();
 
         { // mixing long and integer types is ok, as we convert integer sort to long sort
-            SearchResponse searchResponse = client().prepareSearch("index_long", "index_integer")
-                .addSort(new FieldSortBuilder("foo"))
+            SearchResponse searchResponse = prepareSearch("index_long", "index_integer").addSort(new FieldSortBuilder("foo"))
                 .setSize(10)
                 .get();
             assertNoFailures(searchResponse);
@@ -2102,7 +2046,7 @@ public void testSortMixedFieldTypes() {
         { // mixing long and double types is not allowed
             SearchPhaseExecutionException exc = expectThrows(
                 SearchPhaseExecutionException.class,
-                () -> client().prepareSearch("index_long", "index_double").addSort(new FieldSortBuilder("foo")).setSize(10).get()
+                () -> prepareSearch("index_long", "index_double").addSort(new FieldSortBuilder("foo")).setSize(10).get()
             );
             assertThat(exc.getCause().toString(), containsString(errMsg));
         }
@@ -2110,7 +2054,7 @@ public void testSortMixedFieldTypes() {
         { // mixing long and keyword types is not allowed
             SearchPhaseExecutionException exc = expectThrows(
                 SearchPhaseExecutionException.class,
-                () -> client().prepareSearch("index_long", "index_keyword").addSort(new FieldSortBuilder("foo")).setSize(10).get()
+                () -> prepareSearch("index_long", "index_keyword").addSort(new FieldSortBuilder("foo")).setSize(10).get()
             );
             assertThat(exc.getCause().toString(), containsString(errMsg));
         }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java
index 0518525486388..777db15b596ec 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceIT.java
@@ -614,8 +614,7 @@ public void testDistanceSortingWithUnmappedField() throws Exception {
         refresh();
 
         // Order: Asc
-        SearchResponse searchResponse = client().prepareSearch("test1", "test2")
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch("test1", "test2").setQuery(matchAllQuery())
             .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).ignoreUnmapped(true).order(SortOrder.ASC))
             .get();
 
@@ -625,8 +624,7 @@ public void testDistanceSortingWithUnmappedField() throws Exception {
         assertThat(((Number) searchResponse.getHits().getAt(1).getSortValues()[0]).doubleValue(), equalTo(Double.POSITIVE_INFINITY));
 
         // Order: Desc
-        searchResponse = client().prepareSearch("test1", "test2")
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch("test1", "test2").setQuery(matchAllQuery())
             .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).ignoreUnmapped(true).order(SortOrder.DESC))
             .get();
 
@@ -637,8 +635,7 @@ public void testDistanceSortingWithUnmappedField() throws Exception {
         assertThat(((Number) searchResponse.getHits().getAt(1).getSortValues()[0]).doubleValue(), closeTo(5286d, 10d));
 
         // Make sure that by default the unmapped fields continue to fail
-        searchResponse = client().prepareSearch("test1", "test2")
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch("test1", "test2").setQuery(matchAllQuery())
             .addSort(SortBuilders.geoDistanceSort("locations", 40.7143528, -74.0059731).order(SortOrder.DESC))
             .get();
         assertThat(searchResponse.getFailedShards(), greaterThan(0));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java
index 0862d919843db..a04090e9d04b9 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java
@@ -84,8 +84,7 @@ public void testManyToManyGeoPoints() throws ExecutionException, InterruptedExce
             q[0] = new GeoPoint(2, 1);
         }
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(new GeoDistanceSortBuilder(LOCATION_FIELD, q).sortMode(SortMode.MIN).order(SortOrder.ASC))
             .get();
         assertOrderedSearchHits(searchResponse, "d1", "d2");
@@ -98,8 +97,7 @@ public void testManyToManyGeoPoints() throws ExecutionException, InterruptedExce
             closeTo(GeoDistance.ARC.calculate(2, 1, 5, 1, DistanceUnit.METERS), 10d)
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(new GeoDistanceSortBuilder(LOCATION_FIELD, q).sortMode(SortMode.MIN).order(SortOrder.DESC))
             .get();
         assertOrderedSearchHits(searchResponse, "d2", "d1");
@@ -112,8 +110,7 @@ public void testManyToManyGeoPoints() throws ExecutionException, InterruptedExce
             closeTo(GeoDistance.ARC.calculate(2, 2, 3, 2, DistanceUnit.METERS), 10d)
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(new GeoDistanceSortBuilder(LOCATION_FIELD, q).sortMode(SortMode.MAX).order(SortOrder.ASC))
             .get();
         assertOrderedSearchHits(searchResponse, "d1", "d2");
@@ -126,8 +123,7 @@ public void testManyToManyGeoPoints() throws ExecutionException, InterruptedExce
             closeTo(GeoDistance.ARC.calculate(2, 1, 6, 2, DistanceUnit.METERS), 10d)
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(new GeoDistanceSortBuilder(LOCATION_FIELD, q).sortMode(SortMode.MAX).order(SortOrder.DESC))
             .get();
         assertOrderedSearchHits(searchResponse, "d2", "d1");
@@ -168,8 +164,7 @@ public void testSingeToManyAvgMedian() throws ExecutionException, InterruptedExc
         );
         GeoPoint q = new GeoPoint(0, 0);
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(new GeoDistanceSortBuilder(LOCATION_FIELD, q).sortMode(SortMode.AVG).order(SortOrder.ASC))
             .get();
         assertOrderedSearchHits(searchResponse, "d2", "d1");
@@ -182,8 +177,7 @@ public void testSingeToManyAvgMedian() throws ExecutionException, InterruptedExc
             closeTo(GeoDistance.ARC.calculate(0, 0, 0, 5, DistanceUnit.METERS), 10d)
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(new GeoDistanceSortBuilder(LOCATION_FIELD, q).sortMode(SortMode.MEDIAN).order(SortOrder.ASC))
             .get();
         assertOrderedSearchHits(searchResponse, "d1", "d2");
@@ -251,8 +245,7 @@ public void testManyToManyGeoPointsWithDifferentFormats() throws ExecutionExcept
             }
         }
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(geoDistanceSortBuilder.sortMode(SortMode.MIN).order(SortOrder.ASC))
             .get();
         assertOrderedSearchHits(searchResponse, "d1", "d2");
@@ -265,8 +258,7 @@ public void testManyToManyGeoPointsWithDifferentFormats() throws ExecutionExcept
             closeTo(GeoDistance.ARC.calculate(4.5, 1, 2, 1, DistanceUnit.METERS), 1.e-1)
         );
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(geoDistanceSortBuilder.sortMode(SortMode.MAX).order(SortOrder.ASC))
             .get();
         assertOrderedSearchHits(searchResponse, "d1", "d2");
@@ -297,50 +289,41 @@ public void testSinglePointGeoDistanceSort() throws ExecutionException, Interrup
 
         GeoDistanceSortBuilder geoDistanceSortBuilder = new GeoDistanceSortBuilder(LOCATION_FIELD, hashPoint);
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(geoDistanceSortBuilder.sortMode(SortMode.MIN).order(SortOrder.ASC))
             .get();
         checkCorrectSortOrderForGeoSort(searchResponse);
 
         geoDistanceSortBuilder = new GeoDistanceSortBuilder(LOCATION_FIELD, new GeoPoint(2, 2));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(geoDistanceSortBuilder.sortMode(SortMode.MIN).order(SortOrder.ASC))
             .get();
         checkCorrectSortOrderForGeoSort(searchResponse);
 
         geoDistanceSortBuilder = new GeoDistanceSortBuilder(LOCATION_FIELD, 2, 2);
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addSort(geoDistanceSortBuilder.sortMode(SortMode.MIN).order(SortOrder.ASC))
             .get();
         checkCorrectSortOrderForGeoSort(searchResponse);
 
-        searchResponse = client().prepareSearch()
-            .setSource(new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, 2.0, 2.0)))
+        searchResponse = prepareSearch().setSource(new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, 2.0, 2.0)))
             .get();
         checkCorrectSortOrderForGeoSort(searchResponse);
 
-        searchResponse = client().prepareSearch()
-            .setSource(new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, "s037ms06g7h0")))
-            .get();
+        searchResponse = prepareSearch().setSource(
+            new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, "s037ms06g7h0"))
+        ).get();
         checkCorrectSortOrderForGeoSort(searchResponse);
 
-        searchResponse = client().prepareSearch()
-            .setSource(new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, 2.0, 2.0)))
+        searchResponse = prepareSearch().setSource(new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, 2.0, 2.0)))
             .get();
         checkCorrectSortOrderForGeoSort(searchResponse);
 
-        searchResponse = client().prepareSearch()
-            .setSource(
-                new SearchSourceBuilder().sort(
-                    SortBuilders.geoDistanceSort(LOCATION_FIELD, 2.0, 2.0).validation(GeoValidationMethod.COERCE)
-                )
-            )
-            .get();
+        searchResponse = prepareSearch().setSource(
+            new SearchSourceBuilder().sort(SortBuilders.geoDistanceSort(LOCATION_FIELD, 2.0, 2.0).validation(GeoValidationMethod.COERCE))
+        ).get();
         checkCorrectSortOrderForGeoSort(searchResponse);
     }
 
@@ -369,24 +352,21 @@ public void testCrossIndexIgnoreUnmapped() throws Exception {
         );
 
         assertSortValues(
-            client().prepareSearch("test1", "test2")
-                .addSort(fieldSort("str_field").order(SortOrder.ASC).unmappedType("keyword"))
+            prepareSearch("test1", "test2").addSort(fieldSort("str_field").order(SortOrder.ASC).unmappedType("keyword"))
                 .addSort(fieldSort("str_field2").order(SortOrder.DESC).unmappedType("keyword")),
             new Object[] { "bcd", null },
             new Object[] { null, null }
         );
 
         assertSortValues(
-            client().prepareSearch("test1", "test2")
-                .addSort(fieldSort("long_field").order(SortOrder.ASC).unmappedType("long"))
+            prepareSearch("test1", "test2").addSort(fieldSort("long_field").order(SortOrder.ASC).unmappedType("long"))
                 .addSort(fieldSort("long_field2").order(SortOrder.DESC).unmappedType("long")),
             new Object[] { 3L, Long.MIN_VALUE },
             new Object[] { Long.MAX_VALUE, Long.MIN_VALUE }
         );
 
         assertSortValues(
-            client().prepareSearch("test1", "test2")
-                .addSort(fieldSort("double_field").order(SortOrder.ASC).unmappedType("double"))
+            prepareSearch("test1", "test2").addSort(fieldSort("double_field").order(SortOrder.ASC).unmappedType("double"))
                 .addSort(fieldSort("double_field2").order(SortOrder.DESC).unmappedType("double")),
             new Object[] { 0.65, Double.NEGATIVE_INFINITY },
             new Object[] { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/SimpleSortIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/SimpleSortIT.java
index d5654c8458229..0e430c9618bc8 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/SimpleSortIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/SimpleSortIT.java
@@ -181,8 +181,7 @@ public void testSimpleSorts() throws Exception {
 
         Script script = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['str_value'].value", Collections.emptyMap());
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(size)
             .addSort(new ScriptSortBuilder(script, ScriptSortType.STRING))
             .get();
@@ -198,7 +197,7 @@ public void testSimpleSorts() throws Exception {
         }
 
         size = 1 + random.nextInt(10);
-        searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("str_value", SortOrder.DESC).get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).setSize(size).addSort("str_value", SortOrder.DESC).get();
 
         assertHitCount(searchResponse, 10);
         assertThat(searchResponse.getHits().getHits().length, equalTo(size));
@@ -261,8 +260,7 @@ public void testSortMinValueScript() throws IOException {
         indicesAdmin().prepareRefresh("test").get();
 
         // test the long values
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("min", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "get min long", Collections.emptyMap()))
             .addSort(SortBuilders.fieldSort("ord").order(SortOrder.ASC).unmappedType("long"))
             .setSize(10)
@@ -277,8 +275,7 @@ public void testSortMinValueScript() throws IOException {
         }
 
         // test the double values
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("min", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "get min double", Collections.emptyMap()))
             .addSort(SortBuilders.fieldSort("ord").order(SortOrder.ASC).unmappedType("long"))
             .setSize(10)
@@ -293,8 +290,7 @@ public void testSortMinValueScript() throws IOException {
         }
 
         // test the string values
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("min", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "get min string", Collections.emptyMap()))
             .addSort(SortBuilders.fieldSort("ord").order(SortOrder.ASC).unmappedType("long"))
             .setSize(10)
@@ -309,8 +305,7 @@ public void testSortMinValueScript() throws IOException {
         }
 
         // test the geopoint values
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("min", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "get min geopoint lon", Collections.emptyMap()))
             .addSort(SortBuilders.fieldSort("ord").order(SortOrder.ASC).unmappedType("long"))
             .setSize(10)
@@ -355,8 +350,7 @@ public void testDocumentsWithNullValue() throws Exception {
 
         Script scripField = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['id'].value", Collections.emptyMap());
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("id", scripField)
             .addSort("svalue", SortOrder.ASC)
             .get();
@@ -368,8 +362,7 @@ public void testDocumentsWithNullValue() throws Exception {
         assertThat(searchResponse.getHits().getAt(1).field("id").getValue(), equalTo("3"));
         assertThat(searchResponse.getHits().getAt(2).field("id").getValue(), equalTo("2"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        searchResponse = prepareSearch().setQuery(matchAllQuery())
             .addScriptField("id", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['id'][0]", Collections.emptyMap()))
             .addSort("svalue", SortOrder.ASC)
             .get();
@@ -381,11 +374,7 @@ public void testDocumentsWithNullValue() throws Exception {
         assertThat(searchResponse.getHits().getAt(1).field("id").getValue(), equalTo("3"));
         assertThat(searchResponse.getHits().getAt(2).field("id").getValue(), equalTo("2"));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .addScriptField("id", scripField)
-            .addSort("svalue", SortOrder.DESC)
-            .get();
+        searchResponse = prepareSearch().setQuery(matchAllQuery()).addScriptField("id", scripField).addSort("svalue", SortOrder.DESC).get();
 
         if (searchResponse.getFailedShards() > 0) {
             logger.warn("Failed shards:");
@@ -401,8 +390,7 @@ public void testDocumentsWithNullValue() throws Exception {
         assertThat(searchResponse.getHits().getAt(2).field("id").getValue(), equalTo("2"));
 
         // a query with docs just with null values
-        searchResponse = client().prepareSearch()
-            .setQuery(termQuery("id", "2"))
+        searchResponse = prepareSearch().setQuery(termQuery("id", "2"))
             .addScriptField("id", scripField)
             .addSort("svalue", SortOrder.DESC)
             .get();
@@ -443,8 +431,6 @@ public void test2920() throws IOException {
         refresh();
 
         Script sortScript = new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "\u0027\u0027", Collections.emptyMap());
-        assertNoFailures(
-            client().prepareSearch().setQuery(matchAllQuery()).addSort(scriptSort(sortScript, ScriptSortType.STRING)).setSize(10)
-        );
+        assertNoFailures(prepareSearch().setQuery(matchAllQuery()).addSort(scriptSort(sortScript, ScriptSortType.STRING)).setSize(10));
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/stats/FieldUsageStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/stats/FieldUsageStatsIT.java
index 1e9b4fdbd7cd9..32f5e14b944a2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/stats/FieldUsageStatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/stats/FieldUsageStatsIT.java
@@ -73,8 +73,7 @@ public void testFieldUsageStats() throws ExecutionException, InterruptedExceptio
         assertFalse(stats.hasField("field2"));
         assertFalse(stats.hasField("date_field"));
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setSearchType(SearchType.DEFAULT)
+        SearchResponse searchResponse = prepareSearch().setSearchType(SearchType.DEFAULT)
             .setQuery(QueryBuilders.termQuery("field", "value"))
             .addAggregation(AggregationBuilders.terms("agg1").field("field.keyword"))
             .addAggregation(AggregationBuilders.filter("agg2", QueryBuilders.spanTermQuery("field2", "value2")))
@@ -114,8 +113,7 @@ public void testFieldUsageStats() throws ExecutionException, InterruptedExceptio
         assertEquals(Set.of(UsageContext.DOC_VALUES), stats.get("field.keyword").keySet());
         assertEquals(1L * numShards, stats.get("field.keyword").getDocValues());
 
-        client().prepareSearch()
-            .setSearchType(SearchType.DEFAULT)
+        prepareSearch().setSearchType(SearchType.DEFAULT)
             .setQuery(QueryBuilders.termQuery("field", "value"))
             .addAggregation(AggregationBuilders.terms("agg1").field("field.keyword"))
             .setSize(0)
@@ -144,8 +142,7 @@ public void testFieldUsageStats() throws ExecutionException, InterruptedExceptio
                 .getTotal()
                 .getQueryCount()
         );
-        client().prepareSearch()
-            .setSearchType(SearchType.DEFAULT)
+        prepareSearch().setSearchType(SearchType.DEFAULT)
             .setPreFilterShardSize(1)
             .setQuery(QueryBuilders.rangeQuery("date_field").from("2016/01/01"))
             .setSize(100)
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java
index 28e3cc3cf1b87..07e8c516eda41 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/stats/SearchStatsIT.java
@@ -188,8 +188,7 @@ public void testOpenContexts() {
         assertThat(indicesStats.getTotal().getSearch().getOpenContexts(), equalTo(0L));
 
         int size = scaledRandomIntBetween(1, docs);
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
             .setSize(size)
             .setScroll(TimeValue.timeValueMinutes(2))
             .get();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/SuggestSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/SuggestSearchIT.java
index df3442b1cc58a..95eb0f055b830 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/SuggestSearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/SuggestSearchIT.java
@@ -245,7 +245,7 @@ public void testSizeOneShard() throws Exception {
         }
         refresh();
 
-        SearchResponse search = client().prepareSearch().setQuery(matchQuery("text", "spellchecker")).get();
+        SearchResponse search = prepareSearch().setQuery(matchQuery("text", "spellchecker")).get();
         assertThat("didn't ask for suggestions but got some", search.getSuggest(), nullValue());
 
         TermSuggestionBuilder termSuggestion = termSuggestion("text").suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can
@@ -308,12 +308,12 @@ public void testUnmappedField() throws IOException, InterruptedException, Execut
             candidateGenerator("name").prefixLength(0).minWordLength(0).suggestMode("always").maxEdits(2)
         ).gramSize(3);
         {
-            SearchRequestBuilder searchBuilder = client().prepareSearch().setSize(0);
+            SearchRequestBuilder searchBuilder = prepareSearch().setSize(0);
             searchBuilder.suggest(new SuggestBuilder().setGlobalText("tetsting sugestion").addSuggestion("did_you_mean", phraseSuggestion));
             assertRequestBuilderThrows(searchBuilder, SearchPhaseExecutionException.class);
         }
         {
-            SearchRequestBuilder searchBuilder = client().prepareSearch().setSize(0);
+            SearchRequestBuilder searchBuilder = prepareSearch().setSize(0);
             searchBuilder.suggest(new SuggestBuilder().setGlobalText("tetsting sugestion").addSuggestion("did_you_mean", phraseSuggestion));
             assertRequestBuilderThrows(searchBuilder, SearchPhaseExecutionException.class);
         }
@@ -329,7 +329,7 @@ public void testSimple() throws Exception {
         indexDoc("test", "4", "text", "abcc");
         refresh();
 
-        SearchResponse search = client().prepareSearch().setQuery(matchQuery("text", "spellcecker")).get();
+        SearchResponse search = prepareSearch().setQuery(matchQuery("text", "spellcecker")).get();
         assertThat("didn't ask for suggestions but got some", search.getSuggest(), nullValue());
 
         TermSuggestionBuilder termSuggest = termSuggestion("text").suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary
@@ -828,8 +828,7 @@ public void testShardFailures() throws IOException, InterruptedException {
         refresh();
 
         // When searching on a shard with a non existing mapping, we should fail
-        SearchRequestBuilder request = client().prepareSearch()
-            .setSize(0)
+        SearchRequestBuilder request = prepareSearch().setSize(0)
             .suggest(
                 new SuggestBuilder().setGlobalText("tetsting sugestion")
                     .addSuggestion("did_you_mean", phraseSuggestion("fielddoesnotexist").maxErrors(5.0f))
@@ -837,8 +836,7 @@ public void testShardFailures() throws IOException, InterruptedException {
         assertRequestBuilderThrows(request, SearchPhaseExecutionException.class);
 
         // When searching on a shard which does not hold yet any document of an existing type, we should not fail
-        SearchResponse searchResponse = client().prepareSearch()
-            .setSize(0)
+        SearchResponse searchResponse = prepareSearch().setSize(0)
             .suggest(
                 new SuggestBuilder().setGlobalText("tetsting sugestion")
                     .addSuggestion("did_you_mean", phraseSuggestion("name").maxErrors(5.0f))
@@ -878,8 +876,7 @@ public void testEmptyShards() throws IOException, InterruptedException {
         ensureGreen();
 
         // test phrase suggestion on completely empty index
-        SearchResponse searchResponse = client().prepareSearch()
-            .setSize(0)
+        SearchResponse searchResponse = prepareSearch().setSize(0)
             .suggest(
                 new SuggestBuilder().setGlobalText("tetsting sugestion")
                     .addSuggestion("did_you_mean", phraseSuggestion("name").maxErrors(5.0f))
@@ -897,8 +894,7 @@ public void testEmptyShards() throws IOException, InterruptedException {
         refresh();
 
         // test phrase suggestion but nothing matches
-        searchResponse = client().prepareSearch()
-            .setSize(0)
+        searchResponse = prepareSearch().setSize(0)
             .suggest(
                 new SuggestBuilder().setGlobalText("tetsting sugestion")
                     .addSuggestion("did_you_mean", phraseSuggestion("name").maxErrors(5.0f))
@@ -914,8 +910,7 @@ public void testEmptyShards() throws IOException, InterruptedException {
         indexDoc("test", "1", "name", "Just testing the suggestions api");
         refresh();
 
-        searchResponse = client().prepareSearch()
-            .setSize(0)
+        searchResponse = prepareSearch().setSize(0)
             .suggest(
                 new SuggestBuilder().setGlobalText("tetsting sugestion")
                     .addSuggestion("did_you_mean", phraseSuggestion("name").maxErrors(5.0f))
@@ -1416,7 +1411,7 @@ protected Suggest searchSuggest(String suggestText, String name, SuggestionBuild
     }
 
     protected Suggest searchSuggest(String suggestText, int expectShardsFailed, Map> suggestions) {
-        SearchRequestBuilder builder = client().prepareSearch().setSize(0);
+        SearchRequestBuilder builder = prepareSearch().setSize(0);
         SuggestBuilder suggestBuilder = new SuggestBuilder();
         if (suggestText != null) {
             suggestBuilder.setGlobalText(suggestText);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/similarity/SimilarityIT.java b/server/src/internalClusterTest/java/org/elasticsearch/similarity/SimilarityIT.java
index c43f13be7d100..5c1f925bddc49 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/similarity/SimilarityIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/similarity/SimilarityIT.java
@@ -55,17 +55,11 @@ public void testCustomBM25Similarity() throws Exception {
             .execute()
             .actionGet();
 
-        SearchResponse bm25SearchResponse = client().prepareSearch()
-            .setQuery(matchQuery("field1", "quick brown fox"))
-            .execute()
-            .actionGet();
+        SearchResponse bm25SearchResponse = prepareSearch().setQuery(matchQuery("field1", "quick brown fox")).execute().actionGet();
         assertThat(bm25SearchResponse.getHits().getTotalHits().value, equalTo(1L));
         float bm25Score = bm25SearchResponse.getHits().getHits()[0].getScore();
 
-        SearchResponse booleanSearchResponse = client().prepareSearch()
-            .setQuery(matchQuery("field2", "quick brown fox"))
-            .execute()
-            .actionGet();
+        SearchResponse booleanSearchResponse = prepareSearch().setQuery(matchQuery("field2", "quick brown fox")).execute().actionGet();
         assertThat(booleanSearchResponse.getHits().getTotalHits().value, equalTo(1L));
         float defaultScore = booleanSearchResponse.getHits().getHits()[0].getScore();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/versioning/SimpleVersioningIT.java b/server/src/internalClusterTest/java/org/elasticsearch/versioning/SimpleVersioningIT.java
index c6fbdc909e2e6..5a1c09098f21f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/versioning/SimpleVersioningIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/versioning/SimpleVersioningIT.java
@@ -327,13 +327,13 @@ public void testCompareAndSet() {
         // search with versioning
         for (int i = 0; i < 10; i++) {
             // TODO: ADD SEQ NO!
-            SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setVersion(true).execute().actionGet();
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).setVersion(true).execute().actionGet();
             assertThat(searchResponse.getHits().getAt(0).getVersion(), equalTo(2L));
         }
 
         // search without versioning
         for (int i = 0; i < 10; i++) {
-            SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).execute().actionGet();
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery()).execute().actionGet();
             assertThat(searchResponse.getHits().getAt(0).getVersion(), equalTo(Versions.NOT_FOUND));
         }
 
@@ -396,8 +396,7 @@ public void testSimpleVersioningWithFlush() throws Exception {
         client().admin().indices().prepareRefresh().execute().actionGet();
 
         for (int i = 0; i < 10; i++) {
-            SearchResponse searchResponse = client().prepareSearch()
-                .setQuery(matchAllQuery())
+            SearchResponse searchResponse = prepareSearch().setQuery(matchAllQuery())
                 .setVersion(true)
                 .seqNoAndPrimaryTerm(true)
                 .execute()
diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java
index 45ec7c00bd441..9fbf63a4e0895 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java
@@ -60,9 +60,9 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() {
-        SearchResponse response = client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME)
-            .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))
-            .get();
+        SearchResponse response = prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME).addAggregation(
+            centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)
+        ).get();
         assertNoFailures(response);
 
         CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java
index f63401d34b624..d507e83ffe00e 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java
@@ -127,8 +127,7 @@ public void testUnmapped() throws Exception {
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME)
-            .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))
+        SearchResponse response = prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))
             .get();
 
         assertNoFailures(response);
diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java
index 6aa245d429e51..8574fcbb05959 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java
@@ -303,8 +303,7 @@ public void testShapeRelations() throws Exception {
         client().admin().indices().prepareRefresh().get();
 
         // Point in polygon
-        SearchResponse result = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse result = prepareSearch().setQuery(matchAllQuery())
             .setPostFilter(queryBuilder().intersectionQuery("area", new Point(3, 3)))
             .get();
         assertHitCount(result, 1);
@@ -312,7 +311,7 @@ public void testShapeRelations() throws Exception {
 
         // Point in polygon hole
         assertHitCount(
-            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5))),
+            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5))),
             0
         );
 
@@ -321,32 +320,24 @@ public void testShapeRelations() throws Exception {
         // of the polygon NOT the hole
 
         // Point on polygon border
-        result = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        result = prepareSearch().setQuery(matchAllQuery())
             .setPostFilter(queryBuilder().intersectionQuery("area", new Point(10.0, 5.0)))
             .get();
         assertHitCount(result, 1);
         assertFirstHit(result, hasId("1"));
 
         // Point on hole border
-        result = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        result = prepareSearch().setQuery(matchAllQuery())
             .setPostFilter(queryBuilder().intersectionQuery("area", new Point(5.0, 2.0)))
             .get();
         assertHitCount(result, 1);
         assertFirstHit(result, hasId("1"));
 
         // Point not in polygon
-        assertHitCount(
-            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(3, 3))),
-            0
-        );
+        assertHitCount(prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(3, 3))), 0);
 
         // Point in polygon hole
-        result = client().prepareSearch()
-            .setQuery(matchAllQuery())
-            .setPostFilter(queryBuilder().disjointQuery("area", new Point(4.5, 4.5)))
-            .get();
+        result = prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(4.5, 4.5))).get();
         assertHitCount(result, 1);
         assertFirstHit(result, hasId("1"));
 
@@ -361,8 +352,7 @@ public void testShapeRelations() throws Exception {
         client().admin().indices().prepareRefresh().get();
 
         // re-check point on polygon hole
-        result = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        result = prepareSearch().setQuery(matchAllQuery())
             .setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5)))
             .get();
         assertHitCount(result, 1);
@@ -371,7 +361,7 @@ public void testShapeRelations() throws Exception {
         // Polygon WithIn Polygon
         Polygon WithIn = new Polygon(new LinearRing(new double[] { -30, -30, 30, 30, -30 }, new double[] { -30, 30, 30, -30, -30 }));
 
-        assertHitCount(client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().withinQuery("area", WithIn)), 2);
+        assertHitCount(prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().withinQuery("area", WithIn)), 2);
 
         // Create a polygon crossing longitude 180.
         Polygon crossing = new Polygon(new LinearRing(new double[] { 170, 190, 190, 170, 170 }, new double[] { -10, -10, 10, 10, -10 }));
@@ -391,24 +381,22 @@ public void testShapeRelations() throws Exception {
         client().admin().indices().prepareRefresh().get();
 
         assertHitCount(
-            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(174, -4))),
+            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(174, -4))),
             1
         );
 
         // In geo coordinates the polygon wraps the dateline, so we need to search within valid longitude ranges
         double xWrapped = getFieldTypeName().contains("geo") ? -174 : 186;
         assertHitCount(
-            client().prepareSearch()
-                .setQuery(matchAllQuery())
-                .setPostFilter(queryBuilder().intersectionQuery("area", new Point(xWrapped, -4))),
+            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(xWrapped, -4))),
             1
         );
         assertHitCount(
-            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -4))),
+            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -4))),
             0
         );
         assertHitCount(
-            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -6))),
+            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -6))),
             1
         );
     }
@@ -437,7 +425,7 @@ public void testBulk() throws Exception {
         client().admin().indices().prepareRefresh().get();
         String key = "DE";
 
-        SearchResponse searchResponse = client().prepareSearch().setQuery(matchQuery("_id", key)).get();
+        SearchResponse searchResponse = prepareSearch().setQuery(matchQuery("_id", key)).get();
 
         assertHitCount(searchResponse, 1);
 
diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java
index 96b7ff7a8a38a..14f8ca16f5996 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java
@@ -103,7 +103,7 @@ public void testSimpleBoundingBoxTest() throws Exception {
 
         client().admin().indices().prepareRefresh().get();
 
-        SearchResponse searchResponse = client().prepareSearch() // from NY
+        SearchResponse searchResponse = prepareSearch() // from NY
             .setQuery(geoBoundingBoxQuery("location").setCorners(40.73, -74.1, 40.717, -73.99))
             .get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
@@ -112,7 +112,7 @@ public void testSimpleBoundingBoxTest() throws Exception {
             assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
         }
 
-        searchResponse = client().prepareSearch() // from NY
+        searchResponse = prepareSearch() // from NY
             .setQuery(geoBoundingBoxQuery("location").setCorners(40.73, -74.1, 40.717, -73.99))
             .get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
@@ -121,7 +121,7 @@ public void testSimpleBoundingBoxTest() throws Exception {
             assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
         }
 
-        searchResponse = client().prepareSearch() // top == bottom && left == right
+        searchResponse = prepareSearch() // top == bottom && left == right
             .setQuery(geoBoundingBoxQuery("location").setCorners(40.7143528, -74.0059731, 40.7143528, -74.0059731))
             .get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
@@ -130,7 +130,7 @@ public void testSimpleBoundingBoxTest() throws Exception {
             assertThat(hit.getId(), equalTo("1"));
         }
 
-        searchResponse = client().prepareSearch() // top == bottom
+        searchResponse = prepareSearch() // top == bottom
             .setQuery(geoBoundingBoxQuery("location").setCorners(40.759011, -74.00009, 40.759011, -73.0059731))
             .get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
@@ -139,7 +139,7 @@ public void testSimpleBoundingBoxTest() throws Exception {
             assertThat(hit.getId(), equalTo("2"));
         }
 
-        searchResponse = client().prepareSearch() // left == right
+        searchResponse = prepareSearch() // left == right
             .setQuery(geoBoundingBoxQuery("location").setCorners(41.8, -73.9844722, 40.7, -73.9844722))
             .get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
@@ -149,7 +149,7 @@ public void testSimpleBoundingBoxTest() throws Exception {
         }
 
         // Distance query
-        searchResponse = client().prepareSearch() // from NY
+        searchResponse = prepareSearch() // from NY
             .setQuery(geoDistanceQuery("location").point(40.5, -73.9).distance(25, DistanceUnit.KILOMETERS))
             .get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
@@ -189,120 +189,96 @@ public void testLimit2BoundingBox() throws Exception {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 880))
-                    .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-            )
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 880))
+                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 880))
-                    .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 880))
+                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 534))
-                    .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 534))
+                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 534))
-                    .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 534))
+                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
         // top == bottom && left == right
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 880))
-                    .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 59.328355000000002, 18.036842, 59.328355000000002))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 880))
+                .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 59.328355000000002, 18.036842, 59.328355000000002))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 534))
-                    .filter(
-                        geoBoundingBoxQuery("location").setCorners(
-                            45.509526999999999,
-                            -73.570986000000005,
-                            45.509526999999999,
-                            -73.570986000000005
-                        )
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 534))
+                .filter(
+                    geoBoundingBoxQuery("location").setCorners(
+                        45.509526999999999,
+                        -73.570986000000005,
+                        45.509526999999999,
+                        -73.570986000000005
                     )
-            )
-            .get();
+                )
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
         // top == bottom
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 880))
-                    .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 143.5, 18.036842, 113.96875))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 880))
+                .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 143.5, 18.036842, 113.96875))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 534))
-                    .filter(geoBoundingBoxQuery("location").setCorners(45.509526999999999, 143.5, 45.509526999999999, 113.96875))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 534))
+                .filter(geoBoundingBoxQuery("location").setCorners(45.509526999999999, 143.5, 45.509526999999999, 113.96875))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
         // left == right
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 880))
-                    .filter(
-                        geoBoundingBoxQuery("location").setCorners(
-                            74.579421999999994,
-                            59.328355000000002,
-                            -66.668903999999998,
-                            59.328355000000002
-                        )
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 880))
+                .filter(
+                    geoBoundingBoxQuery("location").setCorners(
+                        74.579421999999994,
+                        59.328355000000002,
+                        -66.668903999999998,
+                        59.328355000000002
                     )
-            )
-            .get();
+                )
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 534))
-                    .filter(
-                        geoBoundingBoxQuery("location").setCorners(
-                            74.579421999999994,
-                            -73.570986000000005,
-                            -66.668903999999998,
-                            -73.570986000000005
-                        )
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 534))
+                .filter(
+                    geoBoundingBoxQuery("location").setCorners(
+                        74.579421999999994,
+                        -73.570986000000005,
+                        -66.668903999999998,
+                        -73.570986000000005
                     )
-            )
-            .get();
+                )
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
         // Distance query
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 880))
-                    .filter(geoDistanceQuery("location").point(20, 60.0).distance(500, DistanceUnit.MILES))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 880))
+                .filter(geoDistanceQuery("location").point(20, 60.0).distance(500, DistanceUnit.MILES))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                boolQuery().must(termQuery("userid", 534))
-                    .filter(geoDistanceQuery("location").point(45.0, -73.0).distance(500, DistanceUnit.MILES))
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            boolQuery().must(termQuery("userid", 534))
+                .filter(geoDistanceQuery("location").point(45.0, -73.0).distance(500, DistanceUnit.MILES))
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
     }
 
@@ -336,60 +312,54 @@ public void testCompleteLonRange() throws Exception {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180))
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
 
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        searchResponse = client().prepareSearch()
-            .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360))
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
 
         // top == bottom
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
-                    .setCorners(59.328355000000002, 0, 59.328355000000002, 360)
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
+                .setCorners(59.328355000000002, 0, 59.328355000000002, 360)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = client().prepareSearch()
-            .setQuery(
-                geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
-                    .setCorners(59.328355000000002, -180, 59.328355000000002, 180)
-            )
-            .get();
+        searchResponse = prepareSearch().setQuery(
+            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
+                .setCorners(59.328355000000002, -180, 59.328355000000002, 180)
+        ).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
 
         // Distance query
-        searchResponse = client().prepareSearch()
-            .setQuery(geoDistanceQuery("location").point(60.0, -20.0).distance(1800, DistanceUnit.MILES))
-            .get();
+        searchResponse = prepareSearch().setQuery(geoDistanceQuery("location").point(60.0, -20.0).distance(1800, DistanceUnit.MILES)).get();
         assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
     }
 }
diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java
index 547ec9f2578e0..29307f7f63ce9 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoShapeIntegTestCase.java
@@ -73,12 +73,11 @@ public void testIndexPolygonDateLine() throws Exception {
     /** The testBulk method uses this only for Geo-specific tests */
     protected void doDistanceAndBoundingBoxTest(String key) {
         assertHitCount(
-            client().prepareSearch().addStoredField("pin").setQuery(geoBoundingBoxQuery("pin").setCorners(90, -179.99999, -90, 179.99999)),
+            prepareSearch().addStoredField("pin").setQuery(geoBoundingBoxQuery("pin").setCorners(90, -179.99999, -90, 179.99999)),
             53
         );
 
-        SearchResponse distance = client().prepareSearch()
-            .addStoredField("pin")
+        SearchResponse distance = prepareSearch().addStoredField("pin")
             .setQuery(geoDistanceQuery("pin").distance("425km").point(51.11, 9.851))
             .get();
 
diff --git a/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java b/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java
index f366a18c7393f..d2caed465d6e3 100644
--- a/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java
+++ b/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java
@@ -234,8 +234,7 @@ public void testRetryPointInTime() throws Exception {
         ).keepAlive(TimeValue.timeValueMinutes(2));
         final String pitId = client().execute(OpenPointInTimeAction.INSTANCE, openPointInTimeRequest).actionGet().getPointInTimeId();
         try {
-            SearchResponse resp = client().prepareSearch()
-                .setIndices(indexName)
+            SearchResponse resp = prepareSearch().setIndices(indexName)
                 .setPreference(null)
                 .setPointInTime(new PointInTimeBuilder(pitId))
                 .get();
@@ -244,8 +243,7 @@ public void testRetryPointInTime() throws Exception {
             assertHitCount(resp, numDocs);
             internalCluster().restartNode(assignedNode);
             ensureGreen(indexName);
-            resp = client().prepareSearch()
-                .setIndices(indexName)
+            resp = prepareSearch().setIndices(indexName)
                 .setQuery(new RangeQueryBuilder("created_date").gte("2011-01-01").lte("2011-12-12"))
                 .setSearchType(SearchType.QUERY_THEN_FETCH)
                 .setPreference(null)
@@ -287,8 +285,7 @@ public void testPointInTimeWithDeletedIndices() {
         try {
             indicesAdmin().prepareDelete("index-1").get();
             // Return partial results if allow partial search result is allowed
-            SearchResponse resp = client().prepareSearch()
-                .setPreference(null)
+            SearchResponse resp = prepareSearch().setPreference(null)
                 .setAllowPartialSearchResults(true)
                 .setPointInTime(new PointInTimeBuilder(pitId))
                 .get();
@@ -297,10 +294,7 @@ public void testPointInTimeWithDeletedIndices() {
             // Fails if allow partial search result is not allowed
             expectThrows(
                 ElasticsearchException.class,
-                client().prepareSearch()
-                    .setPreference(null)
-                    .setAllowPartialSearchResults(false)
-                    .setPointInTime(new PointInTimeBuilder(pitId))::get
+                prepareSearch().setPreference(null).setAllowPartialSearchResults(false).setPointInTime(new PointInTimeBuilder(pitId))::get
             );
         } finally {
             client().execute(ClosePointInTimeAction.INSTANCE, new ClosePointInTimeRequest(pitId)).actionGet();
@@ -323,7 +317,7 @@ public void testOpenPointInTimeWithNoIndexMatched() {
             ).keepAlive(TimeValue.timeValueMinutes(2));
             final String pitId = client().execute(OpenPointInTimeAction.INSTANCE, openPointInTimeRequest).actionGet().getPointInTimeId();
             try {
-                SearchResponse resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+                SearchResponse resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
                 assertNoFailures(resp);
                 assertHitCount(resp, numDocs);
             } finally {
@@ -337,7 +331,7 @@ public void testOpenPointInTimeWithNoIndexMatched() {
             );
             final String pitId = client().execute(OpenPointInTimeAction.INSTANCE, openPointInTimeRequest).actionGet().getPointInTimeId();
             try {
-                SearchResponse resp = client().prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
+                SearchResponse resp = prepareSearch().setPreference(null).setPointInTime(new PointInTimeBuilder(pitId)).get();
                 assertNoFailures(resp);
                 assertHitCount(resp, 0);
             } finally {
diff --git a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java
index d86f090456061..0b0af8f8f9acf 100644
--- a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java
+++ b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongTests.java
@@ -295,11 +295,7 @@ public void testAggs() {
     public void testSortDifferentFormatsShouldFail() {
         Exception exception = expectThrows(
             SearchPhaseExecutionException.class,
-            () -> client().prepareSearch()
-                .setIndices("idx", "idx2")
-                .setQuery(QueryBuilders.matchAllQuery())
-                .addSort("ul_field", SortOrder.ASC)
-                .get()
+            () -> prepareSearch().setIndices("idx", "idx2").setQuery(QueryBuilders.matchAllQuery()).addSort("ul_field", SortOrder.ASC).get()
         );
         assertEquals(
             exception.getCause().getMessage(),
diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobStorageDeletionTaskIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobStorageDeletionTaskIT.java
index c1a74444d19ce..aa8b29228b790 100644
--- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobStorageDeletionTaskIT.java
+++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobStorageDeletionTaskIT.java
@@ -203,8 +203,7 @@ public void testDeleteDedicatedJobWithDataInShared() throws Exception {
 
         // Make sure all results referencing the dedicated job are gone
         assertThat(
-            client().prepareSearch()
-                .setIndices(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*")
+            prepareSearch().setIndices(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*")
                 .setIndicesOptions(IndicesOptions.lenientExpandOpenHidden())
                 .setTrackTotalHits(true)
                 .setSize(0)
diff --git a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java
index 777d563314887..685592afef167 100644
--- a/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java
+++ b/x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/MlDistributedFailureIT.java
@@ -751,8 +751,7 @@ private void run(String jobId, CheckedRunnable disrupt) throws Except
     // so when restarting job on another node the data counts
     // are what we expect them to be:
     private static DataCounts getDataCountsFromIndex(String jobId) {
-        SearchResponse searchResponse = client().prepareSearch()
-            .setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN)
+        SearchResponse searchResponse = prepareSearch().setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN)
             .setQuery(QueryBuilders.idsQuery().addIds(DataCounts.documentId(jobId)))
             .get();
         if (searchResponse.getHits().getTotalHits().value != 1) {
diff --git a/x-pack/plugin/search-business-rules/src/internalClusterTest/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderIT.java b/x-pack/plugin/search-business-rules/src/internalClusterTest/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderIT.java
index 571f75a3ca648..9012946abc686 100644
--- a/x-pack/plugin/search-business-rules/src/internalClusterTest/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderIT.java
+++ b/x-pack/plugin/search-business-rules/src/internalClusterTest/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderIT.java
@@ -113,8 +113,7 @@ public void testPinnedPromotions() throws Exception {
     private void assertPinnedPromotions(PinnedQueryBuilder pqb, LinkedHashSet pins, int iter, int numRelevantDocs) {
         int from = randomIntBetween(0, numRelevantDocs);
         int size = randomIntBetween(10, 100);
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(pqb)
+        SearchResponse searchResponse = prepareSearch().setQuery(pqb)
             .setTrackTotalHits(true)
             .setSize(size)
             .setFrom(from)
@@ -194,11 +193,7 @@ public void testExhaustiveScoring() throws Exception {
     }
 
     private void assertExhaustiveScoring(PinnedQueryBuilder pqb) {
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(pqb)
-            .setTrackTotalHits(true)
-            .setSearchType(DFS_QUERY_THEN_FETCH)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(pqb).setTrackTotalHits(true).setSearchType(DFS_QUERY_THEN_FETCH).get();
 
         long numHits = searchResponse.getHits().getTotalHits().value;
         assertThat(numHits, equalTo(2L));
@@ -232,11 +227,7 @@ public void testExplain() throws Exception {
     }
 
     private void assertExplain(PinnedQueryBuilder pqb) {
-        SearchResponse searchResponse = client().prepareSearch()
-            .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
-            .setQuery(pqb)
-            .setExplain(true)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setSearchType(SearchType.DFS_QUERY_THEN_FETCH).setQuery(pqb).setExplain(true).get();
         assertHitCount(searchResponse, 3);
         assertFirstHit(searchResponse, hasId("2"));
         assertSecondHit(searchResponse, hasId("1"));
@@ -280,8 +271,7 @@ private void assertHighlight(PinnedQueryBuilder pqb) {
         HighlightBuilder testHighlighter = new HighlightBuilder();
         testHighlighter.field("field1");
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
+        SearchResponse searchResponse = prepareSearch().setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
             .setQuery(pqb)
             .highlighter(testHighlighter)
             .setExplain(true)
@@ -340,11 +330,7 @@ public void testMultiIndexDocs() throws Exception {
             new Item("test1", "b")
         );
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(pqb)
-            .setTrackTotalHits(true)
-            .setSearchType(DFS_QUERY_THEN_FETCH)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(pqb).setTrackTotalHits(true).setSearchType(DFS_QUERY_THEN_FETCH).get();
 
         assertHitCount(searchResponse, 4);
         assertFirstHit(searchResponse, both(hasIndex("test2")).and(hasId("a")));
@@ -384,11 +370,7 @@ public void testMultiIndexWithAliases() throws Exception {
             new Item("test", "a")
         );
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(pqb)
-            .setTrackTotalHits(true)
-            .setSearchType(DFS_QUERY_THEN_FETCH)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(pqb).setTrackTotalHits(true).setSearchType(DFS_QUERY_THEN_FETCH).get();
 
         assertHitCount(searchResponse, 3);
         assertFirstHit(searchResponse, both(hasIndex("test")).and(hasId("b")));
@@ -446,11 +428,7 @@ public void testMultiIndexWithAliasesAndDuplicateIds() throws Exception {
             new Item("test-alias", "a")
         );
 
-        SearchResponse searchResponse = client().prepareSearch()
-            .setQuery(pqb)
-            .setTrackTotalHits(true)
-            .setSearchType(DFS_QUERY_THEN_FETCH)
-            .get();
+        SearchResponse searchResponse = prepareSearch().setQuery(pqb).setTrackTotalHits(true).setSearchType(DFS_QUERY_THEN_FETCH).get();
 
         assertHitCount(searchResponse, 4);
         assertFirstHit(searchResponse, both(hasIndex("test1")).and(hasId("b")));
diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/RetrySearchIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/RetrySearchIntegTests.java
index 0fda9e4e66f34..39e476107a0d6 100644
--- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/RetrySearchIntegTests.java
+++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/RetrySearchIntegTests.java
@@ -144,8 +144,7 @@ public void testRetryPointInTime() throws Exception {
         ).keepAlive(TimeValue.timeValueMinutes(2));
         final String pitId = client().execute(OpenPointInTimeAction.INSTANCE, openRequest).actionGet().getPointInTimeId();
         try {
-            SearchResponse resp = client().prepareSearch()
-                .setIndices(indexName)
+            SearchResponse resp = prepareSearch().setIndices(indexName)
                 .setPreference(null)
                 .setPointInTime(new PointInTimeBuilder(pitId))
                 .get();
@@ -158,8 +157,7 @@ public void testRetryPointInTime() throws Exception {
                 internalCluster().restartNode(allocatedNode);
             }
             ensureGreen(indexName);
-            resp = client().prepareSearch()
-                .setIndices(indexName)
+            resp = prepareSearch().setIndices(indexName)
                 .setQuery(new RangeQueryBuilder("created_date").gte("2011-01-01").lte("2011-12-12"))
                 .setSearchType(SearchType.QUERY_THEN_FETCH)
                 .setPreference(null)
diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java
index 6a439dd5c8561..57137075c5942 100644
--- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java
+++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/SecurityScrollTests.java
@@ -77,8 +77,7 @@ public void testSearchAndClearScroll() throws Exception {
             docs[i] = client().prepareIndex("idx").setSource("field", "value");
         }
         indexRandom(true, docs);
-        SearchResponse response = client().prepareSearch()
-            .setQuery(matchAllQuery())
+        SearchResponse response = prepareSearch().setQuery(matchAllQuery())
             .setScroll(TimeValue.timeValueSeconds(5L))
             .setSize(randomIntBetween(1, 10))
             .get();
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
index 31158b2371e50..a0204b38cd0ca 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
@@ -150,8 +150,7 @@ public void testPercolatorGeoQueries() throws Exception {
         refresh();
 
         BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "POINT(4.51 52.20)").endObject());
-        SearchResponse response = client().prepareSearch()
-            .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
+        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
             .addSort("id", SortOrder.ASC)
             .get();
         assertHitCount(response, 3);

From 6b8790571a3cb1e8bf4162197f7e83f5dd282dc4 Mon Sep 17 00:00:00 2001
From: Ignacio Vera 
Date: Mon, 23 Oct 2023 07:47:12 +0200
Subject: [PATCH 050/203] Increase GeoHexGridTiler#FACTOR (#101142)

we added a more aggressive way to handle geotile boundaries which affects the correction factor for GeoHexGridTiler.
---
 .../search/aggregations/bucket/geogrid/GeoHexGridTiler.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridTiler.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridTiler.java
index 240d3a4c32ee7..05a7ec36db9f4 100644
--- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridTiler.java
+++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexGridTiler.java
@@ -240,7 +240,7 @@ static class BoundedGeoHexGridTiler extends GeoHexGridTiler {
         private final GeoBoundingBox bbox;
         private final GeoHexVisitor visitor;
         private final int resolution;
-        private static final double FACTOR = 0.36;
+        private static final double FACTOR = 0.37;
 
         BoundedGeoHexGridTiler(int resolution, GeoBoundingBox bbox) {
             super(resolution);

From cfb0780b7a4cb819dd25ca57291058f571ebcae8 Mon Sep 17 00:00:00 2001
From: David Turner 
Date: Mon, 23 Oct 2023 08:17:40 +0100
Subject: [PATCH 051/203] More robust timeout for repo analysis (#101184)

Replaces the transport-level timeout with an overall timeout on the
whole repository analysis task to ensure that all child tasks terminate
promptly.

Relates #66992 Closes #101182
---
 docs/changelog/101184.yaml                    |  6 +++
 .../rest-api-spec/test/10_analyze.yml         |  3 +-
 .../testkit/RepositoryAnalysisFailureIT.java  | 36 +++++++++++--
 .../testkit/RepositoryAnalyzeAction.java      | 53 ++++++++++++++-----
 4 files changed, 79 insertions(+), 19 deletions(-)
 create mode 100644 docs/changelog/101184.yaml

diff --git a/docs/changelog/101184.yaml b/docs/changelog/101184.yaml
new file mode 100644
index 0000000000000..ac2f5f3ee8af1
--- /dev/null
+++ b/docs/changelog/101184.yaml
@@ -0,0 +1,6 @@
+pr: 101184
+summary: More robust timeout for repo analysis
+area: Snapshot/Restore
+type: bug
+issues:
+ - 101182
diff --git a/x-pack/plugin/snapshot-repo-test-kit/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/10_analyze.yml b/x-pack/plugin/snapshot-repo-test-kit/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/10_analyze.yml
index 6223ca8443b0e..648eb3766fffb 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/10_analyze.yml
+++ b/x-pack/plugin/snapshot-repo-test-kit/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/10_analyze.yml
@@ -176,4 +176,5 @@ setup:
   - match: { status: 500 }
   - match: { error.type: repository_verification_exception }
   - match: { error.reason: "/.*test_repo_slow..analysis.failed.*/" }
-  - match: { error.root_cause.0.type: receive_timeout_transport_exception }
+  - match: { error.root_cause.0.type: repository_verification_exception }
+  - match: { error.root_cause.0.reason: "/.*test_repo_slow..analysis.timed.out.after..1s.*/" }
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
index e5ba4c2c6930b..84e27cc26b77b 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
@@ -27,6 +27,7 @@
 import org.elasticsearch.common.util.concurrent.CountDown;
 import org.elasticsearch.core.CheckedConsumer;
 import org.elasticsearch.core.Nullable;
+import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.env.Environment;
 import org.elasticsearch.indices.recovery.RecoverySettings;
 import org.elasticsearch.plugins.Plugin;
@@ -61,6 +62,7 @@
 import java.util.stream.Collectors;
 
 import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
 
@@ -347,8 +349,26 @@ public BytesReference onCompareAndExchange(BytesRegister register, BytesReferenc
         }
     }
 
-    private RepositoryAnalyzeAction.Response analyseRepository(RepositoryAnalyzeAction.Request request) {
-        return client().execute(RepositoryAnalyzeAction.INSTANCE, request).actionGet(30L, TimeUnit.SECONDS);
+    public void testTimesOutSpinningRegisterAnalysis() {
+        final RepositoryAnalyzeAction.Request request = new RepositoryAnalyzeAction.Request("test-repo");
+        request.timeout(TimeValue.timeValueMillis(between(1, 1000)));
+
+        blobStore.setDisruption(new Disruption() {
+            @Override
+            public boolean compareAndExchangeReturnsWitness() {
+                return false;
+            }
+        });
+        final var exception = expectThrows(RepositoryVerificationException.class, () -> analyseRepository(request));
+        assertThat(exception.getMessage(), containsString("analysis failed"));
+        assertThat(
+            asInstanceOf(RepositoryVerificationException.class, exception.getCause()).getMessage(),
+            containsString("analysis timed out")
+        );
+    }
+
+    private void analyseRepository(RepositoryAnalyzeAction.Request request) {
+        client().execute(RepositoryAnalyzeAction.INSTANCE, request).actionGet(30L, TimeUnit.SECONDS);
     }
 
     private static void assertPurpose(OperationPurpose purpose) {
@@ -464,6 +484,10 @@ default boolean createBlobOnAbort() {
             return false;
         }
 
+        default boolean compareAndExchangeReturnsWitness() {
+            return true;
+        }
+
         default BytesReference onCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
             return register.compareAndExchange(expected, updated);
         }
@@ -637,8 +661,12 @@ public void compareAndExchangeRegister(
             ActionListener listener
         ) {
             assertPurpose(purpose);
-            final var register = registers.computeIfAbsent(key, ignored -> new BytesRegister());
-            listener.onResponse(OptionalBytesReference.of(disruption.onCompareAndExchange(register, expected, updated)));
+            if (disruption.compareAndExchangeReturnsWitness()) {
+                final var register = registers.computeIfAbsent(key, ignored -> new BytesRegister());
+                listener.onResponse(OptionalBytesReference.of(disruption.onCompareAndExchange(register, expected, updated)));
+            } else {
+                listener.onResponse(OptionalBytesReference.MISSING);
+            }
         }
     }
 
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
index 79de8bd7b0248..c49f14ed597fd 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
@@ -9,6 +9,7 @@
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.elasticsearch.ElasticsearchTimeoutException;
 import org.elasticsearch.ExceptionsHelper;
 import org.elasticsearch.TransportVersions;
 import org.elasticsearch.Version;
@@ -22,6 +23,7 @@
 import org.elasticsearch.action.support.ActionFilters;
 import org.elasticsearch.action.support.HandledTransportAction;
 import org.elasticsearch.action.support.RefCountingRunnable;
+import org.elasticsearch.action.support.SubscribableListener;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.node.DiscoveryNode;
 import org.elasticsearch.cluster.node.DiscoveryNodes;
@@ -364,6 +366,7 @@ public static class AsyncAction {
         private final DiscoveryNodes discoveryNodes;
         private final LongSupplier currentTimeMillisSupplier;
         private final ActionListener listener;
+        private final SubscribableListener cancellationListener;
         private final long timeoutTimeMillis;
 
         // choose the blob path nondeterministically to avoid clashes, assuming that the actual path doesn't matter for reproduction
@@ -394,15 +397,24 @@ public AsyncAction(
             this.discoveryNodes = discoveryNodes;
             this.currentTimeMillisSupplier = currentTimeMillisSupplier;
             this.timeoutTimeMillis = currentTimeMillisSupplier.getAsLong() + request.getTimeout().millis();
-            this.listener = listener;
+
+            this.cancellationListener = new SubscribableListener<>();
+            this.listener = ActionListener.runBefore(listener, () -> cancellationListener.onResponse(null));
 
             responses = new ArrayList<>(request.blobCount);
         }
 
-        private void fail(Exception e) {
+        private boolean setFirstFailure(Exception e) {
             if (failure.compareAndSet(null, e)) {
                 transportService.getTaskManager().cancelTaskAndDescendants(task, "task failed", false, ActionListener.noop());
+                return true;
             } else {
+                return false;
+            }
+        }
+
+        private void fail(Exception e) {
+            if (setFirstFailure(e) == false) {
                 if (innerFailures.tryAcquire()) {
                     final Throwable cause = ExceptionsHelper.unwrapCause(e);
                     if (cause instanceof TaskCancelledException || cause instanceof ReceiveTimeoutTransportException) {
@@ -424,24 +436,34 @@ private boolean isRunning() {
             }
 
             if (task.isCancelled()) {
-                failure.compareAndSet(null, new RepositoryVerificationException(request.repositoryName, "verification cancelled"));
+                setFirstFailure(new RepositoryVerificationException(request.repositoryName, "verification cancelled"));
                 // if this CAS failed then we're failing for some other reason, nbd; also if the task is cancelled then its descendants are
                 // also cancelled, so no further action is needed either way.
                 return false;
             }
 
-            if (timeoutTimeMillis < currentTimeMillisSupplier.getAsLong()) {
-                if (failure.compareAndSet(
-                    null,
-                    new RepositoryVerificationException(request.repositoryName, "analysis timed out after [" + request.getTimeout() + "]")
-                )) {
-                    transportService.getTaskManager().cancelTaskAndDescendants(task, "timed out", false, ActionListener.noop());
-                }
-                // if this CAS failed then we're already failing for some other reason, nbd
-                return false;
+            return true;
+        }
+
+        private class CheckForCancelListener implements ActionListener {
+            @Override
+            public void onResponse(Void unused) {
+                // task complete, nothing to do
             }
 
-            return true;
+            @Override
+            public void onFailure(Exception e) {
+                assert e instanceof ElasticsearchTimeoutException : e;
+                if (isRunning()) {
+                    // if this CAS fails then we're already failing for some other reason, nbd
+                    setFirstFailure(
+                        new RepositoryVerificationException(
+                            request.repositoryName,
+                            "analysis timed out after [" + request.getTimeout() + "]"
+                        )
+                    );
+                }
+            }
         }
 
         public void run() {
@@ -450,6 +472,9 @@ public void run() {
 
             logger.info("running analysis of repository [{}] using path [{}]", request.getRepositoryName(), blobPath);
 
+            cancellationListener.addTimeout(request.getTimeout(), repository.threadPool(), EsExecutors.DIRECT_EXECUTOR_SERVICE);
+            cancellationListener.addListener(new CheckForCancelListener());
+
             final Random random = new Random(request.getSeed());
             final List nodes = getSnapshotNodes(discoveryNodes);
 
@@ -536,7 +561,7 @@ private void runBlobAnalysis(Releasable ref, final BlobAnalyzeAction.Request req
                     BlobAnalyzeAction.NAME,
                     request,
                     task,
-                    TransportRequestOptions.timeout(TimeValue.timeValueMillis(timeoutTimeMillis - currentTimeMillisSupplier.getAsLong())),
+                    TransportRequestOptions.EMPTY,
                     new ActionListenerResponseHandler<>(ActionListener.releaseAfter(new ActionListener<>() {
                         @Override
                         public void onResponse(BlobAnalyzeAction.Response response) {

From f0ef872c2cff6219707d469718acc8c3df5a5578 Mon Sep 17 00:00:00 2001
From: Rene Groeschke 
Date: Mon, 23 Oct 2023 10:10:18 +0200
Subject: [PATCH 052/203] Update bundled JDK to 21.0.1 (#101133)

* Update docs/changelog/101133.yaml
---
 build-tools-internal/version.properties |  3 +--
 docs/changelog/101133.yaml              |  5 +++++
 gradle/verification-metadata.xml        | 26 ++++++++++++-------------
 3 files changed, 19 insertions(+), 15 deletions(-)
 create mode 100644 docs/changelog/101133.yaml

diff --git a/build-tools-internal/version.properties b/build-tools-internal/version.properties
index 5f2e9feca975e..dc43523b747b3 100644
--- a/build-tools-internal/version.properties
+++ b/build-tools-internal/version.properties
@@ -2,8 +2,7 @@ elasticsearch     = 8.12.0
 lucene            = 9.8.0
 
 bundled_jdk_vendor = openjdk
-bundled_jdk = 21+35@fd2272bbf8e04c3dbaee13770090416c
-
+bundled_jdk = 21.0.1+12@415e3f918a1f4062a0074a2794853d0d
 # optional dependencies
 spatial4j         = 0.7
 jts               = 1.15.0
diff --git a/docs/changelog/101133.yaml b/docs/changelog/101133.yaml
new file mode 100644
index 0000000000000..546a5392c309a
--- /dev/null
+++ b/docs/changelog/101133.yaml
@@ -0,0 +1,5 @@
+pr: 101133
+summary: Update bundled JDK to 21.0.1
+area: Packaging
+type: upgrade
+issues: []
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 34b3d4e578338..e340efb0c6987 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -1690,25 +1690,25 @@
             
          
       
-      
-         
-            
+      
+         
+            
          
-         
-            
+         
+            
          
       
-      
-         
-            
+      
+         
+            
          
-         
-            
+         
+            
          
       
-      
-         
-            
+      
+         
+            
          
       
       

From a1c1883a189c55beae1ccd074e88a3ea220fd527 Mon Sep 17 00:00:00 2001
From: David Turner 
Date: Mon, 23 Oct 2023 09:44:08 +0100
Subject: [PATCH 053/203] Rename RegisterAnalyzeAction to ContendedR...
 (#101192)

Relates #101185
---
 .../testkit/RepositoryAnalysisFailureIT.java  | 10 ++++---
 ...va => ContendedRegisterAnalyzeAction.java} | 15 +++++-----
 .../testkit/RepositoryAnalyzeAction.java      | 30 +++++++++++--------
 .../testkit/SnapshotRepositoryTestKit.java    |  2 +-
 4 files changed, 32 insertions(+), 25 deletions(-)
 rename x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/{RegisterAnalyzeAction.java => ContendedRegisterAnalyzeAction.java} (95%)

diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
index 84e27cc26b77b..d3537785e0f14 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
@@ -61,6 +61,8 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
+import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.bytesFromLong;
+import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes;
 import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -304,7 +306,7 @@ public void testFailsIfRegisterIncorrect() {
             @Override
             public BytesReference onCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
                 if (registerWasCorrupted.compareAndSet(false, true)) {
-                    register.updateAndGet(bytes -> RegisterAnalyzeAction.bytesFromLong(RegisterAnalyzeAction.longFromBytes(bytes) + 1));
+                    register.updateAndGet(bytes -> bytesFromLong(longFromBytes(bytes) + 1));
                 }
                 return register.compareAndExchange(expected, updated);
             }
@@ -321,9 +323,9 @@ public void testFailsIfRegisterHoldsSpuriousValue() {
             @Override
             public BytesReference onCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
                 if (randomBoolean() && sawSpuriousValue.compareAndSet(false, true)) {
-                    final var currentValue = RegisterAnalyzeAction.longFromBytes(register.get());
+                    final var currentValue = longFromBytes(register.get());
                     if (currentValue == expectedMax) {
-                        return RegisterAnalyzeAction.bytesFromLong(
+                        return bytesFromLong(
                             randomFrom(
                                 randomLongBetween(0L, expectedMax - 1),
                                 randomLongBetween(expectedMax + 1, Long.MAX_VALUE),
@@ -331,7 +333,7 @@ public BytesReference onCompareAndExchange(BytesRegister register, BytesReferenc
                             )
                         );
                     } else {
-                        return RegisterAnalyzeAction.bytesFromLong(
+                        return bytesFromLong(
                             randomFrom(expectedMax, randomLongBetween(expectedMax, Long.MAX_VALUE), randomLongBetween(Long.MIN_VALUE, -1))
                         );
                     }
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java
similarity index 95%
rename from x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java
rename to x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java
index cbe598675e0e2..18a18041c103c 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RegisterAnalyzeAction.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java
@@ -44,22 +44,23 @@
 import java.util.concurrent.ExecutorService;
 
 /**
- * An action which atomically increments a register using {@link BlobContainer#compareAndExchangeRegister}.
+ * An action which atomically increments a register using {@link BlobContainer#compareAndExchangeRegister}. There will be multiple parties
+ * accessing the register concurrently in order to test behaviour under contention.
  */
-public class RegisterAnalyzeAction extends ActionType {
+public class ContendedRegisterAnalyzeAction extends ActionType {
 
-    private static final Logger logger = LogManager.getLogger(RegisterAnalyzeAction.class);
+    private static final Logger logger = LogManager.getLogger(ContendedRegisterAnalyzeAction.class);
 
-    public static final RegisterAnalyzeAction INSTANCE = new RegisterAnalyzeAction();
+    public static final ContendedRegisterAnalyzeAction INSTANCE = new ContendedRegisterAnalyzeAction();
     public static final String NAME = "cluster:admin/repository/analyze/register";
 
-    private RegisterAnalyzeAction() {
+    private ContendedRegisterAnalyzeAction() {
         super(NAME, in -> ActionResponse.Empty.INSTANCE);
     }
 
     public static class TransportAction extends HandledTransportAction {
 
-        private static final Logger logger = RegisterAnalyzeAction.logger;
+        private static final Logger logger = ContendedRegisterAnalyzeAction.logger;
 
         private final RepositoriesService repositoriesService;
         private final ExecutorService executor;
@@ -259,7 +260,7 @@ public String toString() {
         public String getDescription() {
             return Strings.format(
                 """
-                    RegisterAnalyzeAction.Request{\
+                    ContendedRegisterAnalyzeAction.Request{\
                     repositoryName='%s', containerPath='%s', registerName='%s', requestCount='%d', initialRead='%d'}""",
                 repositoryName,
                 containerPath,
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
index c49f14ed597fd..06f9f75d1a5b0 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
@@ -82,6 +82,8 @@
 
 import static org.elasticsearch.core.Strings.format;
 import static org.elasticsearch.repositories.blobstore.testkit.BlobAnalyzeAction.MAX_ATOMIC_WRITE_SIZE;
+import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.bytesFromLong;
+import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes;
 import static org.elasticsearch.repositories.blobstore.testkit.SnapshotRepositoryTestKit.humanReadableNanos;
 
 /**
@@ -96,6 +98,8 @@ public class RepositoryAnalyzeAction extends ActionType nodes = getSnapshotNodes(discoveryNodes);
 
-            final String registerName = "test-register-" + UUIDs.randomBase64UUID(random);
-            try (var registerRefs = new RefCountingRunnable(finalRegisterValueVerifier(registerName, random, requestRefs.acquire()))) {
+            final String contendedRegisterName = CONTENDED_REGISTER_NAME_PREFIX + UUIDs.randomBase64UUID(random);
+            try (
+                var registerRefs = new RefCountingRunnable(finalRegisterValueVerifier(contendedRegisterName, random, requestRefs.acquire()))
+            ) {
                 final int registerOperations = Math.max(nodes.size(), request.getConcurrency());
                 for (int i = 0; i < registerOperations; i++) {
-                    final RegisterAnalyzeAction.Request registerAnalyzeRequest = new RegisterAnalyzeAction.Request(
+                    final ContendedRegisterAnalyzeAction.Request registerAnalyzeRequest = new ContendedRegisterAnalyzeAction.Request(
                         request.getRepositoryName(),
                         blobPath,
-                        registerName,
+                        contendedRegisterName,
                         registerOperations,
                         random.nextInt((registerOperations + 1) * 2)
                     );
                     final DiscoveryNode node = nodes.get(i < nodes.size() ? i : random.nextInt(nodes.size()));
                     final Releasable registerRef = registerRefs.acquire();
-                    queue.add(ref -> runRegisterAnalysis(Releasables.wrap(registerRef, ref), registerAnalyzeRequest, node));
+                    queue.add(ref -> runContendedRegisterAnalysis(Releasables.wrap(registerRef, ref), registerAnalyzeRequest, node));
                 }
             }
 
@@ -593,11 +599,11 @@ private BlobContainer getBlobContainer() {
             return repository.blobStore().blobContainer(repository.basePath().add(blobPath));
         }
 
-        private void runRegisterAnalysis(Releasable ref, RegisterAnalyzeAction.Request request, DiscoveryNode node) {
+        private void runContendedRegisterAnalysis(Releasable ref, ContendedRegisterAnalyzeAction.Request request, DiscoveryNode node) {
             if (node.getVersion().onOrAfter(Version.V_8_8_0) && isRunning()) {
                 transportService.sendChildRequest(
                     node,
-                    RegisterAnalyzeAction.NAME,
+                    ContendedRegisterAnalyzeAction.NAME,
                     request,
                     task,
                     TransportRequestOptions.EMPTY,
@@ -629,9 +635,7 @@ private Runnable finalRegisterValueVerifier(String registerName, Random random,
                             @Override
                             public void onResponse(OptionalBytesReference actualFinalRegisterValue) {
                                 if (actualFinalRegisterValue.isPresent() == false
-                                    || RegisterAnalyzeAction.longFromBytes(
-                                        actualFinalRegisterValue.bytesReference()
-                                    ) != expectedFinalRegisterValue) {
+                                    || longFromBytes(actualFinalRegisterValue.bytesReference()) != expectedFinalRegisterValue) {
                                     fail(
                                         new RepositoryVerificationException(
                                             request.getRepositoryName(),
@@ -659,18 +663,18 @@ public void onFailure(Exception exp) {
                                 case 1 -> getBlobContainer().compareAndExchangeRegister(
                                     OperationPurpose.REPOSITORY_ANALYSIS,
                                     registerName,
-                                    RegisterAnalyzeAction.bytesFromLong(expectedFinalRegisterValue),
+                                    bytesFromLong(expectedFinalRegisterValue),
                                     new BytesArray(new byte[] { (byte) 0xff }),
                                     listener
                                 );
                                 case 2 -> getBlobContainer().compareAndSetRegister(
                                     OperationPurpose.REPOSITORY_ANALYSIS,
                                     registerName,
-                                    RegisterAnalyzeAction.bytesFromLong(expectedFinalRegisterValue),
+                                    bytesFromLong(expectedFinalRegisterValue),
                                     new BytesArray(new byte[] { (byte) 0xff }),
                                     listener.map(
                                         b -> b
-                                            ? OptionalBytesReference.of(RegisterAnalyzeAction.bytesFromLong(expectedFinalRegisterValue))
+                                            ? OptionalBytesReference.of(bytesFromLong(expectedFinalRegisterValue))
                                             : OptionalBytesReference.MISSING
                                     )
                                 );
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java
index 98a5bb34f99ae..57c4ca8e4b964 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java
@@ -34,7 +34,7 @@ public class SnapshotRepositoryTestKit extends Plugin implements ActionPlugin {
             new ActionHandler<>(RepositoryAnalyzeAction.INSTANCE, RepositoryAnalyzeAction.TransportAction.class),
             new ActionHandler<>(BlobAnalyzeAction.INSTANCE, BlobAnalyzeAction.TransportAction.class),
             new ActionHandler<>(GetBlobChecksumAction.INSTANCE, GetBlobChecksumAction.TransportAction.class),
-            new ActionHandler<>(RegisterAnalyzeAction.INSTANCE, RegisterAnalyzeAction.TransportAction.class)
+            new ActionHandler<>(ContendedRegisterAnalyzeAction.INSTANCE, ContendedRegisterAnalyzeAction.TransportAction.class)
         );
     }
 

From ea430ec97a3354eaef49e39bdd6916a8fd6ba53b Mon Sep 17 00:00:00 2001
From: Ignacio Vera 
Date: Mon, 23 Oct 2023 11:40:08 +0200
Subject: [PATCH 054/203] Remove explicit SearchResponse references from
 spatial module test code (#101196)

---
 .../metrics/CentroidAggregationTestBase.java  | 160 ++++---
 .../SpatialBoundsAggregationTestBase.java     | 283 ++++++------
 .../search/geo/BaseShapeIntegTestCase.java    | 126 ++---
 .../search/geo/BaseShapeQueryTestCase.java    | 311 +++++++------
 .../geo/GeoBoundingBoxQueryIntegTestCase.java | 436 ++++++++++--------
 .../hamcrest/ElasticsearchAssertions.java     | 133 +++---
 .../GeoGridAggAndQueryConsistencyIT.java      |  87 ++--
 .../search/GeoShapeScriptDocValuesIT.java     |  72 +--
 .../search/GeoShapeWithDocValuesIT.java       |  45 +-
 .../search/LegacyGeoShapeWithDocValuesIT.java |   6 +-
 .../search/ShapeQueryOverShapeTests.java      | 146 +++---
 .../spatial/search/ShapeQueryTestCase.java    | 183 ++++----
 ...LegacyGeoShapeWithDocValuesQueryTests.java |  14 +-
 13 files changed, 1050 insertions(+), 952 deletions(-)

diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java
index 9fbf63a4e0895..664590d65c818 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/CentroidAggregationTestBase.java
@@ -8,7 +8,6 @@
 
 package org.elasticsearch.search.aggregations.metrics;
 
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.common.geo.SpatialPoint;
 import org.elasticsearch.search.aggregations.InternalAggregation;
@@ -18,7 +17,7 @@
 
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.search.aggregations.AggregationBuilders.global;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
 import static org.hamcrest.Matchers.closeTo;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.notNullValue;
@@ -34,94 +33,105 @@ public abstract class CentroidAggregationTestBase extends AbstractGeoTestCase {
     protected abstract ValuesSourceAggregationBuilder centroidAgg(String name);
 
     public void testEmptyAggregation() {
-        SearchResponse response = prepareSearch(EMPTY_IDX_NAME).setQuery(matchAllQuery())
-            .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))
-            .get();
-        assertNoFailures(response);
-
-        CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
-        assertThat(response.getHits().getTotalHits().value, equalTo(0L));
-        assertThat(geoCentroid, notNullValue());
-        assertThat(geoCentroid.getName(), equalTo(aggName()));
-        assertThat(geoCentroid.centroid(), equalTo(null));
-        assertEquals(0, geoCentroid.count());
+        assertNoFailuresAndResponse(
+            client().prepareSearch(EMPTY_IDX_NAME)
+                .setQuery(matchAllQuery())
+                .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
+                assertThat(response.getHits().getTotalHits().value, equalTo(0L));
+                assertThat(geoCentroid, notNullValue());
+                assertThat(geoCentroid.getName(), equalTo(aggName()));
+                assertThat(geoCentroid.centroid(), equalTo(null));
+                assertEquals(0, geoCentroid.count());
+            }
+        );
+
     }
 
     public void testUnmapped() throws Exception {
-        SearchResponse response = prepareSearch(UNMAPPED_IDX_NAME).addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))
-            .get();
-        assertNoFailures(response);
-
-        CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
-        assertThat(geoCentroid, notNullValue());
-        assertThat(geoCentroid.getName(), equalTo(aggName()));
-        assertThat(geoCentroid.centroid(), equalTo(null));
-        assertEquals(0, geoCentroid.count());
+        assertNoFailuresAndResponse(
+            client().prepareSearch(UNMAPPED_IDX_NAME).addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
+                assertThat(geoCentroid, notNullValue());
+                assertThat(geoCentroid.getName(), equalTo(aggName()));
+                assertThat(geoCentroid.centroid(), equalTo(null));
+                assertEquals(0, geoCentroid.count());
+            }
+        );
     }
 
     public void testPartiallyUnmapped() {
-        SearchResponse response = prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME).addAggregation(
-            centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)
-        ).get();
-        assertNoFailures(response);
-
-        CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
-        assertThat(geoCentroid, notNullValue());
-        assertThat(geoCentroid.getName(), equalTo(aggName()));
-        assertSameCentroid(geoCentroid.centroid(), singleCentroid);
-        assertEquals(numDocs, geoCentroid.count());
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME).addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
+                assertThat(geoCentroid, notNullValue());
+                assertThat(geoCentroid.getName(), equalTo(aggName()));
+                assertSameCentroid(geoCentroid.centroid(), singleCentroid);
+                assertEquals(numDocs, geoCentroid.count());
+            }
+        );
+
     }
 
     public void testSingleValuedField() {
-        SearchResponse response = prepareSearch(IDX_NAME).setQuery(matchAllQuery())
-            .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))
-            .get();
-        assertNoFailures(response);
-
-        CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
-        assertThat(geoCentroid, notNullValue());
-        assertThat(geoCentroid.getName(), equalTo(aggName()));
-        assertSameCentroid(geoCentroid.centroid(), singleCentroid);
-        assertEquals(numDocs, geoCentroid.count());
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME)
+                .setQuery(matchAllQuery())
+                .addAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
+                assertThat(geoCentroid, notNullValue());
+                assertThat(geoCentroid.getName(), equalTo(aggName()));
+                assertSameCentroid(geoCentroid.centroid(), singleCentroid);
+                assertEquals(numDocs, geoCentroid.count());
+            }
+        );
     }
 
     public void testSingleValueFieldGetProperty() {
-        SearchResponse response = prepareSearch(IDX_NAME).setQuery(matchAllQuery())
-            .addAggregation(global("global").subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME)))
-            .get();
-        assertNoFailures(response);
-
-        Global global = response.getAggregations().get("global");
-        assertThat(global, notNullValue());
-        assertThat(global.getName(), equalTo("global"));
-        assertThat(global.getDocCount(), equalTo((long) numDocs));
-        assertThat(global.getAggregations(), notNullValue());
-        assertThat(global.getAggregations().asMap().size(), equalTo(1));
-
-        CentroidAggregation geoCentroid = global.getAggregations().get(aggName());
-        InternalAggregation agg = (InternalAggregation) global;
-        assertThat(geoCentroid, notNullValue());
-        assertThat(geoCentroid.getName(), equalTo(aggName()));
-        assertThat((CentroidAggregation) agg.getProperty(aggName()), sameInstance(geoCentroid));
-        assertSameCentroid(geoCentroid.centroid(), singleCentroid);
-        assertSimilarValue(((SpatialPoint) agg.getProperty(aggName() + ".value")).getY(), singleCentroid.getY());
-        assertSimilarValue(((SpatialPoint) agg.getProperty(aggName() + ".value")).getX(), singleCentroid.getX());
-        assertSimilarValue((double) agg.getProperty(aggName() + "." + coordinateName("y")), singleCentroid.getY());
-        assertSimilarValue((double) agg.getProperty(aggName() + "." + coordinateName("x")), singleCentroid.getX());
-        assertEquals(numDocs, (long) ((InternalAggregation) global).getProperty(aggName() + ".count"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME)
+                .setQuery(matchAllQuery())
+                .addAggregation(global("global").subAggregation(centroidAgg(aggName()).field(SINGLE_VALUED_FIELD_NAME))),
+            response -> {
+                Global global = response.getAggregations().get("global");
+                assertThat(global, notNullValue());
+                assertThat(global.getName(), equalTo("global"));
+                assertThat(global.getDocCount(), equalTo((long) numDocs));
+                assertThat(global.getAggregations(), notNullValue());
+                assertThat(global.getAggregations().asMap().size(), equalTo(1));
+
+                CentroidAggregation geoCentroid = global.getAggregations().get(aggName());
+                InternalAggregation agg = (InternalAggregation) global;
+                assertThat(geoCentroid, notNullValue());
+                assertThat(geoCentroid.getName(), equalTo(aggName()));
+                assertThat((CentroidAggregation) agg.getProperty(aggName()), sameInstance(geoCentroid));
+                assertSameCentroid(geoCentroid.centroid(), singleCentroid);
+                assertSimilarValue(((SpatialPoint) agg.getProperty(aggName() + ".value")).getY(), singleCentroid.getY());
+                assertSimilarValue(((SpatialPoint) agg.getProperty(aggName() + ".value")).getX(), singleCentroid.getX());
+                assertSimilarValue((double) agg.getProperty(aggName() + "." + coordinateName("y")), singleCentroid.getY());
+                assertSimilarValue((double) agg.getProperty(aggName() + "." + coordinateName("x")), singleCentroid.getX());
+                assertEquals(numDocs, (long) ((InternalAggregation) global).getProperty(aggName() + ".count"));
+            }
+        );
     }
 
     public void testMultiValuedField() throws Exception {
-        SearchResponse searchResponse = prepareSearch(IDX_NAME).setQuery(matchAllQuery())
-            .addAggregation(centroidAgg(aggName()).field(MULTI_VALUED_FIELD_NAME))
-            .get();
-        assertNoFailures(searchResponse);
-
-        CentroidAggregation geoCentroid = searchResponse.getAggregations().get(aggName());
-        assertThat(geoCentroid, notNullValue());
-        assertThat(geoCentroid.getName(), equalTo(aggName()));
-        assertSameCentroid(geoCentroid.centroid(), multiCentroid);
-        assertEquals(2 * numDocs, geoCentroid.count());
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME)
+                .setQuery(matchAllQuery())
+                .addAggregation(centroidAgg(aggName()).field(MULTI_VALUED_FIELD_NAME)),
+            response -> {
+                CentroidAggregation geoCentroid = response.getAggregations().get(aggName());
+                assertThat(geoCentroid, notNullValue());
+                assertThat(geoCentroid.getName(), equalTo(aggName()));
+                assertSameCentroid(geoCentroid.centroid(), multiCentroid);
+                assertEquals(2 * numDocs, geoCentroid.count());
+            }
+        );
     }
 
     /** Override this if the spatial data uses different coordinate names (eg. Geo uses lon/at instead of x/y */
diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java
index d507e83ffe00e..81c9c37ad4f9a 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/metrics/SpatialBoundsAggregationTestBase.java
@@ -8,7 +8,6 @@
 
 package org.elasticsearch.search.aggregations.metrics;
 
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.geo.SpatialPoint;
 import org.elasticsearch.common.util.BigArray;
 import org.elasticsearch.search.aggregations.InternalAggregation;
@@ -23,7 +22,7 @@
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.search.aggregations.AggregationBuilders.global;
 import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
 import static org.hamcrest.Matchers.closeTo;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.notNullValue;
@@ -39,164 +38,176 @@ public abstract class SpatialBoundsAggregationTestBase e
     protected abstract void assertBoundsLimits(SpatialBounds spatialBounds);
 
     public void testSingleValuedField() throws Exception {
-        SearchResponse response = prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get();
-
-        assertNoFailures(response);
-
-        SpatialBounds geoBounds = response.getAggregations().get(aggName());
-        assertThat(geoBounds, notNullValue());
-        assertThat(geoBounds.getName(), equalTo(aggName()));
-        T topLeft = geoBounds.topLeft();
-        T bottomRight = geoBounds.bottomRight();
-        assertThat(topLeft.getY(), closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE));
-        assertThat(topLeft.getX(), closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getY(), closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getX(), closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                SpatialBounds geoBounds = response.getAggregations().get(aggName());
+                assertThat(geoBounds, notNullValue());
+                assertThat(geoBounds.getName(), equalTo(aggName()));
+                T topLeft = geoBounds.topLeft();
+                T bottomRight = geoBounds.bottomRight();
+                assertThat(topLeft.getY(), closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE));
+                assertThat(topLeft.getX(), closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getY(), closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getX(), closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE));
+            }
+        );
+
     }
 
     public void testSingleValuedField_getProperty() {
-        SearchResponse searchResponse = prepareSearch(IDX_NAME).setQuery(matchAllQuery())
-            .addAggregation(global("global").subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)))
-            .get();
-
-        assertNoFailures(searchResponse);
-
-        Global global = searchResponse.getAggregations().get("global");
-        assertThat(global, notNullValue());
-        assertThat(global.getName(), equalTo("global"));
-        assertThat(global.getDocCount(), equalTo((long) numDocs));
-        assertThat(global.getAggregations(), notNullValue());
-        assertThat(global.getAggregations().asMap().size(), equalTo(1));
-
-        SpatialBounds geobounds = global.getAggregations().get(aggName());
-        assertThat(geobounds, notNullValue());
-        assertThat(geobounds.getName(), equalTo(aggName()));
-        assertThat((SpatialBounds) ((InternalAggregation) global).getProperty(aggName()), sameInstance(geobounds));
-        T topLeft = geobounds.topLeft();
-        T bottomRight = geobounds.bottomRight();
-        assertThat(topLeft.getY(), closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE));
-        assertThat(topLeft.getX(), closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getY(), closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getX(), closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE));
-        assertThat(
-            (double) ((InternalAggregation) global).getProperty(aggName() + ".top"),
-            closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE)
-        );
-        assertThat(
-            (double) ((InternalAggregation) global).getProperty(aggName() + ".left"),
-            closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE)
-        );
-        assertThat(
-            (double) ((InternalAggregation) global).getProperty(aggName() + ".bottom"),
-            closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE)
-        );
-        assertThat(
-            (double) ((InternalAggregation) global).getProperty(aggName() + ".right"),
-            closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE)
+
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME)
+                .setQuery(matchAllQuery())
+                .addAggregation(global("global").subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))),
+            response -> {
+                Global global = response.getAggregations().get("global");
+                assertThat(global, notNullValue());
+                assertThat(global.getName(), equalTo("global"));
+                assertThat(global.getDocCount(), equalTo((long) numDocs));
+                assertThat(global.getAggregations(), notNullValue());
+                assertThat(global.getAggregations().asMap().size(), equalTo(1));
+
+                SpatialBounds geobounds = global.getAggregations().get(aggName());
+                assertThat(geobounds, notNullValue());
+                assertThat(geobounds.getName(), equalTo(aggName()));
+                assertThat((SpatialBounds) ((InternalAggregation) global).getProperty(aggName()), sameInstance(geobounds));
+                T topLeft = geobounds.topLeft();
+                T bottomRight = geobounds.bottomRight();
+                assertThat(topLeft.getY(), closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE));
+                assertThat(topLeft.getX(), closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getY(), closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getX(), closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE));
+                assertThat(
+                    (double) ((InternalAggregation) global).getProperty(aggName() + ".top"),
+                    closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE)
+                );
+                assertThat(
+                    (double) ((InternalAggregation) global).getProperty(aggName() + ".left"),
+                    closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE)
+                );
+                assertThat(
+                    (double) ((InternalAggregation) global).getProperty(aggName() + ".bottom"),
+                    closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE)
+                );
+                assertThat(
+                    (double) ((InternalAggregation) global).getProperty(aggName() + ".right"),
+                    closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE)
+                );
+            }
         );
+
     }
 
     public void testMultiValuedField() throws Exception {
-        SearchResponse response = prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), MULTI_VALUED_FIELD_NAME)).get();
-
-        assertNoFailures(response);
-
-        SpatialBounds geoBounds = response.getAggregations().get(aggName());
-        assertThat(geoBounds, notNullValue());
-        assertThat(geoBounds.getName(), equalTo(aggName()));
-        T topLeft = geoBounds.topLeft();
-        T bottomRight = geoBounds.bottomRight();
-        assertThat(topLeft.getY(), closeTo(multiTopLeft.getY(), GEOHASH_TOLERANCE));
-        assertThat(topLeft.getX(), closeTo(multiTopLeft.getX(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getY(), closeTo(multiBottomRight.getY(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getX(), closeTo(multiBottomRight.getX(), GEOHASH_TOLERANCE));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME).addAggregation(boundsAgg(aggName(), MULTI_VALUED_FIELD_NAME)),
+            response -> {
+                SpatialBounds geoBounds = response.getAggregations().get(aggName());
+                assertThat(geoBounds, notNullValue());
+                assertThat(geoBounds.getName(), equalTo(aggName()));
+                T topLeft = geoBounds.topLeft();
+                T bottomRight = geoBounds.bottomRight();
+                assertThat(topLeft.getY(), closeTo(multiTopLeft.getY(), GEOHASH_TOLERANCE));
+                assertThat(topLeft.getX(), closeTo(multiTopLeft.getX(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getY(), closeTo(multiBottomRight.getY(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getX(), closeTo(multiBottomRight.getX(), GEOHASH_TOLERANCE));
+            }
+        );
     }
 
     public void testUnmapped() throws Exception {
-        SearchResponse response = prepareSearch(UNMAPPED_IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get();
-
-        assertNoFailures(response);
-
-        SpatialBounds geoBounds = response.getAggregations().get(aggName());
-        assertThat(geoBounds, notNullValue());
-        assertThat(geoBounds.getName(), equalTo(aggName()));
-        T topLeft = geoBounds.topLeft();
-        T bottomRight = geoBounds.bottomRight();
-        assertThat(topLeft, equalTo(null));
-        assertThat(bottomRight, equalTo(null));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(UNMAPPED_IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                SpatialBounds geoBounds = response.getAggregations().get(aggName());
+                assertThat(geoBounds, notNullValue());
+                assertThat(geoBounds.getName(), equalTo(aggName()));
+                T topLeft = geoBounds.topLeft();
+                T bottomRight = geoBounds.bottomRight();
+                assertThat(topLeft, equalTo(null));
+                assertThat(bottomRight, equalTo(null));
+            }
+        );
     }
 
     public void testPartiallyUnmapped() throws Exception {
-        SearchResponse response = prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))
-            .get();
-
-        assertNoFailures(response);
-
-        SpatialBounds geoBounds = response.getAggregations().get(aggName());
-        assertThat(geoBounds, notNullValue());
-        assertThat(geoBounds.getName(), equalTo(aggName()));
-        T topLeft = geoBounds.topLeft();
-        T bottomRight = geoBounds.bottomRight();
-        assertThat(topLeft.getY(), closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE));
-        assertThat(topLeft.getX(), closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getY(), closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getX(), closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                SpatialBounds geoBounds = response.getAggregations().get(aggName());
+                assertThat(geoBounds, notNullValue());
+                assertThat(geoBounds.getName(), equalTo(aggName()));
+                T topLeft = geoBounds.topLeft();
+                T bottomRight = geoBounds.bottomRight();
+                assertThat(topLeft.getY(), closeTo(singleTopLeft.getY(), GEOHASH_TOLERANCE));
+                assertThat(topLeft.getX(), closeTo(singleTopLeft.getX(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getY(), closeTo(singleBottomRight.getY(), GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getX(), closeTo(singleBottomRight.getX(), GEOHASH_TOLERANCE));
+            }
+        );
+
     }
 
     public void testEmptyAggregation() throws Exception {
-        SearchResponse searchResponse = prepareSearch(EMPTY_IDX_NAME).setQuery(matchAllQuery())
-            .addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))
-            .get();
-
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L));
-        SpatialBounds geoBounds = searchResponse.getAggregations().get(aggName());
-        assertThat(geoBounds, notNullValue());
-        assertThat(geoBounds.getName(), equalTo(aggName()));
-        T topLeft = geoBounds.topLeft();
-        T bottomRight = geoBounds.bottomRight();
-        assertThat(topLeft, equalTo(null));
-        assertThat(bottomRight, equalTo(null));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(EMPTY_IDX_NAME).setQuery(matchAllQuery()).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(0L));
+                SpatialBounds geoBounds = response.getAggregations().get(aggName());
+                assertThat(geoBounds, notNullValue());
+                assertThat(geoBounds.getName(), equalTo(aggName()));
+                T topLeft = geoBounds.topLeft();
+                T bottomRight = geoBounds.bottomRight();
+                assertThat(topLeft, equalTo(null));
+                assertThat(bottomRight, equalTo(null));
+            }
+        );
+
     }
 
     /**
      * This test forces the bounds {@link MetricsAggregator} to resize the {@link BigArray}s it uses to ensure they are resized correctly
      */
     public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() {
-        SearchResponse response = prepareSearch(HIGH_CARD_IDX_NAME).addAggregation(
-            terms("terms").field(NUMBER_FIELD_NAME).subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))
-        ).get();
-
-        assertNoFailures(response);
-
-        Terms terms = response.getAggregations().get("terms");
-        assertThat(terms, notNullValue());
-        assertThat(terms.getName(), equalTo("terms"));
-        List buckets = terms.getBuckets();
-        assertThat(buckets.size(), equalTo(10));
-        for (int i = 0; i < 10; i++) {
-            Bucket bucket = buckets.get(i);
-            assertThat(bucket, notNullValue());
-            assertThat("InternalBucket " + bucket.getKey() + " has wrong number of documents", bucket.getDocCount(), equalTo(1L));
-            SpatialBounds geoBounds = bucket.getAggregations().get(aggName());
-            assertThat(geoBounds, notNullValue());
-            assertThat(geoBounds.getName(), equalTo(aggName()));
-            assertBoundsLimits(geoBounds);
-        }
+
+        assertNoFailuresAndResponse(
+            client().prepareSearch(HIGH_CARD_IDX_NAME)
+                .addAggregation(terms("terms").field(NUMBER_FIELD_NAME).subAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME))),
+            response -> {
+                Terms terms = response.getAggregations().get("terms");
+                assertThat(terms, notNullValue());
+                assertThat(terms.getName(), equalTo("terms"));
+                List buckets = terms.getBuckets();
+                assertThat(buckets.size(), equalTo(10));
+                for (int i = 0; i < 10; i++) {
+                    Bucket bucket = buckets.get(i);
+                    assertThat(bucket, notNullValue());
+                    assertThat("InternalBucket " + bucket.getKey() + " has wrong number of documents", bucket.getDocCount(), equalTo(1L));
+                    SpatialBounds geoBounds = bucket.getAggregations().get(aggName());
+                    assertThat(geoBounds, notNullValue());
+                    assertThat(geoBounds.getName(), equalTo(aggName()));
+                    assertBoundsLimits(geoBounds);
+                }
+            }
+        );
     }
 
     public void testSingleValuedFieldWithZeroLon() {
-        SearchResponse response = prepareSearch(IDX_ZERO_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)).get();
-
-        assertNoFailures(response);
-
-        SpatialBounds geoBounds = response.getAggregations().get(aggName());
-        assertThat(geoBounds, notNullValue());
-        assertThat(geoBounds.getName(), equalTo(aggName()));
-        T topLeft = geoBounds.topLeft();
-        T bottomRight = geoBounds.bottomRight();
-        assertThat(topLeft.getY(), closeTo(1.0, GEOHASH_TOLERANCE));
-        assertThat(topLeft.getX(), closeTo(0.0, GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getY(), closeTo(1.0, GEOHASH_TOLERANCE));
-        assertThat(bottomRight.getX(), closeTo(0.0, GEOHASH_TOLERANCE));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(IDX_ZERO_NAME).addAggregation(boundsAgg(aggName(), SINGLE_VALUED_FIELD_NAME)),
+            response -> {
+                SpatialBounds geoBounds = response.getAggregations().get(aggName());
+                assertThat(geoBounds, notNullValue());
+                assertThat(geoBounds.getName(), equalTo(aggName()));
+                T topLeft = geoBounds.topLeft();
+                T bottomRight = geoBounds.bottomRight();
+                assertThat(topLeft.getY(), closeTo(1.0, GEOHASH_TOLERANCE));
+                assertThat(topLeft.getX(), closeTo(0.0, GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getY(), closeTo(1.0, GEOHASH_TOLERANCE));
+                assertThat(bottomRight.getX(), closeTo(0.0, GEOHASH_TOLERANCE));
+            }
+        );
     }
 }
diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java
index 8574fcbb05959..97e68507f0c55 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeIntegTestCase.java
@@ -12,7 +12,6 @@
 import org.elasticsearch.action.bulk.BulkItemResponse;
 import org.elasticsearch.action.bulk.BulkResponse;
 import org.elasticsearch.action.search.SearchRequestBuilder;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
@@ -52,6 +51,8 @@
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFirstHit;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.equalTo;
@@ -184,8 +185,7 @@ public void testIgnoreMalformed() throws Exception {
         );
 
         indexRandom(true, client().prepareIndex("test").setId("0").setSource("shape", polygonGeoJson));
-        SearchResponse searchResponse = prepareSearch("test").setQuery(matchAllQuery()).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(client().prepareSearch("test").setQuery(matchAllQuery()), 1L);
     }
 
     /**
@@ -216,12 +216,11 @@ public void testIndexShapeRouting() throws Exception {
             }""";
 
         indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, XContentType.JSON).setRouting("ABC"));
-
-        SearchResponse searchResponse = prepareSearch("test").setQuery(
-            queryBuilder().shapeQuery("shape", "0").indexedShapeIndex("test").indexedShapeRouting("ABC")
-        ).get();
-
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch("test")
+                .setQuery(queryBuilder().shapeQuery("shape", "0").indexedShapeIndex("test").indexedShapeRouting("ABC")),
+            1L
+        );
     }
 
     public void testDisallowExpensiveQueries() throws InterruptedException, IOException {
@@ -248,9 +247,10 @@ public void testDisallowExpensiveQueries() throws InterruptedException, IOExcept
             // Execute with search.allow_expensive_queries to false
             updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", false));
 
-            SearchRequestBuilder builder = prepareSearch("test").setQuery(queryBuilder().shapeQuery("shape", new Circle(0, 0, 77000)));
+            SearchRequestBuilder builder = client().prepareSearch("test")
+                .setQuery(queryBuilder().shapeQuery("shape", new Circle(0, 0, 77000)));
             if (allowExpensiveQueries()) {
-                assertThat(builder.get().getHits().getTotalHits().value, equalTo(1L));
+                assertHitCount(builder, 1L);
             } else {
                 ElasticsearchException e = expectThrows(ElasticsearchException.class, builder::get);
                 assertEquals(
@@ -266,7 +266,7 @@ public void testDisallowExpensiveQueries() throws InterruptedException, IOExcept
 
             // Set search.allow_expensive_queries to "true"
             updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", true));
-            assertThat(builder.get().getHits().getTotalHits().value, equalTo(1L));
+            assertHitCount(builder, 1L);
         } finally {
             updateClusterSettings(Settings.builder().put("search.allow_expensive_queries", (String) null));
         }
@@ -303,15 +303,17 @@ public void testShapeRelations() throws Exception {
         client().admin().indices().prepareRefresh().get();
 
         // Point in polygon
-        SearchResponse result = prepareSearch().setQuery(matchAllQuery())
-            .setPostFilter(queryBuilder().intersectionQuery("area", new Point(3, 3)))
-            .get();
-        assertHitCount(result, 1);
-        assertFirstHit(result, hasId("1"));
+        assertResponse(
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(3, 3))),
+            response -> {
+                assertHitCount(response, 1L);
+                assertFirstHit(response, hasId("1"));
+            }
+        );
 
         // Point in polygon hole
         assertHitCount(
-            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5))),
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5))),
             0
         );
 
@@ -320,26 +322,39 @@ public void testShapeRelations() throws Exception {
         // of the polygon NOT the hole
 
         // Point on polygon border
-        result = prepareSearch().setQuery(matchAllQuery())
-            .setPostFilter(queryBuilder().intersectionQuery("area", new Point(10.0, 5.0)))
-            .get();
-        assertHitCount(result, 1);
-        assertFirstHit(result, hasId("1"));
+        assertResponse(
+            client().prepareSearch()
+                .setQuery(matchAllQuery())
+                .setPostFilter(queryBuilder().intersectionQuery("area", new Point(10.0, 5.0))),
+            response -> {
+                assertHitCount(response, 1L);
+                assertFirstHit(response, hasId("1"));
+            }
+        );
 
         // Point on hole border
-        result = prepareSearch().setQuery(matchAllQuery())
-            .setPostFilter(queryBuilder().intersectionQuery("area", new Point(5.0, 2.0)))
-            .get();
-        assertHitCount(result, 1);
-        assertFirstHit(result, hasId("1"));
+        assertResponse(
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(5.0, 2.0))),
+            response -> {
+                assertHitCount(response, 1L);
+                assertFirstHit(response, hasId("1"));
+            }
+        );
 
         // Point not in polygon
-        assertHitCount(prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(3, 3))), 0);
+        assertHitCount(
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(3, 3))),
+            0
+        );
 
         // Point in polygon hole
-        result = prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(4.5, 4.5))).get();
-        assertHitCount(result, 1);
-        assertFirstHit(result, hasId("1"));
+        assertResponse(
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().disjointQuery("area", new Point(4.5, 4.5))),
+            response -> {
+                assertHitCount(response, 1L);
+                assertFirstHit(response, hasId("1"));
+            }
+        );
 
         // Create a polygon that fills the empty area of the polygon defined above
         Polygon inverse = new Polygon(
@@ -352,16 +367,18 @@ public void testShapeRelations() throws Exception {
         client().admin().indices().prepareRefresh().get();
 
         // re-check point on polygon hole
-        result = prepareSearch().setQuery(matchAllQuery())
-            .setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5)))
-            .get();
-        assertHitCount(result, 1);
-        assertFirstHit(result, hasId("2"));
+        assertResponse(
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(4.5, 4.5))),
+            response -> {
+                assertHitCount(response, 1L);
+                assertFirstHit(response, hasId("2"));
+            }
+        );
 
         // Polygon WithIn Polygon
         Polygon WithIn = new Polygon(new LinearRing(new double[] { -30, -30, 30, 30, -30 }, new double[] { -30, 30, 30, -30, -30 }));
 
-        assertHitCount(prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().withinQuery("area", WithIn)), 2);
+        assertHitCount(client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().withinQuery("area", WithIn)), 2);
 
         // Create a polygon crossing longitude 180.
         Polygon crossing = new Polygon(new LinearRing(new double[] { 170, 190, 190, 170, 170 }, new double[] { -10, -10, 10, 10, -10 }));
@@ -381,23 +398,25 @@ public void testShapeRelations() throws Exception {
         client().admin().indices().prepareRefresh().get();
 
         assertHitCount(
-            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(174, -4))),
-            1
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(174, -4))),
+            1L
         );
 
         // In geo coordinates the polygon wraps the dateline, so we need to search within valid longitude ranges
         double xWrapped = getFieldTypeName().contains("geo") ? -174 : 186;
         assertHitCount(
-            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(xWrapped, -4))),
-            1
+            client().prepareSearch()
+                .setQuery(matchAllQuery())
+                .setPostFilter(queryBuilder().intersectionQuery("area", new Point(xWrapped, -4))),
+            1L
         );
         assertHitCount(
-            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -4))),
-            0
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -4))),
+            0L
         );
         assertHitCount(
-            prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -6))),
-            1
+            client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(queryBuilder().intersectionQuery("area", new Point(180, -6))),
+            1L
         );
     }
 
@@ -422,16 +441,15 @@ public void testBulk() throws Exception {
             assertFalse("unable to index data: " + item.getFailureMessage(), item.isFailed());
         }
 
-        client().admin().indices().prepareRefresh().get();
+        assertNoFailures(client().admin().indices().prepareRefresh().get());
         String key = "DE";
 
-        SearchResponse searchResponse = prepareSearch().setQuery(matchQuery("_id", key)).get();
-
-        assertHitCount(searchResponse, 1);
-
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), equalTo(key));
-        }
+        assertResponse(client().prepareSearch().setQuery(matchQuery("_id", key)), response -> {
+            assertHitCount(response, 1);
+            for (SearchHit hit : response.getHits()) {
+                assertThat(hit.getId(), equalTo(key));
+            }
+        });
 
         // We extract this to another method to allow some tests to ignore this part
         doDistanceAndBoundingBoxTest(key);
diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java
index bd8f369e32046..5da9103d49771 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/geo/BaseShapeQueryTestCase.java
@@ -9,7 +9,6 @@
 package org.elasticsearch.search.geo;
 
 import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.geo.GeoJson;
 import org.elasticsearch.common.geo.ShapeRelation;
@@ -29,7 +28,6 @@
 import org.elasticsearch.index.query.AbstractGeometryQueryBuilder;
 import org.elasticsearch.index.query.ExistsQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.search.SearchHits;
 import org.elasticsearch.xcontent.ToXContent;
 import org.elasticsearch.xcontent.XContentBuilder;
 import org.elasticsearch.xcontent.XContentFactory;
@@ -43,7 +41,8 @@
 import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -93,9 +92,7 @@ public void testFieldAlias() throws IOException {
             .setSource(GeoJson.toXContent(multiPoint, jsonBuilder().startObject().field(defaultFieldName), null).endObject())
             .setRefreshPolicy(IMMEDIATE)
             .get();
-
-        SearchResponse response = client().prepareSearch(defaultIndexName).setQuery(queryBuilder().shapeQuery("alias", multiPoint)).get();
-        assertEquals(1, response.getHits().getTotalHits().value);
+        assertHitCount(client().prepareSearch(defaultIndexName).setQuery(queryBuilder().shapeQuery("alias", multiPoint)), 1L);
     }
 
     public void testShapeFetchingPath() throws Exception {
@@ -149,50 +146,40 @@ public void testShapeFetchingPath() throws Exception {
             .relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex("shapes")
             .indexedShapePath(defaultFieldName);
-        SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 1L);
+
         filter = queryBuilder().shapeQuery(defaultFieldName, "1")
             .relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex("shapes")
             .indexedShapePath("1.geo");
-        result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 1L);
+
         filter = queryBuilder().shapeQuery(defaultFieldName, "1")
             .relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex("shapes")
             .indexedShapePath("1.2.geo");
-        result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 1L);
+
         filter = queryBuilder().shapeQuery(defaultFieldName, "1")
             .relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex("shapes")
             .indexedShapePath("1.2.3.geo");
-        result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 1L);
 
         // now test the query variant
         QueryBuilder query = queryBuilder().shapeQuery(defaultFieldName, "1")
             .indexedShapeIndex("shapes")
             .indexedShapePath(defaultFieldName);
-        result = client().prepareSearch(defaultIndexName).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(query), 1L);
+
         query = queryBuilder().shapeQuery(defaultFieldName, "1").indexedShapeIndex("shapes").indexedShapePath("1.geo");
-        result = client().prepareSearch(defaultIndexName).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(query), 1L);
+
         query = queryBuilder().shapeQuery(defaultFieldName, "1").indexedShapeIndex("shapes").indexedShapePath("1.2.geo");
-        result = client().prepareSearch(defaultIndexName).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(query), 1L);
+
         query = queryBuilder().shapeQuery(defaultFieldName, "1").indexedShapeIndex("shapes").indexedShapePath("1.2.3.geo");
-        result = client().prepareSearch(defaultIndexName).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(query), 1L);
     }
 
     public void testRandomGeoCollectionQuery() throws Exception {
@@ -219,9 +206,9 @@ public void testRandomGeoCollectionQuery() throws Exception {
         GeometryCollection queryCollection = new GeometryCollection<>(queryGeometries);
 
         QueryBuilder intersects = queryBuilder().intersectionQuery(defaultFieldName, queryCollection);
-        SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(intersects).get();
-        assertNoFailures(result);
-        assertTrue("query: " + intersects + " doc: " + Strings.toString(docSource), result.getHits().getTotalHits().value > 0);
+        assertNoFailuresAndResponse(client().prepareSearch(defaultIndexName).setQuery(intersects), response -> {
+            assertTrue("query: " + intersects + " doc: " + Strings.toString(docSource), response.getHits().getTotalHits().value > 0);
+        });
     }
 
     public void testGeometryCollectionRelations() throws Exception {
@@ -243,18 +230,24 @@ public void testGeometryCollectionRelations() throws Exception {
             geometries.add(new Point(1, 2));
             geometries.add(new Point(-2, -1));
             GeometryCollection collection = new GeometryCollection<>(geometries);
-            SearchResponse response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.CONTAINS))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
-            response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
-            response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.DISJOINT))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.CONTAINS)),
+                1L
+            );
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.INTERSECTS)),
+                1L
+            );
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.DISJOINT)),
+                0L
+            );
         }
         {
             // A geometry collection that is partially within the indexed shape
@@ -262,18 +255,24 @@ public void testGeometryCollectionRelations() throws Exception {
             geometries.add(new Point(1, 2));
             geometries.add(new Point(20, 30));
             GeometryCollection collection = new GeometryCollection<>(geometries);
-            SearchResponse response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.CONTAINS))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
-            response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
-            response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.DISJOINT))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.CONTAINS)),
+                0L
+            );
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.INTERSECTS)),
+                1L
+            );
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.DISJOINT)),
+                0L
+            );
         }
         {
             // A geometry collection that is disjoint with the indexed shape
@@ -281,18 +280,24 @@ public void testGeometryCollectionRelations() throws Exception {
             geometries.add(new Point(-20, -30));
             geometries.add(new Point(20, 30));
             GeometryCollection collection = new GeometryCollection<>(geometries);
-            SearchResponse response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.CONTAINS))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
-            response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
-            response = client().prepareSearch(defaultIndexName)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.DISJOINT))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.CONTAINS)),
+                0L
+            );
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.INTERSECTS)),
+                0L
+            );
+
+            assertHitCount(
+                client().prepareSearch(defaultIndexName)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, collection).relation(ShapeRelation.DISJOINT)),
+                1L
+            );
         }
     }
 
@@ -346,14 +351,15 @@ public void testEdgeCases() throws Exception {
 
         // This search would fail if both geoshape indexing and geoshape filtering
         // used the bottom-level optimization in SpatialPrefixTree#recursiveGetNodes.
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().intersectionQuery(defaultFieldName, query))
-            .get();
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName).setQuery(queryBuilder().intersectionQuery(defaultFieldName, query)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("blakely"));
+            }
+        );
 
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("blakely"));
     }
 
     public void testIndexedShapeReferenceSourceDisabled() throws Exception {
@@ -392,11 +398,10 @@ public void testPointQuery() throws Exception {
             .endObject();
         client().prepareIndex(defaultIndexName).setId("1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
 
-        SearchResponse result = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().intersectionQuery(defaultFieldName, point))
-            .get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(
+            client().prepareSearch(defaultIndexName).setQuery(queryBuilder().intersectionQuery(defaultFieldName, point)),
+            1L
+        );
     }
 
     public void testContainsShapeQuery() throws Exception {
@@ -407,9 +412,8 @@ public void testContainsShapeQuery() throws Exception {
         XContentBuilder docSource = GeoJson.toXContent(polygon, jsonBuilder().startObject().field(defaultFieldName), null).endObject();
         client().prepareIndex(defaultIndexName).setId("1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
         QueryBuilder filter = queryBuilder().shapeQuery(defaultFieldName, innerPolygon).relation(ShapeRelation.CONTAINS);
-        SearchResponse response = client().prepareSearch(defaultIndexName).setQuery(filter).get();
-        assertNoFailures(response);
-        assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(filter), 1L);
     }
 
     public void testExistsQuery() throws Exception {
@@ -423,9 +427,7 @@ public void testExistsQuery() throws Exception {
         client().prepareIndex(defaultIndexName).setId("1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
 
         ExistsQueryBuilder eqb = existsQuery(defaultFieldName);
-        SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(eqb).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(eqb), 1L);
     }
 
     public void testIndexedShapeReference() throws Exception {
@@ -457,23 +459,23 @@ public void testIndexedShapeReference() throws Exception {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().intersectionQuery(defaultFieldName, "Big_Rectangle"))
-            .get();
-
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
-
-        searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().shapeQuery(defaultFieldName, "Big_Rectangle"))
-            .get();
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName).setQuery(queryBuilder().intersectionQuery(defaultFieldName, "Big_Rectangle")),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
+            }
+        );
 
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName).setQuery(queryBuilder().shapeQuery(defaultFieldName, "Big_Rectangle")),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
+            }
+        );
     }
 
     public void testQueryRandomGeoCollection() throws Exception {
@@ -489,11 +491,10 @@ public void testQueryRandomGeoCollection() throws Exception {
         XContentBuilder docSource = GeoJson.toXContent(gcb, jsonBuilder().startObject().field(defaultFieldName), null).endObject();
         client().prepareIndex(defaultIndexName).setId("1").setSource(docSource).setRefreshPolicy(IMMEDIATE).get();
 
-        SearchResponse result = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().intersectionQuery(defaultFieldName, polygon))
-            .get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(
+            client().prepareSearch(defaultIndexName).setQuery(queryBuilder().intersectionQuery(defaultFieldName, polygon)),
+            1L
+        );
     }
 
     public void testShapeFilterWithDefinedGeoCollection() throws Exception {
@@ -538,28 +539,20 @@ public void testShapeFilterWithDefinedGeoCollection() throws Exception {
 
         {
             QueryBuilder filter = queryBuilder().intersectionQuery(defaultFieldName, new GeometryCollection<>(List.of(polygon1)));
-            SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-            assertNoFailures(result);
-            assertHitCount(result, 1);
+            assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 1L);
         }
         {
             QueryBuilder filter = queryBuilder().intersectionQuery(defaultFieldName, new GeometryCollection<>(List.of(polygon2)));
-            SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-            assertNoFailures(result);
-            assertHitCount(result, 0);
+            assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 0L);
         }
         {
             QueryBuilder filter = queryBuilder().intersectionQuery(defaultFieldName, new GeometryCollection<>(List.of(polygon1, polygon2)));
-            SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-            assertNoFailures(result);
-            assertHitCount(result, 1);
+            assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 1L);
         }
         {
             // no shape
             QueryBuilder filter = queryBuilder().shapeQuery(defaultFieldName, GeometryCollection.EMPTY);
-            SearchResponse result = client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter).get();
-            assertNoFailures(result);
-            assertHitCount(result, 0);
+            assertHitCountAndNoFailures(client().prepareSearch(defaultIndexName).setQuery(matchAllQuery()).setPostFilter(filter), 0L);
         }
     }
 
@@ -592,22 +585,29 @@ public void testDistanceQuery() throws Exception {
             ).setRefreshPolicy(IMMEDIATE)
         ).actionGet();
 
-        SearchResponse response = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.WITHIN))
-            .get();
-        assertEquals(2, response.getHits().getTotalHits().value);
-        response = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.INTERSECTS))
-            .get();
-        assertEquals(2, response.getHits().getTotalHits().value);
-        response = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.DISJOINT))
-            .get();
-        assertEquals(2, response.getHits().getTotalHits().value);
-        response = client().prepareSearch(defaultIndexName)
-            .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.CONTAINS))
-            .get();
-        assertEquals(0, response.getHits().getTotalHits().value);
+        assertHitCount(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.WITHIN)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.INTERSECTS)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.DISJOINT)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(queryBuilder().shapeQuery(defaultFieldName, circle).relation(ShapeRelation.CONTAINS)),
+            0L
+        );
     }
 
     public void testIndexLineQueryPoints() throws Exception {
@@ -623,13 +623,12 @@ public void testIndexLineQueryPoints() throws Exception {
         // all points from a line intersect with the line
         for (int i = 0; i < line.length(); i++) {
             Point point = new Point(line.getLon(i), line.getLat(i));
-            SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-                .setTrackTotalHits(true)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, point).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertNoFailures(searchResponse);
-            SearchHits searchHits = searchResponse.getHits();
-            assertThat(searchHits.getTotalHits().value, equalTo(1L));
+            assertHitCountAndNoFailures(
+                client().prepareSearch(defaultIndexName)
+                    .setTrackTotalHits(true)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, point).relation(ShapeRelation.INTERSECTS)),
+                1L
+            );
         }
     }
 
@@ -648,13 +647,12 @@ public void testIndexPolygonQueryPoints() throws Exception {
         LinearRing linearRing = polygon.getPolygon();
         for (int i = 0; i < linearRing.length(); i++) {
             Point point = new Point(linearRing.getLon(i), linearRing.getLat(i));
-            SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-                .setTrackTotalHits(true)
-                .setQuery(queryBuilder().shapeQuery(defaultFieldName, point).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertNoFailures(searchResponse);
-            SearchHits searchHits = searchResponse.getHits();
-            assertThat(searchHits.getTotalHits().value, equalTo(1L));
+            assertHitCountAndNoFailures(
+                client().prepareSearch(defaultIndexName)
+                    .setTrackTotalHits(true)
+                    .setQuery(queryBuilder().shapeQuery(defaultFieldName, point).relation(ShapeRelation.INTERSECTS)),
+                1L
+            );
         }
     }
 
@@ -681,13 +679,12 @@ public void testNeighbours() throws Exception {
                 .get();
         }
         Geometry center = WellKnownText.fromWKT(StandardValidator.instance(false), false, polygons[0]);
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setTrackTotalHits(true)
-            .setQuery(queryBuilder().shapeQuery(defaultFieldName, center).relation(ShapeRelation.INTERSECTS))
-            .get();
-        assertNoFailures(searchResponse);
-        SearchHits searchHits = searchResponse.getHits();
-        assertThat(searchHits.getTotalHits().value, equalTo((long) polygons.length));
+        assertHitCountAndNoFailures(
+            client().prepareSearch(defaultIndexName)
+                .setTrackTotalHits(true)
+                .setQuery(queryBuilder().shapeQuery(defaultFieldName, center).relation(ShapeRelation.INTERSECTS)),
+            polygons.length
+        );
     }
 
     protected abstract Line makeRandomLine();
diff --git a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java
index 14f8ca16f5996..7cc53560e8403 100644
--- a/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/search/geo/GeoBoundingBoxQueryIntegTestCase.java
@@ -8,7 +8,6 @@
 
 package org.elasticsearch.search.geo;
 
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.DistanceUnit;
@@ -26,6 +25,8 @@
 import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery;
 import static org.elasticsearch.index.query.QueryBuilders.termQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.equalTo;
@@ -103,60 +104,79 @@ public void testSimpleBoundingBoxTest() throws Exception {
 
         client().admin().indices().prepareRefresh().get();
 
-        SearchResponse searchResponse = prepareSearch() // from NY
-            .setQuery(geoBoundingBoxQuery("location").setCorners(40.73, -74.1, 40.717, -73.99))
-            .get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(2));
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
-        }
-
-        searchResponse = prepareSearch() // from NY
-            .setQuery(geoBoundingBoxQuery("location").setCorners(40.73, -74.1, 40.717, -73.99))
-            .get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(2));
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
-        }
-
-        searchResponse = prepareSearch() // top == bottom && left == right
-            .setQuery(geoBoundingBoxQuery("location").setCorners(40.7143528, -74.0059731, 40.7143528, -74.0059731))
-            .get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), equalTo("1"));
-        }
-
-        searchResponse = prepareSearch() // top == bottom
-            .setQuery(geoBoundingBoxQuery("location").setCorners(40.759011, -74.00009, 40.759011, -73.0059731))
-            .get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), equalTo("2"));
-        }
-
-        searchResponse = prepareSearch() // left == right
-            .setQuery(geoBoundingBoxQuery("location").setCorners(41.8, -73.9844722, 40.7, -73.9844722))
-            .get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), equalTo("2"));
-        }
+        assertResponse(
+            client().prepareSearch() // from NY
+                .setQuery(geoBoundingBoxQuery("location").setCorners(40.73, -74.1, 40.717, -73.99)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(2L));
+                assertThat(response.getHits().getHits().length, equalTo(2));
+                for (SearchHit hit : response.getHits()) {
+                    assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
+                }
+            }
+        );
+
+        assertResponse(
+            client().prepareSearch() // from NY
+                .setQuery(geoBoundingBoxQuery("location").setCorners(40.73, -74.1, 40.717, -73.99)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(2L));
+                assertThat(response.getHits().getHits().length, equalTo(2));
+                for (SearchHit hit : response.getHits()) {
+                    assertThat(hit.getId(), anyOf(equalTo("1"), equalTo("3"), equalTo("5")));
+                }
+            }
+        );
+
+        assertResponse(
+            client().prepareSearch() // top == bottom && left == right
+                .setQuery(geoBoundingBoxQuery("location").setCorners(40.7143528, -74.0059731, 40.7143528, -74.0059731)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                for (SearchHit hit : response.getHits()) {
+                    assertThat(hit.getId(), equalTo("1"));
+                }
+            }
+        );
+
+        assertResponse(
+            client().prepareSearch() // top == bottom
+                .setQuery(geoBoundingBoxQuery("location").setCorners(40.759011, -74.00009, 40.759011, -73.0059731)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                for (SearchHit hit : response.getHits()) {
+                    assertThat(hit.getId(), equalTo("2"));
+                }
+            }
+        );
+
+        assertResponse(
+            client().prepareSearch() // left == right
+                .setQuery(geoBoundingBoxQuery("location").setCorners(41.8, -73.9844722, 40.7, -73.9844722)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                for (SearchHit hit : response.getHits()) {
+                    assertThat(hit.getId(), equalTo("2"));
+                }
+            }
+        );
 
         // Distance query
-        searchResponse = prepareSearch() // from NY
-            .setQuery(geoDistanceQuery("location").point(40.5, -73.9).distance(25, DistanceUnit.KILOMETERS))
-            .get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(2));
-        for (SearchHit hit : searchResponse.getHits()) {
-            assertThat(hit.getId(), anyOf(equalTo("7"), equalTo("4")));
-        }
+        assertResponse(
+            client().prepareSearch() // from NY
+                .setQuery(geoDistanceQuery("location").point(40.5, -73.9).distance(25, DistanceUnit.KILOMETERS)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(2L));
+                assertThat(response.getHits().getHits().length, equalTo(2));
+                for (SearchHit hit : response.getHits()) {
+                    assertThat(hit.getId(), anyOf(equalTo("7"), equalTo("4")));
+                }
+            }
+        );
+
     }
 
     public void testLimit2BoundingBox() throws Exception {
@@ -189,97 +209,128 @@ public void testLimit2BoundingBox() throws Exception {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 880))
-                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 880))
-                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 534))
-                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 534))
-                .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 880))
+                        .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+                ),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 534))
+                        .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+                ),
+            1L
+        );
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 534))
+                        .filter(geoBoundingBoxQuery("location").setCorners(74.579421999999994, 143.5, -66.668903999999998, 113.96875))
+                ),
+            1L
+        );
 
         // top == bottom && left == right
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 880))
-                .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 59.328355000000002, 18.036842, 59.328355000000002))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 534))
-                .filter(
-                    geoBoundingBoxQuery("location").setCorners(
-                        45.509526999999999,
-                        -73.570986000000005,
-                        45.509526999999999,
-                        -73.570986000000005
-                    )
-                )
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 880))
+                        .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 59.328355000000002, 18.036842, 59.328355000000002))
+                ),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 534))
+                        .filter(
+                            geoBoundingBoxQuery("location").setCorners(
+                                45.509526999999999,
+                                -73.570986000000005,
+                                45.509526999999999,
+                                -73.570986000000005
+                            )
+                        )
+                ),
+            1L
+        );
 
         // top == bottom
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 880))
-                .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 143.5, 18.036842, 113.96875))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 534))
-                .filter(geoBoundingBoxQuery("location").setCorners(45.509526999999999, 143.5, 45.509526999999999, 113.96875))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 880))
+                        .filter(geoBoundingBoxQuery("location").setCorners(18.036842, 143.5, 18.036842, 113.96875))
+                ),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 534))
+                        .filter(geoBoundingBoxQuery("location").setCorners(45.509526999999999, 143.5, 45.509526999999999, 113.96875))
+                ),
+            1L
+        );
 
         // left == right
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 880))
-                .filter(
-                    geoBoundingBoxQuery("location").setCorners(
-                        74.579421999999994,
-                        59.328355000000002,
-                        -66.668903999999998,
-                        59.328355000000002
-                    )
-                )
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 534))
-                .filter(
-                    geoBoundingBoxQuery("location").setCorners(
-                        74.579421999999994,
-                        -73.570986000000005,
-                        -66.668903999999998,
-                        -73.570986000000005
-                    )
-                )
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 880))
+                        .filter(
+                            geoBoundingBoxQuery("location").setCorners(
+                                74.579421999999994,
+                                59.328355000000002,
+                                -66.668903999999998,
+                                59.328355000000002
+                            )
+                        )
+                ),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 534))
+                        .filter(
+                            geoBoundingBoxQuery("location").setCorners(
+                                74.579421999999994,
+                                -73.570986000000005,
+                                -66.668903999999998,
+                                -73.570986000000005
+                            )
+                        )
+                ),
+            1L
+        );
 
         // Distance query
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 880))
-                .filter(geoDistanceQuery("location").point(20, 60.0).distance(500, DistanceUnit.MILES))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-
-        searchResponse = prepareSearch().setQuery(
-            boolQuery().must(termQuery("userid", 534))
-                .filter(geoDistanceQuery("location").point(45.0, -73.0).distance(500, DistanceUnit.MILES))
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 880))
+                        .filter(geoDistanceQuery("location").point(20, 60.0).distance(500, DistanceUnit.MILES))
+                ),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    boolQuery().must(termQuery("userid", 534))
+                        .filter(geoDistanceQuery("location").point(45.0, -73.0).distance(500, DistanceUnit.MILES))
+                ),
+            1L
+        );
     }
 
     public void testCompleteLonRange() throws Exception {
@@ -312,54 +363,77 @@ public void testCompleteLonRange() throws Exception {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, -180, -50, 180)),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, -180, -90, 180)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(50, 0, -50, 360)),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE).setCorners(90, 0, -90, 360)),
+            2L
+        );
 
         // top == bottom
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
-                .setCorners(59.328355000000002, 0, 59.328355000000002, 360)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        searchResponse = prepareSearch().setQuery(
-            geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
-                .setCorners(59.328355000000002, -180, 59.328355000000002, 180)
-        ).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
+                        .setCorners(59.328355000000002, 0, 59.328355000000002, 360)
+                ),
+            1L
+        );
+
+        assertHitCount(
+            client().prepareSearch()
+                .setQuery(
+                    geoBoundingBoxQuery("location").setValidationMethod(GeoValidationMethod.COERCE)
+                        .setCorners(59.328355000000002, -180, 59.328355000000002, 180)
+                ),
+            1L
+        );
 
         // Distance query
-        searchResponse = prepareSearch().setQuery(geoDistanceQuery("location").point(60.0, -20.0).distance(1800, DistanceUnit.MILES)).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(
+            client().prepareSearch().setQuery(geoDistanceQuery("location").point(60.0, -20.0).distance(1800, DistanceUnit.MILES)),
+            1L
+        );
     }
 }
diff --git a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
index 03f197ced56df..b93a24c656671 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
@@ -33,6 +33,7 @@
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
 import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.core.CheckedConsumer;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.rest.RestStatus;
@@ -58,6 +59,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 import static org.apache.lucene.tests.util.LuceneTestCase.expectThrows;
 import static org.apache.lucene.tests.util.LuceneTestCase.expectThrowsAnyOf;
@@ -225,12 +227,7 @@ public static String formatShardStatus(SearchResponse response) {
     }
 
     public static void assertNoSearchHits(SearchRequestBuilder searchRequestBuilder) {
-        var searchResponse = searchRequestBuilder.get();
-        try {
-            assertNoSearchHits(searchResponse);
-        } finally {
-            searchResponse.decRef();
-        }
+        assertResponse(searchRequestBuilder, ElasticsearchAssertions::assertNoSearchHits);
     }
 
     public static void assertNoSearchHits(SearchResponse searchResponse) {
@@ -238,12 +235,7 @@ public static void assertNoSearchHits(SearchResponse searchResponse) {
     }
 
     public static void assertSearchHits(SearchRequestBuilder searchRequestBuilder, String... ids) {
-        var res = searchRequestBuilder.get();
-        try {
-            assertSearchHits(res, ids);
-        } finally {
-            res.decRef();
-        }
+        assertResponse(searchRequestBuilder, res -> assertSearchHits(res, ids));
     }
 
     public static void assertSearchHits(SearchResponse searchResponse, String... ids) {
@@ -255,38 +247,26 @@ public static void assertSearchHits(SearchResponse searchResponse, String... ids
     }
 
     public static void assertSearchHitsWithoutFailures(SearchRequestBuilder requestBuilder, String... ids) {
-        var res = requestBuilder.get();
-        try {
+        assertResponse(requestBuilder, res -> {
             assertNoFailures(res);
             assertHitCount(res, ids.length);
             assertSearchHits(res, ids);
-        } finally {
-            res.decRef();
-        }
+        });
     }
 
     public static void assertSortValues(SearchRequestBuilder searchRequestBuilder, Object[]... sortValues) {
-        var searchResponse = searchRequestBuilder.get();
-        try {
-            assertNoFailures(searchResponse);
-            SearchHit[] hits = searchResponse.getHits().getHits();
+        assertNoFailuresAndResponse(searchRequestBuilder, res -> {
+            SearchHit[] hits = res.getHits().getHits();
             assertEquals(sortValues.length, hits.length);
             for (int i = 0; i < sortValues.length; ++i) {
                 final Object[] hitsSortValues = hits[i].getSortValues();
                 assertArrayEquals("Offset " + i + ", id " + hits[i].getId(), sortValues[i], hitsSortValues);
             }
-        } finally {
-            searchResponse.decRef();
-        }
+        });
     }
 
     public static void assertOrderedSearchHits(SearchRequestBuilder searchRequestBuilder, String... ids) {
-        var res = searchRequestBuilder.get();
-        try {
-            assertOrderedSearchHits(res, ids);
-        } finally {
-            res.decRef();
-        }
+        assertResponse(searchRequestBuilder, res -> assertOrderedSearchHits(res, ids));
     }
 
     public static void assertOrderedSearchHits(SearchResponse searchResponse, String... ids) {
@@ -298,26 +278,15 @@ public static void assertOrderedSearchHits(SearchResponse searchResponse, String
     }
 
     public static void assertHitCount(SearchRequestBuilder searchRequestBuilder, long expectedHitCount) {
-        var res = searchRequestBuilder.get();
-        try {
-            assertHitCount(res, expectedHitCount);
-        } finally {
-            res.decRef();
-        }
+        assertResponse(searchRequestBuilder, res -> assertHitCount(res, expectedHitCount));
     }
 
     public static void assertHitCount(ActionFuture responseFuture, long expectedHitCount) {
-        SearchResponse res;
         try {
-            res = responseFuture.get();
+            assertResponse(responseFuture, res -> assertHitCount(res, expectedHitCount));
         } catch (ExecutionException | InterruptedException ex) {
             throw new AssertionError(ex);
         }
-        try {
-            assertHitCount(res, expectedHitCount);
-        } finally {
-            res.decRef();
-        }
     }
 
     public static void assertHitCount(SearchResponse countResponse, long expectedHitCount) {
@@ -328,13 +297,7 @@ public static void assertHitCount(SearchResponse countResponse, long expectedHit
     }
 
     public static void assertHitCountAndNoFailures(SearchRequestBuilder searchRequestBuilder, long expectedHitCount) {
-        var res = searchRequestBuilder.get();
-        try {
-            assertHitCount(res, expectedHitCount);
-            assertNoFailures(res);
-        } finally {
-            res.decRef();
-        }
+        assertNoFailuresAndResponse(searchRequestBuilder, response -> assertHitCount(response, expectedHitCount));
     }
 
     public static void assertExists(GetResponse response) {
@@ -365,9 +328,42 @@ public static void assertSearchHit(SearchResponse searchResponse, int number, Ma
     }
 
     public static void assertNoFailures(SearchRequestBuilder searchRequestBuilder) {
+        assertNoFailuresAndResponse(searchRequestBuilder, r -> {});
+    }
+
+    public static void assertNoFailuresAndResponse(SearchRequestBuilder searchRequestBuilder, Consumer consumer) {
+        assertResponse(searchRequestBuilder, res -> {
+            assertNoFailures(res);
+            consumer.accept(res);
+        });
+    }
+
+    public static void assertResponse(SearchRequestBuilder searchRequestBuilder, Consumer consumer) {
         var res = searchRequestBuilder.get();
         try {
-            assertNoFailures(res);
+            consumer.accept(res);
+        } finally {
+            res.decRef();
+        }
+    }
+
+    public static void assertResponse(ActionFuture responseFuture, Consumer consumer)
+        throws ExecutionException, InterruptedException {
+        var res = responseFuture.get();
+        try {
+            consumer.accept(res);
+        } finally {
+            res.decRef();
+        }
+    }
+
+    public static void assertCheckedResponse(
+        SearchRequestBuilder searchRequestBuilder,
+        CheckedConsumer consumer
+    ) throws IOException {
+        var res = searchRequestBuilder.get();
+        try {
+            consumer.accept(res);
         } finally {
             res.decRef();
         }
@@ -393,12 +389,13 @@ public static void assertFailures(SearchRequestBuilder searchRequestBuilder, Res
         // when the number for shards is randomized and we expect failures
         // we can either run into partial or total failures depending on the current number of shards
         try {
-            SearchResponse searchResponse = searchRequestBuilder.get();
-            assertThat("Expected shard failures, got none", searchResponse.getShardFailures(), not(emptyArray()));
-            for (ShardSearchFailure shardSearchFailure : searchResponse.getShardFailures()) {
-                assertThat(shardSearchFailure.status(), equalTo(restStatus));
-                assertThat(shardSearchFailure.reason(), reasonMatcher);
-            }
+            assertResponse(searchRequestBuilder, response -> {
+                assertThat("Expected shard failures, got none", response.getShardFailures(), not(emptyArray()));
+                for (ShardSearchFailure shardSearchFailure : response.getShardFailures()) {
+                    assertThat(shardSearchFailure.status(), equalTo(restStatus));
+                    assertThat(shardSearchFailure.reason(), reasonMatcher);
+                }
+            });
         } catch (SearchPhaseExecutionException e) {
             assertThat(e.status(), equalTo(restStatus));
             assertThat(e.toString(), reasonMatcher);
@@ -445,12 +442,7 @@ public static void assertHighlight(
         int totalFragments,
         Matcher matcher
     ) {
-        var resp = searchRequestBuilder.get();
-        try {
-            assertHighlight(resp, hit, field, fragment, equalTo(totalFragments), matcher);
-        } finally {
-            resp.decRef();
-        }
+        assertResponse(searchRequestBuilder, response -> assertHighlight(response, hit, field, fragment, equalTo(totalFragments), matcher));
     }
 
     public static void assertHighlight(
@@ -461,12 +453,7 @@ public static void assertHighlight(
         int totalFragments,
         Matcher matcher
     ) throws ExecutionException, InterruptedException {
-        var resp = responseFuture.get();
-        try {
-            assertHighlight(resp, hit, field, fragment, equalTo(totalFragments), matcher);
-        } finally {
-            resp.decRef();
-        }
+        assertResponse(responseFuture, response -> assertHighlight(response, hit, field, fragment, equalTo(totalFragments), matcher));
     }
 
     public static void assertHighlight(
@@ -514,12 +501,7 @@ private static void assertHighlight(
     }
 
     public static void assertNotHighlighted(SearchRequestBuilder searchRequestBuilder, int hit, String field) {
-        var resp = searchRequestBuilder.get();
-        try {
-            assertNotHighlighted(resp, hit, field);
-        } finally {
-            resp.decRef();
-        }
+        assertResponse(searchRequestBuilder, response -> assertNotHighlighted(response, hit, field));
     }
 
     public static void assertNotHighlighted(SearchResponse resp, int hit, String field) {
@@ -595,7 +577,6 @@ public static void assertIndexTemplateExists(GetIndexTemplatesResponse templates
         assertThat(templatesResponse.getIndexTemplates(), hasItem(transformedMatch(IndexTemplateMetadata::name, equalTo(name))));
     }
 
-    /*
     /*
      * matchers
      */
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java
index 46ee89bf8044d..3c64d140e2b56 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoGridAggAndQueryConsistencyIT.java
@@ -8,7 +8,6 @@
 
 import org.elasticsearch.action.bulk.BulkRequestBuilder;
 import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.geo.GeoBoundingBox;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.geo.GeometryTestUtils;
@@ -49,6 +48,9 @@
 import java.util.function.Function;
 import java.util.function.Supplier;
 
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse;
+
 public class GeoGridAggAndQueryConsistencyIT extends ESIntegTestCase {
 
     @Override
@@ -115,21 +117,24 @@ public void testKnownIssueWithCellLeftOfDatelineTouchingPolygonOnRightOfDateline
             .precision(15)
             .setGeoBoundingBox(boundingBox)
             .size(256 * 256);
-        SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get();
-        InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
-        for (InternalGeoGridBucket bucket : gridPoint.getBuckets()) {
-            assertThat(bucket.getDocCount(), Matchers.greaterThan(0L));
-            QueryBuilder queryBuilder = new GeoGridQueryBuilder("geometry").setGridId(
-                GeoGridQueryBuilder.Grid.GEOHEX,
-                bucket.getKeyAsString()
-            );
-            response = prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder).get();
-            assertThat(
-                "Bucket " + bucket.getKeyAsString(),
-                response.getHits().getTotalHits().value,
-                Matchers.equalTo(bucket.getDocCount())
-            );
-        }
+        assertResponse(client().prepareSearch("test").addAggregation(builderPoint).setSize(0), response -> {
+            InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
+            for (InternalGeoGridBucket bucket : gridPoint.getBuckets()) {
+                assertThat(bucket.getDocCount(), Matchers.greaterThan(0L));
+                QueryBuilder queryBuilder = new GeoGridQueryBuilder("geometry").setGridId(
+                    GeoGridQueryBuilder.Grid.GEOHEX,
+                    bucket.getKeyAsString()
+                );
+                assertResponse(
+                    client().prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder),
+                    innerResponse -> assertThat(
+                        "Bucket " + bucket.getKeyAsString(),
+                        innerResponse.getHits().getTotalHits().value,
+                        Matchers.equalTo(bucket.getDocCount())
+                    )
+                );
+            }
+        });
     }
 
     public void testKnownIssueWithCellIntersectingPolygonAndBoundingBox() throws IOException {
@@ -166,17 +171,17 @@ public void testKnownIssueWithCellIntersectingPolygonAndBoundingBox() throws IOE
             .precision(precision)
             .setGeoBoundingBox(boundingBox)
             .size(256 * 256);
-        SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get();
-        InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
-        for (InternalGeoGridBucket bucket : gridPoint.getBuckets()) {
-            assertThat(bucket.getDocCount(), Matchers.greaterThan(0L));
-            QueryBuilder queryBuilder = new GeoGridQueryBuilder("geometry").setGridId(
-                GeoGridQueryBuilder.Grid.GEOHEX,
-                bucket.getKeyAsString()
-            );
-            response = prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder).get();
-            assertThat(response.getHits().getTotalHits().value, Matchers.equalTo(bucket.getDocCount()));
-        }
+        assertResponse(client().prepareSearch("test").addAggregation(builderPoint).setSize(0), response -> {
+            InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
+            for (InternalGeoGridBucket bucket : gridPoint.getBuckets()) {
+                assertThat(bucket.getDocCount(), Matchers.greaterThan(0L));
+                QueryBuilder queryBuilder = new GeoGridQueryBuilder("geometry").setGridId(
+                    GeoGridQueryBuilder.Grid.GEOHEX,
+                    bucket.getKeyAsString()
+                );
+                assertHitCount(client().prepareSearch("test").setTrackTotalHits(true).setQuery(queryBuilder), bucket.getDocCount());
+            }
+        });
     }
 
     private void doTestGeohashGrid(String fieldType, Supplier randomGeometriesSupplier) throws IOException {
@@ -269,9 +274,11 @@ private void doTestGrid(
 
         for (int i = minPrecision; i <= maxPrecision; i++) {
             GeoGridAggregationBuilder builderPoint = aggBuilder.apply("geometry").field("geometry").precision(i);
-            SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get();
-            InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
-            assertQuery(gridPoint.getBuckets(), queryBuilder, i);
+            int finalI = i;
+            assertResponse(client().prepareSearch("test").addAggregation(builderPoint).setSize(0), response -> {
+                InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
+                assertQuery(gridPoint.getBuckets(), queryBuilder, finalI);
+            });
         }
 
         builder = client().prepareBulk();
@@ -297,9 +304,11 @@ private void doTestGrid(
                 .precision(i)
                 .setGeoBoundingBox(boundingBox)
                 .size(256 * 256);
-            SearchResponse response = prepareSearch("test").addAggregation(builderPoint).setSize(0).get();
-            InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
-            assertQuery(gridPoint.getBuckets(), queryBuilder, i);
+            int finalI = i;
+            assertResponse(client().prepareSearch("test").addAggregation(builderPoint).setSize(0), response -> {
+                InternalGeoGrid gridPoint = response.getAggregations().get("geometry");
+                assertQuery(gridPoint.getBuckets(), queryBuilder, finalI);
+            });
         }
     }
 
@@ -307,11 +316,13 @@ private void assertQuery(List buckets, BiFunction assertThat(
+                    "Expected hits at precision " + precision + " for H3 cell " + bucket.getKeyAsString(),
+                    response.getHits().getTotalHits().value,
+                    Matchers.equalTo(bucket.getDocCount())
+                )
             );
         }
     }
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java
index 4d526a6a07718..57e654fc0901c 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeScriptDocValuesIT.java
@@ -7,7 +7,7 @@
 package org.elasticsearch.xpack.spatial.search;
 
 import org.apache.lucene.geo.Circle;
-import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.common.document.DocumentField;
 import org.elasticsearch.common.geo.BoundingBox;
 import org.elasticsearch.common.geo.GeoPoint;
@@ -51,7 +51,8 @@
 import java.util.function.Function;
 
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertCheckedResponse;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
@@ -259,37 +260,36 @@ private void doTestGeometry(Geometry geometry, GeoShapeValues.GeoShapeValue expe
 
         GeoShapeValues.GeoShapeValue value = GeoTestUtils.geoShapeValue(geometry);
 
-        SearchResponse searchResponse = client().prepareSearch()
+        SearchRequestBuilder searchRequest = client().prepareSearch()
             .addStoredField("_source")
             .addScriptField("lat", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "lat", Collections.emptyMap()))
             .addScriptField("lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "lon", Collections.emptyMap()))
             .addScriptField("height", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "height", Collections.emptyMap()))
             .addScriptField("width", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "width", Collections.emptyMap()))
             .addScriptField("label_lat", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lat", Collections.emptyMap()))
-            .addScriptField("label_lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lon", Collections.emptyMap()))
-            .get();
-        assertNoFailures(searchResponse);
-        Map fields = searchResponse.getHits().getHits()[0].getFields();
-        assertThat(fields.get("lat").getValue(), equalTo(value.getY()));
-        assertThat(fields.get("lon").getValue(), equalTo(value.getX()));
-        assertThat(fields.get("height").getValue(), equalTo(value.boundingBox().maxY() - value.boundingBox().minY()));
-        assertThat(fields.get("width").getValue(), equalTo(value.boundingBox().maxX() - value.boundingBox().minX()));
-
-        // Check label position is in the geometry, but with a tolerance constructed as a circle of 1m radius to handle quantization
-        Point labelPosition = new Point(fields.get("label_lon").getValue(), fields.get("label_lat").getValue());
-        Circle tolerance = new Circle(labelPosition.getY(), labelPosition.getX(), 1);
-        assertTrue(
-            "Expect label position " + labelPosition + " to intersect geometry " + geometry,
-            value.relate(tolerance) != GeoRelation.QUERY_DISJOINT
-        );
-
-        // Check that the label position is the expected one, or the centroid in certain polygon cases
-        if (expectedLabelPosition != null) {
-            doTestLabelPosition(fields, expectedLabelPosition);
-        } else if (fallbackToCentroid && value.dimensionalShapeType() == DimensionalShapeType.POLYGON) {
-            // Use the centroid for all polygons, unless overwritten for specific cases
-            doTestLabelPosition(fields, GeoTestUtils.geoShapeValue(new Point(value.getX(), value.getY())));
-        }
+            .addScriptField("label_lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "label_lon", Collections.emptyMap()));
+
+        assertCheckedResponse(searchRequest, response -> {
+            Map fields = response.getHits().getHits()[0].getFields();
+            assertThat(fields.get("lat").getValue(), equalTo(value.getY()));
+            assertThat(fields.get("lon").getValue(), equalTo(value.getX()));
+            assertThat(fields.get("height").getValue(), equalTo(value.boundingBox().maxY() - value.boundingBox().minY()));
+            assertThat(fields.get("width").getValue(), equalTo(value.boundingBox().maxX() - value.boundingBox().minX()));
+            // Check label position is in the geometry, but with a tolerance constructed as a circle of 1m radius to handle quantization
+            Point labelPosition = new Point(fields.get("label_lon").getValue(), fields.get("label_lat").getValue());
+            Circle tolerance = new Circle(labelPosition.getY(), labelPosition.getX(), 1);
+            assertTrue(
+                "Expect label position " + labelPosition + " to intersect geometry " + geometry,
+                value.relate(tolerance) != GeoRelation.QUERY_DISJOINT
+            );
+            // Check that the label position is the expected one, or the centroid in certain polygon cases
+            if (expectedLabelPosition != null) {
+                doTestLabelPosition(fields, expectedLabelPosition);
+            } else if (fallbackToCentroid && value.dimensionalShapeType() == DimensionalShapeType.POLYGON) {
+                // Use the centroid for all polygons, unless overwritten for specific cases
+                doTestLabelPosition(fields, GeoTestUtils.geoShapeValue(new Point(value.getX(), value.getY())));
+            }
+        });
     }
 
     private void doTestLabelPosition(Map fields, GeoShapeValues.GeoShapeValue expectedLabelPosition)
@@ -316,18 +316,18 @@ public void testNullShape() throws Exception {
 
         indicesAdmin().prepareRefresh("test").get();
 
-        SearchResponse searchResponse = client().prepareSearch()
+        SearchRequestBuilder searchRequestBuilder = client().prepareSearch()
             .addStoredField("_source")
             .addScriptField("lat", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "lat", Collections.emptyMap()))
             .addScriptField("lon", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "lon", Collections.emptyMap()))
             .addScriptField("height", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "height", Collections.emptyMap()))
-            .addScriptField("width", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "width", Collections.emptyMap()))
-            .get();
-        assertNoFailures(searchResponse);
-        Map fields = searchResponse.getHits().getHits()[0].getFields();
-        assertThat(fields.get("lat").getValue(), equalTo(Double.NaN));
-        assertThat(fields.get("lon").getValue(), equalTo(Double.NaN));
-        assertThat(fields.get("height").getValue(), equalTo(Double.NaN));
-        assertThat(fields.get("width").getValue(), equalTo(Double.NaN));
+            .addScriptField("width", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "width", Collections.emptyMap()));
+        assertNoFailuresAndResponse(searchRequestBuilder, response -> {
+            Map fields = response.getHits().getHits()[0].getFields();
+            assertThat(fields.get("lat").getValue(), equalTo(Double.NaN));
+            assertThat(fields.get("lon").getValue(), equalTo(Double.NaN));
+            assertThat(fields.get("height").getValue(), equalTo(Double.NaN));
+            assertThat(fields.get("width").getValue(), equalTo(Double.NaN));
+        });
     }
 }
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
index a0204b38cd0ca..1a81a8a8c7604 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
@@ -8,7 +8,6 @@
 package org.elasticsearch.xpack.spatial.search;
 
 import org.apache.lucene.util.BytesRef;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.unit.DistanceUnit;
 import org.elasticsearch.geometry.Geometry;
@@ -41,6 +40,7 @@
 import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -150,13 +150,15 @@ public void testPercolatorGeoQueries() throws Exception {
         refresh();
 
         BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "POINT(4.51 52.20)").endObject());
-        SearchResponse response = prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON))
-            .addSort("id", SortOrder.ASC)
-            .get();
-        assertHitCount(response, 3);
-        assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
-        assertThat(response.getHits().getAt(1).getId(), equalTo("2"));
-        assertThat(response.getHits().getAt(2).getId(), equalTo("3"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).addSort("id", SortOrder.ASC),
+            response -> {
+                assertHitCount(response, 3);
+                assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
+                assertThat(response.getHits().getAt(1).getId(), equalTo("2"));
+                assertThat(response.getHits().getAt(2).getId(), equalTo("3"));
+            }
+        );
     }
 
     // make sure we store the normalised geometry
@@ -177,18 +179,19 @@ public void testStorePolygonDateLine() throws Exception {
 
         indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, XContentType.JSON));
 
-        SearchResponse searchResponse = prepareSearch("test").setFetchSource(false).addStoredField("shape").get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        SearchHit searchHit = searchResponse.getHits().getAt(0);
-        assertThat(searchHit.field("shape").getValue(), instanceOf(BytesRef.class));
-        BytesRef bytesRef = searchHit.field("shape").getValue();
-        Geometry geometry = WellKnownBinary.fromWKB(
-            StandardValidator.instance(true),
-            false,
-            bytesRef.bytes,
-            bytesRef.offset,
-            bytesRef.length
-        );
-        assertThat(geometry.type(), equalTo(ShapeType.MULTIPOLYGON));
+        assertNoFailuresAndResponse(client().prepareSearch("test").setFetchSource(false).addStoredField("shape"), response -> {
+            assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+            SearchHit searchHit = response.getHits().getAt(0);
+            assertThat(searchHit.field("shape").getValue(), instanceOf(BytesRef.class));
+            BytesRef bytesRef = searchHit.field("shape").getValue();
+            Geometry geometry = WellKnownBinary.fromWKB(
+                StandardValidator.instance(true),
+                false,
+                bytesRef.bytes,
+                bytesRef.offset,
+                bytesRef.length
+            );
+            assertThat(geometry.type(), equalTo(ShapeType.MULTIPOLYGON));
+        });
     }
 }
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java
index 8a64f64ed958b..859121650c1d9 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java
@@ -7,7 +7,6 @@
 
 package org.elasticsearch.xpack.spatial.search;
 
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.geometry.Circle;
 import org.elasticsearch.index.IndexVersion;
 import org.elasticsearch.index.IndexVersions;
@@ -25,8 +24,8 @@
 
 import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
 import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
 
 public class LegacyGeoShapeWithDocValuesIT extends GeoShapeIntegTestCase {
 
@@ -103,7 +102,6 @@ public void testLegacyCircle() throws Exception {
         }));
 
         // test self crossing of circles
-        SearchResponse searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))).get();
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCount(client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))), 1L);
     }
 }
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java
index a011f42c14518..5d8a35b3f6165 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java
@@ -8,7 +8,6 @@
 
 import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.geo.GeoJson;
 import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.common.settings.Settings;
@@ -35,10 +34,10 @@
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.nullValue;
 
 public class ShapeQueryOverShapeTests extends ShapeQueryTestCase {
@@ -175,45 +174,29 @@ public void testShapeFetchingPath() throws Exception {
         ShapeQueryBuilder filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex(indexName)
             .indexedShapePath("location");
-        SearchResponse result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter), 1L);
         filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex(indexName)
             .indexedShapePath("1.location");
-        result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter), 1L);
         filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex(indexName)
             .indexedShapePath("1.2.location");
-        result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter), 1L);
         filter = new ShapeQueryBuilder("location", "1").relation(ShapeRelation.INTERSECTS)
             .indexedShapeIndex(indexName)
             .indexedShapePath("1.2.3.location");
-        result = client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(QueryBuilders.matchAllQuery()).setPostFilter(filter), 1L);
 
         // now test the query variant
         ShapeQueryBuilder query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("location");
-        result = client().prepareSearch(searchIndex).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(query), 1L);
         query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("1.location");
-        result = client().prepareSearch(searchIndex).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(query), 1L);
         query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("1.2.location");
-        result = client().prepareSearch(searchIndex).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(query), 1L);
         query = new ShapeQueryBuilder("location", "1").indexedShapeIndex(indexName).indexedShapePath("1.2.3.location");
-        result = client().prepareSearch(searchIndex).setQuery(query).get();
-        assertNoFailures(result);
-        assertHitCount(result, 1);
+        assertHitCountAndNoFailures(client().prepareSearch(searchIndex).setQuery(query), 1L);
     }
 
     /**
@@ -239,11 +222,10 @@ public void testIndexShapeRouting() {
         client().prepareIndex(INDEX).setId("0").setSource(source, XContentType.JSON).setRouting("ABC").get();
         indicesAdmin().prepareRefresh(INDEX).get();
 
-        SearchResponse searchResponse = client().prepareSearch(INDEX)
-            .setQuery(new ShapeQueryBuilder(FIELD, "0").indexedShapeIndex(INDEX).indexedShapeRouting("ABC"))
-            .get();
-
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) numDocs + 1));
+        assertHitCount(
+            client().prepareSearch(INDEX).setQuery(new ShapeQueryBuilder(FIELD, "0").indexedShapeIndex(INDEX).indexedShapeRouting("ABC")),
+            (long) numDocs + 1
+        );
     }
 
     public void testNullShape() {
@@ -264,16 +246,16 @@ public void testNullShape() {
 
     public void testExistsQuery() {
         ExistsQueryBuilder eqb = QueryBuilders.existsQuery(FIELD);
-        SearchResponse result = client().prepareSearch(INDEX).setQuery(eqb).get();
-        assertNoFailures(result);
-        assertHitCount(result, numDocs);
+        assertHitCountAndNoFailures(client().prepareSearch(INDEX).setQuery(eqb), numDocs);
     }
 
     public void testFieldAlias() {
-        SearchResponse response = client().prepareSearch(INDEX)
-            .setQuery(new ShapeQueryBuilder("alias", queryGeometry).relation(ShapeRelation.INTERSECTS))
-            .get();
-        assertTrue(response.getHits().getTotalHits().value > 0);
+        assertResponse(
+            client().prepareSearch(INDEX).setQuery(new ShapeQueryBuilder("alias", queryGeometry).relation(ShapeRelation.INTERSECTS)),
+            response -> {
+                assertTrue(response.getHits().getTotalHits().value > 0);
+            }
+        );
     }
 
     public void testContainsShapeQuery() {
@@ -287,10 +269,7 @@ public void testContainsShapeQuery() {
         // index the mbr of the collection
         Rectangle rectangle = new Rectangle(-50, 50, 50, -50);
         ShapeQueryBuilder queryBuilder = new ShapeQueryBuilder("location", rectangle).relation(ShapeRelation.CONTAINS);
-        SearchResponse response = client().prepareSearch("test_contains").setQuery(queryBuilder).get();
-        assertNoFailures(response);
-
-        assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+        assertHitCountAndNoFailures(client().prepareSearch("test_contains").setQuery(queryBuilder), 1L);
     }
 
     public void testGeometryCollectionRelations() throws IOException {
@@ -318,51 +297,60 @@ public void testGeometryCollectionRelations() throws IOException {
         {
             // A geometry collection that is fully within the indexed shape
             GeometryCollection collection = new GeometryCollection<>(List.of(new Point(1, 2), new Point(-2, -1)));
-            SearchResponse response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.CONTAINS))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
-            response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
-            response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.DISJOINT))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.CONTAINS)),
+                1L
+            );
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.INTERSECTS)),
+                1L
+            );
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.DISJOINT)),
+                0L
+            );
         }
         {
             // A geometry collection (as multi point) that is partially within the indexed shape
             MultiPoint multiPoint = new MultiPoint(List.of(new Point(1, 2), new Point(20, 30)));
-            SearchResponse response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", multiPoint).relation(ShapeRelation.CONTAINS))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
-            response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", multiPoint).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
-            response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", multiPoint).relation(ShapeRelation.DISJOINT))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", multiPoint).relation(ShapeRelation.CONTAINS)),
+                0L
+            );
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", multiPoint).relation(ShapeRelation.INTERSECTS)),
+                1L
+            );
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", multiPoint).relation(ShapeRelation.DISJOINT)),
+                0L
+            );
         }
         {
             // A geometry collection that is disjoint with the indexed shape
             MultiPoint multiPoint = new MultiPoint(List.of(new Point(-20, -30), new Point(20, 30)));
             GeometryCollection collection = new GeometryCollection<>(List.of(multiPoint));
-            SearchResponse response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.CONTAINS))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
-            response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.INTERSECTS))
-                .get();
-            assertEquals(0, response.getHits().getTotalHits().value);
-            response = client().prepareSearch("test_collections")
-                .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.DISJOINT))
-                .get();
-            assertEquals(1, response.getHits().getTotalHits().value);
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.CONTAINS)),
+                0L
+            );
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.INTERSECTS)),
+                0L
+            );
+            assertHitCount(
+                client().prepareSearch("test_collections")
+                    .setQuery(new ShapeQueryBuilder("geometry", collection).relation(ShapeRelation.DISJOINT)),
+                1L
+            );
         }
     }
 }
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java
index 8a42e157d0017..1160af2a98071 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryTestCase.java
@@ -8,7 +8,6 @@
 
 import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.geo.ShapeRelation;
 import org.elasticsearch.geometry.Circle;
@@ -32,7 +31,9 @@
 import java.util.List;
 
 import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
-import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
@@ -83,22 +84,26 @@ public void testIndexPointsFilterRectangle() throws Exception {
 
         Rectangle rectangle = new Rectangle(-45, 45, 45, -45);
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS))
-            .get();
-
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
+            }
+        );
 
         // default query, without specifying relation (expect intersects)
-        searchResponse = client().prepareSearch(defaultIndexName).setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle)).get();
 
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName).setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
+            }
+        );
     }
 
     public void testIndexPointsCircle() throws Exception {
@@ -120,14 +125,15 @@ public void testIndexPointsCircle() throws Exception {
 
         Circle circle = new Circle(-30, -30, 1);
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(new ShapeQueryBuilder(defaultFieldName, circle).relation(ShapeRelation.INTERSECTS))
-            .get();
-
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(new ShapeQueryBuilder(defaultFieldName, circle).relation(ShapeRelation.INTERSECTS)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
+            }
+        );
     }
 
     public void testIndexPointsPolygon() throws Exception {
@@ -149,14 +155,15 @@ public void testIndexPointsPolygon() throws Exception {
 
         Polygon polygon = new Polygon(new LinearRing(new double[] { -35, -35, -25, -25, -35 }, new double[] { -35, -25, -25, -35, -35 }));
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(new ShapeQueryBuilder(defaultFieldName, polygon).relation(ShapeRelation.INTERSECTS))
-            .get();
-
-        assertNoFailures(searchResponse);
-        SearchHits searchHits = searchResponse.getHits();
-        assertThat(searchHits.getTotalHits().value, equalTo(1L));
-        assertThat(searchHits.getAt(0).getId(), equalTo("1"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(new ShapeQueryBuilder(defaultFieldName, polygon).relation(ShapeRelation.INTERSECTS)),
+            response -> {
+                SearchHits searchHits = response.getHits();
+                assertThat(searchHits.getTotalHits().value, equalTo(1L));
+                assertThat(searchHits.getAt(0).getId(), equalTo("1"));
+            }
+        );
     }
 
     public void testIndexPointsMultiPolygon() throws Exception {
@@ -191,15 +198,16 @@ public void testIndexPointsMultiPolygon() throws Exception {
 
         MultiPolygon mp = new MultiPolygon(List.of(encloseDocument1Shape, encloseDocument2Shape));
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(new ShapeQueryBuilder(defaultFieldName, mp).relation(ShapeRelation.INTERSECTS))
-            .get();
-
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(2));
-        assertThat(searchResponse.getHits().getAt(0).getId(), not(equalTo("2")));
-        assertThat(searchResponse.getHits().getAt(1).getId(), not(equalTo("2")));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(new ShapeQueryBuilder(defaultFieldName, mp).relation(ShapeRelation.INTERSECTS)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(2L));
+                assertThat(response.getHits().getHits().length, equalTo(2));
+                assertThat(response.getHits().getAt(0).getId(), not(equalTo("2")));
+                assertThat(response.getHits().getAt(1).getId(), not(equalTo("2")));
+            }
+        );
     }
 
     public void testIndexPointsRectangle() throws Exception {
@@ -221,14 +229,15 @@ public void testIndexPointsRectangle() throws Exception {
 
         Rectangle rectangle = new Rectangle(-50, -40, -45, -55);
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS))
-            .get();
-
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2"));
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(new ShapeQueryBuilder(defaultFieldName, rectangle).relation(ShapeRelation.INTERSECTS)),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("2"));
+            }
+        );
     }
 
     public void testIndexPointsIndexedRectangle() throws Exception {
@@ -275,28 +284,29 @@ public void testIndexPointsIndexedRectangle() throws Exception {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(
-                new ShapeQueryBuilder(defaultFieldName, "shape1").relation(ShapeRelation.INTERSECTS)
-                    .indexedShapeIndex(indexedShapeIndex)
-                    .indexedShapePath(indexedShapePath)
-            )
-            .get();
+        assertNoFailuresAndResponse(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(
+                    new ShapeQueryBuilder(defaultFieldName, "shape1").relation(ShapeRelation.INTERSECTS)
+                        .indexedShapeIndex(indexedShapeIndex)
+                        .indexedShapePath(indexedShapePath)
+                ),
+            response -> {
+                assertThat(response.getHits().getTotalHits().value, equalTo(1L));
+                assertThat(response.getHits().getHits().length, equalTo(1));
+                assertThat(response.getHits().getAt(0).getId(), equalTo("point2"));
+            }
+        );
 
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
-        assertThat(searchResponse.getHits().getHits().length, equalTo(1));
-        assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("point2"));
-
-        searchResponse = client().prepareSearch(defaultIndexName)
-            .setQuery(
-                new ShapeQueryBuilder(defaultFieldName, "shape2").relation(ShapeRelation.INTERSECTS)
-                    .indexedShapeIndex(indexedShapeIndex)
-                    .indexedShapePath(indexedShapePath)
-            )
-            .get();
-        assertNoFailures(searchResponse);
-        assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L));
+        assertHitCountAndNoFailures(
+            client().prepareSearch(defaultIndexName)
+                .setQuery(
+                    new ShapeQueryBuilder(defaultFieldName, "shape2").relation(ShapeRelation.INTERSECTS)
+                        .indexedShapeIndex(indexedShapeIndex)
+                        .indexedShapePath(indexedShapePath)
+                ),
+            0L
+        );
     }
 
     public void testDistanceQuery() throws Exception {
@@ -326,21 +336,24 @@ public void testDistanceQuery() throws Exception {
             ).setRefreshPolicy(IMMEDIATE)
         ).actionGet();
 
-        SearchResponse response = client().prepareSearch("test_distance")
-            .setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.WITHIN))
-            .get();
-        assertEquals(2, response.getHits().getTotalHits().value);
-        response = client().prepareSearch("test_distance")
-            .setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.INTERSECTS))
-            .get();
-        assertEquals(2, response.getHits().getTotalHits().value);
-        response = client().prepareSearch("test_distance")
-            .setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.DISJOINT))
-            .get();
-        assertEquals(2, response.getHits().getTotalHits().value);
-        response = client().prepareSearch("test_distance")
-            .setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.CONTAINS))
-            .get();
-        assertEquals(0, response.getHits().getTotalHits().value);
+        assertHitCount(
+            client().prepareSearch("test_distance").setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.WITHIN)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch("test_distance").setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.INTERSECTS)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch("test_distance").setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.DISJOINT)),
+            2L
+        );
+
+        assertHitCount(
+            client().prepareSearch("test_distance").setQuery(new ShapeQueryBuilder("location", circle).relation(ShapeRelation.CONTAINS)),
+            0L
+        );
     }
 }
diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java
index 227df1c993d41..f560c8591ac56 100644
--- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java
+++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/query/LegacyGeoShapeWithDocValuesQueryTests.java
@@ -7,7 +7,6 @@
 
 package org.elasticsearch.xpack.spatial.index.query;
 
-import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.geo.GeoJson;
 import org.elasticsearch.common.settings.Settings;
@@ -35,6 +34,7 @@
 import static org.elasticsearch.index.query.QueryBuilders.geoIntersectionQuery;
 import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
 import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 import static org.hamcrest.Matchers.containsString;
 
@@ -131,9 +131,7 @@ public void testPointsOnlyExplicit() throws Exception {
             .get();
 
         // test that point was inserted
-        SearchResponse response = client().prepareSearch("geo_points_only").setQuery(matchAllQuery()).get();
-
-        assertEquals(2, response.getHits().getTotalHits().value);
+        assertHitCount(client().prepareSearch("geo_points_only").setQuery(matchAllQuery()), 2L);
     }
 
     public void testPointsOnly() throws Exception {
@@ -183,10 +181,7 @@ public void testPointsOnly() throws Exception {
         }
 
         // test that point was inserted
-        SearchResponse response = client().prepareSearch("geo_points_only")
-            .setQuery(geoIntersectionQuery(defaultFieldName, geometry))
-            .get();
-        assertEquals(1, response.getHits().getTotalHits().value);
+        assertHitCount(client().prepareSearch("geo_points_only").setQuery(geoIntersectionQuery(defaultFieldName, geometry)), 1L);
     }
 
     public void testFieldAlias() throws IOException {
@@ -227,8 +222,7 @@ public void testFieldAlias() throws IOException {
             .setRefreshPolicy(IMMEDIATE)
             .get();
 
-        SearchResponse response = client().prepareSearch(defaultIndexName).setQuery(geoShapeQuery("alias", multiPoint)).get();
-        assertEquals(1, response.getHits().getTotalHits().value);
+        assertHitCount(client().prepareSearch(defaultIndexName).setQuery(geoShapeQuery("alias", multiPoint)), 1L);
     }
 
     @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/86118")

From 13bf22367dfb3303f4058b9b4a684d0616789be9 Mon Sep 17 00:00:00 2001
From: Artem Prigoda 
Date: Mon, 23 Oct 2023 12:05:38 +0200
Subject: [PATCH 055/203] Add a latch to sync when all max upload tasks have
 been submitted (#101203)

Before we check the amount of active tasks on the prewarming executor,
we need to verify that all the tasks have been actually submitted.

Otherwise, we have a race in and amount of active tasks can be lower
then the amount of submitted tasks.

Fixes #99124

---------

Co-authored-by: David Turner 
---
 .../cache/full/SearchableSnapshotsPrewarmingIntegTests.java   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java
index c0d413d09fc5b..50149cf8ca376 100644
--- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java
+++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/full/SearchableSnapshotsPrewarmingIntegTests.java
@@ -251,11 +251,13 @@ public void testConcurrentPrewarming() throws Exception {
         final CountDownLatch startPrewarmingLatch = new CountDownLatch(1);
         final var threadPool = getInstanceFromNode(ThreadPool.class);
         final int maxUploadTasks = threadPool.info(CACHE_PREWARMING_THREAD_POOL_NAME).getMax();
+        final CountDownLatch maxUploadTasksCreated = new CountDownLatch(maxUploadTasks);
         for (int i = 0; i < maxUploadTasks; i++) {
             threadPool.executor(CACHE_PREWARMING_THREAD_POOL_NAME).execute(new AbstractRunnable() {
 
                 @Override
                 protected void doRun() throws Exception {
+                    maxUploadTasksCreated.countDown();
                     startPrewarmingLatch.await();
                 }
 
@@ -265,7 +267,7 @@ public void onFailure(Exception e) {
                 }
             });
         }
-
+        safeAwait(maxUploadTasksCreated);
         var prewarmingExecutor = threadPool.executor(CACHE_PREWARMING_THREAD_POOL_NAME);
         assertThat(prewarmingExecutor, instanceOf(ThreadPoolExecutor.class));
         assertThat(((ThreadPoolExecutor) prewarmingExecutor).getActiveCount(), equalTo(maxUploadTasks));

From f7ba5efcb07cdf1ab83ecd02a6d2eb7b7db28799 Mon Sep 17 00:00:00 2001
From: Rene Groeschke 
Date: Mon, 23 Oct 2023 12:35:02 +0200
Subject: [PATCH 056/203] Fix generation of xcontent provider Manifest
 (#101200)

Fixes #101191
---
 .../internal/GenerateProviderManifest.java       | 16 ++++++----------
 libs/x-content/build.gradle                      |  7 +++----
 2 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java
index 6f07b198e7e8a..621210cd935b2 100644
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/GenerateProviderManifest.java
@@ -11,12 +11,10 @@
 import org.elasticsearch.gradle.util.FileUtils;
 import org.gradle.api.DefaultTask;
 import org.gradle.api.file.ConfigurableFileCollection;
-import org.gradle.api.file.DirectoryProperty;
-import org.gradle.api.provider.Property;
+import org.gradle.api.file.RegularFileProperty;
 import org.gradle.api.tasks.Classpath;
-import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.InputFiles;
-import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.TaskAction;
 
 import java.io.File;
@@ -33,15 +31,13 @@ public GenerateProviderManifest() {}
     @InputFiles
     abstract public ConfigurableFileCollection getProviderImplClasspath();
 
-    @Input
-    abstract public Property getManifestName();
-
-    @OutputDirectory
-    abstract DirectoryProperty getOutputDir();
+    @OutputFile
+    abstract RegularFileProperty getManifestFile();
 
     @TaskAction
     void generateManifest() {
-        File manifestFile = getOutputDir().file(getManifestName().get()).get().getAsFile();
+        File manifestFile = getManifestFile().get().getAsFile();
+        manifestFile.getParentFile().mkdirs();
         FileUtils.write(manifestFile, generateManifestContent(), "UTF-8");
     }
 
diff --git a/libs/x-content/build.gradle b/libs/x-content/build.gradle
index f934d7ce09386..5c9dd49c007b8 100644
--- a/libs/x-content/build.gradle
+++ b/libs/x-content/build.gradle
@@ -67,15 +67,14 @@ tasks.named("dependencyLicenses").configure {
   mapping from: /jackson-.*/, to: 'jackson'
 }
 
-File generatedResourcesDir = new File(buildDir, 'generated-resources')
+Directory generatedResourcesDir = layout.buildDirectory.dir('generated-resources').get()
 def generateProviderManifest = tasks.register("generateProviderManifest", GenerateProviderManifest.class) {
-  outputDir = layout.buildDirectory.dir('generated-resources')
-  manifestName = "LISTING.TXT"
+  manifestFile = generatedResourcesDir.file("LISTING.TXT")
   getProviderImplClasspath().from(configurations.providerImpl)
 }
 
 def generateProviderImpl = tasks.register("generateProviderImpl", Sync) {
-  destinationDir = new File(generatedResourcesDir, "impl")
+  destinationDir = generatedResourcesDir.dir("impl").getAsFile()
   into("IMPL-JARS/x-content") {
     from(configurations.providerImpl)
     from(generateProviderManifest)

From 4bbf760cda9d19c87cbffb7f6288b6a8c3b3492f Mon Sep 17 00:00:00 2001
From: David Turner 
Date: Mon, 23 Oct 2023 11:46:30 +0100
Subject: [PATCH 057/203] Repo analysis of uncontended register behaviour
 (#101185)

Today repository analysis verifies that a register behaves correctly
under contention, retrying until successful, but it turns out that some
repository implementations cannot even perform uncontended register
writes correctly which may cause endless retries in the contended case.
This commit adds another repository analyser which verifies that
uncontended register writes work correctly on the first attempt.
---
 docs/changelog/101185.yaml                    |   5 +
 .../org/elasticsearch/TransportVersions.java  |   2 +-
 .../main/java/fixture/s3/S3HttpHandler.java   |   6 +-
 .../operator/DefaultOperatorOnlyRegistry.java |   1 +
 .../testkit/RepositoryAnalysisFailureIT.java  |  49 +++-
 .../testkit/RepositoryAnalysisSuccessIT.java  |  31 ++-
 .../testkit/RepositoryAnalyzeAction.java      | 108 +++++++--
 .../testkit/SnapshotRepositoryTestKit.java    |   3 +-
 .../UncontendedRegisterAnalyzeAction.java     | 215 ++++++++++++++++++
 9 files changed, 379 insertions(+), 41 deletions(-)
 create mode 100644 docs/changelog/101185.yaml
 create mode 100644 x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java

diff --git a/docs/changelog/101185.yaml b/docs/changelog/101185.yaml
new file mode 100644
index 0000000000000..63d3a4da328b1
--- /dev/null
+++ b/docs/changelog/101185.yaml
@@ -0,0 +1,5 @@
+pr: 101185
+summary: Repo analysis of uncontended register behaviour
+area: Snapshot/Restore
+type: enhancement
+issues: []
diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index a3f36c6a4b6fb..7c76c8a8c9c9e 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -144,7 +144,7 @@ static TransportVersion def(int id) {
     public static final TransportVersion PIPELINES_IN_BULK_RESPONSE_ADDED = def(8_519_00_0);
     public static final TransportVersion PLUGIN_DESCRIPTOR_STRING_VERSION = def(8_520_00_0);
     public static final TransportVersion TOO_MANY_SCROLL_CONTEXTS_EXCEPTION_ADDED = def(8_521_00_0);
-
+    public static final TransportVersion UNCONTENDED_REGISTER_ANALYSIS_ADDED = def(8_522_00_0);
     /*
      * STOP! READ THIS FIRST! No, really,
      *        ____ _____ ___  ____  _        ____  _____    _    ____    _____ _   _ ___ ____    _____ ___ ____  ____ _____ _
diff --git a/test/fixtures/s3-fixture/src/main/java/fixture/s3/S3HttpHandler.java b/test/fixtures/s3-fixture/src/main/java/fixture/s3/S3HttpHandler.java
index 6e65ec2749010..09dfdc991b82c 100644
--- a/test/fixtures/s3-fixture/src/main/java/fixture/s3/S3HttpHandler.java
+++ b/test/fixtures/s3-fixture/src/main/java/fixture/s3/S3HttpHandler.java
@@ -102,8 +102,10 @@ public void handle(final HttpExchange exchange) throws IOException {
                 uploadsList.append("10000");
                 uploadsList.append("false");
 
-                for (MultipartUpload value : uploads.values()) {
-                    value.appendXml(uploadsList);
+                for (final var multipartUpload : uploads.values()) {
+                    if (multipartUpload.getPath().startsWith(prefix)) {
+                        multipartUpload.appendXml(uploadsList);
+                    }
                 }
 
                 uploadsList.append("");
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java
index f75d6adc838a2..e31824b2eb8ab 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java
@@ -46,6 +46,7 @@ public class DefaultOperatorOnlyRegistry implements OperatorOnlyRegistry {
         "cluster:admin/repository/analyze/blob",
         "cluster:admin/repository/analyze/blob/read",
         "cluster:admin/repository/analyze/register",
+        "cluster:admin/repository/analyze/register/uncontended",
         // Node shutdown APIs are operator only
         "cluster:admin/shutdown/create",
         "cluster:admin/shutdown/get",
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
index d3537785e0f14..ef44d75045126 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisFailureIT.java
@@ -63,6 +63,7 @@
 
 import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.bytesFromLong;
 import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes;
+import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -304,7 +305,7 @@ public void testFailsIfRegisterIncorrect() {
             private final AtomicBoolean registerWasCorrupted = new AtomicBoolean();
 
             @Override
-            public BytesReference onCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
+            public BytesReference onContendedCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
                 if (registerWasCorrupted.compareAndSet(false, true)) {
                     register.updateAndGet(bytes -> bytesFromLong(longFromBytes(bytes) + 1));
                 }
@@ -321,7 +322,7 @@ public void testFailsIfRegisterHoldsSpuriousValue() {
         final long expectedMax = Math.max(request.getConcurrency(), internalCluster().getNodeNames().length);
         blobStore.setDisruption(new Disruption() {
             @Override
-            public BytesReference onCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
+            public BytesReference onContendedCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
                 if (randomBoolean() && sawSpuriousValue.compareAndSet(false, true)) {
                     final var currentValue = longFromBytes(register.get());
                     if (currentValue == expectedMax) {
@@ -357,8 +358,9 @@ public void testTimesOutSpinningRegisterAnalysis() {
 
         blobStore.setDisruption(new Disruption() {
             @Override
-            public boolean compareAndExchangeReturnsWitness() {
-                return false;
+            public boolean compareAndExchangeReturnsWitness(String key) {
+                // let uncontended accesses succeed but all contended ones fail
+                return isContendedRegisterKey(key) == false;
             }
         });
         final var exception = expectThrows(RepositoryVerificationException.class, () -> analyseRepository(request));
@@ -369,6 +371,22 @@ public boolean compareAndExchangeReturnsWitness() {
         );
     }
 
+    public void testFailsIfAllRegisterOperationsInconclusive() {
+        final RepositoryAnalyzeAction.Request request = new RepositoryAnalyzeAction.Request("test-repo");
+        blobStore.setDisruption(new Disruption() {
+            @Override
+            public boolean compareAndExchangeReturnsWitness(String key) {
+                return false;
+            }
+        });
+        final var exception = expectThrows(RepositoryVerificationException.class, () -> analyseRepository(request));
+        assertThat(exception.getMessage(), containsString("analysis failed"));
+        assertThat(
+            asInstanceOf(RepositoryVerificationException.class, ExceptionsHelper.unwrapCause(exception.getCause())).getMessage(),
+            allOf(containsString("uncontended register operation failed"), containsString("did not observe any value"))
+        );
+    }
+
     private void analyseRepository(RepositoryAnalyzeAction.Request request) {
         client().execute(RepositoryAnalyzeAction.INSTANCE, request).actionGet(30L, TimeUnit.SECONDS);
     }
@@ -486,11 +504,11 @@ default boolean createBlobOnAbort() {
             return false;
         }
 
-        default boolean compareAndExchangeReturnsWitness() {
+        default boolean compareAndExchangeReturnsWitness(String key) {
             return true;
         }
 
-        default BytesReference onCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
+        default BytesReference onContendedCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
             return register.compareAndExchange(expected, updated);
         }
     }
@@ -663,13 +681,28 @@ public void compareAndExchangeRegister(
             ActionListener listener
         ) {
             assertPurpose(purpose);
-            if (disruption.compareAndExchangeReturnsWitness()) {
+            final boolean isContendedRegister = isContendedRegisterKey(key); // validate key
+            if (disruption.compareAndExchangeReturnsWitness(key)) {
                 final var register = registers.computeIfAbsent(key, ignored -> new BytesRegister());
-                listener.onResponse(OptionalBytesReference.of(disruption.onCompareAndExchange(register, expected, updated)));
+                if (isContendedRegister) {
+                    listener.onResponse(OptionalBytesReference.of(disruption.onContendedCompareAndExchange(register, expected, updated)));
+                } else {
+                    listener.onResponse(OptionalBytesReference.of(register.compareAndExchange(expected, updated)));
+                }
             } else {
                 listener.onResponse(OptionalBytesReference.MISSING);
             }
         }
     }
 
+    static boolean isContendedRegisterKey(String key) {
+        if (key.startsWith(RepositoryAnalyzeAction.CONTENDED_REGISTER_NAME_PREFIX)) {
+            return true;
+        }
+        if (key.startsWith(RepositoryAnalyzeAction.UNCONTENDED_REGISTER_NAME_PREFIX)) {
+            return false;
+        }
+        return fail(null, "unknown register: %s", key);
+    }
+
 }
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java
index 80c42fc9630e1..d6c793984736f 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/internalClusterTest/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalysisSuccessIT.java
@@ -57,6 +57,7 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
+import static org.elasticsearch.repositories.blobstore.testkit.RepositoryAnalysisFailureIT.isContendedRegisterKey;
 import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.lessThanOrEqualTo;
@@ -442,12 +443,15 @@ public Map listBlobsByPrefix(OperationPurpose purpose, Str
         @Override
         public void getRegister(OperationPurpose purpose, String key, ActionListener listener) {
             assertPurpose(purpose);
-            if (firstRegisterRead.compareAndSet(true, false) && randomBoolean() && randomBoolean()) {
+            if (isContendedRegisterKey(key) && firstRegisterRead.compareAndSet(true, false) && randomBoolean() && randomBoolean()) {
+                // it's ok if _contended_ register accesses are a little disrupted since they retry until success, however,
                 // only fail the first read, we must not fail the final check
                 listener.onResponse(OptionalBytesReference.EMPTY);
             } else if (randomBoolean()) {
+                // read the register directly
                 listener.onResponse(OptionalBytesReference.of(registers.computeIfAbsent(key, ignored -> new BytesRegister()).get()));
             } else {
+                // read using a compare-and-exchange that cannot succeed, but which returns the current value anyway
                 final var bogus = randomFrom(BytesArray.EMPTY, new BytesArray(new byte[] { randomByte() }));
                 compareAndExchangeRegister(purpose, key, bogus, bogus, listener);
             }
@@ -462,17 +466,22 @@ public void compareAndExchangeRegister(
             ActionListener listener
         ) {
             assertPurpose(purpose);
-            firstRegisterRead.set(false);
-            if (updated.length() > 1 && randomBoolean() && randomBoolean()) {
-                // updated.length() > 1 so we don't fail the final check because we know there can be no concurrent operations at that point
-                listener.onResponse(OptionalBytesReference.MISSING);
-            } else {
-                listener.onResponse(
-                    OptionalBytesReference.of(
-                        registers.computeIfAbsent(key, ignored -> new BytesRegister()).compareAndExchange(expected, updated)
-                    )
-                );
+            if (isContendedRegisterKey(key)) {
+                // it's ok if _contended_ register accesses are a little disrupted since they retry until success
+
+                firstRegisterRead.set(false);
+                if (updated.length() > 1 && randomBoolean() && randomBoolean()) {
+                    // updated.length() > 1 so the final check succeeds because we know there can be no concurrent operations at that point
+                    listener.onResponse(OptionalBytesReference.MISSING);
+                    return;
+                }
             }
+
+            listener.onResponse(
+                OptionalBytesReference.of(
+                    registers.computeIfAbsent(key, ignored -> new BytesRegister()).compareAndExchange(expected, updated)
+                )
+            );
         }
     }
 
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
index 06f9f75d1a5b0..f71ad161aa865 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java
@@ -11,8 +11,8 @@
 import org.apache.logging.log4j.Logger;
 import org.elasticsearch.ElasticsearchTimeoutException;
 import org.elasticsearch.ExceptionsHelper;
+import org.elasticsearch.TransportVersion;
 import org.elasticsearch.TransportVersions;
-import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.ActionListenerResponseHandler;
 import org.elasticsearch.action.ActionRequest;
@@ -74,6 +74,7 @@
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
@@ -98,6 +99,7 @@ public class RepositoryAnalyzeAction extends ActionType li
                     (CancellableTask) task,
                     request,
                     state.nodes(),
+                    state.getMinTransportVersion(),
                     threadPool::relativeTimeInMillis,
                     listener
                 ).run();
@@ -368,6 +371,7 @@ public static class AsyncAction {
         private final CancellableTask task;
         private final Request request;
         private final DiscoveryNodes discoveryNodes;
+        private final TransportVersion minClusterTransportVersion;
         private final LongSupplier currentTimeMillisSupplier;
         private final ActionListener listener;
         private final SubscribableListener cancellationListener;
@@ -391,6 +395,7 @@ public AsyncAction(
             CancellableTask task,
             Request request,
             DiscoveryNodes discoveryNodes,
+            TransportVersion minClusterTransportVersion,
             LongSupplier currentTimeMillisSupplier,
             ActionListener listener
         ) {
@@ -399,6 +404,7 @@ public AsyncAction(
             this.task = task;
             this.request = request;
             this.discoveryNodes = discoveryNodes;
+            this.minClusterTransportVersion = minClusterTransportVersion;
             this.currentTimeMillisSupplier = currentTimeMillisSupplier;
             this.timeoutTimeMillis = currentTimeMillisSupplier.getAsLong() + request.getTimeout().millis();
 
@@ -482,22 +488,35 @@ public void run() {
             final Random random = new Random(request.getSeed());
             final List nodes = getSnapshotNodes(discoveryNodes);
 
-            final String contendedRegisterName = CONTENDED_REGISTER_NAME_PREFIX + UUIDs.randomBase64UUID(random);
-            try (
-                var registerRefs = new RefCountingRunnable(finalRegisterValueVerifier(contendedRegisterName, random, requestRefs.acquire()))
-            ) {
-                final int registerOperations = Math.max(nodes.size(), request.getConcurrency());
-                for (int i = 0; i < registerOperations; i++) {
-                    final ContendedRegisterAnalyzeAction.Request registerAnalyzeRequest = new ContendedRegisterAnalyzeAction.Request(
-                        request.getRepositoryName(),
-                        blobPath,
-                        contendedRegisterName,
-                        registerOperations,
-                        random.nextInt((registerOperations + 1) * 2)
-                    );
-                    final DiscoveryNode node = nodes.get(i < nodes.size() ? i : random.nextInt(nodes.size()));
-                    final Releasable registerRef = registerRefs.acquire();
-                    queue.add(ref -> runContendedRegisterAnalysis(Releasables.wrap(registerRef, ref), registerAnalyzeRequest, node));
+            if (minClusterTransportVersion.onOrAfter(TransportVersions.V_8_8_0)) {
+                final String contendedRegisterName = CONTENDED_REGISTER_NAME_PREFIX + UUIDs.randomBase64UUID(random);
+                final AtomicBoolean contendedRegisterAnalysisComplete = new AtomicBoolean();
+                try (
+                    var registerRefs = new RefCountingRunnable(
+                        finalRegisterValueVerifier(
+                            contendedRegisterName,
+                            random,
+                            Releasables.wrap(requestRefs.acquire(), () -> contendedRegisterAnalysisComplete.set(true))
+                        )
+                    )
+                ) {
+                    final int registerOperations = Math.max(nodes.size(), request.getConcurrency());
+                    for (int i = 0; i < registerOperations; i++) {
+                        final ContendedRegisterAnalyzeAction.Request registerAnalyzeRequest = new ContendedRegisterAnalyzeAction.Request(
+                            request.getRepositoryName(),
+                            blobPath,
+                            contendedRegisterName,
+                            registerOperations,
+                            random.nextInt((registerOperations + 1) * 2)
+                        );
+                        final DiscoveryNode node = nodes.get(i < nodes.size() ? i : random.nextInt(nodes.size()));
+                        final Releasable registerRef = registerRefs.acquire();
+                        queue.add(ref -> runContendedRegisterAnalysis(Releasables.wrap(registerRef, ref), registerAnalyzeRequest, node));
+                    }
+                }
+
+                if (minClusterTransportVersion.onOrAfter(TransportVersions.UNCONTENDED_REGISTER_ANALYSIS_ADDED)) {
+                    new UncontendedRegisterAnalysis(new Random(random.nextLong()), nodes, contendedRegisterAnalysisComplete).run();
                 }
             }
 
@@ -600,7 +619,7 @@ private BlobContainer getBlobContainer() {
         }
 
         private void runContendedRegisterAnalysis(Releasable ref, ContendedRegisterAnalyzeAction.Request request, DiscoveryNode node) {
-            if (node.getVersion().onOrAfter(Version.V_8_8_0) && isRunning()) {
+            if (isRunning()) {
                 transportService.sendChildRequest(
                     node,
                     ContendedRegisterAnalyzeAction.NAME,
@@ -690,6 +709,59 @@ public void onFailure(Exception exp) {
             };
         }
 
+        private class UncontendedRegisterAnalysis implements Runnable {
+            private final Random random;
+            private final String registerName;
+            private final List nodes;
+            private final AtomicBoolean otherAnalysisComplete;
+            private int currentValue; // actions run in strict sequence so no need for synchronization
+
+            UncontendedRegisterAnalysis(Random random, List nodes, AtomicBoolean otherAnalysisComplete) {
+                this.random = random;
+                this.registerName = UNCONTENDED_REGISTER_NAME_PREFIX + UUIDs.randomBase64UUID(random);
+                this.nodes = nodes;
+                this.otherAnalysisComplete = otherAnalysisComplete;
+            }
+
+            private final ActionListener stepListener = new ActionListener<>() {
+                @Override
+                public void onResponse(ActionResponse.Empty ignored) {
+                    currentValue += 1;
+                    run();
+                }
+
+                @Override
+                public void onFailure(Exception e) {
+                    fail(e);
+                }
+            };
+
+            @Override
+            public void run() {
+                if (isRunning() == false) {
+                    return;
+                }
+
+                // complete at least request.getConcurrency() steps, but we may as well keep running for longer too
+                if (currentValue > request.getConcurrency() && otherAnalysisComplete.get()) {
+                    return;
+                }
+
+                transportService.sendChildRequest(
+                    nodes.get(currentValue < nodes.size() ? currentValue : random.nextInt(nodes.size())),
+                    UncontendedRegisterAnalyzeAction.NAME,
+                    new UncontendedRegisterAnalyzeAction.Request(request.getRepositoryName(), blobPath, registerName, currentValue),
+                    task,
+                    TransportRequestOptions.EMPTY,
+                    new ActionListenerResponseHandler<>(
+                        ActionListener.releaseAfter(stepListener, requestRefs.acquire()),
+                        in -> ActionResponse.Empty.INSTANCE,
+                        TransportResponseHandler.TRANSPORT_WORKER
+                    )
+                );
+            }
+        }
+
         private void runCleanUp() {
             transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT).execute(ActionRunnable.wrap(listener, l -> {
                 final long listingStartTimeNanos = System.nanoTime();
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java
index 57c4ca8e4b964..439542ead868c 100644
--- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java
@@ -34,7 +34,8 @@ public class SnapshotRepositoryTestKit extends Plugin implements ActionPlugin {
             new ActionHandler<>(RepositoryAnalyzeAction.INSTANCE, RepositoryAnalyzeAction.TransportAction.class),
             new ActionHandler<>(BlobAnalyzeAction.INSTANCE, BlobAnalyzeAction.TransportAction.class),
             new ActionHandler<>(GetBlobChecksumAction.INSTANCE, GetBlobChecksumAction.TransportAction.class),
-            new ActionHandler<>(ContendedRegisterAnalyzeAction.INSTANCE, ContendedRegisterAnalyzeAction.TransportAction.class)
+            new ActionHandler<>(ContendedRegisterAnalyzeAction.INSTANCE, ContendedRegisterAnalyzeAction.TransportAction.class),
+            new ActionHandler<>(UncontendedRegisterAnalyzeAction.INSTANCE, UncontendedRegisterAnalyzeAction.TransportAction.class)
         );
     }
 
diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java
new file mode 100644
index 0000000000000..9fdb0d7f5228a
--- /dev/null
+++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.repositories.blobstore.testkit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.TransportVersions;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.ActionRequest;
+import org.elasticsearch.action.ActionRequestValidationException;
+import org.elasticsearch.action.ActionResponse;
+import org.elasticsearch.action.ActionType;
+import org.elasticsearch.action.support.ActionFilters;
+import org.elasticsearch.action.support.HandledTransportAction;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.blobstore.BlobContainer;
+import org.elasticsearch.common.blobstore.BlobPath;
+import org.elasticsearch.common.blobstore.OperationPurpose;
+import org.elasticsearch.common.blobstore.OptionalBytesReference;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.repositories.RepositoriesService;
+import org.elasticsearch.repositories.Repository;
+import org.elasticsearch.repositories.RepositoryVerificationException;
+import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
+import org.elasticsearch.tasks.CancellableTask;
+import org.elasticsearch.tasks.Task;
+import org.elasticsearch.tasks.TaskId;
+import org.elasticsearch.threadpool.ThreadPool;
+import org.elasticsearch.transport.TransportService;
+
+import java.io.IOException;
+import java.util.Map;
+
+import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.bytesFromLong;
+import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes;
+
+public class UncontendedRegisterAnalyzeAction extends ActionType {
+
+    private static final Logger logger = LogManager.getLogger(UncontendedRegisterAnalyzeAction.class);
+
+    public static final UncontendedRegisterAnalyzeAction INSTANCE = new UncontendedRegisterAnalyzeAction();
+    public static final String NAME = "cluster:admin/repository/analyze/register/uncontended";
+
+    private UncontendedRegisterAnalyzeAction() {
+        super(NAME, in -> ActionResponse.Empty.INSTANCE);
+    }
+
+    public static class TransportAction extends HandledTransportAction {
+
+        private static final Logger logger = UncontendedRegisterAnalyzeAction.logger;
+
+        private final RepositoriesService repositoriesService;
+
+        @Inject
+        public TransportAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) {
+            super(
+                NAME,
+                transportService,
+                actionFilters,
+                Request::new,
+                transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT)
+            );
+            this.repositoriesService = repositoriesService;
+        }
+
+        @Override
+        protected void doExecute(Task task, Request request, ActionListener outerListener) {
+            final ActionListener listener = ActionListener.assertOnce(outerListener.map(ignored -> ActionResponse.Empty.INSTANCE));
+            final Repository repository = repositoriesService.repository(request.getRepositoryName());
+            if (repository instanceof BlobStoreRepository == false) {
+                throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository");
+            }
+            if (repository.isReadOnly()) {
+                throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only");
+            }
+            final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository;
+            final BlobPath path = blobStoreRepository.basePath().add(request.getContainerPath());
+            final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path);
+
+            logger.trace("handling [{}]", request);
+
+            assert task instanceof CancellableTask;
+            blobContainer.compareAndExchangeRegister(
+                OperationPurpose.REPOSITORY_ANALYSIS,
+                request.getRegisterName(),
+                bytesFromLong(request.getExpectedValue()),
+                bytesFromLong(request.getExpectedValue() + 1),
+                new ActionListener<>() {
+                    @Override
+                    public void onResponse(OptionalBytesReference optionalBytesReference) {
+                        ActionListener.completeWith(listener, () -> {
+                            if (optionalBytesReference.isPresent() == false) {
+                                throw new RepositoryVerificationException(
+                                    repository.getMetadata().name(),
+                                    Strings.format(
+                                        "uncontended register operation failed: expected [%d] but did not observe any value",
+                                        request.getExpectedValue()
+                                    )
+                                );
+                            }
+
+                            final var witness = longFromBytes(optionalBytesReference.bytesReference());
+                            if (witness != request.getExpectedValue()) {
+                                throw new RepositoryVerificationException(
+                                    repository.getMetadata().name(),
+                                    Strings.format(
+                                        "uncontended register operation failed: expected [%d] but observed [%d]",
+                                        request.getExpectedValue(),
+                                        witness
+                                    )
+                                );
+                            }
+
+                            return null;
+                        });
+                    }
+
+                    @Override
+                    public void onFailure(Exception e) {
+                        if (e instanceof UnsupportedOperationException) {
+                            // Registers are not supported on all repository types, and that's ok.
+                            listener.onResponse(null);
+                        } else {
+                            listener.onFailure(e);
+                        }
+                    }
+                }
+            );
+        }
+    }
+
+    public static class Request extends ActionRequest {
+        private final String repositoryName;
+        private final String containerPath;
+        private final String registerName;
+        private final long expectedValue;
+
+        public Request(String repositoryName, String containerPath, String registerName, long expectedValue) {
+            this.repositoryName = repositoryName;
+            this.containerPath = containerPath;
+            this.registerName = registerName;
+            this.expectedValue = expectedValue;
+        }
+
+        public Request(StreamInput in) throws IOException {
+            super(in);
+            assert in.getTransportVersion().onOrAfter(TransportVersions.UNCONTENDED_REGISTER_ANALYSIS_ADDED);
+            repositoryName = in.readString();
+            containerPath = in.readString();
+            registerName = in.readString();
+            expectedValue = in.readVLong();
+        }
+
+        @Override
+        public void writeTo(StreamOutput out) throws IOException {
+            assert out.getTransportVersion().onOrAfter(TransportVersions.UNCONTENDED_REGISTER_ANALYSIS_ADDED);
+            super.writeTo(out);
+            out.writeString(repositoryName);
+            out.writeString(containerPath);
+            out.writeString(registerName);
+            out.writeVLong(expectedValue);
+        }
+
+        @Override
+        public ActionRequestValidationException validate() {
+            return null;
+        }
+
+        public String getRepositoryName() {
+            return repositoryName;
+        }
+
+        public String getContainerPath() {
+            return containerPath;
+        }
+
+        public String getRegisterName() {
+            return registerName;
+        }
+
+        public long getExpectedValue() {
+            return expectedValue;
+        }
+
+        @Override
+        public Task createTask(long id, String type, String action, TaskId parentTaskId, Map headers) {
+            return new CancellableTask(id, type, action, getDescription(), parentTaskId, headers);
+        }
+
+        @Override
+        public String toString() {
+            return getDescription();
+        }
+
+        @Override
+        public String getDescription() {
+            return Strings.format(
+                """
+                    UncontendedRegisterAnalyzeAction.Request{\
+                    repositoryName='%s', containerPath='%s', registerName='%s', expectedValue='%d'}""",
+                repositoryName,
+                containerPath,
+                registerName,
+                expectedValue
+            );
+        }
+    }
+}

From 2ef6bb3a90a075430a9bc5a1fee97d05300aa1ef Mon Sep 17 00:00:00 2001
From: Daniel Mitterdorfer 
Date: Mon, 23 Oct 2023 13:04:52 +0200
Subject: [PATCH 058/203] Increase K/V look-back time interval (#101205)

With this commit we increase the look-back time interval from 3 hours to
4 hours by default. This look-back time interval is applied to determine
the correct K/V indices to query around a rollover. As the new index may
not have all data immediately after a rollover, we also need to query
the old index. Clients may cache data for up to 3 hours but to avoid
unlucky timing we add a bit of slack and increase the time interval to 4
hours.
---
 docs/changelog/101205.yaml                                   | 5 +++++
 .../xpack/profiling/TransportGetStackTracesAction.java       | 5 +++--
 2 files changed, 8 insertions(+), 2 deletions(-)
 create mode 100644 docs/changelog/101205.yaml

diff --git a/docs/changelog/101205.yaml b/docs/changelog/101205.yaml
new file mode 100644
index 0000000000000..528f6fb35846e
--- /dev/null
+++ b/docs/changelog/101205.yaml
@@ -0,0 +1,5 @@
+pr: 101205
+summary: Increase K/V look-back time interval
+area: Application
+type: bug
+issues: []
diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java
index 2a489fed7c2cd..3f019f01b7c77 100644
--- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java
+++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java
@@ -79,11 +79,12 @@ public class TransportGetStackTracesAction extends HandledTransportAction PROFILING_KV_INDEX_OVERLAP = Setting.positiveTimeSetting(
         "xpack.profiling.kv_index.overlap",
-        TimeValue.timeValueHours(3),
+        TimeValue.timeValueHours(4),
         Setting.Property.NodeScope
     );
 

From a579504e11d7d2ce5518ad409f92da05ac36d3d4 Mon Sep 17 00:00:00 2001
From: Daniel Mitterdorfer 
Date: Mon, 23 Oct 2023 13:14:14 +0200
Subject: [PATCH 059/203] Remove auto_configure privilege for profiling
 (#101026)

With this commit we remove the `auto_configure` privilege for the Fleet
service account that targets profiling-related indices. This privilege
was needed to automatically create indices and data streams in the past
but as this managed by the Elasticsearch plugin, there is no need to
grant this privilege to Fleet-managed components.
---
 docs/changelog/101026.yaml                                   | 5 +++++
 .../rest-api/security/get-service-accounts.asciidoc          | 3 +--
 .../xpack/security/authc/service/ServiceAccountIT.java       | 3 +--
 .../xpack/security/authc/service/ElasticServiceAccounts.java | 2 +-
 .../security/authc/service/ElasticServiceAccountsTests.java  | 2 +-
 5 files changed, 9 insertions(+), 6 deletions(-)
 create mode 100644 docs/changelog/101026.yaml

diff --git a/docs/changelog/101026.yaml b/docs/changelog/101026.yaml
new file mode 100644
index 0000000000000..cee85a722d7fa
--- /dev/null
+++ b/docs/changelog/101026.yaml
@@ -0,0 +1,5 @@
+pr: 101026
+summary: Remove `auto_configure` privilege for profiling
+area: Authorization
+type: enhancement
+issues: []
diff --git a/docs/reference/rest-api/security/get-service-accounts.asciidoc b/docs/reference/rest-api/security/get-service-accounts.asciidoc
index b08e73f789053..526c6e65ccf33 100644
--- a/docs/reference/rest-api/security/get-service-accounts.asciidoc
+++ b/docs/reference/rest-api/security/get-service-accounts.asciidoc
@@ -91,8 +91,7 @@ GET /_security/service/elastic/fleet-server
           ],
           "privileges": [
             "read",
-            "write",
-            "auto_configure"
+            "write"
           ],
           "allow_restricted_indices": false
         },
diff --git a/x-pack/plugin/security/qa/service-account/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIT.java b/x-pack/plugin/security/qa/service-account/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIT.java
index f58ddd3a067cd..f66631a57b4bb 100644
--- a/x-pack/plugin/security/qa/service-account/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIT.java
+++ b/x-pack/plugin/security/qa/service-account/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/service/ServiceAccountIT.java
@@ -110,8 +110,7 @@ public class ServiceAccountIT extends ESRestTestCase {
                   ],
                   "privileges": [
                     "read",
-                    "write",
-                    "auto_configure"
+                    "write"
                   ],
                   "allow_restricted_indices": false
                 },
diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java
index a33c45adf814e..8942be0bee29c 100644
--- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java
+++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccounts.java
@@ -75,7 +75,7 @@ final class ElasticServiceAccounts {
                     )
                     .privileges("write", "create_index", "auto_configure")
                     .build(),
-                RoleDescriptor.IndicesPrivileges.builder().indices("profiling-*").privileges("read", "write", "auto_configure").build(),
+                RoleDescriptor.IndicesPrivileges.builder().indices("profiling-*").privileges("read", "write").build(),
                 RoleDescriptor.IndicesPrivileges.builder()
                     // APM Server (and hence Fleet Server, which issues its API Keys) needs additional privileges
                     // for the non-sensitive "sampled traces" data stream:
diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java
index 2c65a06a486a2..08cfdde03815d 100644
--- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java
+++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java
@@ -211,7 +211,7 @@ public void testElasticFleetServerPrivileges() {
 
         final IndexAbstraction profilingIndex = mockIndexAbstraction("profiling-" + randomAlphaOfLengthBetween(1, 20));
         assertThat(role.indices().allowedIndicesMatcher(AutoPutMappingAction.NAME).test(profilingIndex), is(true));
-        assertThat(role.indices().allowedIndicesMatcher(AutoCreateAction.NAME).test(profilingIndex), is(true));
+        assertThat(role.indices().allowedIndicesMatcher(AutoCreateAction.NAME).test(profilingIndex), is(false));
         assertThat(role.indices().allowedIndicesMatcher(DeleteAction.NAME).test(profilingIndex), is(true));
         assertThat(role.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(profilingIndex), is(false));
         assertThat(role.indices().allowedIndicesMatcher(IndexAction.NAME).test(profilingIndex), is(true));

From af08d28d3883682b3afe4583e25969d40fcccaf1 Mon Sep 17 00:00:00 2001
From: Martijn van Groningen 
Date: Mon, 23 Oct 2023 16:40:21 +0200
Subject: [PATCH 060/203] Fix painless execute api and tsdb issue. (#101212)

Today using painless execute api with tsdb index can fail with a `_id must be unset or set to [cn4exTOUtxytuLkQAAABeRnR_mY] but was [_id] because [test_index] is in time_series mode` error.
This change addresses this.

The painless execute api shouldn't set use a static _id, but
let the TsidExtractingIdFieldMapper generate it.
Otherwise validation TsidExtractingIdFieldMapper fails.

Closes #101072
---
 docs/changelog/101212.yaml                    |  6 ++
 .../action/PainlessExecuteAction.java         |  9 ++-
 .../test/tsdb/150_runtime_fields.yml          | 60 +++++++++++++++++++
 3 files changed, 74 insertions(+), 1 deletion(-)
 create mode 100644 docs/changelog/101212.yaml
 create mode 100644 rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/150_runtime_fields.yml

diff --git a/docs/changelog/101212.yaml b/docs/changelog/101212.yaml
new file mode 100644
index 0000000000000..ed2b433209e8d
--- /dev/null
+++ b/docs/changelog/101212.yaml
@@ -0,0 +1,6 @@
+pr: 101212
+summary: Fix painless execute api and tsdb issue
+area: TSDB
+type: bug
+issues:
+ - 101072
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
index 8ec90c7d04979..7393dff40fa11 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/action/PainlessExecuteAction.java
@@ -53,6 +53,7 @@
 import org.elasticsearch.geometry.Geometry;
 import org.elasticsearch.geometry.Point;
 import org.elasticsearch.index.Index;
+import org.elasticsearch.index.IndexMode;
 import org.elasticsearch.index.IndexService;
 import org.elasticsearch.index.mapper.DateFieldMapper;
 import org.elasticsearch.index.mapper.DocumentMapper;
@@ -776,7 +777,13 @@ private static Response prepareRamIndex(
                 try (IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig(defaultAnalyzer))) {
                     BytesReference document = request.contextSetup.document;
                     XContentType xContentType = request.contextSetup.xContentType;
-                    SourceToParse sourceToParse = new SourceToParse("_id", document, xContentType);
+                    String id;
+                    if (indexService.getIndexSettings().getMode() == IndexMode.TIME_SERIES) {
+                        id = null; // The id gets auto generated for time series indices.
+                    } else {
+                        id = "_id";
+                    }
+                    SourceToParse sourceToParse = new SourceToParse(id, document, xContentType);
                     DocumentMapper documentMapper = indexService.mapperService().documentMapper();
                     if (documentMapper == null) {
                         documentMapper = DocumentMapper.createEmpty(indexService.mapperService());
diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/150_runtime_fields.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/150_runtime_fields.yml
new file mode 100644
index 0000000000000..8cb2802c2ef95
--- /dev/null
+++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/150_runtime_fields.yml
@@ -0,0 +1,60 @@
+---
+tsdb_execute_painless_api:
+  - skip:
+      version: " - 8.11.99"
+      reason: fixed in 8.12.0 and later
+
+  - do:
+      indices.create:
+        index: test_index
+        body:
+          settings:
+            index:
+              mode: time_series
+              routing_path: [metricset, k8s.pod.uid]
+          mappings:
+            properties:
+              "@timestamp":
+                type: date
+              metricset:
+                type: keyword
+                time_series_dimension: true
+              k8s:
+                properties:
+                  pod:
+                    properties:
+                      uid:
+                        type: keyword
+                        time_series_dimension: true
+                      name:
+                        type: keyword
+                      ip:
+                        type: ip
+                      network:
+                        properties:
+                          tx:
+                            type: long
+                          rx:
+                            type: long
+
+  - do:
+      scripts_painless_execute:
+        body:
+          script:
+            source: "emit(doc['k8s.pod.network.tx'].value < 1000);"
+          context: "boolean_field"
+          context_setup:
+            index: test_index
+            document:
+              "@timestamp": "2021-04-28T18:51:03.142Z"
+              metricset: pod
+              k8s:
+                pod:
+                  name: dog
+                  uid: df3145b3-0563-4d3b-a0f7-897eb2876ea9
+                  ip: 10.10.55.3
+                  network:
+                    tx: 111434595272
+                    rx: 430605511
+
+  - match: { result: [false] }

From 931dcae41dfebb132e0206a819a32638260d47db Mon Sep 17 00:00:00 2001
From: AlexB 
Date: Mon, 23 Oct 2023 07:45:42 -0700
Subject: [PATCH 061/203] Add improvements to the ES|QL docs (#101195)

Content and structural improvements to the ES|QL docs

---------

Co-authored-by: Alexandros Batsakis 
Co-authored-by: Abdon Pijpelink 
---
 docs/reference/esql/esql-commands.asciidoc    | 62 ++++++++++--
 docs/reference/esql/esql-enrich-data.asciidoc |  8 +-
 .../esql/esql-functions-operators.asciidoc    | 21 ++--
 docs/reference/esql/esql-kibana.asciidoc      |  9 +-
 docs/reference/esql/esql-language.asciidoc    | 11 ++-
 docs/reference/esql/esql-query-api.asciidoc   | 15 ++-
 docs/reference/esql/esql-rest.asciidoc        |  1 +
 docs/reference/esql/esql-syntax.asciidoc      |  4 +-
 .../functions/aggregation-functions.asciidoc  |  6 +-
 ...itional-functions-and-expressions.asciidoc |  8 +-
 .../functions/date-time-functions.asciidoc    |  6 +-
 .../esql/functions/math-functions.asciidoc    |  6 +-
 .../esql/functions/mv-functions.asciidoc      |  6 +-
 .../esql/functions/operators.asciidoc         |  6 +-
 .../esql/functions/string-functions.asciidoc  |  6 +-
 .../type-conversion-functions.asciidoc        |  6 +-
 docs/reference/esql/index.asciidoc            | 97 +++++--------------
 .../esql-processing-commands.asciidoc         | 41 --------
 .../esql/processing-commands/stats.asciidoc   |  2 +-
 .../esql-source-commands.asciidoc             | 22 -----
 docs/reference/esql/task-management.asciidoc  |  2 +-
 21 files changed, 155 insertions(+), 190 deletions(-)
 delete mode 100644 docs/reference/esql/processing-commands/esql-processing-commands.asciidoc
 delete mode 100644 docs/reference/esql/source-commands/esql-source-commands.asciidoc

diff --git a/docs/reference/esql/esql-commands.asciidoc b/docs/reference/esql/esql-commands.asciidoc
index 93508331d6be1..bb00731f3b032 100644
--- a/docs/reference/esql/esql-commands.asciidoc
+++ b/docs/reference/esql/esql-commands.asciidoc
@@ -1,17 +1,63 @@
 [[esql-commands]]
-== {esql} commands
+=== {esql} commands
 
 ++++
 Commands
 ++++
 
-{esql} provides a comprehensive set of source and processing commands:
+// tag::source_commands[]
+==== Source commands
 
-<>::
-include::source-commands/esql-source-commands.asciidoc[tag=list]
+An {esql} source command produces a table, typically with data from {es}.
 
-<>::
-include::processing-commands/esql-processing-commands.asciidoc[tag=list]
+image::images/esql/source-command.svg[A source command producing a table from {es},align="center"]
 
-include::source-commands/esql-source-commands.asciidoc[]
-include::processing-commands/esql-processing-commands.asciidoc[]
+{esql} supports these source commands:
+
+* <>
+* <>
+* <>
+
+include::source-commands/from.asciidoc[]
+include::source-commands/row.asciidoc[]
+include::source-commands/show.asciidoc[]
+
+// end::source_command[]
+
+// tag::proc_commands[]
+==== Processing commands
+
+{esql} processing commands change an input table by adding, removing, or changing
+rows and columns.
+
+image::images/esql/processing-command.svg[A processing command changing an input table,align="center"]
+
+{esql} supports these processing commands:
+
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+* <>
+
+include::processing-commands/dissect.asciidoc[]
+include::processing-commands/drop.asciidoc[]
+include::processing-commands/enrich.asciidoc[]
+include::processing-commands/eval.asciidoc[]
+include::processing-commands/grok.asciidoc[]
+include::processing-commands/keep.asciidoc[]
+include::processing-commands/limit.asciidoc[]
+include::processing-commands/mv_expand.asciidoc[]
+include::processing-commands/rename.asciidoc[]
+include::processing-commands/sort.asciidoc[]
+include::processing-commands/stats.asciidoc[]
+include::processing-commands/where.asciidoc[]
+
+// end::proc_command[]
diff --git a/docs/reference/esql/esql-enrich-data.asciidoc b/docs/reference/esql/esql-enrich-data.asciidoc
index d7a2c81df9ece..9708728e6b305 100644
--- a/docs/reference/esql/esql-enrich-data.asciidoc
+++ b/docs/reference/esql/esql-enrich-data.asciidoc
@@ -82,22 +82,22 @@ your query.
 ====
 
 [[esql-enrich-prereqs]]
-===== Prerequisites
+==== Prerequisites
 
 include::{es-repo-dir}/ingest/apis/enrich/put-enrich-policy.asciidoc[tag=enrich-policy-api-prereqs]
 
 [[esql-create-enrich-source-index]]
-===== Add enrich data
+==== Add enrich data
 
 include::../ingest/enrich.asciidoc[tag=create-enrich-source-index]
 
 [[esql-create-enrich-policy]]
-===== Create an enrich policy
+==== Create an enrich policy
 
 include::../ingest/enrich.asciidoc[tag=create-enrich-policy]
 
 [[esql-execute-enrich-policy]]
-===== Execute the enrich policy
+==== Execute the enrich policy
 
 include::../ingest/enrich.asciidoc[tag=execute-enrich-policy1]
 
diff --git a/docs/reference/esql/esql-functions-operators.asciidoc b/docs/reference/esql/esql-functions-operators.asciidoc
index 279c273278eb9..375bb4ee9dd00 100644
--- a/docs/reference/esql/esql-functions-operators.asciidoc
+++ b/docs/reference/esql/esql-functions-operators.asciidoc
@@ -1,36 +1,37 @@
 [[esql-functions-operators]]
-== {esql} functions and operators
+=== {esql} functions and operators
 
 ++++
 Functions and operators
 ++++
 
-{esql} provides a comprehensive set of functions and operators:
+{esql} provides a comprehensive set of functions and operators for working with data.
+The functions are divided into the following categories:
 
 [[esql-functions]]
 <>::
-include::functions/aggregation-functions.asciidoc[tag=list]
+include::functions/aggregation-functions.asciidoc[tag=agg_list]
 
 <>::
-include::functions/math-functions.asciidoc[tag=list]
+include::functions/math-functions.asciidoc[tag=math_list]
 
 <>::
-include::functions/string-functions.asciidoc[tag=list]
+include::functions/string-functions.asciidoc[tag=string_list]
 
 <>::
-include::functions/date-time-functions.asciidoc[tag=list]
+include::functions/date-time-functions.asciidoc[tag=date_list]
 
 <>::
-include::functions/type-conversion-functions.asciidoc[tag=list]
+include::functions/type-conversion-functions.asciidoc[tag=type_list]
 
 <>::
-include::functions/conditional-functions-and-expressions.asciidoc[tag=list]
+include::functions/conditional-functions-and-expressions.asciidoc[tag=cond_list]
 
 <>::
-include::functions/mv-functions.asciidoc[tag=list]
+include::functions/mv-functions.asciidoc[tag=mv_list]
 
 <>::
-include::functions/operators.asciidoc[tag=list]
+include::functions/operators.asciidoc[tag=op_list]
 
 include::functions/aggregation-functions.asciidoc[]
 include::functions/math-functions.asciidoc[]
diff --git a/docs/reference/esql/esql-kibana.asciidoc b/docs/reference/esql/esql-kibana.asciidoc
index 405d8d816ac15..534cba22ed1a1 100644
--- a/docs/reference/esql/esql-kibana.asciidoc
+++ b/docs/reference/esql/esql-kibana.asciidoc
@@ -5,4 +5,11 @@
 Kibana
 ++++
 
-coming::[8.11]
\ No newline at end of file
+
+Use {esql} in Discover to explore a data set. From the data view dropdown,
+select *Try {esql}* to get started.
+
+NOTE: {esql} queries in Discover and Lens are subject to the time range selected
+with the time filter.
+
+
diff --git a/docs/reference/esql/esql-language.asciidoc b/docs/reference/esql/esql-language.asciidoc
index 399034737efaa..2becd04cec948 100644
--- a/docs/reference/esql/esql-language.asciidoc
+++ b/docs/reference/esql/esql-language.asciidoc
@@ -1,18 +1,23 @@
 [[esql-language]]
-== {esql} language
+== Working with the {esql} language
 
 ++++
-{esql} language
+Working with the {esql} language
 ++++
 
 Detailed information about the {esql} language:
 
 * <>
+* <>
+* <>
 * <>
 * <>
 * <>
 
 include::esql-syntax.asciidoc[]
+include::esql-commands.asciidoc[]
+include::esql-functions-operators.asciidoc[]
 include::multivalued-fields.asciidoc[]
 include::metadata-fields.asciidoc[]
-include::esql-enrich-data.asciidoc[]
\ No newline at end of file
+include::esql-enrich-data.asciidoc[]
+
diff --git a/docs/reference/esql/esql-query-api.asciidoc b/docs/reference/esql/esql-query-api.asciidoc
index a6c1d3d598332..437871d31a88f 100644
--- a/docs/reference/esql/esql-query-api.asciidoc
+++ b/docs/reference/esql/esql-query-api.asciidoc
@@ -6,7 +6,20 @@
 
 Returns search results for an <> query.
 
-include::index.asciidoc[tag=esql-query-api-example]
+[source,console]
+----
+POST /_query
+{
+  "query": """
+    FROM library
+    | EVAL year = DATE_TRUNC(1 YEARS, release_date)
+    | STATS MAX(page_count) BY year
+    | SORT year
+    | LIMIT 5
+  """
+}
+----
+// TEST[setup:library]
 
 [discrete]
 [[esql-query-api-request]]
diff --git a/docs/reference/esql/esql-rest.asciidoc b/docs/reference/esql/esql-rest.asciidoc
index 28ecfb7eea840..55c9946ad08b4 100644
--- a/docs/reference/esql/esql-rest.asciidoc
+++ b/docs/reference/esql/esql-rest.asciidoc
@@ -45,6 +45,7 @@ highly recommended), take advantage of the triple quotes `"""` when creating the
 query. This not only automatically escapes double quotes (`"`) inside the query
 string but also supports multi-line requests:
 
+// tag::esql-query-api[]
 [source,console]
 ----
 POST /_query?format=txt
diff --git a/docs/reference/esql/esql-syntax.asciidoc b/docs/reference/esql/esql-syntax.asciidoc
index 805b879ab676e..725b1d3ff1e03 100644
--- a/docs/reference/esql/esql-syntax.asciidoc
+++ b/docs/reference/esql/esql-syntax.asciidoc
@@ -9,8 +9,8 @@
 [[esql-basic-syntax]]
 === Basic syntax
 
-An {esql} query is composed of a <> followed
-by an optional series of <>,
+An {esql} query is composed of a <> followed
+by an optional series of <>,
 separated by a pipe character: `|`. For example:
 
 [source,esql]
diff --git a/docs/reference/esql/functions/aggregation-functions.asciidoc b/docs/reference/esql/functions/aggregation-functions.asciidoc
index ca16e07c2565c..bd501ea49f158 100644
--- a/docs/reference/esql/functions/aggregation-functions.asciidoc
+++ b/docs/reference/esql/functions/aggregation-functions.asciidoc
@@ -1,5 +1,5 @@
 [[esql-agg-functions]]
-=== {esql} aggregate functions
+==== {esql} aggregate functions
 
 ++++
 Aggregate functions
@@ -7,7 +7,7 @@
 
 The <> function supports these aggregate functions:
 
-// tag::list[]
+// tag::agg_list[]
 * <>
 * <>
 * <>
@@ -17,7 +17,7 @@ The <> function supports these aggregate functions:
 * <>
 * <>
 * <>
-// end::list[]
+// end::agg_list[]
 
 include::avg.asciidoc[]
 include::count.asciidoc[]
diff --git a/docs/reference/esql/functions/conditional-functions-and-expressions.asciidoc b/docs/reference/esql/functions/conditional-functions-and-expressions.asciidoc
index 692cb19e19562..d835a14856c03 100644
--- a/docs/reference/esql/functions/conditional-functions-and-expressions.asciidoc
+++ b/docs/reference/esql/functions/conditional-functions-and-expressions.asciidoc
@@ -1,19 +1,19 @@
 [[esql-conditional-functions-and-expressions]]
-=== {esql} conditional functions and expressions
+==== {esql} conditional functions and expressions
 
 ++++
 Conditional functions and expressions
 ++++
 
 Conditional functions return one of their arguments by evaluating in an if-else
-manner. {esql} supports these conditional functions: 
+manner. {esql} supports these conditional functions:
 
-// tag::list[]
+// tag::cond_list[]
 * <>
 * <>
 * <>
 * <>
-// end::list[]
+// end::cond_list[]
 
 include::case.asciidoc[]
 include::coalesce.asciidoc[]
diff --git a/docs/reference/esql/functions/date-time-functions.asciidoc b/docs/reference/esql/functions/date-time-functions.asciidoc
index 59f999f1843fc..8ff7b1e974eeb 100644
--- a/docs/reference/esql/functions/date-time-functions.asciidoc
+++ b/docs/reference/esql/functions/date-time-functions.asciidoc
@@ -1,5 +1,5 @@
 [[esql-date-time-functions]]
-=== {esql} date-time functions
+==== {esql} date-time functions
 
 ++++
 Date-time functions
@@ -7,14 +7,14 @@
 
 {esql} supports these date-time functions:
 
-// tag::list[]
+// tag::date_list[]
 * <>
 * <>
 * <>
 * <>
 * <>
 * <>
-// end::list[]
+// end::date_list[]
 
 include::auto_bucket.asciidoc[]
 include::date_extract.asciidoc[]
diff --git a/docs/reference/esql/functions/math-functions.asciidoc b/docs/reference/esql/functions/math-functions.asciidoc
index 9338959354d3f..21131ae9074d7 100644
--- a/docs/reference/esql/functions/math-functions.asciidoc
+++ b/docs/reference/esql/functions/math-functions.asciidoc
@@ -1,5 +1,5 @@
 [[esql-math-functions]]
-=== {esql} mathematical functions
+==== {esql} mathematical functions
 
 ++++
 Mathematical functions
@@ -7,7 +7,7 @@
 
 {esql} supports these mathematical functions:
 
-// tag::list[]
+// tag::math_list[]
 * <>
 * <>
 * <>
@@ -28,7 +28,7 @@
 * <>
 * <>
 * <>
-// end::list[]
+// end::math_list[]
 
 include::abs.asciidoc[]
 include::acos.asciidoc[]
diff --git a/docs/reference/esql/functions/mv-functions.asciidoc b/docs/reference/esql/functions/mv-functions.asciidoc
index d4f9a07af4ff7..83dbaaadc5c06 100644
--- a/docs/reference/esql/functions/mv-functions.asciidoc
+++ b/docs/reference/esql/functions/mv-functions.asciidoc
@@ -1,5 +1,5 @@
 [[esql-mv-functions]]
-=== {esql} multivalue functions
+==== {esql} multivalue functions
 
 ++++
 Multivalue functions
@@ -7,7 +7,7 @@
 
 {esql} supports these multivalue functions:
 
-// tag::list[]
+// tag::mv_list[]
 * <>
 * <>
 * <>
@@ -16,7 +16,7 @@
 * <>
 * <>
 * <>
-// end::list[]
+// end::mv_list[]
 
 include::mv_avg.asciidoc[]
 include::mv_concat.asciidoc[]
diff --git a/docs/reference/esql/functions/operators.asciidoc b/docs/reference/esql/functions/operators.asciidoc
index f1698bb2450b7..c236413b5dd7e 100644
--- a/docs/reference/esql/functions/operators.asciidoc
+++ b/docs/reference/esql/functions/operators.asciidoc
@@ -1,5 +1,5 @@
 [[esql-operators]]
-=== {esql} operators
+==== {esql} operators
 
 ++++
 Operators
@@ -7,7 +7,7 @@
 
 Boolean operators for comparing against one or multiple expressions.
 
-// tag::list[]
+// tag::op_list[]
 * <>
 * <>
 * <>
@@ -20,7 +20,7 @@ Boolean operators for comparing against one or multiple expressions.
 * <>
 * <>
 * <>
-// end::list[]
+// end::op_list[]
 
 include::binary.asciidoc[]
 include::logical.asciidoc[]
diff --git a/docs/reference/esql/functions/string-functions.asciidoc b/docs/reference/esql/functions/string-functions.asciidoc
index af77d4a08bff2..b209244b93297 100644
--- a/docs/reference/esql/functions/string-functions.asciidoc
+++ b/docs/reference/esql/functions/string-functions.asciidoc
@@ -1,5 +1,5 @@
 [[esql-string-functions]]
-=== {esql} string functions
+==== {esql} string functions
 
 ++++
 String functions
@@ -7,7 +7,7 @@
 
 {esql} supports these string functions:
 
-// tag::list[]
+// tag::string_list[]
 * <>
 * <>
 * <>
@@ -18,7 +18,7 @@
 * <>
 * <>
 * <>
-// end::list[]
+// end::string_list[]
 
 include::concat.asciidoc[]
 include::left.asciidoc[]
diff --git a/docs/reference/esql/functions/type-conversion-functions.asciidoc b/docs/reference/esql/functions/type-conversion-functions.asciidoc
index c24cf6685b84c..640006c936526 100644
--- a/docs/reference/esql/functions/type-conversion-functions.asciidoc
+++ b/docs/reference/esql/functions/type-conversion-functions.asciidoc
@@ -1,5 +1,5 @@
 [[esql-type-conversion-functions]]
-=== {esql} type conversion functions
+==== {esql} type conversion functions
 
 ++++
 Type conversion functions
@@ -7,7 +7,7 @@
 
 {esql} supports these type conversion functions:
 
-// tag::list[]
+// tag::type_list[]
 * <>
 * <>
 * <>
@@ -19,7 +19,7 @@
 * <>
 * <>
 * <>
-// end::list[]
+// end::type_list[]
 
 include::to_boolean.asciidoc[]
 include::to_datetime.asciidoc[]
diff --git a/docs/reference/esql/index.asciidoc b/docs/reference/esql/index.asciidoc
index c164fcc95b7d5..09b74740a5b67 100644
--- a/docs/reference/esql/index.asciidoc
+++ b/docs/reference/esql/index.asciidoc
@@ -5,22 +5,36 @@
 :esql-specs: {esql-tests}/testFixtures/src/main/resources
 
 [partintro]
---
 
 preview::[]
 
-The {es} Query Language ({esql}) is a query language that enables the iterative
-exploration of data.
+The {es} Query Language ({esql}) provides a powerful way to filter, transform, and analyze data stored in {es}.
+Users can author {esql} queries to find specific events, perform statistical analysis, and generate visualizations.
+It supports a wide range of commands and functions that enable users to perform various data operations,
+such as filtering, aggregation, time-series analysis, and more.
 
-An {esql} query consists of a series of commands, separated by pipes. Each query
-starts with a <>. A source command produces
+The {es} Query Language ({esql}) makes use of "pipes" to manipulate and transform data in a step-by-step fashion.
+This approach allows users to compose a series of operations, where the output of one operation becomes the input for the next,
+enabling complex data transformations and analysis.
+
+A simple example of an {esql} query is shown below:
+[source,esql]
+----
+FROM employees
+| EVAL age = DATE_DIFF(NOW(), birth_date, 'Y')
+| STATS AVG(age) BY department
+| SORT age DESC
+----
+
+Each {esql} query starts with a <>. A source command produces
 a table, typically with data from {es}.
 
 image::images/esql/source-command.svg[A source command producing a table from {es},align="center"]
 
 A source command can be followed by one or more
-<>. Processing commands change an
+<>. Processing commands change an
 input table by adding, removing, or changing rows and columns.
+Processing commands can perform filtering, projection, aggregation, and more.
 
 image::images/esql/processing-command.svg[A processing command changing an input table,align="center"]
 
@@ -32,78 +46,19 @@ image::images/esql/chaining-processing-commands.svg[Processing commands can be c
 The result of a query is the table produced by the final processing command.
 
 [discrete]
-[[esql-console]]
-=== Run an {esql} query
+=== The {esql} Compute Engine
 
-[discrete]
-==== The {esql} API
+{esql} is more than a language. It represents a significant investment in new compute capabilities within {es}.
+To achieve both the functional and performance requirements for {esql}, it was necessary to build an entirely new
+compute architecture. {esql} search, aggregation, and transformation functions are directly executed within Elasticsearch
+itself. Query expressions are not transpiled to Query DSL for execution. This approach allows {esql} to be extremely performant and versatile.
 
-Use the <> to run an {esql} query:
-
-// tag::esql-query-api-example[]
-[source,console]
-----
-POST /_query
-{
-  "query": """
-    FROM library
-    | EVAL year = DATE_TRUNC(1 YEARS, release_date)
-    | STATS MAX(page_count) BY year
-    | SORT year
-    | LIMIT 5
-  """
-}
-----
-// TEST[setup:library]
-// end::esql-query-api-example[]
-
-The results come back in rows:
-
-[source,console-result]
-----
-{
-  "columns": [
-    { "name": "MAX(page_count)", "type": "integer"},
-    { "name": "year"           , "type": "date"}
-  ],
-  "values": [
-    [268, "1932-01-01T00:00:00.000Z"],
-    [224, "1951-01-01T00:00:00.000Z"],
-    [227, "1953-01-01T00:00:00.000Z"],
-    [335, "1959-01-01T00:00:00.000Z"],
-    [604, "1965-01-01T00:00:00.000Z"]
-  ]
-}
-----
-
-By default, results are returned as JSON. You can return data in other
-<> by specifying the `format` parameter in
-the URL or by setting the `Accept` or `Content-Type` HTTP header.
-
-By default, an {esql} query returns up to 500 rows. You can change this using
-the <>. The previous query's `LIMIT` command limits
-results to 5 rows. The maximum number of returned rows is 10,000 rows,
-regardless of the `LIMIT` value.
-
-[discrete]
-==== {kib}
-
-Use {esql} in Discover to explore a data set. From the data view dropdown,
-select *Try {esql}* to get started.
-
-NOTE: {esql} queries in Discover and Lens are subject to the time range selected
-with the time filter.
-
---
+The new {esql} execution engine was designed with performance in mind — it operates on blocks at a time instead of per row, targets vectorization and cache locality, and embraces specialization and multi-threading. It is a separate component from the existing Elasticsearch aggregation framework with different performance characteristics.
 
 include::esql-get-started.asciidoc[]
 
 include::esql-language.asciidoc[]
 
-include::esql-commands.asciidoc[]
-
-include::esql-functions-operators.asciidoc[]
-
 include::esql-rest.asciidoc[]
 
 include::esql-kibana.asciidoc[]
diff --git a/docs/reference/esql/processing-commands/esql-processing-commands.asciidoc b/docs/reference/esql/processing-commands/esql-processing-commands.asciidoc
deleted file mode 100644
index e075477af3303..0000000000000
--- a/docs/reference/esql/processing-commands/esql-processing-commands.asciidoc
+++ /dev/null
@@ -1,41 +0,0 @@
-[[esql-processing-commands]]
-=== {esql} processing commands
-
-++++
-Processing commands
-++++
-
-{esql} processing commands change an input table by adding, removing, or changing
-rows and columns.
-
-image::images/esql/processing-command.svg[A processing command changing an input table,align="center"]
-
-{esql} supports these processing commands:
-
-// tag::list[]
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-* <>
-// end::list[]
-
-include::dissect.asciidoc[]
-include::drop.asciidoc[]
-include::enrich.asciidoc[]
-include::eval.asciidoc[]
-include::grok.asciidoc[]
-include::keep.asciidoc[]
-include::limit.asciidoc[]
-include::mv_expand.asciidoc[]
-include::rename.asciidoc[]
-include::sort.asciidoc[]
-include::stats.asciidoc[]
-include::where.asciidoc[]
diff --git a/docs/reference/esql/processing-commands/stats.asciidoc b/docs/reference/esql/processing-commands/stats.asciidoc
index 638782a92e8e6..71f4470e3dfb0 100644
--- a/docs/reference/esql/processing-commands/stats.asciidoc
+++ b/docs/reference/esql/processing-commands/stats.asciidoc
@@ -42,4 +42,4 @@ include::{esql-specs}/docs.csv-spec[tag=statsGroupByMultipleValues]
 
 The following aggregation functions are supported:
 
-include::../functions/aggregation-functions.asciidoc[tag=list]
+include::../functions/aggregation-functions.asciidoc[tag=agg_list]
diff --git a/docs/reference/esql/source-commands/esql-source-commands.asciidoc b/docs/reference/esql/source-commands/esql-source-commands.asciidoc
deleted file mode 100644
index 3a795e617e8a5..0000000000000
--- a/docs/reference/esql/source-commands/esql-source-commands.asciidoc
+++ /dev/null
@@ -1,22 +0,0 @@
-[[esql-source-commands]]
-=== {esql} source commands
-
-++++
-Source commands
-++++
-
-An {esql} source command produces a table, typically with data from {es}.
-
-image::images/esql/source-command.svg[A source command producing a table from {es},align="center"]
-
-{esql} supports these source commands:
-
-// tag::list[]
-* <>
-* <>
-* <>
-// end::list[]
-
-include::from.asciidoc[]
-include::row.asciidoc[]
-include::show.asciidoc[]
diff --git a/docs/reference/esql/task-management.asciidoc b/docs/reference/esql/task-management.asciidoc
index bc06e70f24bd7..96a624c89bf7d 100644
--- a/docs/reference/esql/task-management.asciidoc
+++ b/docs/reference/esql/task-management.asciidoc
@@ -5,7 +5,7 @@
 Task management
 ++++
 
-You can get running {esql} queries with the <>:
+You can list running {esql} queries with the <>:
 
 [source,console,id=esql-task-management-get-all]
 ----

From 84afa5cee3acdd3bc361d769deed49f92d5a437e Mon Sep 17 00:00:00 2001
From: Rene Groeschke 
Date: Mon, 23 Oct 2023 18:54:43 +0200
Subject: [PATCH 062/203] Use gradle toolchain to resolve JDKs by BWC builds
 (#101224)

Fixes a problem when a versioned java home has been defined but not properly propagated
to the BWC build
---
 .../gradle/internal/BwcSetupExtension.java    | 23 ++++++++++++++--
 .../InternalDistributionBwcSetupPlugin.java   | 12 +++++++--
 .../gradle/internal/util/JavaUtil.java        | 26 -------------------
 3 files changed, 31 insertions(+), 30 deletions(-)
 delete mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/util/JavaUtil.java

diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcSetupExtension.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcSetupExtension.java
index bb12bf95847b7..d71c893cdd20f 100644
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcSetupExtension.java
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcSetupExtension.java
@@ -17,8 +17,13 @@
 import org.gradle.api.GradleException;
 import org.gradle.api.Project;
 import org.gradle.api.logging.LogLevel;
+import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.tasks.TaskProvider;
+import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.jvm.toolchain.JavaToolchainService;
+import org.gradle.jvm.toolchain.JvmVendorSpec;
 
 import java.io.File;
 import java.io.IOException;
@@ -26,8 +31,6 @@
 import java.util.List;
 import java.util.Locale;
 
-import static org.elasticsearch.gradle.internal.util.JavaUtil.getJavaHome;
-
 /**
  * By registering bwc tasks via this extension we can support declaring custom bwc tasks from the build script
  * without relying on groovy closures and sharing common logic for tasks created by the BwcSetup plugin already.
@@ -37,16 +40,22 @@ public class BwcSetupExtension {
     private static final String MINIMUM_COMPILER_VERSION_PATH = "src/main/resources/minimumCompilerVersion";
     private static final Version BUILD_TOOL_MINIMUM_VERSION = Version.fromString("7.14.0");
     private final Project project;
+    private final ObjectFactory objectFactory;
+    private final JavaToolchainService toolChainService;
     private final Provider unreleasedVersionInfo;
 
     private Provider checkoutDir;
 
     public BwcSetupExtension(
         Project project,
+        ObjectFactory objectFactory,
+        JavaToolchainService toolChainService,
         Provider unreleasedVersionInfo,
         Provider checkoutDir
     ) {
         this.project = project;
+        this.objectFactory = objectFactory;
+        this.toolChainService = toolChainService;
         this.unreleasedVersionInfo = unreleasedVersionInfo;
         this.checkoutDir = checkoutDir;
     }
@@ -137,4 +146,14 @@ private static String readFromFile(File file) {
             throw new GradleException("Cannot read java properties file.", ioException);
         }
     }
+
+    /** A convenience method for getting java home for a version of java and requiring that version for the given task to execute */
+    public String getJavaHome(final int version) {
+        Property value = objectFactory.property(JavaLanguageVersion.class).value(JavaLanguageVersion.of(version));
+        return toolChainService.launcherFor(javaToolchainSpec -> {
+            javaToolchainSpec.getLanguageVersion().value(value);
+            javaToolchainSpec.getVendor().set(JvmVendorSpec.ORACLE);
+        }).get().getMetadata().getInstallationPath().getAsFile().getAbsolutePath();
+    }
+
 }
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPlugin.java
index c4bb331b7de0a..2468711561ae4 100644
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPlugin.java
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPlugin.java
@@ -16,10 +16,13 @@
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
+import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.plugins.JvmToolchainsPlugin;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.tasks.Copy;
 import org.gradle.api.tasks.TaskProvider;
+import org.gradle.jvm.toolchain.JavaToolchainService;
 import org.gradle.language.base.plugins.LifecycleBasePlugin;
 
 import java.io.File;
@@ -44,16 +47,21 @@
  */
 public class InternalDistributionBwcSetupPlugin implements Plugin {
 
+    private final ObjectFactory objectFactory;
     private ProviderFactory providerFactory;
+    private JavaToolchainService toolChainService;
 
     @Inject
-    public InternalDistributionBwcSetupPlugin(ProviderFactory providerFactory) {
+    public InternalDistributionBwcSetupPlugin(ObjectFactory objectFactory, ProviderFactory providerFactory) {
+        this.objectFactory = objectFactory;
         this.providerFactory = providerFactory;
     }
 
     @Override
     public void apply(Project project) {
         project.getRootProject().getPluginManager().apply(GlobalBuildInfoPlugin.class);
+        project.getPlugins().apply(JvmToolchainsPlugin.class);
+        toolChainService = project.getExtensions().getByType(JavaToolchainService.class);
         BuildParams.getBwcVersions().forPreviousUnreleased((BwcVersions.UnreleasedVersionInfo unreleasedVersion) -> {
             configureBwcProject(project.project(unreleasedVersion.gradleProjectPath()), unreleasedVersion);
         });
@@ -63,7 +71,7 @@ private void configureBwcProject(Project project, BwcVersions.UnreleasedVersionI
         Provider versionInfoProvider = providerFactory.provider(() -> versionInfo);
         Provider checkoutDir = versionInfoProvider.map(info -> new File(project.getBuildDir(), "bwc/checkout-" + info.branch()));
         BwcSetupExtension bwcSetupExtension = project.getExtensions()
-            .create("bwcSetup", BwcSetupExtension.class, project, versionInfoProvider, checkoutDir);
+            .create("bwcSetup", BwcSetupExtension.class, project, objectFactory, toolChainService, versionInfoProvider, checkoutDir);
         BwcGitExtension gitExtension = project.getPlugins().apply(InternalBwcGitPlugin.class).getGitExtension();
         Provider bwcVersion = versionInfoProvider.map(info -> info.version());
         gitExtension.setBwcVersion(versionInfoProvider.map(info -> info.version()));
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/util/JavaUtil.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/util/JavaUtil.java
deleted file mode 100644
index de9dffa35d3fd..0000000000000
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/util/JavaUtil.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-package org.elasticsearch.gradle.internal.util;
-
-import org.elasticsearch.gradle.internal.info.BuildParams;
-import org.elasticsearch.gradle.internal.info.JavaHome;
-import org.gradle.api.GradleException;
-
-import java.util.List;
-import java.util.Optional;
-
-public class JavaUtil {
-
-    /** A convenience method for getting java home for a version of java and requiring that version for the given task to execute */
-    public static String getJavaHome(final int version) {
-        List javaHomes = BuildParams.getJavaVersions();
-        Optional java = javaHomes.stream().filter(j -> j.getVersion() == version).findFirst();
-        return java.orElseThrow(() -> new GradleException("JAVA" + version + "_HOME required")).getJavaHome().get().getAbsolutePath();
-    }
-}

From 3945ee75d0e8cf634d958397fb9bd86262a6f1ad Mon Sep 17 00:00:00 2001
From: Armin Braun 
Date: Mon, 23 Oct 2023 19:48:10 +0200
Subject: [PATCH 063/203] Use assertAcked more (#101201)

Just found that we have a lot of inconsistency and needless verbosity
here in tests. We can just use `assertAcked` in a couple spots
to save `.get`, `.actionGet` etc., especially with the signature
change I added here.
---
 .../bucket/TimeSeriesAggregationsIT.java      |  4 +--
 .../datastreams/DataStreamIT.java             |  2 +-
 .../datastreams/DataStreamsSnapshotsIT.java   | 27 +++-------------
 .../DataStreamLifecycleServiceIT.java         |  6 ++--
 .../datastreams/DataStreamsStatsTests.java    | 21 +++++--------
 .../legacygeo/search/LegacyGeoShapeIT.java    |  1 -
 .../hdfs/HdfsRepositoryTests.java             | 26 ++++++++--------
 .../repositories/hdfs/HdfsTests.java          | 30 +++++++++---------
 .../org/elasticsearch/search/CCSDuelIT.java   |  7 ++---
 .../ClusterAllocationExplainIT.java           |  1 -
 .../create/AutoCreateSystemIndexIT.java       |  7 ++---
 .../admin/indices/create/CloneIndexIT.java    |  1 -
 .../indices/create/CreateSystemIndicesIT.java | 11 +++----
 .../admin/indices/create/ShrinkIndexIT.java   |  9 +-----
 .../admin/indices/create/SplitIndexIT.java    |  4 ---
 .../indices/delete/DeleteIndexBlocksIT.java   |  5 +--
 .../admin/indices/rollover/RolloverIT.java    |  2 +-
 .../action/bulk/BulkIntegrationIT.java        |  7 +----
 .../support/ActiveShardsObserverIT.java       |  7 ++---
 .../cluster/SimpleClusterStateIT.java         |  1 -
 .../metadata/TemplateUpgradeServiceIT.java    | 10 ++----
 .../cluster/routing/PrimaryAllocationIT.java  | 10 ++----
 .../cluster/settings/ClusterSettingsIT.java   |  4 +--
 .../cluster/settings/SettingsFilteringIT.java |  1 -
 .../cluster/shards/ClusterShardLimitIT.java   |  2 --
 .../elasticsearch/document/ShardInfoIT.java   |  1 -
 .../elasticsearch/index/HiddenIndexIT.java    | 16 +++-------
 .../indices/IndicesRequestCacheIT.java        |  6 ----
 .../mapping/MalformedDynamicTemplateIT.java   |  2 +-
 .../indices/recovery/IndexRecoveryIT.java     |  6 +---
 .../settings/UpdateNumberOfReplicasIT.java    |  1 -
 .../indices/settings/UpdateSettingsIT.java    | 31 ++++++-------------
 .../indices/state/SimpleIndexStateIT.java     | 10 ++----
 .../indices/stats/IndexStatsIT.java           |  3 --
 .../template/SimpleIndexTemplateIT.java       |  2 --
 .../repositories/RepositoriesServiceIT.java   | 12 ++-----
 .../aggregations/bucket/DateHistogramIT.java  |  1 -
 .../aggregations/bucket/DateRangeIT.java      |  1 -
 .../aggregations/bucket/DoubleTermsIT.java    |  1 -
 .../aggregations/bucket/HistogramIT.java      |  1 -
 .../aggregations/bucket/LongTermsIT.java      |  1 -
 .../search/aggregations/bucket/RangeIT.java   |  1 -
 .../SignificantTermsSignificanceScoreIT.java  |  1 -
 .../bucket/terms/StringTermsIT.java           |  4 ---
 .../aggregations/metrics/ExtendedStatsIT.java |  1 -
 .../metrics/HDRPercentileRanksIT.java         |  1 -
 .../metrics/HDRPercentilesIT.java             |  1 -
 .../metrics/MedianAbsoluteDeviationIT.java    |  1 -
 .../metrics/ScriptedMetricIT.java             |  1 -
 .../search/aggregations/metrics/StatsIT.java  |  1 -
 .../search/aggregations/metrics/SumIT.java    |  1 -
 .../metrics/TDigestPercentileRanksIT.java     |  1 -
 .../metrics/TDigestPercentilesIT.java         |  1 -
 .../aggregations/metrics/TopHitsIT.java       |  1 -
 .../aggregations/metrics/ValueCountIT.java    |  1 -
 .../highlight/HighlighterSearchIT.java        |  1 -
 .../search/fields/SearchFieldsIT.java         |  2 +-
 .../FunctionScoreFieldValueIT.java            |  2 +-
 .../aggregation/AggregationProfilerIT.java    |  3 --
 .../search/slice/SearchSliceIT.java           |  1 -
 .../search/sort/FieldSortIT.java              |  4 +--
 .../search/sort/GeoDistanceSortBuilderIT.java |  4 +--
 .../suggest/CompletionSuggestSearchIT.java    |  7 ++---
 .../ContextCompletionSuggestSearchIT.java     |  2 +-
 .../DedicatedClusterSnapshotRestoreIT.java    |  1 -
 .../snapshots/RepositoriesIT.java             |  1 -
 .../snapshots/RestoreSnapshotIT.java          |  6 +---
 .../FieldStatsProviderRefreshTests.java       |  1 -
 .../AbstractIndexRecoveryIntegTestCase.java   |  1 -
 .../test/ESSingleNodeTestCase.java            |  2 +-
 .../hamcrest/ElasticsearchAssertions.java     |  9 ++++--
 .../storage/ReactiveStorageIT.java            |  2 --
 .../elasticsearch/xpack/ccr/AutoFollowIT.java |  1 -
 .../xpack/ccr/FollowerFailOverIT.java         |  1 -
 .../xpack/ccr/IndexFollowingIT.java           |  6 ++--
 .../core/async/AsyncTaskServiceTests.java     |  5 ++-
 .../DownsampleClusterDisruptionIT.java        |  1 -
 .../DownsampleTransportFailureIT.java         |  8 ++---
 .../DownsampleActionSingleNodeTests.java      |  3 --
 .../eql/action/AsyncEqlSearchActionIT.java    |  1 -
 .../xpack/eql/action/EqlCancellationIT.java   |  4 +--
 .../eql/action/RestEqlCancellationIT.java     |  4 +--
 .../xpack/esql/action/EsqlActionIT.java       |  8 -----
 .../index/engine/frozen/FrozenIndexIT.java    |  4 +--
 .../index/engine/frozen/FrozenIndexTests.java |  4 +--
 .../FrozenSearchableSnapshotsIntegTests.java  |  1 -
 .../integration/DataStreamSecurityIT.java     |  2 +-
 .../integration/DlsFlsRequestCacheTests.java  | 16 ++--------
 .../security/authz/IndexAliasesTests.java     |  7 +----
 .../SecurityIndexManagerIntegTests.java       |  3 +-
 .../xpack/shutdown/NodeShutdownShardsIT.java  |  2 +-
 .../search/GeoShapeWithDocValuesIT.java       |  4 +--
 .../search/LegacyGeoShapeWithDocValuesIT.java |  2 --
 .../search/ShapeQueryOverShapeTests.java      |  1 -
 .../sql/action/AsyncSqlSearchActionIT.java    |  1 -
 .../sql/action/RestSqlCancellationIT.java     |  4 +--
 .../xpack/sql/action/SqlCancellationIT.java   |  4 +--
 .../AbstractWatcherIntegrationTestCase.java   |  6 +---
 .../test/integration/BasicWatcherTests.java   |  1 -
 .../WriteLoadForecasterIT.java                |  2 +-
 100 files changed, 130 insertions(+), 343 deletions(-)

diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java
index 78479a9f1d811..53d5fb9c00991 100644
--- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java
+++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java
@@ -126,7 +126,7 @@ public void setupSuiteScopeCluster() throws Exception {
                         .put("time_series.start_time", boundaries[i])
                         .put("time_series.end_time", boundaries[i + 1])
                         .build()
-                ).setMapping(builder).addAlias(new Alias("index")).get()
+                ).setMapping(builder).addAlias(new Alias("index"))
             );
         }
 
@@ -478,7 +478,7 @@ public void testGetHitsFailure() throws Exception {
                     .put("time_series.end_time", "2022-01-01T00:00:00Z")
                     .put("number_of_shards", 1)
                     .build()
-            ).setMapping("key", "type=keyword,time_series_dimension=true", "val", "type=double").get()
+            ).setMapping("key", "type=keyword,time_series_dimension=true", "val", "type=double")
         );
 
         client().prepareBulk()
diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java
index 69ebbbde2cc35..cf5e23c272985 100644
--- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java
+++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java
@@ -1846,7 +1846,7 @@ public void onFailure(Exception e) {
             client().execute(
                 ModifyDataStreamsAction.INSTANCE,
                 new ModifyDataStreamsAction.Request(List.of(DataStreamAction.removeBackingIndex(dataStreamName, ghostReference.getName())))
-            ).actionGet()
+            )
         );
         ClusterState after = internalCluster().getCurrentMasterNodeInstance(ClusterService.class).state();
         assertThat(after.getMetadata().dataStreams().get(dataStreamName).getIndices(), hasSize(1));
diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java
index 715d2a7a4de2f..ceac7423b0b72 100644
--- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java
+++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java
@@ -149,11 +149,7 @@ public void testSnapshotAndRestore() throws Exception {
 
         assertEquals(Collections.singletonList(dsBackingIndexName), getSnapshot(REPO, SNAPSHOT).indices());
 
-        assertTrue(
-            client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "ds" }))
-                .get()
-                .isAcknowledged()
-        );
+        assertAcked(client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "ds" })));
 
         RestoreSnapshotResponse restoreSnapshotResponse = client.admin()
             .cluster()
@@ -788,11 +784,7 @@ public void testDataStreamNotRestoredWhenIndexRequested() throws Exception {
         RestStatus status = createSnapshotResponse.getSnapshotInfo().status();
         assertEquals(RestStatus.OK, status);
 
-        assertTrue(
-            client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "ds" }))
-                .get()
-                .isAcknowledged()
-        );
+        assertAcked(client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "ds" })));
 
         RestoreSnapshotResponse restoreSnapshotResponse = client.admin()
             .cluster()
@@ -818,12 +810,7 @@ public void testDataStreamNotIncludedInLimitedSnapshot() throws ExecutionExcepti
             .get();
         assertThat(createSnapshotResponse.getSnapshotInfo().state(), Matchers.is(SnapshotState.SUCCESS));
 
-        assertThat(
-            client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "*" }))
-                .get()
-                .isAcknowledged(),
-            is(true)
-        );
+        assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "*" })));
 
         final RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot(REPO, snapshotName).get();
         assertThat(restoreSnapshotResponse.getRestoreInfo().indices(), empty());
@@ -911,9 +898,7 @@ public void testCloneSnapshotThatIncludesDataStream() throws Exception {
                 .setIncludeGlobalState(false)
                 .execute()
         );
-        assertAcked(
-            clusterAdmin().prepareCloneSnapshot(REPO, sourceSnapshotName, "target-snapshot-1").setIndices(indexWithoutDataStream).get()
-        );
+        assertAcked(clusterAdmin().prepareCloneSnapshot(REPO, sourceSnapshotName, "target-snapshot-1").setIndices(indexWithoutDataStream));
     }
 
     public void testPartialRestoreSnapshotThatIncludesDataStream() {
@@ -1001,9 +986,7 @@ public void testSnapshotDSDuringRolloverAndDeleteOldIndex() throws Exception {
             snapshotInfo.dataStreams(),
             not(hasItems("ds"))
         );
-        assertAcked(
-            client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "other-ds" })).get()
-        );
+        assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { "other-ds" })));
 
         RestoreInfo restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot(repoName, snapshotName)
             .setWaitForCompletion(true)
diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java
index 4e70f709a4263..80b1b89054304 100644
--- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java
+++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java
@@ -29,7 +29,6 @@
 import org.elasticsearch.action.datastreams.ModifyDataStreamsAction;
 import org.elasticsearch.action.datastreams.lifecycle.ExplainIndexDataStreamLifecycle;
 import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
 import org.elasticsearch.cluster.metadata.DataStream;
 import org.elasticsearch.cluster.metadata.DataStreamAction;
@@ -75,6 +74,7 @@
 import static org.elasticsearch.datastreams.lifecycle.DataStreamLifecycleService.TARGET_MERGE_FACTOR_VALUE;
 import static org.elasticsearch.index.IndexSettings.LIFECYCLE_ORIGINATION_DATE;
 import static org.elasticsearch.indices.ShardLimitValidator.SETTING_CLUSTER_MAX_SHARDS_PER_NODE;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
@@ -742,8 +742,6 @@ static void updateLifecycle(String dataStreamName, TimeValue dataRetention) {
             new String[] { dataStreamName },
             dataRetention
         );
-        AcknowledgedResponse putDataLifecycleResponse = client().execute(PutDataStreamLifecycleAction.INSTANCE, putDataLifecycleRequest)
-            .actionGet();
-        assertThat(putDataLifecycleResponse.isAcknowledged(), equalTo(true));
+        assertAcked(client().execute(PutDataStreamLifecycleAction.INSTANCE, putDataLifecycleRequest));
     }
 }
diff --git a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/DataStreamsStatsTests.java b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/DataStreamsStatsTests.java
index b66f734dfac5e..da0caff9e591d 100644
--- a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/DataStreamsStatsTests.java
+++ b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/DataStreamsStatsTests.java
@@ -39,6 +39,7 @@
 import java.util.concurrent.TimeUnit;
 
 import static java.lang.Math.max;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 
 public class DataStreamsStatsTests extends ESSingleNodeTestCase {
 
@@ -235,15 +236,13 @@ private String createDataStream(boolean hidden) throws Exception {
             new ComposableIndexTemplate.DataStreamTemplate(hidden, false),
             null
         );
-        assertTrue(
+        assertAcked(
             client().execute(
                 PutComposableIndexTemplateAction.INSTANCE,
                 new PutComposableIndexTemplateAction.Request(dataStreamName + "_template").indexTemplate(template)
-            ).actionGet().isAcknowledged()
-        );
-        assertTrue(
-            client().execute(CreateDataStreamAction.INSTANCE, new CreateDataStreamAction.Request(dataStreamName)).get().isAcknowledged()
+            )
         );
+        assertAcked(client().execute(CreateDataStreamAction.INSTANCE, new CreateDataStreamAction.Request(dataStreamName)));
         createdDataStreams.add(dataStreamName);
         return dataStreamName;
     }
@@ -281,17 +280,13 @@ private DataStreamsStatsAction.Response getDataStreamsStats(boolean includeHidde
         return client().execute(DataStreamsStatsAction.INSTANCE, request).get();
     }
 
-    private void deleteDataStream(String dataStreamName) throws InterruptedException, java.util.concurrent.ExecutionException {
-        assertTrue(
-            client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { dataStreamName }))
-                .get()
-                .isAcknowledged()
-        );
-        assertTrue(
+    private void deleteDataStream(String dataStreamName) {
+        assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { dataStreamName })));
+        assertAcked(
             client().execute(
                 DeleteComposableIndexTemplateAction.INSTANCE,
                 new DeleteComposableIndexTemplateAction.Request(dataStreamName + "_template")
-            ).actionGet().isAcknowledged()
+            )
         );
     }
 }
diff --git a/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java b/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java
index 14a7c873ec6e3..ee588740c340e 100644
--- a/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java
+++ b/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java
@@ -57,7 +57,6 @@ public void testLegacyCircle() throws Exception {
         assertAcked(
             prepareCreate("test").setSettings(settings(randomSupportedVersion()).build())
                 .setMapping("shape", "type=geo_shape,strategy=recursive,tree=geohash")
-                .get()
         );
         ensureGreen();
 
diff --git a/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsRepositoryTests.java b/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsRepositoryTests.java
index b3d391ce13cb4..cd38cc04e6b31 100644
--- a/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsRepositoryTests.java
+++ b/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsRepositoryTests.java
@@ -10,7 +10,6 @@
 import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
 
 import org.elasticsearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryResponse;
-import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.common.settings.MockSecureSettings;
 import org.elasticsearch.common.settings.SecureSettings;
 import org.elasticsearch.common.settings.Settings;
@@ -19,6 +18,7 @@
 
 import java.util.Collection;
 
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 
@@ -37,18 +37,18 @@ protected SecureSettings credentials() {
 
     @Override
     protected void createRepository(String repoName) {
-        AcknowledgedResponse putRepositoryResponse = clusterAdmin().preparePutRepository(repoName)
-            .setType("hdfs")
-            .setSettings(
-                Settings.builder()
-                    .put("uri", "hdfs:///")
-                    .put("conf.fs.AbstractFileSystem.hdfs.impl", TestingFs.class.getName())
-                    .put("path", "foo")
-                    .put("chunk_size", randomIntBetween(100, 1000) + "k")
-                    .put("compress", randomBoolean())
-            )
-            .get();
-        assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true));
+        assertAcked(
+            clusterAdmin().preparePutRepository(repoName)
+                .setType("hdfs")
+                .setSettings(
+                    Settings.builder()
+                        .put("uri", "hdfs:///")
+                        .put("conf.fs.AbstractFileSystem.hdfs.impl", TestingFs.class.getName())
+                        .put("path", "foo")
+                        .put("chunk_size", randomIntBetween(100, 1000) + "k")
+                        .put("compress", randomBoolean())
+                )
+        );
     }
 
     // HDFS repository doesn't have precise cleanup stats so we only check whether or not any blobs were removed
diff --git a/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsTests.java b/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsTests.java
index f72a5eeea90d0..b76d2e27be66a 100644
--- a/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsTests.java
+++ b/plugins/repository-hdfs/src/test/java/org/elasticsearch/repositories/hdfs/HdfsTests.java
@@ -11,7 +11,6 @@
 
 import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
 import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
-import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.client.internal.Client;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.common.settings.Settings;
@@ -25,6 +24,7 @@
 
 import java.util.Collection;
 
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 
@@ -39,20 +39,20 @@ protected Collection> getPlugins() {
     public void testSimpleWorkflow() {
         Client client = client();
 
-        AcknowledgedResponse putRepositoryResponse = client.admin()
-            .cluster()
-            .preparePutRepository("test-repo")
-            .setType("hdfs")
-            .setSettings(
-                Settings.builder()
-                    .put("uri", "hdfs:///")
-                    .put("conf.fs.AbstractFileSystem.hdfs.impl", TestingFs.class.getName())
-                    .put("path", "foo")
-                    .put("chunk_size", randomIntBetween(100, 1000) + "k")
-                    .put("compress", randomBoolean())
-            )
-            .get();
-        assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true));
+        assertAcked(
+            client.admin()
+                .cluster()
+                .preparePutRepository("test-repo")
+                .setType("hdfs")
+                .setSettings(
+                    Settings.builder()
+                        .put("uri", "hdfs:///")
+                        .put("conf.fs.AbstractFileSystem.hdfs.impl", TestingFs.class.getName())
+                        .put("path", "foo")
+                        .put("chunk_size", randomIntBetween(100, 1000) + "k")
+                        .put("compress", randomBoolean())
+                )
+        );
 
         createIndex("test-idx-1");
         createIndex("test-idx-2");
diff --git a/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java b/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java
index 8ca4ce7a4eb2f..5255cbf401c9a 100644
--- a/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java
+++ b/qa/multi-cluster-search/src/test/java/org/elasticsearch/search/CCSDuelIT.java
@@ -19,7 +19,6 @@
 import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.LatchedActionListener;
-import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
 import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
 import org.elasticsearch.action.bulk.BulkProcessor2;
 import org.elasticsearch.action.bulk.BulkRequest;
@@ -187,8 +186,7 @@ private static void indexDocuments(String idPrefix) throws IOException, Interrup
         IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
         assertEquals(201, indexResponse.status().getStatus());
 
-        CreateIndexResponse response = createIndex(INDEX_NAME + "_empty");
-        assertTrue(response.isAcknowledged());
+        ElasticsearchAssertions.assertAcked(createIndex(INDEX_NAME + "_empty"));
 
         int numShards = randomIntBetween(1, 5);
         Settings settings = indexSettings(numShards, 0).build();
@@ -209,8 +207,7 @@ private static void indexDocuments(String idPrefix) throws IOException, Interrup
                 }
               }
             }""";
-        response = createIndex(INDEX_NAME, settings, mapping);
-        assertTrue(response.isAcknowledged());
+        ElasticsearchAssertions.assertAcked(createIndex(INDEX_NAME, settings, mapping));
 
         BulkProcessor2 bulkProcessor = BulkProcessor2.builder(
             (r, l) -> restHighLevelClient.bulkAsync(r, RequestOptions.DEFAULT, l),
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java
index ba1a8b7919963..1c358fe06b68f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java
@@ -1259,7 +1259,6 @@ private void prepareIndex(
             indicesAdmin().prepareCreate("idx")
                 .setSettings(indexSettings(numPrimaries, numReplicas).put(settings))
                 .setWaitForActiveShards(activeShardCount)
-                .get()
         );
 
         if (activeShardCount != ActiveShardCount.NONE) {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/AutoCreateSystemIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/AutoCreateSystemIndexIT.java
index 127d399eab04a..e5edeccbad55d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/AutoCreateSystemIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/AutoCreateSystemIndexIT.java
@@ -169,7 +169,6 @@ private String autoCreateSystemAliasViaV1Template(String indexName) throws Excep
             indicesAdmin().preparePutTemplate("test-template")
                 .setPatterns(List.of(indexName + "*"))
                 .addAlias(new Alias(indexName + "-legacy-alias"))
-                .get()
         );
 
         String nonPrimaryIndex = indexName + "-2";
@@ -222,7 +221,7 @@ private String autoCreateSystemAliasViaComposableTemplate(String indexName) thro
             client().execute(
                 PutComposableIndexTemplateAction.INSTANCE,
                 new PutComposableIndexTemplateAction.Request("test-composable-template").indexTemplate(cit)
-            ).get()
+            )
         );
 
         String nonPrimaryIndex = indexName + "-2";
@@ -246,7 +245,7 @@ public void testAutoCreateSystemAliasViaComposableTemplate() throws Exception {
             client().execute(
                 DeleteComposableIndexTemplateAction.INSTANCE,
                 new DeleteComposableIndexTemplateAction.Request("test-composable-template")
-            ).get()
+            )
         );
     }
 
@@ -269,7 +268,7 @@ public void testAutoCreateSystemAliasViaComposableTemplateAllowsTemplates() thro
             client().execute(
                 DeleteComposableIndexTemplateAction.INSTANCE,
                 new DeleteComposableIndexTemplateAction.Request("test-composable-template")
-            ).get()
+            )
         );
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java
index b18564beb6557..93d12c686297f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CloneIndexIT.java
@@ -64,7 +64,6 @@ public void testCreateCloneIndex() {
                     .setSettings(
                         Settings.builder().put("index.number_of_replicas", createWithReplicas ? 1 : 0).putNull("index.blocks.write").build()
                     )
-                    .get()
             );
             ensureGreen();
             assertNoResizeSourceIndexSettings("target");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateSystemIndicesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateSystemIndicesIT.java
index f5a2121b2dde9..a0dffa8b7caa8 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateSystemIndicesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/CreateSystemIndicesIT.java
@@ -66,9 +66,7 @@ public void beforeEach() {
     @After
     public void afterEach() throws Exception {
         assertAcked(indicesAdmin().prepareDeleteTemplate("*").get());
-        assertAcked(
-            client().execute(DeleteComposableIndexTemplateAction.INSTANCE, new DeleteComposableIndexTemplateAction.Request("*")).get()
-        );
+        assertAcked(client().execute(DeleteComposableIndexTemplateAction.INSTANCE, new DeleteComposableIndexTemplateAction.Request("*")));
     }
 
     @Override
@@ -160,7 +158,6 @@ private void createSystemAliasViaV1Template(String indexName, String primaryInde
             indicesAdmin().preparePutTemplate("test-template")
                 .setPatterns(List.of(indexName + "*"))
                 .addAlias(new Alias(indexName + "-legacy-alias"))
-                .get()
         );
 
         assertAcked(prepareCreate(primaryIndexName));
@@ -213,7 +210,7 @@ private void createIndexWithComposableTemplates(String indexName, String primary
             client().execute(
                 PutComposableIndexTemplateAction.INSTANCE,
                 new PutComposableIndexTemplateAction.Request("test-composable-template").indexTemplate(cit)
-            ).get()
+            )
         );
 
         assertAcked(prepareCreate(primaryIndexName));
@@ -232,7 +229,7 @@ public void testCreateSystemAliasViaComposableTemplate() throws Exception {
             client().execute(
                 DeleteComposableIndexTemplateAction.INSTANCE,
                 new DeleteComposableIndexTemplateAction.Request("test-composable-template")
-            ).get()
+            )
         );
     }
 
@@ -259,7 +256,7 @@ public void testCreateSystemAliasViaComposableTemplateWithAllowsTemplates() thro
             client().execute(
                 DeleteComposableIndexTemplateAction.INSTANCE,
                 new DeleteComposableIndexTemplateAction.Request("test-composable-template")
-            ).get()
+            )
         );
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java
index ac206a0ed060a..b0ec5de81984a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java
@@ -104,7 +104,6 @@ public void testCreateShrinkIndexToN() {
         assertAcked(
             indicesAdmin().prepareResizeIndex("source", "first_shrink")
                 .setSettings(indexSettings(shardSplits[1], 0).putNull("index.blocks.write").build())
-                .get()
         );
         ensureGreen();
         assertHitCount(prepareSearch("first_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20);
@@ -131,7 +130,6 @@ public void testCreateShrinkIndexToN() {
                 .setSettings(
                     indexSettings(shardSplits[2], 0).putNull("index.blocks.write").putNull("index.routing.allocation.require._name").build()
                 )
-                .get()
         );
         ensureGreen();
         assertHitCount(prepareSearch("second_shrink").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), 20);
@@ -272,7 +270,6 @@ public void testCreateShrinkIndex() {
                         .putNull("index.routing.allocation.require._name")
                         .build()
                 )
-                .get()
         );
         ensureGreen();
 
@@ -462,9 +459,7 @@ public void testCreateShrinkWithIndexSort() throws Exception {
 
         // check that the index sort order of `source` is correctly applied to the `target`
         assertAcked(
-            indicesAdmin().prepareResizeIndex("source", "target")
-                .setSettings(indexSettings(2, 0).putNull("index.blocks.write").build())
-                .get()
+            indicesAdmin().prepareResizeIndex("source", "target").setSettings(indexSettings(2, 0).putNull("index.blocks.write").build())
         );
         ensureGreen();
         assertNoResizeSourceIndexSettings("target");
@@ -513,7 +508,6 @@ public void testShrinkCommitsMergeOnIdle() throws Exception {
             assertAcked(
                 indicesAdmin().prepareResizeIndex("source", "target")
                     .setSettings(Settings.builder().put("index.number_of_replicas", 0).build())
-                    .get()
             );
             ensureGreen();
             assertNoResizeSourceIndexSettings("target");
@@ -586,7 +580,6 @@ public void testShrinkThenSplitWithFailedNode() throws Exception {
                     ).build()
                 )
                 .setResizeType(ResizeType.SHRINK)
-                .get()
         );
         ensureGreen();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java
index 68b75fc04770c..54add487a3dd4 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/create/SplitIndexIT.java
@@ -186,7 +186,6 @@ private void splitToN(int sourceShards, int firstSplitShards, int secondSplitSha
             indicesAdmin().prepareResizeIndex("source", "first_split")
                 .setResizeType(ResizeType.SPLIT)
                 .setSettings(firstSplitSettingsBuilder.build())
-                .get()
         );
         ensureGreen();
         assertHitCount(prepareSearch("first_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs);
@@ -214,7 +213,6 @@ private void splitToN(int sourceShards, int firstSplitShards, int secondSplitSha
             indicesAdmin().prepareResizeIndex("first_split", "second_split")
                 .setResizeType(ResizeType.SPLIT)
                 .setSettings(indexSettings(secondSplitShards, 0).putNull("index.blocks.write").build())
-                .get()
         );
         ensureGreen();
         assertHitCount(prepareSearch("second_split").setSize(100).setQuery(new TermsQueryBuilder("foo", "bar")), numDocs);
@@ -367,7 +365,6 @@ public void testCreateSplitIndex() throws Exception {
                 indicesAdmin().prepareResizeIndex("source", "target")
                     .setResizeType(ResizeType.SPLIT)
                     .setSettings(indexSettings(2, createWithReplicas ? 1 : 0).putNull("index.blocks.write").build())
-                    .get()
             );
             ensureGreen();
             assertNoResizeSourceIndexSettings("target");
@@ -478,7 +475,6 @@ public void testCreateSplitWithIndexSort() throws Exception {
             indicesAdmin().prepareResizeIndex("source", "target")
                 .setResizeType(ResizeType.SPLIT)
                 .setSettings(indexSettings(4, 0).putNull("index.blocks.write").build())
-                .get()
         );
         ensureGreen();
         flushAndRefresh();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java
index 7af46dd994fb2..dc5cc49092f7a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/delete/DeleteIndexBlocksIT.java
@@ -58,10 +58,7 @@ public void testDeleteIndexOnIndexReadOnlyAllowDeleteSetting() {
         } finally {
             Settings settings = Settings.builder().putNull(IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE).build();
             assertAcked(
-                indicesAdmin().prepareUpdateSettings("test")
-                    .setIndicesOptions(IndicesOptions.lenientExpandOpen())
-                    .setSettings(settings)
-                    .get()
+                indicesAdmin().prepareUpdateSettings("test").setIndicesOptions(IndicesOptions.lenientExpandOpen()).setSettings(settings)
             );
         }
     }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java
index 682883af044a1..d7e4e42b73554 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java
@@ -525,7 +525,7 @@ public void testRolloverMaxPrimaryShardSize() throws Exception {
 
     public void testRolloverMaxPrimaryShardDocs() throws Exception {
         assertAcked(
-            prepareCreate("test-1").setSettings(Settings.builder().put("index.number_of_shards", 1)).addAlias(new Alias("test_alias")).get()
+            prepareCreate("test-1").setSettings(Settings.builder().put("index.number_of_shards", 1)).addAlias(new Alias("test_alias"))
         );
         int numDocs = randomIntBetween(10, 20);
         for (int i = 0; i < numDocs; i++) {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java
index 96e3939312870..224db253675d2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java
@@ -15,7 +15,6 @@
 import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
 import org.elasticsearch.action.index.IndexRequest;
 import org.elasticsearch.action.ingest.PutPipelineRequest;
-import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.action.support.replication.ReplicationRequest;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.bytes.BytesReference;
@@ -145,11 +144,7 @@ private void createSamplePipeline(String pipelineId) throws IOException, Executi
             .endArray()
             .endObject();
 
-        AcknowledgedResponse acknowledgedResponse = clusterAdmin().putPipeline(
-            new PutPipelineRequest(pipelineId, BytesReference.bytes(pipeline), XContentType.JSON)
-        ).get();
-
-        assertTrue(acknowledgedResponse.isAcknowledged());
+        assertAcked(clusterAdmin().putPipeline(new PutPipelineRequest(pipelineId, BytesReference.bytes(pipeline), XContentType.JSON)));
     }
 
     /** This test ensures that index deletion makes indexing fail quickly, not wait on the index that has disappeared */
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java
index b5ca2de799f92..a377ba9eb94ad 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/action/support/ActiveShardsObserverIT.java
@@ -55,8 +55,7 @@ public void testCreateIndexNoActiveShardsNoWaiting() throws Exception {
             settingsBuilder.put("index.routing.allocation.exclude._name", exclude);
         }
         Settings settings = settingsBuilder.build();
-        CreateIndexResponse response = prepareCreate("test-idx").setSettings(settings).setWaitForActiveShards(ActiveShardCount.NONE).get();
-        assertTrue(response.isAcknowledged());
+        assertAcked(prepareCreate("test-idx").setSettings(settings).setWaitForActiveShards(ActiveShardCount.NONE));
     }
 
     public void testCreateIndexNotEnoughActiveShardsTimesOut() throws Exception {
@@ -86,9 +85,7 @@ public void testCreateIndexEnoughActiveShards() throws Exception {
             .put(INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), internalCluster().numDataNodes() + randomIntBetween(0, 3))
             .build();
         assertAcked(
-            prepareCreate(indexName).setSettings(settings)
-                .setWaitForActiveShards(randomIntBetween(0, internalCluster().numDataNodes()))
-                .get()
+            prepareCreate(indexName).setSettings(settings).setWaitForActiveShards(randomIntBetween(0, internalCluster().numDataNodes()))
         );
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java
index 5731163def260..fe24ed320d057 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java
@@ -256,7 +256,6 @@ public void testLargeClusterStatePublishing() throws Exception {
                 )
                 .setMapping(mapping)
                 .setTimeout("60s")
-                .get()
         );
         ensureGreen(); // wait for green state, so its both green, and there are no more pending events
         MappingMetadata masterMappingMetadata = indicesAdmin().prepareGetMappings("test").get().getMappings().get("test");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java
index aff04ca521844..d599b0649e829 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceIT.java
@@ -128,13 +128,9 @@ public void testTemplateUpdate() throws Exception {
         assertTemplates();
 
         // Change some templates
-        assertAcked(indicesAdmin().preparePutTemplate("test_dummy_template").setOrder(0).setPatterns(Collections.singletonList("*")).get());
-        assertAcked(
-            indicesAdmin().preparePutTemplate("test_changed_template").setOrder(0).setPatterns(Collections.singletonList("*")).get()
-        );
-        assertAcked(
-            indicesAdmin().preparePutTemplate("test_removed_template").setOrder(1).setPatterns(Collections.singletonList("*")).get()
-        );
+        assertAcked(indicesAdmin().preparePutTemplate("test_dummy_template").setOrder(0).setPatterns(Collections.singletonList("*")));
+        assertAcked(indicesAdmin().preparePutTemplate("test_changed_template").setOrder(0).setPatterns(Collections.singletonList("*")));
+        assertAcked(indicesAdmin().preparePutTemplate("test_removed_template").setOrder(1).setPatterns(Collections.singletonList("*")));
 
         AtomicInteger updateCount = new AtomicInteger();
         // Wait for the templates to be updated back to normal
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java
index 73ea91155a087..2f3618f1d6aa7 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/PrimaryAllocationIT.java
@@ -86,7 +86,7 @@ public void testBulkWeirdScenario() throws Exception {
         internalCluster().startDataOnlyNodes(2);
 
         assertAcked(
-            indicesAdmin().prepareCreate("test").setSettings(indexSettings(1, 1).put("index.global_checkpoint_sync.interval", "1s")).get()
+            indicesAdmin().prepareCreate("test").setSettings(indexSettings(1, 1).put("index.global_checkpoint_sync.interval", "1s"))
         );
         ensureGreen();
 
@@ -399,9 +399,7 @@ public void testDoNotRemoveAllocationIdOnNodeLeave() throws Exception {
         internalCluster().startMasterOnlyNode(Settings.EMPTY);
         internalCluster().startDataOnlyNode(Settings.EMPTY);
         assertAcked(
-            indicesAdmin().prepareCreate("test")
-                .setSettings(indexSettings(1, 1).put("index.unassigned.node_left.delayed_timeout", "0ms"))
-                .get()
+            indicesAdmin().prepareCreate("test").setSettings(indexSettings(1, 1).put("index.unassigned.node_left.delayed_timeout", "0ms"))
         );
         String replicaNode = internalCluster().startDataOnlyNode(Settings.EMPTY);
         ensureGreen("test");
@@ -430,9 +428,7 @@ public void testRemoveAllocationIdOnWriteAfterNodeLeave() throws Exception {
         internalCluster().startMasterOnlyNode(Settings.EMPTY);
         internalCluster().startDataOnlyNode(Settings.EMPTY);
         assertAcked(
-            indicesAdmin().prepareCreate("test")
-                .setSettings(indexSettings(1, 1).put("index.unassigned.node_left.delayed_timeout", "0ms"))
-                .get()
+            indicesAdmin().prepareCreate("test").setSettings(indexSettings(1, 1).put("index.unassigned.node_left.delayed_timeout", "0ms"))
         );
         String replicaNode = internalCluster().startDataOnlyNode(Settings.EMPTY);
         ensureGreen("test");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java
index 022f7acedc517..7e3adf8e0283f 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java
@@ -349,9 +349,7 @@ private void testRemoveArchiveSettingsWithBlocks(boolean readOnly, boolean readO
         if (readOnlyAllowDelete) {
             settingsBuilder.put(Metadata.SETTING_READ_ONLY_ALLOW_DELETE_SETTING.getKey(), "true");
         }
-        assertAcked(
-            clusterAdmin().prepareUpdateSettings().setPersistentSettings(settingsBuilder).setTransientSettings(settingsBuilder).get()
-        );
+        assertAcked(clusterAdmin().prepareUpdateSettings().setPersistentSettings(settingsBuilder).setTransientSettings(settingsBuilder));
 
         ClusterState state = clusterAdmin().prepareState().get().getState();
         if (readOnly) {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java
index 2838987388598..85aa7d6206a5e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java
@@ -80,7 +80,6 @@ public void testSettingsFiltering() {
                         .put("filter_test.notfoo", "test")
                         .build()
                 )
-                .get()
         );
         GetSettingsResponse response = indicesAdmin().prepareGetSettings("test-idx").get();
         Settings settings = response.getIndexToSettings().get("test-idx");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/shards/ClusterShardLimitIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/shards/ClusterShardLimitIT.java
index d0311740fc637..58c13b8a6c721 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/shards/ClusterShardLimitIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/shards/ClusterShardLimitIT.java
@@ -121,7 +121,6 @@ public void testIndexCreationOverLimitFromTemplate() {
                 .setPatterns(Collections.singletonList("should-fail"))
                 .setOrder(1)
                 .setSettings(indexSettings(counts.getFailingIndexShards(), counts.getFailingIndexReplicas()))
-                .get()
         );
 
         final IllegalArgumentException e = expectThrows(
@@ -243,7 +242,6 @@ public void testPreserveExistingSkipsCheck() {
             indicesAdmin().prepareUpdateSettings("test-index")
                 .setPreserveExisting(true)
                 .setSettings(Settings.builder().put("number_of_replicas", dataNodes))
-                .get()
         );
         ClusterState clusterState = clusterAdmin().prepareState().get().getState();
         assertEquals(0, clusterState.getMetadata().index("test-index").getNumberOfReplicas());
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/document/ShardInfoIT.java b/server/src/internalClusterTest/java/org/elasticsearch/document/ShardInfoIT.java
index 75b818d082dff..6571b9a6c928c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/document/ShardInfoIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/document/ShardInfoIT.java
@@ -96,7 +96,6 @@ private void prepareIndex(int numberOfPrimaryShards, boolean routingRequired) th
         assertAcked(
             prepareCreate("idx").setSettings(indexSettings(numberOfPrimaryShards, numCopies - 1))
                 .setMapping("_routing", "required=" + routingRequired)
-                .get()
         );
         for (int i = 0; i < numberOfPrimaryShards; i++) {
             ensureActiveShardCopies(i, numNodes);
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java
index 63e5c7acc67ba..41bdf944edd59 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/index/HiddenIndexIT.java
@@ -74,21 +74,16 @@ public void testHiddenIndexSearch() {
     }
 
     public void testGlobalTemplatesDoNotApply() {
-        assertAcked(indicesAdmin().preparePutTemplate("a_global_template").setPatterns(List.of("*")).setMapping("foo", "type=text").get());
+        assertAcked(indicesAdmin().preparePutTemplate("a_global_template").setPatterns(List.of("*")).setMapping("foo", "type=text"));
+        assertAcked(indicesAdmin().preparePutTemplate("not_global_template").setPatterns(List.of("a*")).setMapping("bar", "type=text"));
         assertAcked(
-            indicesAdmin().preparePutTemplate("not_global_template").setPatterns(List.of("a*")).setMapping("bar", "type=text").get()
+            indicesAdmin().preparePutTemplate("specific_template").setPatterns(List.of("a_hidden_index")).setMapping("baz", "type=text")
         );
         assertAcked(
-            indicesAdmin().preparePutTemplate("specific_template")
-                .setPatterns(List.of("a_hidden_index"))
-                .setMapping("baz", "type=text")
-                .get()
-        );
-        assertAcked(
-            indicesAdmin().preparePutTemplate("unused_template").setPatterns(List.of("not_used")).setMapping("foobar", "type=text").get()
+            indicesAdmin().preparePutTemplate("unused_template").setPatterns(List.of("not_used")).setMapping("foobar", "type=text")
         );
 
-        assertAcked(indicesAdmin().prepareCreate("a_hidden_index").setSettings(Settings.builder().put("index.hidden", true).build()).get());
+        assertAcked(indicesAdmin().prepareCreate("a_hidden_index").setSettings(Settings.builder().put("index.hidden", true).build()));
 
         GetMappingsResponse mappingsResponse = indicesAdmin().prepareGetMappings("a_hidden_index").get();
         assertThat(mappingsResponse.mappings().size(), is(1));
@@ -125,7 +120,6 @@ public void testNonGlobalTemplateCanMakeIndexHidden() {
                 .setPatterns(List.of("my_hidden_pattern*"))
                 .setMapping("foo", "type=text")
                 .setSettings(Settings.builder().put("index.hidden", true).build())
-                .get()
         );
         assertAcked(indicesAdmin().prepareCreate("my_hidden_pattern1").get());
         GetSettingsResponse getSettingsResponse = indicesAdmin().prepareGetSettings("my_hidden_pattern1").get();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java
index 93a19e5d921b5..b10d4147af25c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/IndicesRequestCacheIT.java
@@ -48,7 +48,6 @@ public void testCacheAggs() throws Exception {
             indicesAdmin().prepareCreate("index")
                 .setMapping("f", "type=date")
                 .setSettings(Settings.builder().put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
         indexRandom(
             true,
@@ -110,7 +109,6 @@ public void testQueryRewrite() throws Exception {
                     indexSettings(5, 0).put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true)
                         .put("index.number_of_routing_shards", 5)
                 )
-                .get()
         );
         indexRandom(
             true,
@@ -173,7 +171,6 @@ public void testQueryRewriteMissingValues() throws Exception {
             indicesAdmin().prepareCreate("index")
                 .setMapping("s", "type=date")
                 .setSettings(indexSettings(1, 0).put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
         indexRandom(
             true,
@@ -232,7 +229,6 @@ public void testQueryRewriteDates() throws Exception {
             indicesAdmin().prepareCreate("index")
                 .setMapping("d", "type=date")
                 .setSettings(indexSettings(1, 0).put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
         indexRandom(
             true,
@@ -470,7 +466,6 @@ public void testCacheWithFilteredAlias() {
                 .setMapping("created_at", "type=date")
                 .setSettings(settings)
                 .addAlias(new Alias("last_week").filter(QueryBuilders.rangeQuery("created_at").gte("now-7d/d")))
-                .get()
         );
         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
         client.prepareIndex("index").setId("1").setRouting("1").setSource("created_at", DateTimeFormatter.ISO_LOCAL_DATE.format(now)).get();
@@ -516,7 +511,6 @@ public void testProfileDisableCache() throws Exception {
             indicesAdmin().prepareCreate("index")
                 .setMapping("k", "type=keyword")
                 .setSettings(indexSettings(1, 0).put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
         indexRandom(true, client.prepareIndex("index").setSource("k", "hello"));
         ensureSearchable("index");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java
index c3f7fbd86f1bf..7a9aa7a47215a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/mapping/MalformedDynamicTemplateIT.java
@@ -56,7 +56,7 @@ public void testBWCMalformedDynamicTemplate() {
                     .put(indexSettings())
                     .put("number_of_shards", 1)
                     .put("index.version.created", IndexVersionUtils.randomPreviousCompatibleVersion(random(), IndexVersions.V_8_0_0))
-            ).setMapping(mapping).get()
+            ).setMapping(mapping)
         );
         client().prepareIndex(indexName).setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get();
         assertNoFailures((indicesAdmin().prepareRefresh(indexName)).get());
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java
index 60be1f11371d6..e0a0d1f5254b2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java
@@ -1641,7 +1641,6 @@ public void testAllocateEmptyPrimaryResetsGlobalCheckpoint() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate(indexName)
                 .setSettings(indexSettings(1, 1).put(MockEngineSupport.DISABLE_FLUSH_ON_CLOSE.getKey(), randomBoolean()))
-                .get()
         );
         final List indexRequests = IntStream.range(0, between(10, 500))
             .mapToObj(n -> client().prepareIndex(indexName).setSource("foo", "bar"))
@@ -1651,9 +1650,7 @@ public void testAllocateEmptyPrimaryResetsGlobalCheckpoint() throws Exception {
         internalCluster().stopRandomDataNode();
         internalCluster().stopRandomDataNode();
         final String nodeWithoutData = internalCluster().startDataOnlyNode();
-        assertAcked(
-            clusterAdmin().prepareReroute().add(new AllocateEmptyPrimaryAllocationCommand(indexName, 0, nodeWithoutData, true)).get()
-        );
+        assertAcked(clusterAdmin().prepareReroute().add(new AllocateEmptyPrimaryAllocationCommand(indexName, 0, nodeWithoutData, true)));
         internalCluster().startDataOnlyNode(randomNodeDataPathSettings);
         ensureGreen();
         for (ShardStats shardStats : indicesAdmin().prepareStats(indexName).get().getIndex(indexName).getShards()) {
@@ -2009,7 +2006,6 @@ private void createRepository(boolean enableSnapshotPeerRecoveries) {
                         .put(BlobStoreRepository.USE_FOR_PEER_RECOVERY_SETTING.getKey(), enableSnapshotPeerRecoveries)
                         .put("compress", false)
                 )
-                .get()
         );
     }
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java
index 0a8c7ffc9d293..b3e0d258cb113 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateNumberOfReplicasIT.java
@@ -466,7 +466,6 @@ public void testUpdateNumberOfReplicasAllowNoIndices() {
             indicesAdmin().prepareUpdateSettings("non-existent-*")
                 .setSettings(Settings.builder().put("index.number_of_replicas", 1))
                 .setIndicesOptions(options)
-                .get()
         );
         final int numberOfReplicas = Integer.parseInt(
             indicesAdmin().prepareGetSettings("test-index").get().getSetting("test-index", "index.number_of_replicas")
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java
index c1851eb9924c8..e770127bf577c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java
@@ -468,7 +468,7 @@ public void testSettingsVersion() {
         {
             final long settingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             assertAcked(
-                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.refresh_interval", "500ms")).get()
+                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.refresh_interval", "500ms"))
             );
             final long newSettingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             assertThat(newSettingsVersion, equalTo(1 + settingsVersion));
@@ -476,14 +476,10 @@ public void testSettingsVersion() {
 
         {
             final boolean block = randomBoolean();
-            assertAcked(
-                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block)).get()
-            );
+            assertAcked(indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block)));
             final long settingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             assertAcked(
-                indicesAdmin().prepareUpdateSettings("test")
-                    .setSettings(Settings.builder().put("index.blocks.read_only", block == false))
-                    .get()
+                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block == false))
             );
             final long newSettingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             assertThat(newSettingsVersion, equalTo(1 + settingsVersion));
@@ -491,7 +487,7 @@ public void testSettingsVersion() {
             // if the read-only block is present, remove it
             if (block == false) {
                 assertAcked(
-                    indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", false)).get()
+                    indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", false))
                 );
             }
         }
@@ -505,9 +501,7 @@ public void testSettingsVersionUnchanged() {
             final long settingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             final String refreshInterval = indicesAdmin().prepareGetSettings("test").get().getSetting("test", "index.refresh_interval");
             assertAcked(
-                indicesAdmin().prepareUpdateSettings("test")
-                    .setSettings(Settings.builder().put("index.refresh_interval", refreshInterval))
-                    .get()
+                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.refresh_interval", refreshInterval))
             );
             final long newSettingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             assertThat(newSettingsVersion, equalTo(settingsVersion));
@@ -515,21 +509,17 @@ public void testSettingsVersionUnchanged() {
 
         {
             final boolean block = randomBoolean();
-            assertAcked(
-                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block)).get()
-            );
+            assertAcked(indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block)));
             // now put the same block again
             final long settingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
-            assertAcked(
-                indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block)).get()
-            );
+            assertAcked(indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", block)));
             final long newSettingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
             assertThat(newSettingsVersion, equalTo(settingsVersion));
 
             // if the read-only block is present, remove it
             if (block) {
                 assertAcked(
-                    indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", false)).get()
+                    indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.read_only", false))
                 );
             }
         }
@@ -548,9 +538,7 @@ public void testNumberOfReplicasSettingsVersionUnchanged() {
             indicesAdmin().prepareGetSettings("test").get().getSetting("test", "index.number_of_replicas")
         );
         assertAcked(
-            indicesAdmin().prepareUpdateSettings("test")
-                .setSettings(Settings.builder().put("index.number_of_replicas", numberOfReplicas))
-                .get()
+            indicesAdmin().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.number_of_replicas", numberOfReplicas))
         );
         final long newSettingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
         assertThat(newSettingsVersion, equalTo(settingsVersion));
@@ -571,7 +559,6 @@ public void testNumberOfReplicasSettingsVersion() {
         assertAcked(
             indicesAdmin().prepareUpdateSettings("test")
                 .setSettings(Settings.builder().put("index.number_of_replicas", 1 + numberOfReplicas))
-                .get()
         );
         final long newSettingsVersion = clusterAdmin().prepareState().get().getState().metadata().index("test").getSettingsVersion();
         assertThat(newSettingsVersion, equalTo(1 + settingsVersion));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/SimpleIndexStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/SimpleIndexStateIT.java
index e2ddb7c7b9957..9b763ea581187 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/SimpleIndexStateIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/SimpleIndexStateIT.java
@@ -12,8 +12,6 @@
 import org.apache.logging.log4j.Logger;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
 import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
-import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
-import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.cluster.health.ClusterHealthStatus;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
@@ -67,8 +65,7 @@ public void testSimpleOpenClose() {
         }
 
         logger.info("--> opening index...");
-        OpenIndexResponse openIndexResponse = indicesAdmin().prepareOpen("test").get();
-        assertThat(openIndexResponse.isAcknowledged(), equalTo(true));
+        assertAcked(indicesAdmin().prepareOpen("test"));
 
         logger.info("--> waiting for green status");
         ensureGreen();
@@ -139,9 +136,6 @@ public void testConsistencyAfterIndexCreationFailure() {
         }
 
         logger.info("--> creating test index with valid settings ");
-        CreateIndexResponse response = indicesAdmin().prepareCreate("test")
-            .setSettings(Settings.builder().put("number_of_shards", 1))
-            .get();
-        assertThat(response.isAcknowledged(), equalTo(true));
+        assertAcked(indicesAdmin().prepareCreate("test").setSettings(Settings.builder().put("number_of_shards", 1)));
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java
index 73f3dd88c9cec..a5a9ca2862a0e 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/stats/IndexStatsIT.java
@@ -131,7 +131,6 @@ public void testFieldDataStats() {
             indicesAdmin().prepareCreate("test")
                 .setSettings(settingsBuilder().put("index.number_of_shards", 2))
                 .setMapping("field", "type=text,fielddata=true", "field2", "type=text,fielddata=true")
-                .get()
         );
         ensureGreen();
         client().prepareIndex("test").setId("1").setSource("field", "value1", "field2", "value1").execute().actionGet();
@@ -236,7 +235,6 @@ public void testClearAllCaches() throws Exception {
             indicesAdmin().prepareCreate("test")
                 .setSettings(settingsBuilder().put("index.number_of_replicas", 0).put("index.number_of_shards", 2))
                 .setMapping("field", "type=text,fielddata=true")
-                .get()
         );
         ensureGreen();
         clusterAdmin().prepareHealth().setWaitForGreenStatus().execute().actionGet();
@@ -326,7 +324,6 @@ public void testQueryCache() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate("idx")
                 .setSettings(Settings.builder().put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
         ensureGreen();
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java
index 17f64a689d1a2..359b90a351b60 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/template/SimpleIndexTemplateIT.java
@@ -811,7 +811,6 @@ public void testOrderAndVersion() {
                 .setVersion(version)
                 .setOrder(order)
                 .setMapping("field", "type=text")
-                .get()
         );
 
         GetIndexTemplatesResponse response = indicesAdmin().prepareGetTemplates("versioned_template").get();
@@ -913,7 +912,6 @@ public void testPartitionedTemplate() throws Exception {
             indicesAdmin().preparePutTemplate("just_partitions")
                 .setPatterns(Collections.singletonList("te*"))
                 .setSettings(Settings.builder().put("index.routing_partition_size", "6"))
-                .get()
         );
 
         // create an index with too few shards
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/repositories/RepositoriesServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/repositories/RepositoriesServiceIT.java
index a5bb89670389c..76f3ca328d222 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/repositories/RepositoriesServiceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/repositories/RepositoriesServiceIT.java
@@ -46,9 +46,7 @@ public void testUpdateRepository() {
 
         final Settings.Builder repoSettings = Settings.builder().put("location", randomRepoPath());
 
-        assertAcked(
-            client.admin().cluster().preparePutRepository(repositoryName).setType(FsRepository.TYPE).setSettings(repoSettings).get()
-        );
+        assertAcked(client.admin().cluster().preparePutRepository(repositoryName).setType(FsRepository.TYPE).setSettings(repoSettings));
 
         final GetRepositoriesResponse originalGetRepositoriesResponse = client.admin()
             .cluster()
@@ -66,9 +64,7 @@ public void testUpdateRepository() {
         final boolean updated = randomBoolean();
         final String updatedRepositoryType = updated ? "mock" : FsRepository.TYPE;
 
-        assertAcked(
-            client.admin().cluster().preparePutRepository(repositoryName).setType(updatedRepositoryType).setSettings(repoSettings).get()
-        );
+        assertAcked(client.admin().cluster().preparePutRepository(repositoryName).setType(updatedRepositoryType).setSettings(repoSettings));
 
         final GetRepositoriesResponse updatedGetRepositoriesResponse = client.admin()
             .cluster()
@@ -86,8 +82,6 @@ public void testUpdateRepository() {
         // check that a noop update does not verify. Since the new data node does not share the same `path.repo`, verification will fail if
         // it runs.
         internalCluster().startDataOnlyNode(Settings.builder().put(Environment.PATH_REPO_SETTING.getKey(), createTempDir()).build());
-        assertAcked(
-            client.admin().cluster().preparePutRepository(repositoryName).setType(updatedRepositoryType).setSettings(repoSettings).get()
-        );
+        assertAcked(client.admin().cluster().preparePutRepository(repositoryName).setType(updatedRepositoryType).setSettings(repoSettings));
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java
index d0a4fa4865694..920fd79401cc6 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateHistogramIT.java
@@ -1494,7 +1494,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=date")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         String date = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.format(date(1, 1));
         String date2 = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.format(date(2, 1));
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java
index f67a7461d3bae..44b0ff05ea274 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java
@@ -597,7 +597,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("date", "type=date")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java
index 0c0209df7d5a3..0381a5521dea0 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/DoubleTermsIT.java
@@ -915,7 +915,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=float")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java
index cea3872b9a7c8..07b678e89c024 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/HistogramIT.java
@@ -1089,7 +1089,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=float")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java
index 6feee8e36701b..6c3d1c44aafed 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/LongTermsIT.java
@@ -875,7 +875,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java
index cfe3a6708341a..742d403ba42b0 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/RangeIT.java
@@ -888,7 +888,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("i", "type=integer")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java
index 0180bcff738ba..4d94173f8d978 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java
@@ -557,7 +557,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("s", "type=long", "t", "type=text")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java
index 5d337500ea14d..ceafd07c67d65 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/bucket/terms/StringTermsIT.java
@@ -122,7 +122,6 @@ public void setupSuiteScopeCluster() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate("idx")
                 .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword")
-                .get()
         );
         List builders = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
@@ -148,7 +147,6 @@ public void setupSuiteScopeCluster() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate("high_card_idx")
                 .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword")
-                .get()
         );
         for (int i = 0; i < 100; i++) {
             builders.add(
@@ -226,7 +224,6 @@ private void getMultiSortDocs(List builders) throws IOExcep
         assertAcked(
             indicesAdmin().prepareCreate("sort_idx")
                 .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword")
-                .get()
         );
         for (int i = 1; i <= 3; i++) {
             builders.add(
@@ -1156,7 +1153,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=keyword")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java
index ff94a9f5cad82..64a97bf0f6f16 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ExtendedStatsIT.java
@@ -827,7 +827,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java
index 841419a04424d..7d5e446d591bb 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksIT.java
@@ -537,7 +537,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java
index fe48fe4fe48b2..3ac50c7b5e104 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java
@@ -509,7 +509,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java
index 9e150e4d8b769..dae90424495a3 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java
@@ -474,7 +474,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
 
         indexRandom(
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java
index e7570f0138422..2ea09960071f9 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java
@@ -1111,7 +1111,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java
index 0492ad315c8da..eb4d5aa74f2a0 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/StatsIT.java
@@ -225,7 +225,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java
index b100e7eb9f955..a837b22694ef5 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/SumIT.java
@@ -203,7 +203,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java
index cdede393bd4f5..421d6f118c277 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentileRanksIT.java
@@ -449,7 +449,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java
index 28b1911c9441a..58b2b13853848 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java
@@ -424,7 +424,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java
index 6622e4d908efe..ab9ab37894f70 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java
@@ -1077,7 +1077,6 @@ public void testScriptCaching() throws Exception {
                     .setSettings(
                         Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1)
                     )
-                    .get()
             );
             indexRandom(
                 true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java
index 719a5831375df..d122ee10d90a5 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/ValueCountIT.java
@@ -203,7 +203,6 @@ public void testScriptCaching() throws Exception {
         assertAcked(
             prepareCreate("cache_test_idx").setMapping("d", "type=long")
                 .setSettings(Settings.builder().put("requests.cache.enable", true).put("number_of_shards", 1).put("number_of_replicas", 1))
-                .get()
         );
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java
index 1caed3ad45793..79a28a053b3c2 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java
@@ -3392,7 +3392,6 @@ public void testHighlightQueryRewriteDatesWithNow() throws Exception {
             indicesAdmin().prepareCreate("index-1")
                 .setMapping("d", "type=date", "field", "type=text,store=true,term_vector=with_positions_offsets")
                 .setSettings(indexSettings(2, 0))
-                .get()
         );
         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
         DateFormatter formatter = DateFormatter.forPattern("strict_date_optional_time");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java
index 89d001df58d12..e3c9558eba907 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fields/SearchFieldsIT.java
@@ -988,7 +988,7 @@ public void testScriptFields() throws Exception {
                 "type=long",
                 "md",
                 "type=double"
-            ).get()
+            )
         );
         final int numDocs = randomIntBetween(3, 8);
         List reqs = new ArrayList<>();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java
index 3484559e6b04f..61cccfdf114b1 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueIT.java
@@ -43,7 +43,7 @@ public void testFieldValueFactor() throws IOException {
                     .endObject()
                     .endObject()
                     .endObject()
-            ).get()
+            )
         );
 
         client().prepareIndex("test").setId("1").setSource("test", 5, "body", "foo").get();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java
index 2a1bfa36ad25d..526d523bb0638 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/profile/aggregation/AggregationProfilerIT.java
@@ -96,7 +96,6 @@ protected void setupSuiteScopeCluster() throws Exception {
             indicesAdmin().prepareCreate("idx")
                 .setSettings(Map.of("number_of_shards", 1, "number_of_replicas", 0))
                 .setMapping(STRING_FIELD, "type=keyword", NUMBER_FIELD, "type=integer", TAG_FIELD, "type=keyword")
-                .get()
         );
         List builders = new ArrayList<>();
 
@@ -627,7 +626,6 @@ public void testFilterByFilter() throws InterruptedException, IOException {
             indicesAdmin().prepareCreate("dateidx")
                 .setSettings(Map.of("number_of_shards", 1, "number_of_replicas", 0))
                 .setMapping("date", "type=date")
-                .get()
         );
         List builders = new ArrayList<>();
         for (int i = 0; i < RangeAggregator.DOCS_PER_RANGE_TO_USE_FILTERS * 2; i++) {
@@ -701,7 +699,6 @@ public void testDateHistogramFilterByFilterDisabled() throws InterruptedExceptio
                 indicesAdmin().prepareCreate("date_filter_by_filter_disabled")
                     .setSettings(Map.of("number_of_shards", 1, "number_of_replicas", 0))
                     .setMapping("date", "type=date", "keyword", "type=keyword")
-                    .get()
             );
             List builders = new ArrayList<>();
             for (int i = 0; i < RangeAggregator.DOCS_PER_RANGE_TO_USE_FILTERS * 2; i++) {
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java
index 7edc0ff139a1e..948b7261ded1c 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/slice/SearchSliceIT.java
@@ -141,7 +141,6 @@ public void testWithPreferenceAndRoutings() throws Exception {
                     .addAliasAction(IndicesAliasesRequest.AliasActions.add().index("test").alias("alias1").routing("foo"))
                     .addAliasAction(IndicesAliasesRequest.AliasActions.add().index("test").alias("alias2").routing("bar"))
                     .addAliasAction(IndicesAliasesRequest.AliasActions.add().index("test").alias("alias3").routing("baz"))
-                    .get()
             );
             SearchResponse sr = prepareSearch("alias1", "alias3").setQuery(matchAllQuery()).setSize(0).get();
             int numDocs = (int) sr.getHits().getTotalHits().value;
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
index 87d87f2260734..2926d36becb4a 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/FieldSortIT.java
@@ -1654,12 +1654,11 @@ public void testSortDuelBetweenSingleShardAndMultiShardIndex() throws Exception
         assertAcked(
             prepareCreate("test1").setSettings(
                 Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, between(2, maximumNumberOfShards()))
-            ).setMapping(sortField, "type=long").get()
+            ).setMapping(sortField, "type=long")
         );
         assertAcked(
             prepareCreate("test2").setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1))
                 .setMapping(sortField, "type=long")
-                .get()
         );
 
         for (String index : new String[] { "test1", "test2" }) {
@@ -1983,7 +1982,6 @@ public void testLongSortOptimizationCorrectResults() {
         assertAcked(
             prepareCreate("test1").setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2))
                 .setMapping("long_field", "type=long")
-                .get()
         );
 
         BulkRequestBuilder bulkBuilder = client().prepareBulk();
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java
index a04090e9d04b9..54d730cec2bc3 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderIT.java
@@ -341,9 +341,9 @@ private static void checkCorrectSortOrderForGeoSort(SearchResponse searchRespons
 
     public void testCrossIndexIgnoreUnmapped() throws Exception {
         assertAcked(
-            prepareCreate("test1").setMapping("str_field", "type=keyword", "long_field", "type=long", "double_field", "type=double").get()
+            prepareCreate("test1").setMapping("str_field", "type=keyword", "long_field", "type=long", "double_field", "type=double")
         );
-        assertAcked(prepareCreate("test2").get());
+        assertAcked(prepareCreate("test2"));
 
         indexRandom(
             true,
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
index c782b01742a67..9592d3904a90d 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
@@ -1221,7 +1221,7 @@ private void createIndexAndMappingAndSettings(Settings settings, CompletionMappi
         mapping = mapping.endObject().endObject().endObject().endObject();
 
         assertAcked(
-            indicesAdmin().prepareCreate(INDEX).setSettings(Settings.builder().put(indexSettings()).put(settings)).setMapping(mapping).get()
+            indicesAdmin().prepareCreate(INDEX).setSettings(Settings.builder().put(indexSettings()).put(settings)).setMapping(mapping)
         );
     }
 
@@ -1275,7 +1275,6 @@ public void testVeryLongInput() throws IOException {
                         .endObject()
                         .endObject()
                 )
-                .get()
         );
         // can cause stack overflow without the default max_input_length
         String longString = replaceReservedChars(randomRealisticUnicodeOfLength(randomIntBetween(5000, 10000)), (char) 0x01);
@@ -1304,7 +1303,6 @@ public void testReservedChars() throws IOException {
                         .endObject()
                         .endObject()
                 )
-                .get()
         );
         // can cause stack overflow without the default max_input_length
         String string = "foo" + (char) 0x00 + "bar";
@@ -1342,7 +1340,6 @@ public void testIssue5930() throws IOException {
                         .endObject()
                         .endObject()
                 )
-                .get()
         );
         String string = "foo bar";
         client().prepareIndex(INDEX)
@@ -1454,7 +1451,7 @@ public void testCompletionWithCollapse() throws Exception {
 
         String index = "test";
         assertAcked(
-            indicesAdmin().prepareCreate(index).setSettings(Settings.builder().put("index.number_of_shards", 2)).setMapping(mapping).get()
+            indicesAdmin().prepareCreate(index).setSettings(Settings.builder().put("index.number_of_shards", 2)).setMapping(mapping)
         );
 
         int numDocs = 2;
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java
index ea4c21cc06fe5..a526781bcc3db 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java
@@ -725,7 +725,7 @@ private void createIndexAndMappingAndSettings(Settings settings, CompletionMappi
         mapping.endObject().endObject().endObject();
 
         assertAcked(
-            indicesAdmin().prepareCreate(INDEX).setSettings(Settings.builder().put(indexSettings()).put(settings)).setMapping(mapping).get()
+            indicesAdmin().prepareCreate(INDEX).setSettings(Settings.builder().put(indexSettings()).put(settings)).setMapping(mapping)
         );
     }
 }
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java
index bf7292554bf53..9753a9138ff07 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java
@@ -247,7 +247,6 @@ public void testRestoreIndexWithMissingShards() throws Exception {
         assertAcked(
             prepareCreate("test-idx-none", 1, indexSettingsNoReplicas(6).put("index.routing.allocation.include.tag", "nowhere"))
                 .setWaitForActiveShards(ActiveShardCount.NONE)
-                .get()
         );
         assertTrue(indexExists("test-idx-none"));
 
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java
index 5f4c270f69348..2005d63ab6413 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RepositoriesIT.java
@@ -257,7 +257,6 @@ public void testRepositoryConflict() throws Exception {
                         .put("random", randomAlphaOfLength(10))
                         .put("wait_after_unblock", 200)
                 )
-                .get()
         );
 
         logger.info("--> snapshot");
diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java
index e57731efbcf2b..cd34f68471156 100644
--- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java
+++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/RestoreSnapshotIT.java
@@ -337,7 +337,6 @@ public void testRestoreAliases() throws Exception {
                 .addAlias("test-idx-2", "alias-123")
                 .addAlias("test-idx-3", "alias-123")
                 .addAlias("test-idx-1", "alias-1")
-                .get()
         );
 
         assertFalse(indicesAdmin().prepareGetAliases("alias-123").get().getAliases().isEmpty());
@@ -398,7 +397,7 @@ public void testRestoreTemplates() throws Exception {
         createRepository("test-repo", "fs");
 
         logger.info("-->  creating test template");
-        assertThat(
+        assertAcked(
             indicesAdmin().preparePutTemplate("test-template")
                 .setPatterns(Collections.singletonList("te*"))
                 .setMapping(
@@ -418,9 +417,6 @@ public void testRestoreTemplates() throws Exception {
                         .endObject()
                         .endObject()
                 )
-                .get()
-                .isAcknowledged(),
-            equalTo(true)
         );
 
         createSnapshot("test-repo", "test-snap", Collections.emptyList());
diff --git a/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java b/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java
index b1b04550d22f2..e79b088893acd 100644
--- a/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java
+++ b/server/src/test/java/org/elasticsearch/index/fieldstats/FieldStatsProviderRefreshTests.java
@@ -29,7 +29,6 @@ public void testQueryRewriteOnRefresh() throws Exception {
             indicesAdmin().prepareCreate("index")
                 .setMapping("s", "type=text")
                 .setSettings(indexSettings(1, 0).put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
 
         // Index some documents
diff --git a/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java
index 40da3fb0432d3..5bcec997c26c3 100644
--- a/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java
@@ -500,7 +500,6 @@ private static void createSnapshotThatCanBeUsedDuringRecovery(String indexName)
                         .put(BlobStoreRepository.USE_FOR_PEER_RECOVERY_SETTING.getKey(), true)
                         .put("compress", false)
                 )
-                .get()
         );
 
         // create snapshot
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
index 76ed45e2bbbe5..ca7cb72417fac 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
@@ -133,7 +133,7 @@ public void tearDown() throws Exception {
         var deleteDataStreamsRequest = new DeleteDataStreamAction.Request("*");
         deleteDataStreamsRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN_CLOSED_HIDDEN);
         try {
-            assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, deleteDataStreamsRequest).actionGet());
+            assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, deleteDataStreamsRequest));
         } catch (IllegalStateException e) {
             // Ignore if action isn't registered, because data streams is a module and
             // if the delete action isn't registered then there no data streams to delete.
diff --git a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
index b93a24c656671..f355b736ec002 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java
@@ -26,7 +26,6 @@
 import org.elasticsearch.action.search.ShardSearchFailure;
 import org.elasticsearch.action.support.DefaultShardOperationFailedException;
 import org.elasticsearch.action.support.broadcast.BaseBroadcastResponse;
-import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
 import org.elasticsearch.action.support.master.IsAcknowledgedSupplier;
 import org.elasticsearch.cluster.block.ClusterBlock;
 import org.elasticsearch.cluster.block.ClusterBlockException;
@@ -90,11 +89,15 @@
 
 public class ElasticsearchAssertions {
 
-    public static void assertAcked(AcknowledgedRequestBuilder builder) {
+    public static void assertAcked(ActionRequestBuilder builder) {
         assertAcked(builder, TimeValue.timeValueSeconds(30));
     }
 
-    public static void assertAcked(AcknowledgedRequestBuilder builder, TimeValue timeValue) {
+    public static void assertAcked(ActionFuture future) {
+        assertAcked(future.actionGet());
+    }
+
+    public static void assertAcked(ActionRequestBuilder builder, TimeValue timeValue) {
         assertAcked(builder.get(timeValue));
     }
 
diff --git a/x-pack/plugin/autoscaling/src/internalClusterTest/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageIT.java b/x-pack/plugin/autoscaling/src/internalClusterTest/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageIT.java
index 0bd1731c48f0a..51d0c2c0aef80 100644
--- a/x-pack/plugin/autoscaling/src/internalClusterTest/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageIT.java
+++ b/x-pack/plugin/autoscaling/src/internalClusterTest/java/org/elasticsearch/xpack/autoscaling/storage/ReactiveStorageIT.java
@@ -351,7 +351,6 @@ public void testScaleWhileShrinking() throws Exception {
                         .build()
                 )
                 .setWaitForActiveShards(ActiveShardCount.NONE)
-                .get()
         );
 
         // * 2 since worst case is no hard links, see DiskThresholdDecider.getExpectedShardSize.
@@ -468,7 +467,6 @@ public void testScaleDuringSplitOrClone() throws Exception {
                 )
                 .setWaitForActiveShards(ActiveShardCount.NONE)
                 .setResizeType(resizeType)
-                .get()
         );
 
         // * 2 since worst case is no hard links, see DiskThresholdDecider.getExpectedShardSize.
diff --git a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java
index 5896ecacb9ca8..5031a52630033 100644
--- a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java
+++ b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java
@@ -694,7 +694,6 @@ public void testAutoFollowDatastreamWithClosingFollowerIndex() throws Exception
                 .indices()
                 .prepareCreate(indexInDatastream)
                 .setMapping(MetadataIndexTemplateService.DEFAULT_TIMESTAMP_MAPPING_WITHOUT_ROUTING.toString())
-                .get()
         );
         leaderClient().prepareIndex(indexInDatastream)
             .setCreate(true)
diff --git a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java
index 26eb241c0293e..501a664d64698 100644
--- a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java
+++ b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/FollowerFailOverIT.java
@@ -254,7 +254,6 @@ public void testReadRequestsReturnLatestMappingVersion() throws Exception {
                         .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
                         .put("index.routing.allocation.require.box", "large")
                 )
-                .get()
         );
         getFollowerCluster().startNode(
             onlyRoles(nodeAttributes, Set.of(DiscoveryNodeRole.DATA_ROLE, DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE))
diff --git a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/IndexFollowingIT.java b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/IndexFollowingIT.java
index dae2a102948dd..f21bfc07deba2 100644
--- a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/IndexFollowingIT.java
+++ b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/IndexFollowingIT.java
@@ -109,7 +109,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -609,7 +608,6 @@ public void testFollowNonExistentIndex() throws Exception {
                 .prepareCreate("test-follower")
                 .setSource(indexSettings, XContentType.JSON)
                 .setMasterNodeTimeout(TimeValue.MAX_VALUE)
-                .get()
         );
         ensureLeaderGreen("test-leader");
         ensureFollowerGreen("test-follower");
@@ -1754,14 +1752,14 @@ private String getIndexSettingsWithNestedMapping(
         return settings;
     }
 
-    private void putFollowerTemplate(String setting, String settingValue) throws InterruptedException, ExecutionException {
+    private void putFollowerTemplate(String setting, String settingValue) {
         Template template = new Template(Settings.builder().put(setting, settingValue).build(), null, null);
         ComposableIndexTemplate cit = new ComposableIndexTemplate(List.of("follower"), template, null, null, null, null);
         assertAcked(
             followerClient().execute(
                 PutComposableIndexTemplateAction.INSTANCE,
                 new PutComposableIndexTemplateAction.Request("my-it").indexTemplate(cit)
-            ).get()
+            )
         );
     }
 
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/async/AsyncTaskServiceTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/async/AsyncTaskServiceTests.java
index d43295b2fe543..fc35a4b4761bd 100644
--- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/async/AsyncTaskServiceTests.java
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/async/AsyncTaskServiceTests.java
@@ -11,7 +11,6 @@
 import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
 import org.elasticsearch.action.delete.DeleteResponse;
 import org.elasticsearch.action.support.PlainActionFuture;
-import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.action.update.UpdateResponse;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
@@ -36,6 +35,7 @@
 import java.util.List;
 import java.util.Map;
 
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
 import static org.hamcrest.Matchers.is;
 
 // TODO: test CRUD operations
@@ -224,8 +224,7 @@ public void testAutoCreateIndex() throws Exception {
         }
 
         // Delete the index, so we can test subsequent auto-create behaviour
-        AcknowledgedResponse ack = client().admin().indices().prepareDelete(index).get();
-        assertTrue(ack.isAcknowledged());
+        assertAcked(client().admin().indices().prepareDelete(index));
 
         // Subsequent response deletes throw a (wrapped) index not found exception
         {
diff --git a/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleClusterDisruptionIT.java b/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleClusterDisruptionIT.java
index cf234e31f1f7c..30fb751d1805c 100644
--- a/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleClusterDisruptionIT.java
+++ b/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleClusterDisruptionIT.java
@@ -419,7 +419,6 @@ private void prepareSourceIndex(String sourceIndex) {
         assertAcked(
             indicesAdmin().prepareUpdateSettings(sourceIndex)
                 .setSettings(Settings.builder().put(IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.getKey(), true).build())
-                .get()
         );
     }
 
diff --git a/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java b/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java
index 89f5cefb401c2..26740d7c52a2c 100644
--- a/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java
+++ b/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java
@@ -52,6 +52,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
+
 @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 2, numClientNodes = 1, supportsDedicatedMasters = false)
 public class DownsampleTransportFailureIT extends ESIntegTestCase {
 
@@ -222,11 +224,9 @@ public void indexDocuments(final String indexName, final List documentsJ
         assertFalse(bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get().hasFailures());
     }
 
-    public void blockIndexWrites(final String indexName) throws ExecutionException, InterruptedException {
+    public void blockIndexWrites(final String indexName) {
         final Settings blockWritesSetting = Settings.builder().put(IndexMetadata.SETTING_BLOCKS_WRITE, true).build();
-        assertTrue(
-            client().admin().indices().updateSettings(new UpdateSettingsRequest(blockWritesSetting, indexName)).get().isAcknowledged()
-        );
+        assertAcked(client().admin().indices().updateSettings(new UpdateSettingsRequest(blockWritesSetting, indexName)));
     }
 
     private void createTimeSeriesIndex(final String indexName) throws IOException {
diff --git a/x-pack/plugin/downsample/src/test/java/org/elasticsearch/xpack/downsample/DownsampleActionSingleNodeTests.java b/x-pack/plugin/downsample/src/test/java/org/elasticsearch/xpack/downsample/DownsampleActionSingleNodeTests.java
index 1e0f7d596abda..9bf580673df2e 100644
--- a/x-pack/plugin/downsample/src/test/java/org/elasticsearch/xpack/downsample/DownsampleActionSingleNodeTests.java
+++ b/x-pack/plugin/downsample/src/test/java/org/elasticsearch/xpack/downsample/DownsampleActionSingleNodeTests.java
@@ -683,7 +683,6 @@ public void testDownsampleBulkFailed() throws IOException {
             indicesAdmin().preparePutTemplate(downsampleIndex)
                 .setPatterns(List.of(downsampleIndex))
                 .setSettings(Settings.builder().put("index.blocks.write", "true").build())
-                .get()
         );
 
         ElasticsearchException exception = expectThrows(ElasticsearchException.class, indexer::execute);
@@ -1047,14 +1046,12 @@ private void prepareSourceIndex(final String sourceIndex, boolean blockWrite) {
         assertAcked(
             indicesAdmin().prepareUpdateSettings(sourceIndex)
                 .setSettings(Settings.builder().put(IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.getKey(), blockWrite).build())
-                .get()
         );
     }
 
     private void downsample(String sourceIndex, String downsampleIndex, DownsampleConfig config) {
         assertAcked(
             client().execute(DownsampleAction.INSTANCE, new DownsampleAction.Request(sourceIndex, downsampleIndex, TIMEOUT, config))
-                .actionGet()
         );
     }
 
diff --git a/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/AsyncEqlSearchActionIT.java b/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/AsyncEqlSearchActionIT.java
index 194cb9be23c63..3a631c7724d09 100644
--- a/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/AsyncEqlSearchActionIT.java
+++ b/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/AsyncEqlSearchActionIT.java
@@ -85,7 +85,6 @@ private void prepareIndex() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate("test")
                 .setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date", "i", "type=integer")
-                .get()
         );
         createIndex("idx_unmapped");
 
diff --git a/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/EqlCancellationIT.java b/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/EqlCancellationIT.java
index c4e94b9867b08..3ec8d02befb54 100644
--- a/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/EqlCancellationIT.java
+++ b/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/EqlCancellationIT.java
@@ -40,9 +40,7 @@ public void shutdownExec() {
 
     public void testCancellation() throws Exception {
         assertAcked(
-            indicesAdmin().prepareCreate("test")
-                .setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
-                .get()
+            indicesAdmin().prepareCreate("test").setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
         );
         createIndex("idx_unmapped");
 
diff --git a/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/RestEqlCancellationIT.java b/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/RestEqlCancellationIT.java
index 44b75311ee9ca..31f2a4e178c91 100644
--- a/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/RestEqlCancellationIT.java
+++ b/x-pack/plugin/eql/src/internalClusterTest/java/org/elasticsearch/xpack/eql/action/RestEqlCancellationIT.java
@@ -60,9 +60,7 @@ protected Collection> nodePlugins() {
 
     public void testRestCancellation() throws Exception {
         assertAcked(
-            indicesAdmin().prepareCreate("test")
-                .setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
-                .get()
+            indicesAdmin().prepareCreate("test").setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
         );
         createIndex("idx_unmapped");
 
diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java
index 45e3ea5e3fa6e..adf6e1841bab2 100644
--- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java
+++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java
@@ -703,7 +703,6 @@ public void testRefreshSearchIdleShards() throws Exception {
                         .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
                         .put("index.routing.rebalance.enable", "none")
                 )
-                .get()
         );
         ensureYellow(indexName);
         AtomicLong totalValues = new AtomicLong();
@@ -766,7 +765,6 @@ public void testESFilter() throws Exception {
                 .indices()
                 .prepareCreate(indexName)
                 .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, between(1, 5)))
-                .get()
         );
         ensureYellow(indexName);
         int numDocs = randomIntBetween(1, 5000);
@@ -807,7 +805,6 @@ public void testExtractFields() throws Exception {
                 .prepareCreate(indexName)
                 .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, between(1, 5)))
                 .setMapping("val", "type=long", "tag", "type=keyword")
-                .get()
         );
         int numDocs = randomIntBetween(1, 100);
         List indexRequests = new ArrayList<>();
@@ -896,7 +893,6 @@ public void testIndexPatterns() throws Exception {
                     .prepareCreate(indexName)
                     .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, between(1, 5)))
                     .setMapping("data", "type=long", "count", "type=long")
-                    .get()
             );
             ensureYellow(indexName);
             client().prepareBulk()
@@ -957,7 +953,6 @@ public void testOverlappingIndexPatterns() throws Exception {
                 .prepareCreate("test_overlapping_index_patterns_1")
                 .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, between(1, 5)))
                 .setMapping("field", "type=long")
-                .get()
         );
         ensureYellow("test_overlapping_index_patterns_1");
         client().prepareBulk()
@@ -971,7 +966,6 @@ public void testOverlappingIndexPatterns() throws Exception {
                 .prepareCreate("test_overlapping_index_patterns_2")
                 .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, between(1, 5)))
                 .setMapping("field", "type=keyword")
-                .get()
         );
         ensureYellow("test_overlapping_index_patterns_2");
         client().prepareBulk()
@@ -1295,7 +1289,6 @@ private void createNestedMappingIndex(String indexName) throws IOException {
                 .prepareCreate(indexName)
                 .setSettings(Settings.builder().put("index.number_of_shards", ESTestCase.randomIntBetween(1, 3)))
                 .setMapping(builder)
-                .get()
         );
     }
 
@@ -1372,7 +1365,6 @@ private void createAndPopulateIndex(String indexName, Settings additionalSetting
                     "color",
                     "type=keyword"
                 )
-                .get()
         );
         long timestamp = epoch;
         for (int i = 0; i < 10; i++) {
diff --git a/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java b/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java
index d2caed465d6e3..0b1693e0c3712 100644
--- a/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java
+++ b/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexIT.java
@@ -89,9 +89,7 @@ public void testTimestampRangeRecalculatedOnStalePrimaryAllocation() throws IOEx
 
         assertThat(client().prepareDelete("index", indexResponse.getId()).get().status(), equalTo(RestStatus.OK));
 
-        assertAcked(
-            client().execute(FreezeIndexAction.INSTANCE, new FreezeRequest("index").waitForActiveShards(ActiveShardCount.ONE)).actionGet()
-        );
+        assertAcked(client().execute(FreezeIndexAction.INSTANCE, new FreezeRequest("index").waitForActiveShards(ActiveShardCount.ONE)));
 
         assertThat(
             clusterAdmin().prepareState().get().getState().metadata().index("index").getTimestampRange(),
diff --git a/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexTests.java b/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexTests.java
index a4ec5222ffcc2..6b028e5ea0815 100644
--- a/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexTests.java
+++ b/x-pack/plugin/frozen-indices/src/internalClusterTest/java/org/elasticsearch/index/engine/frozen/FrozenIndexTests.java
@@ -287,7 +287,7 @@ public void testUnfreezeClosedIndices() {
             client().execute(
                 FreezeIndexAction.INSTANCE,
                 new FreezeRequest("idx*").setFreeze(false).indicesOptions(IndicesOptions.strictExpand())
-            ).actionGet()
+            )
         );
         ClusterStateResponse stateResponse = clusterAdmin().prepareState().get();
         assertEquals(IndexMetadata.State.CLOSE, stateResponse.getState().getMetadata().index("idx-closed").getState());
@@ -479,7 +479,7 @@ public void testIgnoreUnavailable() {
                 new FreezeRequest("idx*", "not_available").indicesOptions(
                     IndicesOptions.fromParameters(null, "true", null, null, IndicesOptions.strictExpandOpen())
                 )
-            ).actionGet()
+            )
         );
         assertIndexFrozen("idx");
         assertEquals(IndexMetadata.State.CLOSE, clusterAdmin().prepareState().get().getState().metadata().index("idx-close").getState());
diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java
index bd39393d7f338..ee1ce56528361 100644
--- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java
+++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java
@@ -427,7 +427,6 @@ public void testRequestCacheOnFrozen() throws Exception {
             indicesAdmin().prepareCreate("test-index")
                 .setMapping("f", "type=date")
                 .setSettings(indexSettings(1, 0).put(IndicesRequestCache.INDEX_CACHE_REQUEST_ENABLED_SETTING.getKey(), true))
-                .get()
         );
         indexRandom(
             true,
diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DataStreamSecurityIT.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DataStreamSecurityIT.java
index d711fe4639fe6..58d33fc221b21 100644
--- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DataStreamSecurityIT.java
+++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DataStreamSecurityIT.java
@@ -135,7 +135,7 @@ public void onFailure(Exception e) {
             client.execute(
                 ModifyDataStreamsAction.INSTANCE,
                 new ModifyDataStreamsAction.Request(List.of(DataStreamAction.removeBackingIndex(dataStreamName, ghostReference.getName())))
-            ).actionGet()
+            )
         );
         ClusterState after = internalCluster().getCurrentMasterNodeInstance(ClusterService.class).state();
         assertThat(after.getMetadata().dataStreams().get(dataStreamName).getIndices(), hasSize(1));
diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DlsFlsRequestCacheTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DlsFlsRequestCacheTests.java
index 301b264308af0..7c33c69460768 100644
--- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DlsFlsRequestCacheTests.java
+++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/DlsFlsRequestCacheTests.java
@@ -386,18 +386,8 @@ public void testRequestCacheWithTemplateRoleQuery() {
     private void prepareIndices() {
         final Client client = client();
 
-        assertAcked(
-            client.admin()
-                .cluster()
-                .preparePutStoredScript()
-                .setId("my-script")
-                .setContent(
-                    new BytesArray("""
-                        {"script":{"source":"{\\"match\\":{\\"username\\":\\"{{_user.username}}\\"}}","lang":"mustache"}}"""),
-                    XContentType.JSON
-                )
-                .get()
-        );
+        assertAcked(client.admin().cluster().preparePutStoredScript().setId("my-script").setContent(new BytesArray("""
+            {"script":{"source":"{\\"match\\":{\\"username\\":\\"{{_user.username}}\\"}}","lang":"mustache"}}"""), XContentType.JSON));
 
         assertAcked(indicesAdmin().prepareCreate(DLS_INDEX).addAlias(new Alias("dls-alias")).get());
         client.prepareIndex(DLS_INDEX).setId("101").setSource("number", 101, "letter", "A").get();
@@ -408,7 +398,7 @@ private void prepareIndices() {
         client.prepareIndex(FLS_INDEX).setId("202").setSource("public", "Y", "private", "y").get();
 
         assertAcked(
-            indicesAdmin().prepareCreate(INDEX).addAlias(new Alias(ALIAS1)).addAlias(new Alias(ALIAS2)).addAlias(new Alias(ALL_ALIAS)).get()
+            indicesAdmin().prepareCreate(INDEX).addAlias(new Alias(ALIAS1)).addAlias(new Alias(ALIAS2)).addAlias(new Alias(ALL_ALIAS))
         );
         client.prepareIndex(INDEX).setId("1").setSource("number", 1, "letter", "a", "private", "sesame_1", "public", "door_1").get();
         client.prepareIndex(INDEX).setId("2").setSource("number", 2, "letter", "b", "private", "sesame_2", "public", "door_2").get();
diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/IndexAliasesTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/IndexAliasesTests.java
index 5418bbf7f8dd9..f7bc8a1770981 100644
--- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/IndexAliasesTests.java
+++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authz/IndexAliasesTests.java
@@ -351,7 +351,6 @@ public void testDeleteAliasesCreateAndAliasesPermission() {
                 .addAlias(new Alias("test_alias_2"))
                 .addAlias(new Alias("test_alias_3"))
                 .addAlias(new Alias("test_alias_4"))
-                .get()
         );
         // ok: user has manage_aliases on test_*
         assertAcked(client.admin().indices().prepareAliases().removeAlias("test_1", "test_alias_1").get());
@@ -824,11 +823,7 @@ public void testAliasesForHiddenIndices() {
         final Client aliasesClient = client(aliasHeaders);
 
         assertAcked(
-            createClient.admin()
-                .indices()
-                .prepareCreate(hiddenIndex)
-                .setSettings(Settings.builder().put("index.hidden", true).build())
-                .get()
+            createClient.admin().indices().prepareCreate(hiddenIndex).setSettings(Settings.builder().put("index.hidden", true).build())
         );
 
         assertAcked(
diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/support/SecurityIndexManagerIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/support/SecurityIndexManagerIntegTests.java
index 263480b9cb8e7..4705361e51dbd 100644
--- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/support/SecurityIndexManagerIntegTests.java
+++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/support/SecurityIndexManagerIntegTests.java
@@ -123,7 +123,6 @@ public void testSecurityIndexSettingsCannotBeChanged() throws Exception {
                         .put("index.number_of_replicas", "8")
                         .build()
                 )
-                .get()
         );
         // create an new-style template
         ComposableIndexTemplate cit = new ComposableIndexTemplate(
@@ -146,7 +145,7 @@ public void testSecurityIndexSettingsCannotBeChanged() throws Exception {
             client().execute(
                 PutComposableIndexTemplateAction.INSTANCE,
                 new PutComposableIndexTemplateAction.Request("composable-template-covering-the-main-security-index").indexTemplate(cit)
-            ).get()
+            )
         );
         // trigger index auto-creation
         final PutUserResponse putUserResponse = new PutUserRequestBuilder(client()).username("user")
diff --git a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownShardsIT.java b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownShardsIT.java
index ec5b25b21da32..4e2b1bd6c5a58 100644
--- a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownShardsIT.java
+++ b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownShardsIT.java
@@ -461,7 +461,7 @@ private void putNodeShutdown(String nodeId, SingleNodeShutdownMetadata.Type type
             client().execute(
                 PutShutdownNodeAction.INSTANCE,
                 new PutShutdownNodeAction.Request(nodeId, type, this.getTestName(), null, nodeReplacementName, null)
-            ).get()
+            )
         );
     }
 
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
index 1a81a8a8c7604..e354feb60c95f 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/GeoShapeWithDocValuesIT.java
@@ -71,9 +71,7 @@ protected boolean allowExpensiveQueries() {
     public void testMappingUpdate() {
         // create index
         IndexVersion version = randomSupportedVersion();
-        assertAcked(
-            indicesAdmin().prepareCreate("test").setSettings(settings(version).build()).setMapping("shape", "type=geo_shape").get()
-        );
+        assertAcked(indicesAdmin().prepareCreate("test").setSettings(settings(version).build()).setMapping("shape", "type=geo_shape"));
         ensureGreen();
 
         String update = """
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java
index 859121650c1d9..562f2fd681d97 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/LegacyGeoShapeWithDocValuesIT.java
@@ -57,7 +57,6 @@ public void testMappingUpdate() {
             indicesAdmin().prepareCreate("test")
                 .setSettings(settings(randomSupportedVersion()).build())
                 .setMapping("shape", "type=geo_shape,strategy=recursive")
-                .get()
         );
         ensureGreen();
 
@@ -85,7 +84,6 @@ public void testLegacyCircle() throws Exception {
         assertAcked(
             prepareCreate("test").setSettings(settings(randomSupportedVersion()).build())
                 .setMapping("shape", "type=geo_shape,strategy=recursive,tree=geohash")
-                .get()
         );
         ensureGreen();
 
diff --git a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java
index 5d8a35b3f6165..0abf475e59048 100644
--- a/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java
+++ b/x-pack/plugin/spatial/src/internalClusterTest/java/org/elasticsearch/xpack/spatial/search/ShapeQueryOverShapeTests.java
@@ -73,7 +73,6 @@ public void setUp() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate(IGNORE_MALFORMED_INDEX)
                 .setMapping(FIELD, "type=shape,ignore_malformed=true", "_source", "enabled=false")
-                .get()
         );
         ensureGreen();
 
diff --git a/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/AsyncSqlSearchActionIT.java b/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/AsyncSqlSearchActionIT.java
index af14866011c05..7bffa67fe2a52 100644
--- a/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/AsyncSqlSearchActionIT.java
+++ b/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/AsyncSqlSearchActionIT.java
@@ -81,7 +81,6 @@ private void prepareIndex() throws Exception {
         assertAcked(
             indicesAdmin().prepareCreate("test")
                 .setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date", "i", "type=integer")
-                .get()
         );
         createIndex("idx_unmapped");
 
diff --git a/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/RestSqlCancellationIT.java b/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/RestSqlCancellationIT.java
index 52a756786f00e..0100634766bfe 100644
--- a/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/RestSqlCancellationIT.java
+++ b/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/RestSqlCancellationIT.java
@@ -73,9 +73,7 @@ protected Collection> nodePlugins() {
     @TestLogging(value = "org.elasticsearch.xpack.sql:TRACE", reason = "debug")
     public void testRestCancellation() throws Exception {
         assertAcked(
-            indicesAdmin().prepareCreate("test")
-                .setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
-                .get()
+            indicesAdmin().prepareCreate("test").setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
         );
         createIndex("idx_unmapped");
 
diff --git a/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/SqlCancellationIT.java b/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/SqlCancellationIT.java
index bd80543a26df3..aba659de53874 100644
--- a/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/SqlCancellationIT.java
+++ b/x-pack/plugin/sql/src/internalClusterTest/java/org/elasticsearch/xpack/sql/action/SqlCancellationIT.java
@@ -39,9 +39,7 @@ public void shutdownExec() {
 
     public void testCancellation() throws Exception {
         assertAcked(
-            indicesAdmin().prepareCreate("test")
-                .setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
-                .get()
+            indicesAdmin().prepareCreate("test").setMapping("val", "type=integer", "event_type", "type=keyword", "@timestamp", "type=date")
         );
         createIndex("idx_unmapped");
 
diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java
index 33852bca07247..93741f8e48ea5 100644
--- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java
+++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java
@@ -274,11 +274,7 @@ public void replaceWatcherIndexWithRandomlyNamedIndex(String originalIndexOrAlia
         newSettings.remove("index.creation_date");
         newSettings.remove("index.version.created");
 
-        CreateIndexResponse createIndexResponse = indicesAdmin().prepareCreate(to)
-            .setMapping(mapping.sourceAsMap())
-            .setSettings(newSettings)
-            .get();
-        assertTrue(createIndexResponse.isAcknowledged());
+        assertAcked(indicesAdmin().prepareCreate(to).setMapping(mapping.sourceAsMap()).setSettings(newSettings));
         ensureGreen(to);
 
         AtomicReference originalIndex = new AtomicReference<>(originalIndexOrAlias);
diff --git a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java
index 2d64fcb7bc833..5f572b3646365 100644
--- a/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java
+++ b/x-pack/plugin/watcher/src/internalClusterTest/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java
@@ -236,7 +236,6 @@ public void testConditionSearchWithIndexedTemplate() throws Exception {
                     ),
                     XContentType.JSON
                 )
-                .get()
         );
 
         Script template = new Script(ScriptType.STORED, null, "my-template", Collections.emptyMap());
diff --git a/x-pack/plugin/write-load-forecaster/src/internalClusterTest/java/org/elasticsearch/xpack/writeloadforecaster/WriteLoadForecasterIT.java b/x-pack/plugin/write-load-forecaster/src/internalClusterTest/java/org/elasticsearch/xpack/writeloadforecaster/WriteLoadForecasterIT.java
index 7a264d4b71ed5..39d9b7cef1d32 100644
--- a/x-pack/plugin/write-load-forecaster/src/internalClusterTest/java/org/elasticsearch/xpack/writeloadforecaster/WriteLoadForecasterIT.java
+++ b/x-pack/plugin/write-load-forecaster/src/internalClusterTest/java/org/elasticsearch/xpack/writeloadforecaster/WriteLoadForecasterIT.java
@@ -164,7 +164,7 @@ private void setUpDataStreamWriteDocsAndRollover(String dataStreamName, Settings
                         null
                     )
                 )
-            ).actionGet()
+            )
         );
         assertAcked(client().execute(CreateDataStreamAction.INSTANCE, new CreateDataStreamAction.Request(dataStreamName)).actionGet());
 

From d8b2c52c82b533c400c76cb181fdb31d32d56847 Mon Sep 17 00:00:00 2001
From: Stuart Tettemer 
Date: Mon, 23 Oct 2023 13:28:46 -0500
Subject: [PATCH 064/203] Metrics refactor - split registry and service
 (#101154)

This splits out the registry and the service, which makes testing easier and removes much of the delegation from the old `APMMeter` to `Instruments` (now renamed `APMMeterRegistry`).

APMMeterService takes care of the lifecycle and APMMeterRegistry holds the instruments.
---
 .../org/elasticsearch/telemetry/apm/APM.java  |   7 +-
 ...Instruments.java => APMMeterRegistry.java} |  35 ++--
 .../metrics => }/AbstractInstrument.java      |   8 +-
 .../apm/internal/APMAgentSettings.java        |  10 +-
 .../apm/internal/APMMeterService.java         |  93 +++++++++
 .../apm/internal/APMTelemetryProvider.java    |  10 +-
 .../apm/internal/metrics/APMMeter.java        | 180 ------------------
 .../metrics/DoubleCounterAdapter.java         |   9 +-
 .../internal/metrics/DoubleGaugeAdapter.java  |   7 +-
 .../metrics/DoubleHistogramAdapter.java       |   7 +-
 .../metrics/DoubleUpDownCounterAdapter.java   |   7 +-
 .../internal/metrics/LongCounterAdapter.java  |   9 +-
 .../internal/metrics/LongGaugeAdapter.java    |  10 +-
 .../metrics/LongHistogramAdapter.java         |   9 +-
 .../metrics/LongUpDownCounterAdapter.java     |   7 +-
 ...rTests.java => APMMeterRegistryTests.java} |  32 ++--
 ...ava => MeterRegistryConcurrencyTests.java} |  14 +-
 .../apm/internal/TestAPMMeterService.java     |  26 +++
 .../internal/metrics/InstrumentsTests.java    |  77 --------
 .../repositories/azure/AzureRepository.java   |   4 +-
 .../gcs/GoogleCloudStorageRepository.java     |   4 +-
 .../s3/S3BlobStoreRepositoryTests.java        |  12 +-
 .../s3/S3RepositoryThirdPartyTests.java       |   4 +-
 .../repositories/s3/S3BlobStore.java          |  10 +-
 .../repositories/s3/S3Repository.java         |   8 +-
 .../repositories/s3/S3RepositoryPlugin.java   |  12 +-
 .../s3/RepositoryCredentialsTests.java        |   4 +-
 .../s3/S3BlobContainerRetriesTests.java       |   4 +-
 .../repositories/s3/S3RepositoryTests.java    |   4 +-
 .../repositories/RepositoriesModule.java      |   2 +-
 .../blobstore/MeteredBlobStoreRepository.java |   8 +-
 .../telemetry/TelemetryProvider.java          |   8 +-
 .../metric/{Meter.java => MeterRegistry.java} |   4 +-
 .../RepositoriesServiceTests.java             |   6 +-
 ...eter.java => DelegatingMeterRegistry.java} |   8 +-
 35 files changed, 274 insertions(+), 385 deletions(-)
 rename modules/apm/src/main/java/org/elasticsearch/telemetry/apm/{internal/metrics/Instruments.java => APMMeterRegistry.java} (80%)
 rename modules/apm/src/main/java/org/elasticsearch/telemetry/apm/{internal/metrics => }/AbstractInstrument.java (92%)
 create mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMMeterService.java
 delete mode 100644 modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java
 rename modules/apm/src/test/java/org/elasticsearch/telemetry/apm/{internal/metrics/APMMeterTests.java => APMMeterRegistryTests.java} (65%)
 rename modules/apm/src/test/java/org/elasticsearch/telemetry/apm/{internal/metrics/InstrumentsConcurrencyTests.java => MeterRegistryConcurrencyTests.java} (86%)
 create mode 100644 modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/TestAPMMeterService.java
 delete mode 100644 modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java
 rename server/src/main/java/org/elasticsearch/telemetry/metric/{Meter.java => MeterRegistry.java} (98%)
 rename test/framework/src/main/java/org/elasticsearch/telemetry/{DelegatingMeter.java => DelegatingMeterRegistry.java} (93%)

diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java
index 935c4958ba3d7..80e77a78ac011 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java
@@ -26,8 +26,8 @@
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.telemetry.TelemetryProvider;
 import org.elasticsearch.telemetry.apm.internal.APMAgentSettings;
+import org.elasticsearch.telemetry.apm.internal.APMMeterService;
 import org.elasticsearch.telemetry.apm.internal.APMTelemetryProvider;
-import org.elasticsearch.telemetry.apm.internal.metrics.APMMeter;
 import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.watcher.ResourceWatcherService;
@@ -96,9 +96,8 @@ public Collection createComponents(
 
         final APMAgentSettings apmAgentSettings = new APMAgentSettings();
         apmAgentSettings.syncAgentSystemProperties(settings);
-        apmAgentSettings.addClusterSettingsListeners(clusterService, telemetryProvider.get());
-
-        final APMMeter apmMeter = telemetryProvider.get().getMeter();
+        final APMMeterService apmMeter = new APMMeterService(settings);
+        apmAgentSettings.addClusterSettingsListeners(clusterService, telemetryProvider.get(), apmMeter);
 
         return List.of(apmTracer, apmMeter);
     }
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java
similarity index 80%
rename from modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java
rename to modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java
index 92d7d692f0ea5..ac3ff7f28a4cf 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/Instruments.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java
@@ -6,12 +6,20 @@
  * Side Public License, v 1.
  */
 
-package org.elasticsearch.telemetry.apm.internal.metrics;
+package org.elasticsearch.telemetry.apm;
 
 import io.opentelemetry.api.metrics.Meter;
 
 import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
 import org.elasticsearch.common.util.concurrent.ReleasableLock;
+import org.elasticsearch.telemetry.apm.internal.metrics.DoubleCounterAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.DoubleGaugeAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.DoubleHistogramAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.DoubleUpDownCounterAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.LongCounterAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.LongGaugeAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.LongHistogramAdapter;
+import org.elasticsearch.telemetry.apm.internal.metrics.LongUpDownCounterAdapter;
 import org.elasticsearch.telemetry.metric.DoubleCounter;
 import org.elasticsearch.telemetry.metric.DoubleGauge;
 import org.elasticsearch.telemetry.metric.DoubleHistogram;
@@ -20,17 +28,18 @@
 import org.elasticsearch.telemetry.metric.LongGauge;
 import org.elasticsearch.telemetry.metric.LongHistogram;
 import org.elasticsearch.telemetry.metric.LongUpDownCounter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * Container for registering and fetching instruments by type and name.
+ * Container for registering and fetching meterRegistrar by type and name.
  * Instrument names must be unique for a given type on registration.
- * {@link #setProvider(Meter)} is used to change the provider for all existing instruments.
+ * {@link #setProvider(Meter)} is used to change the provider for all existing meterRegistrar.
  */
-public class Instruments {
+public class APMMeterRegistry implements MeterRegistry {
     private final Registrar doubleCounters = new Registrar<>();
     private final Registrar doubleUpDownCounters = new Registrar<>();
     private final Registrar doubleGauges = new Registrar<>();
@@ -42,7 +51,7 @@ public class Instruments {
 
     private final Meter meter;
 
-    public Instruments(Meter meter) {
+    public APMMeterRegistry(Meter meter) {
         this.meter = meter;
     }
 
@@ -60,7 +69,7 @@ public Instruments(Meter meter) {
     // Access to registration has to be restricted when the provider is updated in ::setProvider
     protected final ReleasableLock registerLock = new ReleasableLock(new ReentrantLock());
 
-    public  DoubleCounter registerDoubleCounter(String name, String description, String unit) {
+    public DoubleCounter registerDoubleCounter(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return doubleCounters.register(new DoubleCounterAdapter(meter, name, description, unit));
         }
@@ -70,7 +79,7 @@ public DoubleCounter getDoubleCounter(String name) {
         return doubleCounters.get(name);
     }
 
-    public  DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) {
+    public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return doubleUpDownCounters.register(new DoubleUpDownCounterAdapter(meter, name, description, unit));
         }
@@ -80,7 +89,7 @@ public DoubleUpDownCounter getDoubleUpDownCounter(String name) {
         return doubleUpDownCounters.get(name);
     }
 
-    public  DoubleGauge registerDoubleGauge(String name, String description, String unit) {
+    public DoubleGauge registerDoubleGauge(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return doubleGauges.register(new DoubleGaugeAdapter(meter, name, description, unit));
         }
@@ -90,7 +99,7 @@ public DoubleGauge getDoubleGauge(String name) {
         return doubleGauges.get(name);
     }
 
-    public  DoubleHistogram registerDoubleHistogram(String name, String description, String unit) {
+    public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return doubleHistograms.register(new DoubleHistogramAdapter(meter, name, description, unit));
         }
@@ -100,7 +109,7 @@ public DoubleHistogram getDoubleHistogram(String name) {
         return doubleHistograms.get(name);
     }
 
-    public  LongCounter registerLongCounter(String name, String description, String unit) {
+    public LongCounter registerLongCounter(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return longCounters.register(new LongCounterAdapter(meter, name, description, unit));
         }
@@ -110,7 +119,7 @@ public LongCounter getLongCounter(String name) {
         return longCounters.get(name);
     }
 
-    public  LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) {
+    public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return longUpDownCounters.register(new LongUpDownCounterAdapter(meter, name, description, unit));
         }
@@ -120,7 +129,7 @@ public LongUpDownCounter getLongUpDownCounter(String name) {
         return longUpDownCounters.get(name);
     }
 
-    public  LongGauge registerLongGauge(String name, String description, String unit) {
+    public LongGauge registerLongGauge(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return longGauges.register(new LongGaugeAdapter(meter, name, description, unit));
         }
@@ -130,7 +139,7 @@ public LongGauge getLongGauge(String name) {
         return longGauges.get(name);
     }
 
-    public  LongHistogram registerLongHistogram(String name, String description, String unit) {
+    public LongHistogram registerLongHistogram(String name, String description, String unit) {
         try (ReleasableLock lock = registerLock.acquire()) {
             return longHistograms.register(new LongHistogramAdapter(meter, name, description, unit));
         }
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/AbstractInstrument.java
similarity index 92%
rename from modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java
rename to modules/apm/src/main/java/org/elasticsearch/telemetry/apm/AbstractInstrument.java
index d3d485f52bc49..01f65eb60aa74 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/AbstractInstrument.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/AbstractInstrument.java
@@ -6,7 +6,7 @@
  * Side Public License, v 1.
  */
 
-package org.elasticsearch.telemetry.apm.internal.metrics;
+package org.elasticsearch.telemetry.apm;
 
 import io.opentelemetry.api.metrics.Meter;
 
@@ -50,11 +50,11 @@ public String getUnit() {
         return unit.toString();
     }
 
-    T getInstrument() {
+    protected T getInstrument() {
         return delegate.get();
     }
 
-    String getDescription() {
+    protected String getDescription() {
         return description;
     }
 
@@ -62,5 +62,5 @@ void setProvider(@Nullable Meter meter) {
         delegate.set(doBuildInstrument(Objects.requireNonNull(meter)));
     }
 
-    abstract T buildInstrument(Meter meter);
+    protected abstract T buildInstrument(Meter meter);
 }
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java
index e4a194ebe0172..41816318a3586 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMAgentSettings.java
@@ -17,7 +17,6 @@
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.core.SuppressForbidden;
-import org.elasticsearch.telemetry.apm.internal.metrics.APMMeter;
 import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer;
 
 import java.security.AccessController;
@@ -48,17 +47,20 @@ public class APMAgentSettings {
         "true"
     );
 
-    public void addClusterSettingsListeners(ClusterService clusterService, APMTelemetryProvider apmTelemetryProvider) {
+    public void addClusterSettingsListeners(
+        ClusterService clusterService,
+        APMTelemetryProvider apmTelemetryProvider,
+        APMMeterService apmMeterService
+    ) {
         final ClusterSettings clusterSettings = clusterService.getClusterSettings();
         final APMTracer apmTracer = apmTelemetryProvider.getTracer();
-        final APMMeter apmMeter = apmTelemetryProvider.getMeter();
 
         clusterSettings.addSettingsUpdateConsumer(APM_ENABLED_SETTING, enabled -> {
             apmTracer.setEnabled(enabled);
             this.setAgentSetting("instrument", Boolean.toString(enabled));
         });
         clusterSettings.addSettingsUpdateConsumer(TELEMETRY_METRICS_ENABLED_SETTING, enabled -> {
-            apmMeter.setEnabled(enabled);
+            apmMeterService.setEnabled(enabled);
             // The agent records data other than spans, e.g. JVM metrics, so we toggle this setting in order to
             // minimise its impact to a running Elasticsearch.
             this.setAgentSetting("recording", Boolean.toString(enabled));
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMMeterService.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMMeterService.java
new file mode 100644
index 0000000000000..21f0b8491f644
--- /dev/null
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMMeterService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.telemetry.apm.internal;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.metrics.Meter;
+
+import org.elasticsearch.cluster.service.ClusterService;
+import org.elasticsearch.common.component.AbstractLifecycleComponent;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.telemetry.apm.APMMeterRegistry;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.function.Supplier;
+
+public class APMMeterService extends AbstractLifecycleComponent {
+    private final APMMeterRegistry meterRegistry;
+
+    private final Supplier otelMeterSupplier;
+    private final Supplier noopMeterSupplier;
+
+    protected volatile boolean enabled;
+
+    public APMMeterService(Settings settings) {
+        this(settings, APMMeterService.otelMeter(), APMMeterService.noopMeter());
+    }
+
+    public APMMeterService(Settings settings, Supplier otelMeterSupplier, Supplier noopMeterSupplier) {
+        this(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.get(settings), otelMeterSupplier, noopMeterSupplier);
+    }
+
+    public APMMeterService(boolean enabled, Supplier otelMeterSupplier, Supplier noopMeterSupplier) {
+        this.enabled = enabled;
+        this.otelMeterSupplier = otelMeterSupplier;
+        this.noopMeterSupplier = noopMeterSupplier;
+        this.meterRegistry = new APMMeterRegistry(enabled ? createOtelMeter() : createNoopMeter());
+    }
+
+    public APMMeterRegistry getMeterRegistry() {
+        return meterRegistry;
+    }
+
+    /**
+     * @see APMAgentSettings#addClusterSettingsListeners(ClusterService, APMTelemetryProvider, APMMeterService)
+     */
+    void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+        if (enabled) {
+            meterRegistry.setProvider(createOtelMeter());
+        } else {
+            meterRegistry.setProvider(createNoopMeter());
+        }
+    }
+
+    @Override
+    protected void doStart() {}
+
+    @Override
+    protected void doStop() {
+        meterRegistry.setProvider(createNoopMeter());
+    }
+
+    @Override
+    protected void doClose() {}
+
+    protected Meter createOtelMeter() {
+        assert this.enabled;
+        return AccessController.doPrivileged((PrivilegedAction) otelMeterSupplier::get);
+    }
+
+    protected Meter createNoopMeter() {
+        return noopMeterSupplier.get();
+    }
+
+    protected static Supplier noopMeter() {
+        return () -> OpenTelemetry.noop().getMeter("noop");
+    }
+
+    // to be used within doPrivileged block
+    private static Supplier otelMeter() {
+        var openTelemetry = GlobalOpenTelemetry.get();
+        var meter = openTelemetry.getMeter("elasticsearch");
+        return () -> meter;
+    }
+}
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java
index ae9d91cc6ec51..5b78c2f5f6a3c 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/APMTelemetryProvider.java
@@ -10,18 +10,18 @@
 
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.telemetry.TelemetryProvider;
-import org.elasticsearch.telemetry.apm.internal.metrics.APMMeter;
+import org.elasticsearch.telemetry.apm.APMMeterRegistry;
 import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer;
 
 public class APMTelemetryProvider implements TelemetryProvider {
     private final Settings settings;
     private final APMTracer apmTracer;
-    private final APMMeter apmMeter;
+    private final APMMeterService apmMeterService;
 
     public APMTelemetryProvider(Settings settings) {
         this.settings = settings;
         apmTracer = new APMTracer(settings);
-        apmMeter = new APMMeter(settings);
+        apmMeterService = new APMMeterService(settings);
     }
 
     @Override
@@ -30,7 +30,7 @@ public APMTracer getTracer() {
     }
 
     @Override
-    public APMMeter getMeter() {
-        return apmMeter;
+    public APMMeterRegistry getMeterRegistry() {
+        return apmMeterService.getMeterRegistry();
     }
 }
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java
deleted file mode 100644
index 0a8d425579ca2..0000000000000
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeter.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-package org.elasticsearch.telemetry.apm.internal.metrics;
-
-import io.opentelemetry.api.GlobalOpenTelemetry;
-import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.api.metrics.Meter;
-
-import org.elasticsearch.cluster.service.ClusterService;
-import org.elasticsearch.common.component.AbstractLifecycleComponent;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.telemetry.apm.internal.APMTelemetryProvider;
-import org.elasticsearch.telemetry.metric.DoubleCounter;
-import org.elasticsearch.telemetry.metric.DoubleGauge;
-import org.elasticsearch.telemetry.metric.DoubleHistogram;
-import org.elasticsearch.telemetry.metric.DoubleUpDownCounter;
-import org.elasticsearch.telemetry.metric.LongCounter;
-import org.elasticsearch.telemetry.metric.LongGauge;
-import org.elasticsearch.telemetry.metric.LongHistogram;
-import org.elasticsearch.telemetry.metric.LongUpDownCounter;
-
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.function.Supplier;
-
-import static org.elasticsearch.telemetry.apm.internal.APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING;
-
-public class APMMeter extends AbstractLifecycleComponent implements org.elasticsearch.telemetry.metric.Meter {
-    private final Instruments instruments;
-
-    private final Supplier otelMeterSupplier;
-    private final Supplier noopMeterSupplier;
-
-    private volatile boolean enabled;
-
-    public APMMeter(Settings settings) {
-        this(settings, APMMeter.otelMeter(), APMMeter.noopMeter());
-    }
-
-    public APMMeter(Settings settings, Supplier otelMeterSupplier, Supplier noopMeterSupplier) {
-        this.enabled = TELEMETRY_METRICS_ENABLED_SETTING.get(settings);
-        this.otelMeterSupplier = otelMeterSupplier;
-        this.noopMeterSupplier = noopMeterSupplier;
-        this.instruments = new Instruments(enabled ? createOtelMeter() : createNoopMeter());
-    }
-
-    /**
-     * @see org.elasticsearch.telemetry.apm.internal.APMAgentSettings#addClusterSettingsListeners(ClusterService, APMTelemetryProvider)
-     */
-    public void setEnabled(boolean enabled) {
-        this.enabled = enabled;
-        if (enabled) {
-            instruments.setProvider(createOtelMeter());
-        } else {
-            instruments.setProvider(createNoopMeter());
-        }
-    }
-
-    @Override
-    protected void doStart() {}
-
-    @Override
-    protected void doStop() {
-        instruments.setProvider(createNoopMeter());
-    }
-
-    @Override
-    protected void doClose() {}
-
-    @Override
-    public DoubleCounter registerDoubleCounter(String name, String description, String unit) {
-        return instruments.registerDoubleCounter(name, description, unit);
-    }
-
-    @Override
-    public DoubleCounter getDoubleCounter(String name) {
-        return instruments.getDoubleCounter(name);
-    }
-
-    @Override
-    public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) {
-        return instruments.registerDoubleUpDownCounter(name, description, unit);
-    }
-
-    @Override
-    public DoubleUpDownCounter getDoubleUpDownCounter(String name) {
-        return instruments.getDoubleUpDownCounter(name);
-    }
-
-    @Override
-    public DoubleGauge registerDoubleGauge(String name, String description, String unit) {
-        return instruments.registerDoubleGauge(name, description, unit);
-    }
-
-    @Override
-    public DoubleGauge getDoubleGauge(String name) {
-        return instruments.getDoubleGauge(name);
-    }
-
-    @Override
-    public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) {
-        return instruments.registerDoubleHistogram(name, description, unit);
-    }
-
-    @Override
-    public DoubleHistogram getDoubleHistogram(String name) {
-        return instruments.getDoubleHistogram(name);
-    }
-
-    @Override
-    public LongCounter registerLongCounter(String name, String description, String unit) {
-        return instruments.registerLongCounter(name, description, unit);
-    }
-
-    @Override
-    public LongCounter getLongCounter(String name) {
-        return instruments.getLongCounter(name);
-    }
-
-    @Override
-    public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) {
-        return instruments.registerLongUpDownCounter(name, description, unit);
-    }
-
-    @Override
-    public LongUpDownCounter getLongUpDownCounter(String name) {
-        return instruments.getLongUpDownCounter(name);
-    }
-
-    @Override
-    public LongGauge registerLongGauge(String name, String description, String unit) {
-        return instruments.registerLongGauge(name, description, unit);
-    }
-
-    @Override
-    public LongGauge getLongGauge(String name) {
-        return instruments.getLongGauge(name);
-    }
-
-    @Override
-    public LongHistogram registerLongHistogram(String name, String description, String unit) {
-        return instruments.registerLongHistogram(name, description, unit);
-    }
-
-    @Override
-    public LongHistogram getLongHistogram(String name) {
-        return instruments.getLongHistogram(name);
-    }
-
-    Meter createOtelMeter() {
-        assert this.enabled;
-        return AccessController.doPrivileged((PrivilegedAction) otelMeterSupplier::get);
-    }
-
-    private Meter createNoopMeter() {
-        return noopMeterSupplier.get();
-    }
-
-    private static Supplier noopMeter() {
-        return () -> OpenTelemetry.noop().getMeter("noop");
-    }
-
-    // to be used within doPrivileged block
-    private static Supplier otelMeter() {
-        var openTelemetry = GlobalOpenTelemetry.get();
-        var meter = openTelemetry.getMeter("elasticsearch");
-        return () -> meter;
-    }
-
-    // scope for testing
-    Instruments getInstruments() {
-        return instruments;
-    }
-}
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java
index b25ffdff5481b..faba8c2e3e67e 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleCounterAdapter.java
@@ -8,23 +8,24 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
+import io.opentelemetry.api.metrics.DoubleCounter;
 import io.opentelemetry.api.metrics.Meter;
 
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
+
 import java.util.Map;
 import java.util.Objects;
 
 /**
  * DoubleGaugeAdapter wraps an otel ObservableDoubleMeasurement
  */
-public class DoubleCounterAdapter extends AbstractInstrument
-    implements
-        org.elasticsearch.telemetry.metric.DoubleCounter {
+public class DoubleCounterAdapter extends AbstractInstrument implements org.elasticsearch.telemetry.metric.DoubleCounter {
 
     public DoubleCounterAdapter(Meter meter, String name, String description, String unit) {
         super(meter, name, description, unit);
     }
 
-    io.opentelemetry.api.metrics.DoubleCounter buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.DoubleCounter buildInstrument(Meter meter) {
         return Objects.requireNonNull(meter)
             .counterBuilder(getName())
             .ofDoubles()
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java
index 54f33be21698b..727c8c75a6f9b 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java
@@ -9,6 +9,9 @@
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
 import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableDoubleGauge;
+
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
 
 import java.util.Collections;
 import java.util.Map;
@@ -18,7 +21,7 @@
 /**
  * DoubleGaugeAdapter wraps an otel ObservableDoubleMeasurement
  */
-public class DoubleGaugeAdapter extends AbstractInstrument
+public class DoubleGaugeAdapter extends AbstractInstrument
     implements
         org.elasticsearch.telemetry.metric.DoubleGauge {
 
@@ -30,7 +33,7 @@ public DoubleGaugeAdapter(Meter meter, String name, String description, String u
     }
 
     @Override
-    io.opentelemetry.api.metrics.ObservableDoubleGauge buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.ObservableDoubleGauge buildInstrument(Meter meter) {
         return Objects.requireNonNull(meter)
             .gaugeBuilder(getName())
             .setDescription(getDescription())
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java
index 5fd1a8a189b0f..e126aa6af7cf0 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleHistogramAdapter.java
@@ -8,15 +8,18 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
+import io.opentelemetry.api.metrics.DoubleHistogram;
 import io.opentelemetry.api.metrics.Meter;
 
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
+
 import java.util.Map;
 import java.util.Objects;
 
 /**
  * DoubleHistogramAdapter wraps an otel DoubleHistogram
  */
-public class DoubleHistogramAdapter extends AbstractInstrument
+public class DoubleHistogramAdapter extends AbstractInstrument
     implements
         org.elasticsearch.telemetry.metric.DoubleHistogram {
 
@@ -25,7 +28,7 @@ public DoubleHistogramAdapter(Meter meter, String name, String description, Stri
     }
 
     @Override
-    io.opentelemetry.api.metrics.DoubleHistogram buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.DoubleHistogram buildInstrument(Meter meter) {
         var builder = Objects.requireNonNull(meter).histogramBuilder(getName());
         return builder.setDescription(getDescription()).setUnit(getUnit()).build();
     }
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java
index 9a2fc1b564766..a204627a04f1e 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleUpDownCounterAdapter.java
@@ -8,15 +8,18 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
+import io.opentelemetry.api.metrics.DoubleUpDownCounter;
 import io.opentelemetry.api.metrics.Meter;
 
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
+
 import java.util.Map;
 import java.util.Objects;
 
 /**
  * DoubleUpDownCounterAdapter wraps an otel DoubleUpDownCounter
  */
-public class DoubleUpDownCounterAdapter extends AbstractInstrument
+public class DoubleUpDownCounterAdapter extends AbstractInstrument
     implements
         org.elasticsearch.telemetry.metric.DoubleUpDownCounter {
 
@@ -25,7 +28,7 @@ public DoubleUpDownCounterAdapter(Meter meter, String name, String description,
     }
 
     @Override
-    io.opentelemetry.api.metrics.DoubleUpDownCounter buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.DoubleUpDownCounter buildInstrument(Meter meter) {
         return Objects.requireNonNull(meter)
             .upDownCounterBuilder(getName())
             .ofDoubles()
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java
index 122d16d9e1aa4..9b46b8c97994a 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongCounterAdapter.java
@@ -8,24 +8,25 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
+import io.opentelemetry.api.metrics.LongCounter;
 import io.opentelemetry.api.metrics.Meter;
 
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
+
 import java.util.Map;
 import java.util.Objects;
 
 /**
  * LongCounterAdapter wraps an otel LongCounter
  */
-public class LongCounterAdapter extends AbstractInstrument
-    implements
-        org.elasticsearch.telemetry.metric.LongCounter {
+public class LongCounterAdapter extends AbstractInstrument implements org.elasticsearch.telemetry.metric.LongCounter {
 
     public LongCounterAdapter(Meter meter, String name, String description, String unit) {
         super(meter, name, description, unit);
     }
 
     @Override
-    io.opentelemetry.api.metrics.LongCounter buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.LongCounter buildInstrument(Meter meter) {
         var builder = Objects.requireNonNull(meter).counterBuilder(getName());
         return builder.setDescription(getDescription()).setUnit(getUnit()).build();
     }
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java
index 66d2287a765dc..2b2f573941484 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java
@@ -9,6 +9,9 @@
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
 import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableLongGauge;
+
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
 
 import java.util.Collections;
 import java.util.Map;
@@ -18,9 +21,7 @@
 /**
  * LongGaugeAdapter wraps an otel ObservableLongMeasurement
  */
-public class LongGaugeAdapter extends AbstractInstrument
-    implements
-        org.elasticsearch.telemetry.metric.LongGauge {
+public class LongGaugeAdapter extends AbstractInstrument implements org.elasticsearch.telemetry.metric.LongGauge {
     private final AtomicReference valueWithAttributes;
 
     public LongGaugeAdapter(Meter meter, String name, String description, String unit) {
@@ -29,8 +30,7 @@ public LongGaugeAdapter(Meter meter, String name, String description, String uni
     }
 
     @Override
-    io.opentelemetry.api.metrics.ObservableLongGauge buildInstrument(Meter meter) {
-
+    protected io.opentelemetry.api.metrics.ObservableLongGauge buildInstrument(Meter meter) {
         return Objects.requireNonNull(meter)
             .gaugeBuilder(getName())
             .ofLongs()
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java
index bb5be4866e7b7..2b8e76df0dd0e 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongHistogramAdapter.java
@@ -8,24 +8,25 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
+import io.opentelemetry.api.metrics.LongHistogram;
 import io.opentelemetry.api.metrics.Meter;
 
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
+
 import java.util.Map;
 import java.util.Objects;
 
 /**
  * LongHistogramAdapter wraps an otel LongHistogram
  */
-public class LongHistogramAdapter extends AbstractInstrument
-    implements
-        org.elasticsearch.telemetry.metric.LongHistogram {
+public class LongHistogramAdapter extends AbstractInstrument implements org.elasticsearch.telemetry.metric.LongHistogram {
 
     public LongHistogramAdapter(Meter meter, String name, String description, String unit) {
         super(meter, name, description, unit);
     }
 
     @Override
-    io.opentelemetry.api.metrics.LongHistogram buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.LongHistogram buildInstrument(Meter meter) {
         return Objects.requireNonNull(meter)
             .histogramBuilder(getName())
             .ofLongs()
diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java
index e5af85e4ed192..a59a114bc2264 100644
--- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java
+++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongUpDownCounterAdapter.java
@@ -8,15 +8,18 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
+import io.opentelemetry.api.metrics.LongUpDownCounter;
 import io.opentelemetry.api.metrics.Meter;
 
+import org.elasticsearch.telemetry.apm.AbstractInstrument;
+
 import java.util.Map;
 import java.util.Objects;
 
 /**
  * LongUpDownCounterAdapter wraps an otel LongUpDownCounter
  */
-public class LongUpDownCounterAdapter extends AbstractInstrument
+public class LongUpDownCounterAdapter extends AbstractInstrument
     implements
         org.elasticsearch.telemetry.metric.LongUpDownCounter {
 
@@ -25,7 +28,7 @@ public LongUpDownCounterAdapter(Meter meter, String name, String description, St
     }
 
     @Override
-    io.opentelemetry.api.metrics.LongUpDownCounter buildInstrument(Meter meter) {
+    protected io.opentelemetry.api.metrics.LongUpDownCounter buildInstrument(Meter meter) {
         var builder = Objects.requireNonNull(meter).upDownCounterBuilder(getName());
         return builder.setDescription(getDescription()).setUnit(getUnit()).build();
     }
diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/APMMeterRegistryTests.java
similarity index 65%
rename from modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java
rename to modules/apm/src/test/java/org/elasticsearch/telemetry/apm/APMMeterRegistryTests.java
index 1064b8820b089..38fb0f0e0a8ac 100644
--- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/APMMeterTests.java
+++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/APMMeterRegistryTests.java
@@ -6,61 +6,63 @@
  * Side Public License, v 1.
  */
 
-package org.elasticsearch.telemetry.apm.internal.metrics;
+package org.elasticsearch.telemetry.apm;
 
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.metrics.Meter;
 
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.telemetry.apm.internal.APMAgentSettings;
+import org.elasticsearch.telemetry.apm.internal.APMMeterService;
+import org.elasticsearch.telemetry.apm.internal.TestAPMMeterService;
 import org.elasticsearch.telemetry.metric.DoubleCounter;
 import org.elasticsearch.test.ESTestCase;
 
 import static org.hamcrest.Matchers.sameInstance;
 
-public class APMMeterTests extends ESTestCase {
+public class APMMeterRegistryTests extends ESTestCase {
     Meter testOtel = OpenTelemetry.noop().getMeter("test");
 
     Meter noopOtel = OpenTelemetry.noop().getMeter("noop");
 
     public void testMeterIsSetUponConstruction() {
         // test default
-        APMMeter apmMeter = new APMMeter(Settings.EMPTY, () -> testOtel, () -> noopOtel);
+        APMMeterService apmMeter = new APMMeterService(Settings.EMPTY, () -> testOtel, () -> noopOtel);
 
-        Meter meter = apmMeter.getInstruments().getMeter();
+        Meter meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(noopOtel));
 
         // test explicitly enabled
         var settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build();
-        apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel);
+        apmMeter = new APMMeterService(settings, () -> testOtel, () -> noopOtel);
 
-        meter = apmMeter.getInstruments().getMeter();
+        meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(testOtel));
 
         // test explicitly disabled
         settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build();
-        apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel);
+        apmMeter = new APMMeterService(settings, () -> testOtel, () -> noopOtel);
 
-        meter = apmMeter.getInstruments().getMeter();
+        meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(noopOtel));
     }
 
     public void testMeterIsOverridden() {
-        APMMeter apmMeter = new APMMeter(Settings.EMPTY, () -> testOtel, () -> noopOtel);
+        TestAPMMeterService apmMeter = new TestAPMMeterService(Settings.EMPTY, () -> testOtel, () -> noopOtel);
 
-        Meter meter = apmMeter.getInstruments().getMeter();
+        Meter meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(noopOtel));
 
         apmMeter.setEnabled(true);
 
-        meter = apmMeter.getInstruments().getMeter();
+        meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(testOtel));
     }
 
     public void testLookupByName() {
         var settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build();
 
-        var apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel);
+        var apmMeter = new APMMeterService(settings, () -> testOtel, () -> noopOtel).getMeterRegistry();
 
         DoubleCounter registeredCounter = apmMeter.registerDoubleCounter("name", "desc", "unit");
         DoubleCounter lookedUpCounter = apmMeter.getDoubleCounter("name");
@@ -70,15 +72,15 @@ public void testLookupByName() {
 
     public void testNoopIsSetOnStop() {
         var settings = Settings.builder().put(APMAgentSettings.TELEMETRY_METRICS_ENABLED_SETTING.getKey(), true).build();
-        APMMeter apmMeter = new APMMeter(settings, () -> testOtel, () -> noopOtel);
+        APMMeterService apmMeter = new APMMeterService(settings, () -> testOtel, () -> noopOtel);
         apmMeter.start();
 
-        Meter meter = apmMeter.getInstruments().getMeter();
+        Meter meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(testOtel));
 
         apmMeter.stop();
 
-        meter = apmMeter.getInstruments().getMeter();
+        meter = apmMeter.getMeterRegistry().getMeter();
         assertThat(meter, sameInstance(noopOtel));
     }
 
diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/MeterRegistryConcurrencyTests.java
similarity index 86%
rename from modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java
rename to modules/apm/src/test/java/org/elasticsearch/telemetry/apm/MeterRegistryConcurrencyTests.java
index 4390fd4ac0784..f18d39fb39c6c 100644
--- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsConcurrencyTests.java
+++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/MeterRegistryConcurrencyTests.java
@@ -6,7 +6,7 @@
  * Side Public License, v 1.
  */
 
-package org.elasticsearch.telemetry.apm.internal.metrics;
+package org.elasticsearch.telemetry.apm;
 
 import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.metrics.DoubleCounterBuilder;
@@ -27,7 +27,7 @@
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.sameInstance;
 
-public class InstrumentsConcurrencyTests extends ESTestCase {
+public class MeterRegistryConcurrencyTests extends ESTestCase {
     private final String name = "name";
     private final String description = "desc";
     private final String unit = "kg";
@@ -91,26 +91,26 @@ public ObservableLongCounter buildWithCallback(Consumer instruments.registerLongCounter(name, description, unit));
+        var registerThread = new Thread(() -> meterRegistrar.registerLongCounter(name, description, unit));
         // registerThread has a countDown latch that is simulating a long-running registration
         registerThread.start();
         buildLatch.await(); // wait for registerThread to hold the lock
 
-        var setProviderThread = new Thread(() -> instruments.setProvider(noopMeter));
+        var setProviderThread = new Thread(() -> meterRegistrar.setProvider(noopMeter));
         // a setProviderThread will attempt to override a meter, but will wait to acquireLock
         setProviderThread.start();
 
         // assert that a thread is waiting for a lock during long-running registration
         assertBusy(() -> assertThat(setProviderThread.getState(), equalTo(Thread.State.WAITING)));
         // assert that the old lockingMeter is still in place
-        assertBusy(() -> assertThat(instruments.getMeter(), sameInstance(lockingMeter)));
+        assertBusy(() -> assertThat(meterRegistrar.getMeter(), sameInstance(lockingMeter)));
 
         // finish long-running registration
         registerLatch.countDown();
         // assert that a meter was overriden
-        assertBusy(() -> assertThat(instruments.getMeter(), sameInstance(lockingMeter)));
+        assertBusy(() -> assertThat(meterRegistrar.getMeter(), sameInstance(lockingMeter)));
 
     }
 }
diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/TestAPMMeterService.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/TestAPMMeterService.java
new file mode 100644
index 0000000000000..bed611802d1e8
--- /dev/null
+++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/TestAPMMeterService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.telemetry.apm.internal;
+
+import io.opentelemetry.api.metrics.Meter;
+
+import org.elasticsearch.common.settings.Settings;
+
+import java.util.function.Supplier;
+
+public class TestAPMMeterService extends APMMeterService {
+    public TestAPMMeterService(Settings settings, Supplier otelMeterSupplier, Supplier noopMeterSupplier) {
+        super(settings, otelMeterSupplier, noopMeterSupplier);
+    }
+
+    public void setEnabled(boolean enabled) {
+        // expose pkg private for testing
+        super.setEnabled(enabled);
+    }
+}
diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java
deleted file mode 100644
index daf511fcf7042..0000000000000
--- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/InstrumentsTests.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-package org.elasticsearch.telemetry.apm.internal.metrics;
-
-import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.api.metrics.Meter;
-
-import org.elasticsearch.test.ESTestCase;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.sameInstance;
-
-public class InstrumentsTests extends ESTestCase {
-    Meter noopMeter = OpenTelemetry.noop().getMeter("noop");
-    Meter someOtherMeter = OpenTelemetry.noop().getMeter("xyz");
-    String name = "name";
-    String description = "desc";
-    String unit = "kg";
-
-    public void testRegistrationAndLookup() {
-        Instruments instruments = new Instruments(noopMeter);
-        {
-            var registered = instruments.registerDoubleCounter(name, description, unit);
-            var lookedUp = instruments.getDoubleCounter(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerDoubleUpDownCounter(name, description, unit);
-            var lookedUp = instruments.getDoubleUpDownCounter(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerDoubleGauge(name, description, unit);
-            var lookedUp = instruments.getDoubleGauge(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerDoubleHistogram(name, description, unit);
-            var lookedUp = instruments.getDoubleHistogram(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerLongCounter(name, description, unit);
-            var lookedUp = instruments.getLongCounter(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerLongUpDownCounter(name, description, unit);
-            var lookedUp = instruments.getLongUpDownCounter(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerLongGauge(name, description, unit);
-            var lookedUp = instruments.getLongGauge(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-        {
-            var registered = instruments.registerLongHistogram(name, description, unit);
-            var lookedUp = instruments.getLongHistogram(name);
-            assertThat(registered, sameInstance(lookedUp));
-        }
-    }
-
-    public void testNameValidation() {
-        Instruments instruments = new Instruments(noopMeter);
-
-        instruments.registerLongHistogram(name, description, unit);
-        var e = expectThrows(IllegalStateException.class, () -> instruments.registerLongHistogram(name, description, unit));
-        assertThat(e.getMessage(), equalTo("LongHistogramAdapter[name] already registered"));
-    }
-}
diff --git a/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java b/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java
index 36742765edad5..c71bbf02782ca 100644
--- a/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java
+++ b/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java
@@ -22,7 +22,7 @@
 import org.elasticsearch.common.util.BigArrays;
 import org.elasticsearch.indices.recovery.RecoverySettings;
 import org.elasticsearch.repositories.blobstore.MeteredBlobStoreRepository;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
 
 import java.util.Locale;
@@ -109,7 +109,7 @@ public AzureRepository(
             recoverySettings,
             buildBasePath(metadata),
             buildLocation(metadata),
-            Meter.NOOP
+            MeterRegistry.NOOP
         );
         this.chunkSize = Repository.CHUNK_SIZE_SETTING.get(metadata.settings());
         this.storageService = storageService;
diff --git a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java
index a42839c6d0174..21dd7529afaca 100644
--- a/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java
+++ b/modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java
@@ -21,7 +21,7 @@
 import org.elasticsearch.indices.recovery.RecoverySettings;
 import org.elasticsearch.repositories.RepositoryException;
 import org.elasticsearch.repositories.blobstore.MeteredBlobStoreRepository;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
 
 import java.util.Map;
@@ -78,7 +78,7 @@ class GoogleCloudStorageRepository extends MeteredBlobStoreRepository {
             recoverySettings,
             buildBasePath(metadata),
             buildLocation(metadata),
-            Meter.NOOP
+            MeterRegistry.NOOP
         );
         this.storageService = storageService;
 
diff --git a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java
index b23e32b651698..8805955d905c4 100644
--- a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java
+++ b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java
@@ -51,10 +51,10 @@
 import org.elasticsearch.snapshots.SnapshotState;
 import org.elasticsearch.snapshots.SnapshotsService;
 import org.elasticsearch.snapshots.mockstore.BlobStoreWrapper;
-import org.elasticsearch.telemetry.DelegatingMeter;
+import org.elasticsearch.telemetry.DelegatingMeterRegistry;
 import org.elasticsearch.telemetry.TelemetryProvider;
 import org.elasticsearch.telemetry.metric.LongCounter;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.telemetry.tracing.Tracer;
 import org.elasticsearch.test.BackgroundIndexer;
 import org.elasticsearch.test.ESIntegTestCase;
@@ -433,7 +433,7 @@ protected S3Repository createRepository(
             BigArrays bigArrays,
             RecoverySettings recoverySettings
         ) {
-            return new S3Repository(metadata, registry, getService(), clusterService, bigArrays, recoverySettings, getMeter()) {
+            return new S3Repository(metadata, registry, getService(), clusterService, bigArrays, recoverySettings, getMeterRegistry()) {
 
                 @Override
                 public BlobStore blobStore() {
@@ -584,7 +584,7 @@ public String getName() {
             }
         };
 
-        private final Meter meter = new DelegatingMeter(Meter.NOOP) {
+        private final MeterRegistry meterRegistry = new DelegatingMeterRegistry(MeterRegistry.NOOP) {
             @Override
             public LongCounter registerLongCounter(String name, String description, String unit) {
                 assertThat(name, equalTo(METRIC_REQUESTS_COUNT));
@@ -607,8 +607,8 @@ public Tracer getTracer() {
                 }
 
                 @Override
-                public Meter getMeter() {
-                    return meter;
+                public MeterRegistry getMeterRegistry() {
+                    return meterRegistry;
                 }
             };
         }
diff --git a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java
index 0e2e38a5af224..8096eab878734 100644
--- a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java
+++ b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java
@@ -29,7 +29,7 @@
 import org.elasticsearch.plugins.PluginsService;
 import org.elasticsearch.repositories.AbstractThirdPartyRepositoryTestCase;
 import org.elasticsearch.repositories.RepositoriesService;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.test.ClusterServiceUtils;
 import org.elasticsearch.threadpool.TestThreadPool;
 import org.elasticsearch.threadpool.ThreadPool;
@@ -113,7 +113,7 @@ public long absoluteTimeInMillis() {
                 ClusterServiceUtils.createClusterService(threadpool),
                 BigArrays.NON_RECYCLING_INSTANCE,
                 new RecoverySettings(node().settings(), node().injector().getInstance(ClusterService.class).getClusterSettings()),
-                Meter.NOOP
+                MeterRegistry.NOOP
             )
         ) {
             repository.start();
diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java
index 1371e51017bee..2bca95c656483 100644
--- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java
+++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java
@@ -32,7 +32,7 @@
 import org.elasticsearch.common.util.BigArrays;
 import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.telemetry.metric.LongCounter;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.threadpool.ThreadPool;
 
 import java.io.IOException;
@@ -80,7 +80,7 @@ class S3BlobStore implements BlobStore {
 
     private final ThreadPool threadPool;
     private final Executor snapshotExecutor;
-    private final Meter meter;
+    private final MeterRegistry meterRegistry;
     private final LongCounter requestCounter;
 
     private final StatsCollectors statsCollectors = new StatsCollectors();
@@ -99,7 +99,7 @@ class S3BlobStore implements BlobStore {
         RepositoryMetadata repositoryMetadata,
         BigArrays bigArrays,
         ThreadPool threadPool,
-        Meter meter
+        MeterRegistry meterRegistry
     ) {
         this.service = service;
         this.bigArrays = bigArrays;
@@ -111,8 +111,8 @@ class S3BlobStore implements BlobStore {
         this.repositoryMetadata = repositoryMetadata;
         this.threadPool = threadPool;
         this.snapshotExecutor = threadPool.executor(ThreadPool.Names.SNAPSHOT);
-        this.meter = meter;
-        this.requestCounter = this.meter.getLongCounter(METRIC_REQUESTS_COUNT);
+        this.meterRegistry = meterRegistry;
+        this.requestCounter = this.meterRegistry.getLongCounter(METRIC_REQUESTS_COUNT);
         s3RequestRetryStats = new S3RequestRetryStats(getMaxRetries());
         threadPool.scheduleWithFixedDelay(() -> {
             var priorRetryStats = s3RequestRetryStats;
diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java
index 0f15d6802eeb6..ddab811fcb078 100644
--- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java
+++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java
@@ -36,7 +36,7 @@
 import org.elasticsearch.repositories.blobstore.MeteredBlobStoreRepository;
 import org.elasticsearch.snapshots.SnapshotDeleteListener;
 import org.elasticsearch.snapshots.SnapshotsService;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.threadpool.Scheduler;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
@@ -205,7 +205,7 @@ class S3Repository extends MeteredBlobStoreRepository {
         final ClusterService clusterService,
         final BigArrays bigArrays,
         final RecoverySettings recoverySettings,
-        final Meter meter
+        final MeterRegistry meterRegistry
     ) {
         super(
             metadata,
@@ -215,7 +215,7 @@ class S3Repository extends MeteredBlobStoreRepository {
             recoverySettings,
             buildBasePath(metadata),
             buildLocation(metadata),
-            meter
+            meterRegistry
         );
         this.service = service;
         this.snapshotExecutor = threadPool().executor(ThreadPool.Names.SNAPSHOT);
@@ -408,7 +408,7 @@ protected S3BlobStore createBlobStore() {
             metadata,
             bigArrays,
             threadPool,
-            meter
+            meterRegistry
         );
     }
 
diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java
index 4a8d4ab6bab18..ce9e12aa03be2 100644
--- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java
+++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java
@@ -32,7 +32,7 @@
 import org.elasticsearch.repositories.Repository;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.telemetry.TelemetryProvider;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.watcher.ResourceWatcherService;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
@@ -69,7 +69,7 @@ public class S3RepositoryPlugin extends Plugin implements RepositoryPlugin, Relo
     }
 
     private final SetOnce service = new SetOnce<>();
-    private final SetOnce meter = new SetOnce<>();
+    private final SetOnce meterRegistry = new SetOnce<>();
     private final Settings settings;
 
     public S3RepositoryPlugin(Settings settings) {
@@ -88,7 +88,7 @@ protected S3Repository createRepository(
         final BigArrays bigArrays,
         final RecoverySettings recoverySettings
     ) {
-        return new S3Repository(metadata, registry, service.get(), clusterService, bigArrays, recoverySettings, meter.get());
+        return new S3Repository(metadata, registry, service.get(), clusterService, bigArrays, recoverySettings, meterRegistry.get());
     }
 
     @Override
@@ -110,7 +110,7 @@ public Collection createComponents(
     ) {
         service.set(s3Service(environment, clusterService.getSettings()));
         this.service.get().refreshAndClearCache(S3ClientSettings.load(settings));
-        meter.set(telemetryProvider.getMeter());
+        meterRegistry.set(telemetryProvider.getMeterRegistry());
         return List.of(service);
     }
 
@@ -168,7 +168,7 @@ public void close() throws IOException {
         getService().close();
     }
 
-    protected Meter getMeter() {
-        return meter.get();
+    protected MeterRegistry getMeterRegistry() {
+        return meterRegistry.get();
     }
 }
diff --git a/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositoryCredentialsTests.java b/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositoryCredentialsTests.java
index 2cdcc111b01a6..a587f0c731497 100644
--- a/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositoryCredentialsTests.java
+++ b/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/RepositoryCredentialsTests.java
@@ -31,7 +31,7 @@
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.rest.RestResponse;
 import org.elasticsearch.rest.action.admin.cluster.RestGetRepositoriesAction;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.test.ESSingleNodeTestCase;
 import org.elasticsearch.test.rest.FakeRestRequest;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
@@ -264,7 +264,7 @@ protected S3Repository createRepository(
             BigArrays bigArrays,
             RecoverySettings recoverySettings
         ) {
-            return new S3Repository(metadata, registry, getService(), clusterService, bigArrays, recoverySettings, Meter.NOOP) {
+            return new S3Repository(metadata, registry, getService(), clusterService, bigArrays, recoverySettings, MeterRegistry.NOOP) {
                 @Override
                 protected void assertSnapshotOrGenericThread() {
                     // eliminate thread name check as we create repo manually on test/main threads
diff --git a/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java b/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java
index a48fd2474bc59..3875181f98ece 100644
--- a/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java
+++ b/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3BlobContainerRetriesTests.java
@@ -36,7 +36,7 @@
 import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.env.Environment;
 import org.elasticsearch.repositories.blobstore.AbstractBlobContainerRetriesTestCase;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Before;
@@ -158,7 +158,7 @@ protected BlobContainer createBlobContainer(
                 repositoryMetadata,
                 BigArrays.NON_RECYCLING_INSTANCE,
                 new DeterministicTaskQueue().getThreadPool(),
-                Meter.NOOP
+                MeterRegistry.NOOP
             )
         ) {
             @Override
diff --git a/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java b/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java
index c38c8b764af41..db477c16a57e7 100644
--- a/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java
+++ b/modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java
@@ -20,7 +20,7 @@
 import org.elasticsearch.indices.recovery.RecoverySettings;
 import org.elasticsearch.repositories.RepositoryException;
 import org.elasticsearch.repositories.blobstore.BlobStoreTestUtil;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
 import org.hamcrest.Matchers;
@@ -129,7 +129,7 @@ private S3Repository createS3Repo(RepositoryMetadata metadata) {
             BlobStoreTestUtil.mockClusterService(),
             MockBigArrays.NON_RECYCLING_INSTANCE,
             new RecoverySettings(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)),
-            Meter.NOOP
+            MeterRegistry.NOOP
         ) {
             @Override
             protected void assertSnapshotOrGenericThread() {
diff --git a/server/src/main/java/org/elasticsearch/repositories/RepositoriesModule.java b/server/src/main/java/org/elasticsearch/repositories/RepositoriesModule.java
index a0ade5c6d8473..32c32369a5fae 100644
--- a/server/src/main/java/org/elasticsearch/repositories/RepositoriesModule.java
+++ b/server/src/main/java/org/elasticsearch/repositories/RepositoriesModule.java
@@ -48,7 +48,7 @@ public RepositoriesModule(
         RecoverySettings recoverySettings,
         TelemetryProvider telemetryProvider
     ) {
-        telemetryProvider.getMeter().registerLongCounter(METRIC_REQUESTS_COUNT, "repository request counter", "unit");
+        telemetryProvider.getMeterRegistry().registerLongCounter(METRIC_REQUESTS_COUNT, "repository request counter", "unit");
         Map factories = new HashMap<>();
         factories.put(
             FsRepository.TYPE,
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/MeteredBlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/MeteredBlobStoreRepository.java
index c69270011fc7b..4029938c6fc5f 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/MeteredBlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/MeteredBlobStoreRepository.java
@@ -16,7 +16,7 @@
 import org.elasticsearch.indices.recovery.RecoverySettings;
 import org.elasticsearch.repositories.RepositoryInfo;
 import org.elasticsearch.repositories.RepositoryStatsSnapshot;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.xcontent.NamedXContentRegistry;
 
@@ -24,7 +24,7 @@
 
 public abstract class MeteredBlobStoreRepository extends BlobStoreRepository {
     private final RepositoryInfo repositoryInfo;
-    protected final Meter meter;
+    protected final MeterRegistry meterRegistry;
 
     public MeteredBlobStoreRepository(
         RepositoryMetadata metadata,
@@ -34,10 +34,10 @@ public MeteredBlobStoreRepository(
         RecoverySettings recoverySettings,
         BlobPath basePath,
         Map location,
-        Meter meter
+        MeterRegistry meterRegistry
     ) {
         super(metadata, namedXContentRegistry, clusterService, bigArrays, recoverySettings, basePath);
-        this.meter = meter;
+        this.meterRegistry = meterRegistry;
         ThreadPool threadPool = clusterService.getClusterApplierService().threadPool();
         this.repositoryInfo = new RepositoryInfo(
             UUIDs.randomBase64UUID(),
diff --git a/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java b/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java
index add994787227f..d9c37afa268ba 100644
--- a/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java
+++ b/server/src/main/java/org/elasticsearch/telemetry/TelemetryProvider.java
@@ -8,14 +8,14 @@
 
 package org.elasticsearch.telemetry;
 
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.telemetry.tracing.Tracer;
 
 public interface TelemetryProvider {
 
     Tracer getTracer();
 
-    Meter getMeter();
+    MeterRegistry getMeterRegistry();
 
     TelemetryProvider NOOP = new TelemetryProvider() {
 
@@ -25,8 +25,8 @@ public Tracer getTracer() {
         }
 
         @Override
-        public Meter getMeter() {
-            return Meter.NOOP;
+        public MeterRegistry getMeterRegistry() {
+            return MeterRegistry.NOOP;
         }
     };
 }
diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java b/server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java
similarity index 98%
rename from server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java
rename to server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java
index 77bbf6f673fd3..e6f81786bb2f2 100644
--- a/server/src/main/java/org/elasticsearch/telemetry/metric/Meter.java
+++ b/server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java
@@ -13,7 +13,7 @@
  * only be registered once.
  * TODO(stu): describe name, unit and description
  */
-public interface Meter {
+public interface MeterRegistry {
     /**
      * Register a {@link DoubleCounter}.  The returned object may be reused.
      * @param name name of the counter
@@ -145,7 +145,7 @@ public interface Meter {
     /**
      * Noop implementation for tests
      */
-    Meter NOOP = new Meter() {
+    MeterRegistry NOOP = new MeterRegistry() {
         @Override
         public DoubleCounter registerDoubleCounter(String name, String description, String unit) {
             return DoubleCounter.NOOP;
diff --git a/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java b/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java
index f474b8d34a177..ab2f7b8f0a9a7 100644
--- a/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java
@@ -39,7 +39,7 @@
 import org.elasticsearch.repositories.blobstore.MeteredBlobStoreRepository;
 import org.elasticsearch.snapshots.SnapshotDeleteListener;
 import org.elasticsearch.snapshots.SnapshotId;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.Transport;
@@ -483,7 +483,7 @@ private MeteredRepositoryTypeA(RepositoryMetadata metadata, ClusterService clust
                 mock(RecoverySettings.class),
                 BlobPath.EMPTY,
                 Map.of("bucket", "bucket-a"),
-                Meter.NOOP
+                MeterRegistry.NOOP
             );
         }
 
@@ -511,7 +511,7 @@ private MeteredRepositoryTypeB(RepositoryMetadata metadata, ClusterService clust
                 mock(RecoverySettings.class),
                 BlobPath.EMPTY,
                 Map.of("bucket", "bucket-b"),
-                Meter.NOOP
+                MeterRegistry.NOOP
             );
         }
 
diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeter.java b/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java
similarity index 93%
rename from test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeter.java
rename to test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java
index 25333c869dbf3..ad54ac769e86b 100644
--- a/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeter.java
+++ b/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java
@@ -16,13 +16,13 @@
 import org.elasticsearch.telemetry.metric.LongGauge;
 import org.elasticsearch.telemetry.metric.LongHistogram;
 import org.elasticsearch.telemetry.metric.LongUpDownCounter;
-import org.elasticsearch.telemetry.metric.Meter;
+import org.elasticsearch.telemetry.metric.MeterRegistry;
 
-public class DelegatingMeter implements Meter {
+public class DelegatingMeterRegistry implements MeterRegistry {
 
-    private final Meter delegate;
+    private final MeterRegistry delegate;
 
-    public DelegatingMeter(Meter delegate) {
+    public DelegatingMeterRegistry(MeterRegistry delegate) {
         this.delegate = delegate;
     }
 

From 48f1e4cf89da7e10a57f2965d9e61da4011af057 Mon Sep 17 00:00:00 2001
From: AlexB 
Date: Mon, 23 Oct 2023 12:23:58 -0700
Subject: [PATCH 065/203] ESQL small doc improvement (#101226)

Move command list to the top

Co-authored-by: Alexandros Batsakis 
---
 docs/reference/esql/esql-commands.asciidoc | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/docs/reference/esql/esql-commands.asciidoc b/docs/reference/esql/esql-commands.asciidoc
index bb00731f3b032..8b0e99344add1 100644
--- a/docs/reference/esql/esql-commands.asciidoc
+++ b/docs/reference/esql/esql-commands.asciidoc
@@ -8,7 +8,7 @@
 // tag::source_commands[]
 ==== Source commands
 
-An {esql} source command produces a table, typically with data from {es}.
+An {esql} source command produces a table, typically with data from {es}. An {esql} query must start with a source command.
 
 image::images/esql/source-command.svg[A source command producing a table from {es},align="center"]
 
@@ -18,10 +18,6 @@ image::images/esql/source-command.svg[A source command producing a table from {e
 * <>
 * <>
 
-include::source-commands/from.asciidoc[]
-include::source-commands/row.asciidoc[]
-include::source-commands/show.asciidoc[]
-
 // end::source_command[]
 
 // tag::proc_commands[]
@@ -47,6 +43,12 @@ image::images/esql/processing-command.svg[A processing command changing an input
 * <>
 * <>
 
+// end::proc_command[]
+
+include::source-commands/from.asciidoc[]
+include::source-commands/row.asciidoc[]
+include::source-commands/show.asciidoc[]
+
 include::processing-commands/dissect.asciidoc[]
 include::processing-commands/drop.asciidoc[]
 include::processing-commands/enrich.asciidoc[]
@@ -59,5 +61,3 @@ include::processing-commands/rename.asciidoc[]
 include::processing-commands/sort.asciidoc[]
 include::processing-commands/stats.asciidoc[]
 include::processing-commands/where.asciidoc[]
-
-// end::proc_command[]

From 4d10ea18497062b26c06a265e499cd56dd85bf60 Mon Sep 17 00:00:00 2001
From: Brian Seeders 
Date: Mon, 23 Oct 2023 16:05:10 -0400
Subject: [PATCH 066/203] [buildkite] Increase release-tests timeout

---
 .buildkite/pipelines/periodic.template.yml | 2 +-
 .buildkite/pipelines/periodic.yml          | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.buildkite/pipelines/periodic.template.yml b/.buildkite/pipelines/periodic.template.yml
index ec3ae76ffcdfb..08ba9529eb882 100644
--- a/.buildkite/pipelines/periodic.template.yml
+++ b/.buildkite/pipelines/periodic.template.yml
@@ -89,7 +89,7 @@ steps:
           GRADLE_TASK: "{{matrix.GRADLE_TASK}}"
   - label: release-tests
     command: .buildkite/scripts/release-tests.sh
-    timeout_in_minutes: 300
+    timeout_in_minutes: 360
     agents:
       provider: gcp
       image: family/elasticsearch-ubuntu-2004
diff --git a/.buildkite/pipelines/periodic.yml b/.buildkite/pipelines/periodic.yml
index 9afb088a5a50e..6f417e8f8ca84 100644
--- a/.buildkite/pipelines/periodic.yml
+++ b/.buildkite/pipelines/periodic.yml
@@ -1150,7 +1150,7 @@ steps:
           GRADLE_TASK: "{{matrix.GRADLE_TASK}}"
   - label: release-tests
     command: .buildkite/scripts/release-tests.sh
-    timeout_in_minutes: 300
+    timeout_in_minutes: 360
     agents:
       provider: gcp
       image: family/elasticsearch-ubuntu-2004

From 24ef5173557fb456da1dcca22ebeafeae1b24d4d Mon Sep 17 00:00:00 2001
From: Brian Seeders 
Date: Mon, 23 Oct 2023 16:19:09 -0400
Subject: [PATCH 067/203] [buildkite] Do collapsing annotations for Terrazzo
 pipelines as well

---
 .buildkite/hooks/pre-command | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.buildkite/hooks/pre-command b/.buildkite/hooks/pre-command
index 40fb970a76196..b6b730fc3de8b 100644
--- a/.buildkite/hooks/pre-command
+++ b/.buildkite/hooks/pre-command
@@ -92,7 +92,7 @@ fi
 
 # Initialize the build scan and gobld annotations with empty/open 
tags # This ensures that they are collapsible when they get appended to -if [[ "${BUILDKITE_LABEL:-}" == *"Pipeline upload"* ]]; then +if [[ "${BUILDKITE_LABEL:-}" == *"Pipeline upload"* || "${BUILDKITE_LABEL:-}" == *"Upload Pipeline"* ]]; then cat << EOF | buildkite-agent annotate --context "gradle-build-scans" --style "info"
From 6e6e85d0b4566eb9b7903708c92783adb49098d7 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 23 Oct 2023 16:20:14 -0400 Subject: [PATCH 068/203] [ci] Disable periodic java-matrix, java-fips-matrix, and bwc jobs in Jenkins (#101234) --- ...tic+elasticsearch+periodic+bwc-trigger.yml | 6 ---- .../elastic+elasticsearch+periodic+bwc.yml | 2 +- ...arch+periodic+java-fips-matrix-trigger.yml | 6 ---- ...lasticsearch+periodic+java-fips-matrix.yml | 3 +- ...ticsearch+periodic+java-matrix-trigger.yml | 6 ---- ...tic+elasticsearch+periodic+java-matrix.yml | 3 +- .../matrix-gradle-unix-disabled.yml | 32 +++++++++++++++++++ 7 files changed, 37 insertions(+), 21 deletions(-) delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+bwc-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+java-matrix-trigger.yml create mode 100644 .ci/templates.t/matrix-gradle-unix-disabled.yml diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+bwc-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+bwc-trigger.yml deleted file mode 100644 index 291ed41a5facf..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+bwc-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+bwc - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/8 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml index f4cadb7bad693..f7fc27816c4be 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml @@ -1,5 +1,5 @@ --- -jjbb-template: matrix-gradle-unix.yml +jjbb-template: matrix-gradle-unix-disabled.yml vars: - job-name: elastic+elasticsearch+%BRANCH%+periodic+bwc - job-display-name: "elastic / elasticsearch # %BRANCH% - backwards compatibility matrix" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix-trigger.yml deleted file mode 100644 index fb2a23855cc9f..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+java-fips-matrix - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix.yml index cc6f2d38d5918..b1fd03c08208c 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+java-fips-matrix.yml @@ -2,7 +2,8 @@ - job: name: "elastic+elasticsearch+%BRANCH%+periodic+java-fips-matrix" display-name: "elastic / elasticsearch # %BRANCH% - java fips compatibility matrix" - description: "Testing of the Elasticsearch %BRANCH% branch java FIPS compatibility matrix.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true project-type: matrix child-workspace: /dev/shm/elastic+elasticsearch+%BRANCH%+periodic+java-fips-matrix node: master diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix-trigger.yml deleted file mode 100644 index 8de3326dd819d..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+java-matrix - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix.yml index 07f4a8c5b6760..963e72b81f305 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+java-matrix.yml @@ -2,7 +2,8 @@ - job: name: "elastic+elasticsearch+%BRANCH%+periodic+java-matrix" display-name: "elastic / elasticsearch # %BRANCH% - java compatibility matrix" - description: "Testing of the Elasticsearch %BRANCH% branch java compatibility matrix.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true project-type: matrix child-workspace: /dev/shm/elastic+elasticsearch+%BRANCH%+periodic+java-matrix node: master diff --git a/.ci/templates.t/matrix-gradle-unix-disabled.yml b/.ci/templates.t/matrix-gradle-unix-disabled.yml new file mode 100644 index 0000000000000..1eafe77a5ec78 --- /dev/null +++ b/.ci/templates.t/matrix-gradle-unix-disabled.yml @@ -0,0 +1,32 @@ +--- +- job: + name: "{job-name}" + display-name: "{job-display-name}" + description: "This job has been migrated to Buildkite.\n" + disabled: true + project-type: matrix + child-workspace: /dev/shm/{job-name} + node: master + scm: + - git: + wipe-workspace: false + axes: + - axis: + type: slave + name: nodes + values: + - "general-purpose" + - axis: + type: yaml + filename: "{matrix-yaml-file}" + name: "{matrix-variable}" + builders: + - inject: + properties-file: ".ci/java-versions.properties" + properties-content: | + JAVA_HOME=$HOME/.java/$ES_BUILD_JAVA + JAVA11_HOME=$HOME/.java/java11 + JAVA16_HOME=$HOME/.java/openjdk16 + - shell: | + #!/usr/local/bin/runbld --redirect-stderr + $WORKSPACE/.ci/scripts/run-gradle.sh {gradle-args} From b0fb121640932c1283eba71cf1fe0bef46f4a7fa Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 23 Oct 2023 18:11:58 -0600 Subject: [PATCH 069/203] Make node client type setting a noop (#101214) The node client type is a remnant of the transport client. This commit cleans up some test reads and an unnecessary override of the setting. It was already not read anywhere in production. Now it is only registered in order to provide validation. In the future it should be deprecated and removed. --- rest-api-spec/build.gradle | 6 ++++ .../test/nodes.info/30_settings.yml | 4 +-- .../client/internal/node/NodeClientIT.java | 33 ------------------- .../elasticsearch/client/internal/Client.java | 1 + .../elasticsearch/node/NodeConstruction.java | 5 +-- .../xpack/core/XPackPluginTests.java | 4 --- 6 files changed, 10 insertions(+), 43 deletions(-) delete mode 100644 server/src/internalClusterTest/java/org/elasticsearch/client/internal/node/NodeClientIT.java diff --git a/rest-api-spec/build.gradle b/rest-api-spec/build.gradle index d9c0ab5294906..cf40c786db17b 100644 --- a/rest-api-spec/build.gradle +++ b/rest-api-spec/build.gradle @@ -218,6 +218,12 @@ tasks.named("yamlRestTestV7CompatTransform").configure { task -> // we can now search using doc values only task.replaceValueInMatch("fields.object\\.nested1.long.searchable", true) + + //client.type no longer exists #101214 + task.replaceKeyInMatch("nodes.\$node_id.settings.client.type", "nodes.\$node_id.settings.node.attr.testattr") + task.replaceValueInMatch("nodes.\$node_id.settings.node.attr.testattr", "test") + task.replaceKeyInMatch("nodes.\$node_id.settings.client\\.type", "nodes.\$node_id.settings.node\\.attr\\.testattr") + task.replaceValueInMatch("nodes.\$node_id.settings.node\\.attr\\.testattr", "test") } tasks.register('enforceYamlTestConvention').configure { diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/nodes.info/30_settings.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/nodes.info/30_settings.yml index 99b8b6f361a47..dc6062c6f282b 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/nodes.info/30_settings.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/nodes.info/30_settings.yml @@ -12,11 +12,11 @@ nodes.info: metric: [ settings ] - - match : { nodes.$node_id.settings.client.type: node } + - match : { nodes.$node_id.settings.node.attr.testattr: test } - do: nodes.info: metric: [ settings ] flat_settings: true - - match : { nodes.$node_id.settings.client\.type: node } + - match : { nodes.$node_id.settings.node\.attr\.testattr: test } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/client/internal/node/NodeClientIT.java b/server/src/internalClusterTest/java/org/elasticsearch/client/internal/node/NodeClientIT.java deleted file mode 100644 index c260b873d5ad9..0000000000000 --- a/server/src/internalClusterTest/java/org/elasticsearch/client/internal/node/NodeClientIT.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -package org.elasticsearch.client.internal.node; - -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.test.ESIntegTestCase.Scope; - -import static org.hamcrest.Matchers.is; - -@ClusterScope(scope = Scope.SUITE) -public class NodeClientIT extends ESIntegTestCase { - @Override - protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { - return Settings.builder() - .put(super.nodeSettings(nodeOrdinal, otherSettings)) - .put(Client.CLIENT_TYPE_SETTING_S.getKey(), "anything") - .build(); - } - - public void testThatClientTypeSettingCannotBeChanged() { - for (Settings settings : internalCluster().getInstances(Settings.class)) { - assertThat(Client.CLIENT_TYPE_SETTING_S.get(settings), is("node")); - } - } -} diff --git a/server/src/main/java/org/elasticsearch/client/internal/Client.java b/server/src/main/java/org/elasticsearch/client/internal/Client.java index 1065efb857fe7..89cb764549767 100644 --- a/server/src/main/java/org/elasticsearch/client/internal/Client.java +++ b/server/src/main/java/org/elasticsearch/client/internal/Client.java @@ -73,6 +73,7 @@ */ public interface Client extends ElasticsearchClient, Releasable { + // Note: This setting is registered only for bwc. The value is never read. Setting CLIENT_TYPE_SETTING_S = new Setting<>("client.type", "node", (s) -> { return switch (s) { case "node", "transport" -> s; diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index 952130ecf6357..246a41c99c922 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -345,10 +345,7 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi throws IOException { // Pass the node settings to the DeprecationLogger class so that it can have the deprecation.skip_deprecated_settings setting: DeprecationLogger.initialize(initialEnvironment.settings()); - Settings environmentSettings = Settings.builder() - .put(initialEnvironment.settings()) - .put(Client.CLIENT_TYPE_SETTING_S.getKey(), "node") - .build(); + Settings environmentSettings = initialEnvironment.settings(); final JvmInfo jvmInfo = JvmInfo.jvmInfo(); logger.info( diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java index 23633138d570d..4f15d719d4193 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java @@ -8,7 +8,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.node.DiscoveryNodeUtils; @@ -74,9 +73,6 @@ public void setup() { public void testXPackInstalledAttrClash() throws Exception { Settings.Builder builder = Settings.builder(); builder.put("node.attr." + XPackPlugin.XPACK_INSTALLED_NODE_ATTR, randomBoolean()); - if (randomBoolean()) { - builder.put(Client.CLIENT_TYPE_SETTING_S.getKey(), "transport"); - } XPackPlugin xpackPlugin = createXPackPlugin(builder.put("path.home", createTempDir()).build()); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, xpackPlugin::additionalSettings); assertThat( From 215d3e4299b9782becbe3df6f9f52ba573bbc899 Mon Sep 17 00:00:00 2001 From: Ievgen Degtiarenko Date: Tue, 24 Oct 2023 08:28:03 +0200 Subject: [PATCH 070/203] Set longer settings update task timeout (#101208) It appears that task cancelation is executed before the settings update is event starting in testClusterSettingsUpdateNotAcknowledged. This change uses longer timeout to improve the probability of blocking. --- .../settings/ClusterSettingsUpdateWithFaultyMasterIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsUpdateWithFaultyMasterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsUpdateWithFaultyMasterIT.java index 1f31b2155846e..5caff409a2052 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsUpdateWithFaultyMasterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/settings/ClusterSettingsUpdateWithFaultyMasterIT.java @@ -62,7 +62,7 @@ public void testClusterSettingsUpdateNotAcknowledged() throws Exception { .cluster() .prepareUpdateSettings() .setPersistentSettings(Settings.builder().put(BlockingClusterSettingTestPlugin.TEST_BLOCKING_SETTING.getKey(), true).build()) - .setMasterNodeTimeout(TimeValue.timeValueMillis(10L)) + .setMasterNodeTimeout(TimeValue.timeValueMillis(100L)) .execute(); logger.info("--> waiting for cluster state update to be blocked"); From b094268961cde716f63ae4c7b3986516673950ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Tue, 24 Oct 2023 08:58:29 +0200 Subject: [PATCH 071/203] Remove not needed node selector from some aggregation tests (#100947) --- .../test/aggregations/adjacency_matrix.yml | 6 ------ .../rest-api-spec/test/aggregations/filter.yml | 14 -------------- .../test/aggregations/filters_bucket.yml | 12 ------------ 3 files changed, 32 deletions(-) diff --git a/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/adjacency_matrix.yml b/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/adjacency_matrix.yml index e02b5fdc8b3cf..25522264c4dc0 100644 --- a/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/adjacency_matrix.yml +++ b/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/adjacency_matrix.yml @@ -66,8 +66,6 @@ setup: - skip: version: " - 7.8.99" reason: fixed in 7.9.0 - features: node_selector - - do: indices.create: index: lookup @@ -91,8 +89,6 @@ setup: { "num": [4] } - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: index: test preference: hit-same-shard-copy @@ -145,8 +141,6 @@ setup: # The second request should hit the cache - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: index: test preference: hit-same-shard-copy diff --git a/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filter.yml b/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filter.yml index e68aa621f1544..7d173ce9511bb 100644 --- a/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filter.yml +++ b/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filter.yml @@ -24,12 +24,7 @@ setup: --- "Terms lookup gets cached": - - skip: - features: node_selector - - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: rest_total_hits_as_int: true size: 0 @@ -63,8 +58,6 @@ setup: - match: { indices.test.total.request_cache.miss_count: 1 } - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: rest_total_hits_as_int: true size: 0 @@ -99,12 +92,7 @@ setup: --- "Standard queries get cached": - - skip: - features: node_selector - - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: rest_total_hits_as_int: true size: 0 @@ -137,8 +125,6 @@ setup: # Try again - it'll cache - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: rest_total_hits_as_int: true size: 0 diff --git a/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filters_bucket.yml b/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filters_bucket.yml index 0053d22f05a80..da3010ad8437a 100644 --- a/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filters_bucket.yml +++ b/modules/aggregations/src/yamlRestTest/resources/rest-api-spec/test/aggregations/filters_bucket.yml @@ -393,7 +393,6 @@ null meta: - skip: version: " - 7.10.99" reason: cache fixed in 7.11.0 - features: node_selector - do: bulk: @@ -407,8 +406,6 @@ null meta: string_field: foo bar - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: index: test_1 body: @@ -440,8 +437,6 @@ null meta: # This should be entirely fresh because updating the mapping busted the cache. - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: index: test_1 body: @@ -512,9 +507,6 @@ nested: --- "cache hits": - - skip: - features: node_selector - - do: indices.create: index: test @@ -539,8 +531,6 @@ nested: {"mentions" : ["abc"]} - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: rest_total_hits_as_int: true size: 0 @@ -579,8 +569,6 @@ nested: - match: { indices.test.total.request_cache.miss_count: 1 } - do: - node_selector: - version: current # the version of the node that parsed the request is part of the cache key. search: rest_total_hits_as_int: true size: 0 From 2757e30010239910b73596d1688b6b4a8155c896 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 24 Oct 2023 08:13:55 +0100 Subject: [PATCH 072/203] Make S3 anti-contention delay configurable (#101245) The anti-contention delay in the S3 repository's compare-and-exchange operation is hard-coded at 1 second today, but sometimes we encounter a repository that needs much longer to perform a compare-and-exchange operation when under contention. With this commit we make the anti-contention delay configurable. --- docs/changelog/101245.yaml | 5 +++++ .../elasticsearch/repositories/s3/S3BlobContainer.java | 4 +++- .../org/elasticsearch/repositories/s3/S3BlobStore.java | 4 ++++ .../org/elasticsearch/repositories/s3/S3Service.java | 10 ++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101245.yaml diff --git a/docs/changelog/101245.yaml b/docs/changelog/101245.yaml new file mode 100644 index 0000000000000..2f9fef318f31a --- /dev/null +++ b/docs/changelog/101245.yaml @@ -0,0 +1,5 @@ +pr: 101245 +summary: Make S3 anti-contention delay configurable +area: Snapshot/Restore +type: bug +issues: [] diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java index 04bdf7b637e27..174bde2d3a5ac 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java @@ -754,7 +754,9 @@ void run(BytesReference expected, BytesReference updated, ActionListener 0) { threadPool.scheduleUnlessShuttingDown( - TimeValue.timeValueMillis(TimeValue.timeValueSeconds(uploadIndex).millis() + Randomness.get().nextInt(50)), + TimeValue.timeValueMillis( + uploadIndex * blobStore.getCompareAndExchangeAntiContentionDelay().millis() + Randomness.get().nextInt(50) + ), blobStore.getSnapshotExecutor(), cancelConcurrentUpdates ); diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java index 2bca95c656483..25a2c4d8e1613 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobStore.java @@ -140,6 +140,10 @@ public TimeValue getCompareAndExchangeTimeToLive() { return service.compareAndExchangeTimeToLive; } + public TimeValue getCompareAndExchangeAntiContentionDelay() { + return service.compareAndExchangeAntiContentionDelay; + } + // metrics collector that ignores null responses that we interpret as the request not reaching the S3 endpoint due to a network // issue class IgnoreNoResponseMetricsCollector extends RequestMetricCollector { diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java index ddacb24be7118..736e8870f21a2 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java @@ -60,6 +60,14 @@ class S3Service implements Closeable { Setting.Property.NodeScope ); + private static final Setting REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING = Setting.timeSetting( + "repository_s3.compare_and_exchange.anti_contention_delay", + TimeValue.timeValueSeconds(1), + TimeValue.timeValueMillis(1), + TimeValue.timeValueHours(24), + Setting.Property.NodeScope + ); + private volatile Map clientsCache = emptyMap(); /** @@ -79,6 +87,7 @@ class S3Service implements Closeable { final CustomWebIdentityTokenCredentialsProvider webIdentityTokenCredentialsProvider; final TimeValue compareAndExchangeTimeToLive; + final TimeValue compareAndExchangeAntiContentionDelay; S3Service(Environment environment, Settings nodeSettings) { webIdentityTokenCredentialsProvider = new CustomWebIdentityTokenCredentialsProvider( @@ -88,6 +97,7 @@ class S3Service implements Closeable { Clock.systemUTC() ); compareAndExchangeTimeToLive = REPOSITORY_S3_CAS_TTL_SETTING.get(nodeSettings); + compareAndExchangeAntiContentionDelay = REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING.get(nodeSettings); } /** From c03ebd63143f570aec5591b73ce29be36bacbb0a Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Tue, 24 Oct 2023 10:30:05 +0200 Subject: [PATCH 073/203] Provide stable resampling (#101255) We resample data randomly if required. So far we have initialized the random number generator based on the hash code of the request with the intent of providing a random resampling that is still stable if the same request is issued multiple times. However, the hash code was not stable in a cluster because a query may use Lucene's `ByteRef` class to store values (such as the upper and lower bound of a date range). That class uses a murmur hash for its hash code. The murmur hash is initialized from `org.apache.lucene.util.StringHelper#GOOD_FAST_HASH_SEED` which intentionally varies across JVM instances. Consequently, the hash code of `ByteRef` (and ultimately the request's hash code) varies depending on which node in the cluster handles a request. With this commit we instead rely on the string representation of a query, which is stable across instances and node restarts to initialize the random number generator. This provides randomness across requests but also a consistent result for identical requests. Converting the query builder to its string representation adds around 1ms of overhead. Given that typical response times are in the range of single digit seconds, we deem this overhead acceptable. --- docs/changelog/101255.yaml | 5 ++++ .../profiling/GetStackTracesRequest.java | 8 ++++++- .../xpack/profiling/ResamplerTests.java | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101255.yaml diff --git a/docs/changelog/101255.yaml b/docs/changelog/101255.yaml new file mode 100644 index 0000000000000..37d8f7e3c14fe --- /dev/null +++ b/docs/changelog/101255.yaml @@ -0,0 +1,5 @@ +pr: 101255 +summary: Provide stable resampling +area: Application +type: bug +issues: [] diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java index 4083776f8c4a6..3932e386225c5 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/GetStackTracesRequest.java @@ -173,7 +173,13 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(query, sampleSize); + // The object representation of `query` may use Lucene's ByteRef to represent values. This class' hashCode implementation + // uses StringUtils.GOOD_FAST_HASH_SEED which is reinitialized for each JVM. This means that hashcode is consistent *within* + // a JVM but will not be consistent across the cluster. As we use hashCode e.g. to initialize the random number generator in + // Resampler to produce a consistent downsampling results, relying on the default hashCode implementation of `query` will + // produce consistent results per node but not across the cluster. To avoid this, we produce the hashCode based on the + // string representation instead, which will produce consistent results for the entire cluster and across node restarts. + return Objects.hash(Objects.toString(query, "null"), sampleSize); } @Override diff --git a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java index 79585986c64e2..07d9b60b31ff7 100644 --- a/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java +++ b/x-pack/plugin/profiling/src/test/java/org/elasticsearch/xpack/profiling/ResamplerTests.java @@ -7,6 +7,8 @@ package org.elasticsearch.xpack.profiling; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.test.ESTestCase; import java.util.random.RandomGenerator; @@ -67,6 +69,27 @@ public void testResamplingNoSampleRateAdjustment() { assertEquals(20_000, resampler.adjustSampleCount(actualSamplesSingleTrace)); } + public void testResamplingNoSampleRateAdjustmentWithQuery() { + double sampleRate = 1.0d; + int requestedSamples = 1; + int actualTotalSamples = 200; + // there is only one event + int actualSamplesSingleTrace = 200; + + GetStackTracesRequest request = new GetStackTracesRequest( + requestedSamples, + new BoolQueryBuilder().filter( + new RangeQueryBuilder("@timestamp").lt("2023-10-19 15:33:00").gte("2023-10-19 15:31:52").format("yyyy-MM-dd HH:mm:ss") + ) + ); + + request.setAdjustSampleCount(false); + // use the real resampler here to ensure we have a stable seed even for complex queries + Resampler resampler = new Resampler(request, sampleRate, actualTotalSamples); + + assertEquals(200, resampler.adjustSampleCount(actualSamplesSingleTrace)); + } + public void testResamplingAndSampleRateAdjustment() { // corresponds to profiling-events-5pow01 double sampleRate = 1.0d / Math.pow(5.0d, 1); From aa30dad01faa03b5134ec395247bdf2a8a412b86 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 24 Oct 2023 19:37:05 +1100 Subject: [PATCH 074/203] S3 CAS operation should respect abortMutipartUpload failure (#101253) We inadvertently made s3 CAS operation to ignore abortMutipartUpload failures in #98664. This PR fixes it. --- .../org/elasticsearch/repositories/s3/S3BlobContainer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java index 174bde2d3a5ac..fa5b6a2aee5d3 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java @@ -744,7 +744,9 @@ void run(BytesReference expected, BytesReference updated, ActionListener safeAbortMultipartUpload(currentUploadId))); + .execute( + ActionRunnable.run(listeners.acquire(), () -> abortMultipartUploadIfExists(currentUploadId)) + ); } } } finally { From b07feb507d30a0ca4700bde180dc664e84b42ff7 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Tue, 24 Oct 2023 11:03:28 +0200 Subject: [PATCH 075/203] Percolator to support parsing script score query with params (#101051) While dot expansion is disabled when parsing percolator queries at index time, as that would interfere with query parsing, we still use a wrapper parser that is conservative about what methods it supports, assuming that document parsing needs nextToken and not much more. Turns out that when parsing queries instead, we need to support all the XContentParser methods including map, list etc. This commit adds a test for script score query parsing through document parsing via percolator field mapper, and removes the limitations in the wrapper parser when dots expansion is disabled. --- docs/changelog/101051.yaml | 6 + .../PercolatorFieldMapperTests.java | 185 +++++++++++++++++- .../mapper/DotExpandingXContentParser.java | 26 +++ .../DotExpandingXContentParserTests.java | 93 +++++++++ 4 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/101051.yaml diff --git a/docs/changelog/101051.yaml b/docs/changelog/101051.yaml new file mode 100644 index 0000000000000..05e7443dac8b3 --- /dev/null +++ b/docs/changelog/101051.yaml @@ -0,0 +1,6 @@ +pr: 101051 +summary: Percolator to support parsing script score query with params +area: Mapping +type: bug +issues: + - 97377 diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java index 5a12e0c9f3a37..b47364e3b1a08 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.BytesRef; import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; @@ -40,6 +41,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Tuple; @@ -53,6 +55,7 @@ import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.TestDocumentParserContext; +import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; @@ -67,13 +70,17 @@ import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder; import org.elasticsearch.index.query.functionscore.ScriptScoreFunctionBuilder; +import org.elasticsearch.index.query.functionscore.ScriptScoreQueryBuilder; import org.elasticsearch.indices.TermsLookup; import org.elasticsearch.join.ParentJoinPlugin; import org.elasticsearch.join.query.HasChildQueryBuilder; import org.elasticsearch.join.query.HasParentQueryBuilder; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.DummyQueryParserPlugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.xcontent.XContentBuilder; @@ -92,6 +99,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -130,7 +138,13 @@ public class PercolatorFieldMapperTests extends ESSingleNodeTestCase { @Override protected Collection> getPlugins() { - return pluginList(InternalSettingsPlugin.class, PercolatorPlugin.class, FoolMeScriptPlugin.class, ParentJoinPlugin.class); + return pluginList( + InternalSettingsPlugin.class, + PercolatorPlugin.class, + FoolMeScriptPlugin.class, + ParentJoinPlugin.class, + CustomQueriesPlugin.class + ); } @Override @@ -540,6 +554,38 @@ public void testPercolatorFieldMapper() throws Exception { assertThat(doc.rootDoc().getFields(fieldType.extractionResultField.name()).get(0).stringValue(), equalTo(EXTRACTION_FAILED)); } + public void testParseScriptScoreQueryWithParams() throws Exception { + addQueryFieldMappings(); + ScriptScoreQueryBuilder scriptScoreQueryBuilder = new ScriptScoreQueryBuilder( + new MatchAllQueryBuilder(), + new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "score", Collections.singletonMap("param", "1")) + ); + ParsedDocument doc = mapperService.documentMapper() + .parse( + new SourceToParse( + "1", + BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, scriptScoreQueryBuilder).endObject()), + XContentType.JSON + ) + ); + assertNotNull(doc); + } + + public void testParseCustomParserQuery() throws Exception { + addQueryFieldMappings(); + ParsedDocument doc = mapperService.documentMapper() + .parse( + new SourceToParse( + "1", + BytesReference.bytes( + XContentFactory.jsonBuilder().startObject().field(fieldName, new CustomParserQueryBuilder()).endObject() + ), + XContentType.JSON + ) + ); + assertNotNull(doc); + } + public void testStoringQueries() throws Exception { addQueryFieldMappings(); QueryBuilder[] queries = new QueryBuilder[] { @@ -1106,7 +1152,7 @@ public static class FoolMeScriptPlugin extends MockScriptPlugin { @Override protected Map, Object>> pluginScripts() { - return Collections.singletonMap("return true", (vars) -> true); + return Map.of("return true", (vars) -> true, "score", (vars) -> 0f); } @Override @@ -1114,4 +1160,139 @@ public String pluginScriptLang() { return Script.DEFAULT_SCRIPT_LANG; } } + + public static class CustomQueriesPlugin extends Plugin implements SearchPlugin { + @Override + public List> getQueries() { + return Collections.singletonList( + new QuerySpec( + CustomParserQueryBuilder.NAME, + CustomParserQueryBuilder::new, + CustomParserQueryBuilder::fromXContent + ) + ); + } + } + + public static final class CustomParserQueryBuilder extends AbstractQueryBuilder { + private static final String NAME = "CUSTOM"; + + CustomParserQueryBuilder() {} + + CustomParserQueryBuilder(StreamInput in) throws IOException { + super(in); + } + + @Override + protected void doWriteTo(StreamOutput out) { + // only the superclass has state + } + + @Override + protected Query doToQuery(SearchExecutionContext context) { + return new DummyQueryParserPlugin.DummyQuery(); + } + + @Override + protected int doHashCode() { + return 0; + } + + @Override + protected boolean doEquals(CustomParserQueryBuilder other) { + return true; + } + + @Override + public TransportVersion getMinimalSupportedVersion() { + return TransportVersions.ZERO; + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + protected void doXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(NAME); + builder.array("list", "value0", "value1", "value2"); + builder.array("listOrdered", "value0", "value1", "value2"); + builder.field("map"); + builder.map(Map.of("key1", "value1", "key2", "value2")); + builder.field("mapOrdered"); + builder.map(Map.of("key3", "value3", "key4", "value4")); + builder.field("mapStrings"); + builder.map(Map.of("key5", "value5", "key6", "value6")); + builder.field("mapSupplier"); + builder.map(Map.of("key7", "value7", "key8", "value8")); + builder.endObject(); + } + + public static CustomParserQueryBuilder fromXContent(XContentParser parser) throws IOException { + { + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("list", parser.currentName()); + List list = parser.list(); + assertEquals(3, list.size()); + for (int i = 0; i < 3; i++) { + assertEquals("value" + i, list.get(i).toString()); + } + assertEquals(XContentParser.Token.END_ARRAY, parser.currentToken()); + } + { + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("listOrdered", parser.currentName()); + List listOrdered = parser.listOrderedMap(); + assertEquals(3, listOrdered.size()); + for (int i = 0; i < 3; i++) { + assertEquals("value" + i, listOrdered.get(i).toString()); + } + assertEquals(XContentParser.Token.END_ARRAY, parser.currentToken()); + } + { + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("map", parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + Map map = parser.map(); + assertEquals(2, map.size()); + assertEquals("value1", map.get("key1").toString()); + assertEquals("value2", map.get("key2").toString()); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + } + { + assertEquals("mapOrdered", parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + Map mapOrdered = parser.mapOrdered(); + assertEquals(2, mapOrdered.size()); + assertEquals("value3", mapOrdered.get("key3").toString()); + assertEquals("value4", mapOrdered.get("key4").toString()); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + } + { + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("mapStrings", parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + Map mapStrings = parser.map(); + assertEquals(2, mapStrings.size()); + assertEquals("value5", mapStrings.get("key5").toString()); + assertEquals("value6", mapStrings.get("key6").toString()); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + } + { + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals("mapSupplier", parser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + Map mapSupplier = parser.map(HashMap::new, XContentParser::text); + assertEquals(2, mapSupplier.size()); + assertEquals("value7", mapSupplier.get("key7").toString()); + assertEquals("value8", mapSupplier.get("key8").toString()); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + } + + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + return new CustomParserQueryBuilder(); + } + } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DotExpandingXContentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DotExpandingXContentParser.java index 0d06eabb4f19b..6cf44ba6bc447 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DotExpandingXContentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DotExpandingXContentParser.java @@ -172,34 +172,60 @@ protected XContentParser delegate() { return parsers.peek(); } + /* + The following methods (map* and list*) are known not be called by DocumentParser when parsing documents, but we support indexing + percolator queries which are also parsed through DocumentParser, and their parsing code is completely up to each query, which are + also pluggable. That means that this parser needs to fully support parsing arbitrary content, when dots expansion is turned off. + We do throw UnsupportedOperationException when dots expansion is enabled as we don't expect such methods to be ever called in + those circumstances. + */ + @Override public Map map() throws IOException { + if (contentPath.isWithinLeafObject()) { + return super.map(); + } throw new UnsupportedOperationException(); } @Override public Map mapOrdered() throws IOException { + if (contentPath.isWithinLeafObject()) { + return super.mapOrdered(); + } throw new UnsupportedOperationException(); } @Override public Map mapStrings() throws IOException { + if (contentPath.isWithinLeafObject()) { + return super.mapStrings(); + } throw new UnsupportedOperationException(); } @Override public Map map(Supplier> mapFactory, CheckedFunction mapValueParser) throws IOException { + if (contentPath.isWithinLeafObject()) { + return super.map(mapFactory, mapValueParser); + } throw new UnsupportedOperationException(); } @Override public List list() throws IOException { + if (contentPath.isWithinLeafObject()) { + return super.list(); + } throw new UnsupportedOperationException(); } @Override public List listOrderedMap() throws IOException { + if (contentPath.isWithinLeafObject()) { + return super.listOrderedMap(); + } throw new UnsupportedOperationException(); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DotExpandingXContentParserTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DotExpandingXContentParserTests.java index f9fcbebe221d4..c55ffaaa70a16 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DotExpandingXContentParserTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DotExpandingXContentParserTests.java @@ -15,6 +15,9 @@ import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class DotExpandingXContentParserTests extends ESTestCase { @@ -348,4 +351,94 @@ public void testGetTokenLocation() throws IOException { assertNull(dotExpandedParser.nextToken()); assertNull(expectedParser.nextToken()); } + + public void testParseMapUOE() throws Exception { + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, ""), + new ContentPath() + ); + expectThrows(UnsupportedOperationException.class, dotExpandedParser::map); + } + + public void testParseMapOrderedUOE() throws Exception { + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, ""), + new ContentPath() + ); + expectThrows(UnsupportedOperationException.class, dotExpandedParser::mapOrdered); + } + + public void testParseMapStringsUOE() throws Exception { + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, ""), + new ContentPath() + ); + expectThrows(UnsupportedOperationException.class, dotExpandedParser::mapStrings); + } + + public void testParseMapSupplierUOE() throws Exception { + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, ""), + new ContentPath() + ); + expectThrows(UnsupportedOperationException.class, () -> dotExpandedParser.map(HashMap::new, XContentParser::text)); + } + + public void testParseMap() throws Exception { + String jsonInput = """ + {"params":{"one":"one", + "two":"two"}}\ + """; + + ContentPath contentPath = new ContentPath(); + contentPath.setWithinLeafObject(true); + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, jsonInput), + contentPath + ); + assertEquals(XContentParser.Token.START_OBJECT, dotExpandedParser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, dotExpandedParser.nextToken()); + assertEquals("params", dotExpandedParser.currentName()); + assertEquals(XContentParser.Token.START_OBJECT, dotExpandedParser.nextToken()); + Map map = dotExpandedParser.map(); + assertEquals(2, map.size()); + assertEquals("one", map.get("one")); + assertEquals("two", map.get("two")); + } + + public void testParseListUOE() throws Exception { + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, ""), + new ContentPath() + ); + expectThrows(UnsupportedOperationException.class, dotExpandedParser::list); + } + + public void testParseListOrderedUOE() throws Exception { + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, ""), + new ContentPath() + ); + expectThrows(UnsupportedOperationException.class, dotExpandedParser::listOrderedMap); + } + + public void testParseList() throws Exception { + String jsonInput = """ + {"params":["one","two"]}\ + """; + + ContentPath contentPath = new ContentPath(); + contentPath.setWithinLeafObject(true); + XContentParser dotExpandedParser = DotExpandingXContentParser.expandDots( + createParser(JsonXContent.jsonXContent, jsonInput), + contentPath + ); + assertEquals(XContentParser.Token.START_OBJECT, dotExpandedParser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, dotExpandedParser.nextToken()); + assertEquals("params", dotExpandedParser.currentName()); + List list = dotExpandedParser.list(); + assertEquals(2, list.size()); + assertEquals("one", list.get(0)); + assertEquals("two", list.get(1)); + } } From 482cf3b6768e70759abe2cf64f4e88b338247aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Tue, 24 Oct 2023 11:24:32 +0200 Subject: [PATCH 076/203] Reapply "Making yaml tests version selector parser compatible with versions returned by Build" (#100953) * Compatible version parsing in YAML tests * Propagate exception in case of non-semantic version where one is expected * Removed remove of SNAPSHOT (no longer needed) --- .../test/rest/yaml/section/DoSection.java | 39 +++++++++++++++---- .../rest/yaml/section/DoSectionTests.java | 35 +++++++++++++++-- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/DoSection.java b/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/DoSection.java index 0220c0931bca1..6e9107152c6f7 100644 --- a/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/DoSection.java +++ b/test/yaml-rest-runner/src/main/java/org/elasticsearch/test/rest/yaml/section/DoSection.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.client.HasAttributeNodeSelector; import org.elasticsearch.client.Node; @@ -38,6 +39,7 @@ import java.util.Objects; import java.util.Set; import java.util.TreeMap; +import java.util.function.Predicate; import java.util.regex.Pattern; import static java.util.Collections.emptyList; @@ -626,24 +628,45 @@ public String toString() { return result; } + private static boolean matchWithRange(String nodeVersionString, List acceptedVersionRanges, XContentLocation location) { + try { + Version version = Version.fromString(nodeVersionString); + return acceptedVersionRanges.stream().anyMatch(v -> v.contains(version)); + } catch (IllegalArgumentException e) { + throw new XContentParseException( + location, + "[version] range node selector expects a semantic version format (x.y.z), but found " + nodeVersionString, + e + ); + } + } + private static NodeSelector parseVersionSelector(XContentParser parser) throws IOException { if (false == parser.currentToken().isValue()) { throw new XContentParseException(parser.getTokenLocation(), "expected [version] to be a value"); } - List skipVersionRanges = parser.text().equals("current") - ? List.of(new VersionRange(Version.CURRENT, Version.CURRENT)) - : SkipSection.parseVersionRanges(parser.text()); + + final Predicate nodeMatcher; + final String versionSelectorString; + if (parser.text().equals("current")) { + nodeMatcher = nodeVersion -> Build.current().version().equals(nodeVersion); + versionSelectorString = "version is " + Build.current().version() + " (current)"; + } else { + var acceptedVersionRange = SkipSection.parseVersionRanges(parser.text()); + nodeMatcher = nodeVersion -> matchWithRange(nodeVersion, acceptedVersionRange, parser.getTokenLocation()); + versionSelectorString = "version ranges " + acceptedVersionRange; + } + return new NodeSelector() { @Override public void select(Iterable nodes) { for (Iterator itr = nodes.iterator(); itr.hasNext();) { Node node = itr.next(); - if (node.getVersion() == null) { + String versionString = node.getVersion(); + if (versionString == null) { throw new IllegalStateException("expected [version] metadata to be set but got " + node); } - Version version = Version.fromString(node.getVersion()); - boolean skip = skipVersionRanges.stream().anyMatch(v -> v.contains(version)); - if (false == skip) { + if (nodeMatcher.test(versionString) == false) { itr.remove(); } } @@ -651,7 +674,7 @@ public void select(Iterable nodes) { @Override public String toString() { - return "version ranges " + skipVersionRanges; + return versionSelectorString; } }; } diff --git a/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/DoSectionTests.java b/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/DoSectionTests.java index 88c5fdfdb1e78..501f83bb02e1f 100644 --- a/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/DoSectionTests.java +++ b/test/yaml-rest-runner/src/test/java/org/elasticsearch/test/rest/yaml/section/DoSectionTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.test.rest.yaml.section; import org.apache.http.HttpHost; +import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.client.Node; import org.elasticsearch.client.NodeSelector; @@ -19,6 +20,7 @@ import org.elasticsearch.test.rest.yaml.ClientYamlTestExecutionContext; import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse; import org.elasticsearch.xcontent.XContentLocation; +import org.elasticsearch.xcontent.XContentParseException; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.yaml.YamlXContent; import org.hamcrest.MatcherAssert; @@ -36,6 +38,7 @@ import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -576,7 +579,7 @@ public void testParseDoSectionAllowedWarnings() throws Exception { assertThat(e.getMessage(), equalTo("the warning [foo] was both allowed and expected")); } - public void testNodeSelectorByVersion() throws IOException { + public void testNodeSelectorByVersionRange() throws IOException { parser = createParser(YamlXContent.yamlXContent, """ node_selector: version: 5.2.0-6.0.0 @@ -626,6 +629,28 @@ public void testNodeSelectorByVersion() throws IOException { } } + public void testNodeSelectorByVersionRangeFailsWithNonSemanticVersion() throws IOException { + parser = createParser(YamlXContent.yamlXContent, """ + node_selector: + version: 5.2.0-6.0.0 + indices.get_field_mapping: + index: test_index"""); + + DoSection doSection = DoSection.parse(parser); + assertNotSame(NodeSelector.ANY, doSection.getApiCallSection().getNodeSelector()); + Node nonSemantic = nodeWithVersion("abddef"); + List nodes = new ArrayList<>(); + + var exception = expectThrows( + XContentParseException.class, + () -> doSection.getApiCallSection().getNodeSelector().select(List.of(nonSemantic)) + ); + assertThat( + exception.getMessage(), + endsWith("[version] range node selector expects a semantic version format (x.y.z), but found abddef") + ); + } + public void testNodeSelectorCurrentVersion() throws IOException { parser = createParser(YamlXContent.yamlXContent, """ node_selector: @@ -638,14 +663,16 @@ public void testNodeSelectorCurrentVersion() throws IOException { Node v170 = nodeWithVersion("1.7.0"); Node v521 = nodeWithVersion("5.2.1"); Node v550 = nodeWithVersion("5.5.0"); - Node current = nodeWithVersion(Version.CURRENT.toString()); + Node oldCurrent = nodeWithVersion(Version.CURRENT.toString()); + Node newCurrent = nodeWithVersion(Build.current().version()); List nodes = new ArrayList<>(); nodes.add(v170); nodes.add(v521); nodes.add(v550); - nodes.add(current); + nodes.add(oldCurrent); + nodes.add(newCurrent); doSection.getApiCallSection().getNodeSelector().select(nodes); - assertEquals(List.of(current), nodes); + assertEquals(List.of(oldCurrent, newCurrent), nodes); } private static Node nodeWithVersion(String version) { From b7145496897523931061c44623f0cc7ffbda4394 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 24 Oct 2023 10:59:06 +0100 Subject: [PATCH 077/203] Improve cancellation in repo analysis (#101213) Today we rely on an `isRunning` check to check for task cancellation, but since #82685 we can actively record the failure arising from the cancellation using a `CancellationListener`. Closes #101197 --- .../testkit/RepositoryAnalyzeAction.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java index f71ad161aa865..2facbfd8f460c 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java @@ -437,22 +437,10 @@ private void fail(Exception e) { } /** - * Check that we haven't already failed or been cancelled or timed out; if newly cancelled or timed out then record this as the root - * cause of failure. + * Check that we haven't already failed (including cancellation and timing out). */ private boolean isRunning() { - if (failure.get() != null) { - return false; - } - - if (task.isCancelled()) { - setFirstFailure(new RepositoryVerificationException(request.repositoryName, "verification cancelled")); - // if this CAS failed then we're failing for some other reason, nbd; also if the task is cancelled then its descendants are - // also cancelled, so no further action is needed either way. - return false; - } - - return true; + return failure.get() == null; } private class CheckForCancelListener implements ActionListener { @@ -485,6 +473,8 @@ public void run() { cancellationListener.addTimeout(request.getTimeout(), repository.threadPool(), EsExecutors.DIRECT_EXECUTOR_SERVICE); cancellationListener.addListener(new CheckForCancelListener()); + task.addListener(() -> setFirstFailure(new RepositoryVerificationException(request.repositoryName, "analysis cancelled"))); + final Random random = new Random(request.getSeed()); final List nodes = getSnapshotNodes(discoveryNodes); From 3031595d6a234c31abe9f7da74a86a72d30d0652 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 24 Oct 2023 11:46:03 +0100 Subject: [PATCH 078/203] Clean up action registration for repo analysis (#101211) - Removes the registration of the inner actions via `getActions()`. - Replace the outer action's `ActionType` subclass using `localOnly()`. - Collapses each outer `Action` class with the inner `TransportAction`. - Tightens up some unnecessary `public` visibility. Closes #101198 --- .../operator/DefaultOperatorOnlyRegistry.java | 6 +- .../blobstore/testkit/BlobAnalyzeAction.java | 91 +++---- .../ContendedRegisterAnalyzeAction.java | 225 ++++++++-------- .../testkit/GetBlobChecksumAction.java | 255 ++++++++---------- .../testkit/RepositoryAnalyzeAction.java | 160 ++++++----- .../testkit/SnapshotRepositoryTestKit.java | 8 +- .../UncontendedRegisterAnalyzeAction.java | 167 ++++++------ 7 files changed, 415 insertions(+), 497 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java index e31824b2eb8ab..2f5f809702ccd 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/operator/DefaultOperatorOnlyRegistry.java @@ -41,12 +41,8 @@ public class DefaultOperatorOnlyRegistry implements OperatorOnlyRegistry { // Autoscaling does not publish its actions to core, literal strings are needed. "cluster:admin/autoscaling/put_autoscaling_policy", "cluster:admin/autoscaling/delete_autoscaling_policy", - // Repository analysis actions are not mentioned in core, literal strings are needed. + // Repository analysis is not mentioned in core, a literal string is needed. "cluster:admin/repository/analyze", - "cluster:admin/repository/analyze/blob", - "cluster:admin/repository/analyze/blob/read", - "cluster:admin/repository/analyze/register", - "cluster:admin/repository/analyze/register/uncontended", // Node shutdown APIs are operator only "cluster:admin/shutdown/create", "cluster:admin/shutdown/get", diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java index afd1ac49c9e1b..d9c85eb37aaa0 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/BlobAnalyzeAction.java @@ -15,7 +15,6 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.ActionType; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.GroupedActionListener; import org.elasticsearch.action.support.HandledTransportAction; @@ -26,7 +25,6 @@ import org.elasticsearch.common.blobstore.OperationPurpose; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Iterators; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -148,55 +146,38 @@ * unnecessary resources. * */ -public class BlobAnalyzeAction extends ActionType { +class BlobAnalyzeAction extends HandledTransportAction { private static final Logger logger = LogManager.getLogger(BlobAnalyzeAction.class); - public static final BlobAnalyzeAction INSTANCE = new BlobAnalyzeAction(); - public static final String NAME = "cluster:admin/repository/analyze/blob"; + static final String NAME = "cluster:admin/repository/analyze/blob"; - private BlobAnalyzeAction() { - super(NAME, Response::new); - } - - public static class TransportAction extends HandledTransportAction { + private final RepositoriesService repositoriesService; + private final TransportService transportService; - private static final Logger logger = BlobAnalyzeAction.logger; - - private final RepositoriesService repositoriesService; - private final TransportService transportService; + BlobAnalyzeAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) { + super(NAME, transportService, actionFilters, Request::new, transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT)); + this.repositoriesService = repositoriesService; + this.transportService = transportService; + } - @Inject - public TransportAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) { - super( - NAME, - transportService, - actionFilters, - Request::new, - transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT) - ); - this.repositoriesService = repositoriesService; - this.transportService = transportService; + @Override + protected void doExecute(Task task, Request request, ActionListener listener) { + final Repository repository = repositoriesService.repository(request.getRepositoryName()); + if (repository instanceof BlobStoreRepository == false) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); } + if (repository.isReadOnly()) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); + } + final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; + final BlobPath path = blobStoreRepository.basePath().add(request.blobPath); + final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path); - @Override - protected void doExecute(Task task, Request request, ActionListener listener) { - final Repository repository = repositoriesService.repository(request.getRepositoryName()); - if (repository instanceof BlobStoreRepository == false) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); - } - if (repository.isReadOnly()) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); - } - final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; - final BlobPath path = blobStoreRepository.basePath().add(request.blobPath); - final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path); - - logger.trace("handling [{}]", request); + logger.trace("handling [{}]", request); - assert task instanceof CancellableTask; - new BlobAnalysis(transportService, (CancellableTask) task, request, blobStoreRepository, blobContainer, listener).run(); - } + assert task instanceof CancellableTask; + new BlobAnalysis(transportService, (CancellableTask) task, request, blobStoreRepository, blobContainer, listener).run(); } /** @@ -208,7 +189,7 @@ protected void doExecute(Task task, Request request, ActionListener li /** * Analysis on a single blob, performing the write(s) and orchestrating the read(s). */ - static class BlobAnalysis { + private static class BlobAnalysis { private final TransportService transportService; private final CancellableTask task; private final BlobAnalyzeAction.Request request; @@ -657,7 +638,7 @@ private WriteDetails(long bytesWritten, long elapsedNanos, long throttledNanos, } } - public static class Request extends ActionRequest { + static class Request extends ActionRequest { private final String repositoryName; private final String blobPath; private final String blobName; @@ -775,29 +756,29 @@ public Task createTask(long id, String type, String action, TaskId parentTaskId, return new CancellableTask(id, type, action, getDescription(), parentTaskId, headers); } - public String getRepositoryName() { + String getRepositoryName() { return repositoryName; } - public String getBlobPath() { + String getBlobPath() { return blobPath; } - public String getBlobName() { + String getBlobName() { return blobName; } - public long getTargetLength() { + long getTargetLength() { return targetLength; } - public boolean getAbortWrite() { + boolean getAbortWrite() { return abortWrite; } } - public static class Response extends ActionResponse implements ToXContentObject { + static class Response extends ActionResponse implements ToXContentObject { private final String nodeId; private final String nodeName; @@ -813,7 +794,7 @@ public static class Response extends ActionResponse implements ToXContentObject private final long writeThrottledNanos; private final List readDetails; - public Response( + Response( String nodeId, String nodeName, String blobName, @@ -841,7 +822,7 @@ public Response( this.readDetails = readDetails; } - public Response(StreamInput in) throws IOException { + Response(StreamInput in) throws IOException { super(in); nodeId = in.readString(); nodeName = in.readString(); @@ -928,7 +909,7 @@ long getChecksumBytes() { } } - public static class ReadDetail implements Writeable, ToXContentFragment { + static class ReadDetail implements Writeable, ToXContentFragment { private final String nodeId; private final String nodeName; @@ -938,7 +919,7 @@ public static class ReadDetail implements Writeable, ToXContentFragment { private final long throttleNanos; private final long elapsedNanos; - public ReadDetail( + ReadDetail( String nodeId, String nodeName, boolean beforeWriteComplete, @@ -956,7 +937,7 @@ public ReadDetail( this.elapsedNanos = elapsedNanos; } - public ReadDetail(StreamInput in) throws IOException { + ReadDetail(StreamInput in) throws IOException { nodeId = in.readString(); nodeName = in.readString(); beforeWriteComplete = in.readBoolean(); diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java index 18a18041c103c..8058b270d310e 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/ContendedRegisterAnalyzeAction.java @@ -15,7 +15,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionRunnable; -import org.elasticsearch.action.ActionType; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.common.Strings; @@ -25,7 +24,6 @@ import org.elasticsearch.common.blobstore.OptionalBytesReference; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.util.ByteUtils; @@ -41,158 +39,145 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; /** * An action which atomically increments a register using {@link BlobContainer#compareAndExchangeRegister}. There will be multiple parties * accessing the register concurrently in order to test behaviour under contention. */ -public class ContendedRegisterAnalyzeAction extends ActionType { +class ContendedRegisterAnalyzeAction extends HandledTransportAction { private static final Logger logger = LogManager.getLogger(ContendedRegisterAnalyzeAction.class); - public static final ContendedRegisterAnalyzeAction INSTANCE = new ContendedRegisterAnalyzeAction(); - public static final String NAME = "cluster:admin/repository/analyze/register"; + static final String NAME = "cluster:admin/repository/analyze/register"; - private ContendedRegisterAnalyzeAction() { - super(NAME, in -> ActionResponse.Empty.INSTANCE); - } - - public static class TransportAction extends HandledTransportAction { - - private static final Logger logger = ContendedRegisterAnalyzeAction.logger; + private final RepositoriesService repositoriesService; + private final Executor executor; - private final RepositoriesService repositoriesService; - private final ExecutorService executor; + ContendedRegisterAnalyzeAction( + TransportService transportService, + ActionFilters actionFilters, + RepositoriesService repositoriesService + ) { + super(NAME, transportService, actionFilters, Request::new, transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT)); + this.repositoriesService = repositoriesService; + this.executor = transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT); + } - @Inject - public TransportAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) { - super( - NAME, - transportService, - actionFilters, - Request::new, - transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT) - ); - this.repositoriesService = repositoriesService; - this.executor = transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT); + @Override + protected void doExecute(Task task, Request request, ActionListener outerListenerOld) { + final var outerListener = ActionListener.assertOnce(outerListenerOld); + final Repository repository = repositoriesService.repository(request.getRepositoryName()); + if (repository instanceof BlobStoreRepository == false) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); + } + if (repository.isReadOnly()) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); } + final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; + final BlobPath path = blobStoreRepository.basePath().add(request.getContainerPath()); + final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path); - @Override - protected void doExecute(Task task, Request request, ActionListener outerListenerOld) { - final var outerListener = ActionListener.assertOnce(outerListenerOld); - final Repository repository = repositoriesService.repository(request.getRepositoryName()); - if (repository instanceof BlobStoreRepository == false) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); - } - if (repository.isReadOnly()) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); - } - final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; - final BlobPath path = blobStoreRepository.basePath().add(request.getContainerPath()); - final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path); + logger.trace("handling [{}]", request); - logger.trace("handling [{}]", request); + assert task instanceof CancellableTask; - assert task instanceof CancellableTask; + final String registerName = request.getRegisterName(); + final ActionListener initialValueListener = new ActionListener<>() { + @Override + public void onResponse(OptionalBytesReference maybeInitialBytes) { + final long initialValue = maybeInitialBytes.isPresent() ? longFromBytes(maybeInitialBytes.bytesReference()) : 0L; - final String registerName = request.getRegisterName(); - final ActionListener initialValueListener = new ActionListener<>() { - @Override - public void onResponse(OptionalBytesReference maybeInitialBytes) { - final long initialValue = maybeInitialBytes.isPresent() ? longFromBytes(maybeInitialBytes.bytesReference()) : 0L; + ActionListener.run(outerListener.map(ignored -> ActionResponse.Empty.INSTANCE), l -> { + if (initialValue < 0 || initialValue >= request.getRequestCount()) { + throw new IllegalStateException("register holds unexpected value [" + initialValue + "]"); + } - ActionListener.run(outerListener.map(ignored -> ActionResponse.Empty.INSTANCE), l -> { - if (initialValue < 0 || initialValue >= request.getRequestCount()) { - throw new IllegalStateException("register holds unexpected value [" + initialValue + "]"); - } + class Execution extends ActionRunnable { + private long currentValue; - class Execution extends ActionRunnable { - private long currentValue; + private final ActionListener witnessListener; - private final ActionListener witnessListener; + Execution(long currentValue) { + super(l); + this.currentValue = currentValue; + this.witnessListener = listener.delegateFailure(this::handleWitness); + } - Execution(long currentValue) { - super(l); - this.currentValue = currentValue; - this.witnessListener = listener.delegateFailure(this::handleWitness); + @Override + protected void doRun() { + if (((CancellableTask) task).notifyIfCancelled(listener) == false) { + blobContainer.compareAndExchangeRegister( + OperationPurpose.REPOSITORY_ANALYSIS, + registerName, + bytesFromLong(currentValue), + bytesFromLong(currentValue + 1L), + witnessListener + ); } + } - @Override - protected void doRun() { - if (((CancellableTask) task).notifyIfCancelled(listener) == false) { - blobContainer.compareAndExchangeRegister( - OperationPurpose.REPOSITORY_ANALYSIS, - registerName, - bytesFromLong(currentValue), - bytesFromLong(currentValue + 1L), - witnessListener - ); - } + private void handleWitness(ActionListener delegate, OptionalBytesReference witnessOrEmpty) { + if (witnessOrEmpty.isPresent() == false) { + // Concurrent activity prevented us from updating the value, or even reading the concurrently-updated + // result, so we must just try again. + executor.execute(Execution.this); + return; } - private void handleWitness(ActionListener delegate, OptionalBytesReference witnessOrEmpty) { - if (witnessOrEmpty.isPresent() == false) { - // Concurrent activity prevented us from updating the value, or even reading the concurrently-updated - // result, so we must just try again. - executor.execute(Execution.this); - return; - } - - final long witness = longFromBytes(witnessOrEmpty.bytesReference()); - if (witness == currentValue) { - delegate.onResponse(null); - } else if (witness < currentValue || witness >= request.getRequestCount()) { - delegate.onFailure(new IllegalStateException("register holds unexpected value [" + witness + "]")); - } else { - currentValue = witness; - executor.execute(Execution.this); - } + final long witness = longFromBytes(witnessOrEmpty.bytesReference()); + if (witness == currentValue) { + delegate.onResponse(null); + } else if (witness < currentValue || witness >= request.getRequestCount()) { + delegate.onFailure(new IllegalStateException("register holds unexpected value [" + witness + "]")); + } else { + currentValue = witness; + executor.execute(Execution.this); } - } - new Execution(initialValue).run(); + } - }); - } + new Execution(initialValue).run(); - @Override - public void onFailure(Exception e) { - if (e instanceof UnsupportedOperationException) { - // Registers are not supported on all repository types, and that's ok. If it's not supported here then the final - // check will also be unsupported, so it doesn't matter that we didn't do anything before this successful response. - outerListener.onResponse(ActionResponse.Empty.INSTANCE); - } else { - outerListener.onFailure(e); - } + }); + } + + @Override + public void onFailure(Exception e) { + if (e instanceof UnsupportedOperationException) { + // Registers are not supported on all repository types, and that's ok. If it's not supported here then the final + // check will also be unsupported, so it doesn't matter that we didn't do anything before this successful response. + outerListener.onResponse(ActionResponse.Empty.INSTANCE); + } else { + outerListener.onFailure(e); } - }; - - if (request.getInitialRead() > request.getRequestCount()) { - blobContainer.getRegister(OperationPurpose.REPOSITORY_ANALYSIS, registerName, initialValueListener); - } else { - blobContainer.compareAndExchangeRegister( - OperationPurpose.REPOSITORY_ANALYSIS, - registerName, - bytesFromLong(request.getInitialRead()), - bytesFromLong( - request.getInitialRead() == request.getRequestCount() ? request.getRequestCount() + 1 : request.getInitialRead() - ), - initialValueListener - ); } + }; + + if (request.getInitialRead() > request.getRequestCount()) { + blobContainer.getRegister(OperationPurpose.REPOSITORY_ANALYSIS, registerName, initialValueListener); + } else { + blobContainer.compareAndExchangeRegister( + OperationPurpose.REPOSITORY_ANALYSIS, + registerName, + bytesFromLong(request.getInitialRead()), + bytesFromLong( + request.getInitialRead() == request.getRequestCount() ? request.getRequestCount() + 1 : request.getInitialRead() + ), + initialValueListener + ); } } - public static class Request extends ActionRequest { + static class Request extends ActionRequest { private final String repositoryName; private final String containerPath; private final String registerName; private final int requestCount; private final int initialRead; - public Request(String repositoryName, String containerPath, String registerName, int requestCount, int initialRead) { + Request(String repositoryName, String containerPath, String registerName, int requestCount, int initialRead) { this.repositoryName = repositoryName; this.containerPath = containerPath; this.registerName = registerName; @@ -200,7 +185,7 @@ public Request(String repositoryName, String containerPath, String registerName, this.initialRead = initialRead; } - public Request(StreamInput in) throws IOException { + Request(StreamInput in) throws IOException { super(in); assert in.getTransportVersion().onOrAfter(TransportVersions.V_8_8_0); repositoryName = in.readString(); @@ -226,23 +211,23 @@ public ActionRequestValidationException validate() { return null; } - public String getRepositoryName() { + String getRepositoryName() { return repositoryName; } - public String getContainerPath() { + String getContainerPath() { return containerPath; } - public String getRegisterName() { + String getRegisterName() { return registerName; } - public int getRequestCount() { + int getRequestCount() { return requestCount; } - public int getInitialRead() { + int getInitialRead() { return initialRead; } diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java index 74530824acb06..f706ff79bf073 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/GetBlobChecksumAction.java @@ -13,12 +13,10 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.ActionType; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.common.blobstore.BlobContainer; import org.elasticsearch.common.blobstore.OperationPurpose; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.ByteSizeUnit; @@ -46,158 +44,139 @@ * (possibly the entire blob) and compute its checksum. It is acceptable if the blob is not found but we do not accept the blob being * otherwise unreadable. */ -public class GetBlobChecksumAction extends ActionType { +class GetBlobChecksumAction extends HandledTransportAction { private static final Logger logger = LogManager.getLogger(GetBlobChecksumAction.class); - public static final GetBlobChecksumAction INSTANCE = new GetBlobChecksumAction(); + static final String NAME = "cluster:admin/repository/analyze/blob/read"; - public static final String NAME = "cluster:admin/repository/analyze/blob/read"; + private static final int BUFFER_SIZE = ByteSizeUnit.KB.toIntBytes(8); - private GetBlobChecksumAction() { - super(NAME, Response::new); - } - - public static class TransportAction extends HandledTransportAction { + private final RepositoriesService repositoriesService; - private static final Logger logger = GetBlobChecksumAction.logger; + GetBlobChecksumAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) { + super(NAME, transportService, actionFilters, Request::new, transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT)); + this.repositoriesService = repositoriesService; + } - private static final int BUFFER_SIZE = ByteSizeUnit.KB.toIntBytes(8); + @Override + protected void doExecute(Task task, Request request, ActionListener listener) { - private final RepositoriesService repositoriesService; + assert task instanceof CancellableTask; + CancellableTask cancellableTask = (CancellableTask) task; - @Inject - public TransportAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) { - super( - NAME, - transportService, - actionFilters, - Request::new, - transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT) - ); - this.repositoriesService = repositoriesService; + final Repository repository = repositoriesService.repository(request.getRepositoryName()); + if (repository instanceof BlobStoreRepository == false) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob store repository"); } - @Override - protected void doExecute(Task task, Request request, ActionListener listener) { - - assert task instanceof CancellableTask; - CancellableTask cancellableTask = (CancellableTask) task; - - final Repository repository = repositoriesService.repository(request.getRepositoryName()); - if (repository instanceof BlobStoreRepository == false) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob store repository"); + final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; + final BlobContainer blobContainer = blobStoreRepository.blobStore() + .blobContainer(blobStoreRepository.basePath().add(request.getBlobPath())); + + logger.trace("handling [{}]", request); + + final InputStream rawInputStream; + try { + if (request.isWholeBlob()) { + rawInputStream = blobContainer.readBlob(OperationPurpose.REPOSITORY_ANALYSIS, request.getBlobName()); + } else { + rawInputStream = blobContainer.readBlob( + OperationPurpose.REPOSITORY_ANALYSIS, + request.getBlobName(), + request.getRangeStart(), + request.getRangeLength() + ); } + } catch (FileNotFoundException | NoSuchFileException e) { + logger.trace("blob not found for [{}]", request); + listener.onResponse(Response.BLOB_NOT_FOUND); + return; + } catch (IOException e) { + logger.warn("failed to read blob for [{}]", request); + listener.onFailure(e); + return; + } - final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; - final BlobContainer blobContainer = blobStoreRepository.blobStore() - .blobContainer(blobStoreRepository.basePath().add(request.getBlobPath())); - - logger.trace("handling [{}]", request); - - final InputStream rawInputStream; - try { - if (request.isWholeBlob()) { - rawInputStream = blobContainer.readBlob(OperationPurpose.REPOSITORY_ANALYSIS, request.getBlobName()); - } else { - rawInputStream = blobContainer.readBlob( - OperationPurpose.REPOSITORY_ANALYSIS, - request.getBlobName(), - request.getRangeStart(), - request.getRangeLength() - ); + logger.trace("reading blob for [{}]", request); + + final AtomicLong throttleNanos = new AtomicLong(); + final InputStream throttledInputStream = blobStoreRepository.maybeRateLimitRestores(rawInputStream, throttleNanos::addAndGet); + final CRC32 crc32 = new CRC32(); + final byte[] buffer = new byte[BUFFER_SIZE]; + long bytesRead = 0L; + final long startTimeNanos = System.nanoTime(); + long firstByteNanos = startTimeNanos; + + boolean success = false; + try { + while (true) { + final int readSize; + try { + readSize = throttledInputStream.read(buffer, 0, buffer.length); + } catch (IOException e) { + logger.warn("exception while read blob for [{}]", request); + listener.onFailure(e); + return; } - } catch (FileNotFoundException | NoSuchFileException e) { - logger.trace("blob not found for [{}]", request); - listener.onResponse(Response.BLOB_NOT_FOUND); - return; - } catch (IOException e) { - logger.warn("failed to read blob for [{}]", request); - listener.onFailure(e); - return; - } - - logger.trace("reading blob for [{}]", request); - - final AtomicLong throttleNanos = new AtomicLong(); - final InputStream throttledInputStream = blobStoreRepository.maybeRateLimitRestores(rawInputStream, throttleNanos::addAndGet); - final CRC32 crc32 = new CRC32(); - final byte[] buffer = new byte[BUFFER_SIZE]; - long bytesRead = 0L; - final long startTimeNanos = System.nanoTime(); - long firstByteNanos = startTimeNanos; - - boolean success = false; - try { - while (true) { - final int readSize; - try { - readSize = throttledInputStream.read(buffer, 0, buffer.length); - } catch (IOException e) { - logger.warn("exception while read blob for [{}]", request); - listener.onFailure(e); - return; - } - if (readSize == -1) { - break; - } - - if (readSize > 0) { - if (bytesRead == 0L) { - firstByteNanos = System.nanoTime(); - } + if (readSize == -1) { + break; + } - crc32.update(buffer, 0, readSize); - bytesRead += readSize; + if (readSize > 0) { + if (bytesRead == 0L) { + firstByteNanos = System.nanoTime(); } - if (cancellableTask.isCancelled()) { - throw new RepositoryVerificationException( - request.repositoryName, - "cancelled [" + request.getDescription() + "] after reading [" + bytesRead + "] bytes" - ); - } + crc32.update(buffer, 0, readSize); + bytesRead += readSize; } - success = true; - } finally { - if (success == false) { - IOUtils.closeWhileHandlingException(throttledInputStream); + + if (cancellableTask.isCancelled()) { + throw new RepositoryVerificationException( + request.repositoryName, + "cancelled [" + request.getDescription() + "] after reading [" + bytesRead + "] bytes" + ); } } - try { - throttledInputStream.close(); - } catch (IOException e) { - throw new RepositoryVerificationException( - request.repositoryName, - "failed to close input stream when handling [" + request.getDescription() + "]", - e - ); + success = true; + } finally { + if (success == false) { + IOUtils.closeWhileHandlingException(throttledInputStream); } + } + try { + throttledInputStream.close(); + } catch (IOException e) { + throw new RepositoryVerificationException( + request.repositoryName, + "failed to close input stream when handling [" + request.getDescription() + "]", + e + ); + } - final long endTimeNanos = System.nanoTime(); - - if (request.isWholeBlob() == false && bytesRead != request.getRangeLength()) { - throw new RepositoryVerificationException( - request.repositoryName, - "unexpectedly read [" + bytesRead + "] bytes when handling [" + request.getDescription() + "]" - ); - } + final long endTimeNanos = System.nanoTime(); - final Response response = new Response( - bytesRead, - crc32.getValue(), - firstByteNanos - startTimeNanos, - endTimeNanos - startTimeNanos, - throttleNanos.get() + if (request.isWholeBlob() == false && bytesRead != request.getRangeLength()) { + throw new RepositoryVerificationException( + request.repositoryName, + "unexpectedly read [" + bytesRead + "] bytes when handling [" + request.getDescription() + "]" ); - logger.trace("responding to [{}] with [{}]", request, response); - listener.onResponse(response); } + final Response response = new Response( + bytesRead, + crc32.getValue(), + firstByteNanos - startTimeNanos, + endTimeNanos - startTimeNanos, + throttleNanos.get() + ); + logger.trace("responding to [{}] with [{}]", request, response); + listener.onResponse(response); } - public static class Request extends ActionRequest { + static class Request extends ActionRequest { private final String repositoryName; private final String blobPath; @@ -230,29 +209,29 @@ public ActionRequestValidationException validate() { this.rangeEnd = rangeEnd; } - public String getRepositoryName() { + String getRepositoryName() { return repositoryName; } - public String getBlobPath() { + String getBlobPath() { return blobPath; } - public String getBlobName() { + String getBlobName() { return blobName; } - public long getRangeStart() { + long getRangeStart() { assert isWholeBlob() == false; return rangeStart; } - public long getRangeEnd() { + long getRangeEnd() { assert isWholeBlob() == false; return rangeEnd; } - public long getRangeLength() { + long getRangeLength() { assert isWholeBlob() == false; return rangeEnd - rangeStart; } @@ -303,7 +282,7 @@ public boolean shouldCancelChildrenOnCancellation() { } } - public static class Response extends ActionResponse { + static class Response extends ActionResponse { static Response BLOB_NOT_FOUND = new Response(0L, 0L, 0L, 0L, 0L); @@ -355,27 +334,27 @@ public String toString() { + '}'; } - public long getBytesRead() { + long getBytesRead() { return bytesRead; } - public long getChecksum() { + long getChecksum() { return checksum; } - public long getFirstByteNanos() { + long getFirstByteNanos() { return firstByteNanos; } - public long getElapsedNanos() { + long getElapsedNanos() { return elapsedNanos; } - public long getThrottleNanos() { + long getThrottleNanos() { return throttleNanos; } - public boolean isNotFound() { + boolean isNotFound() { return bytesRead == 0L && checksum == 0L && firstByteNanos == 0L && elapsedNanos == 0L && throttleNanos == 0L; } diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java index 2facbfd8f460c..56e5d5c8c0bb1 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/RepositoryAnalyzeAction.java @@ -92,102 +92,100 @@ * the results. Tries to fail fast by cancelling everything if any child task fails, or the timeout is reached, to avoid consuming * unnecessary resources. On completion, does a best-effort wait until the blob list contains all the expected blobs, then deletes them all. */ -public class RepositoryAnalyzeAction extends ActionType { +public class RepositoryAnalyzeAction extends HandledTransportAction { private static final Logger logger = LogManager.getLogger(RepositoryAnalyzeAction.class); - public static final RepositoryAnalyzeAction INSTANCE = new RepositoryAnalyzeAction(); - public static final String NAME = "cluster:admin/repository/analyze"; + public static final ActionType INSTANCE = ActionType.localOnly("cluster:admin/repository/analyze"); static final String UNCONTENDED_REGISTER_NAME_PREFIX = "test-register-uncontended-"; static final String CONTENDED_REGISTER_NAME_PREFIX = "test-register-contended-"; - private RepositoryAnalyzeAction() { - super(NAME, Response::new); + private final TransportService transportService; + private final ClusterService clusterService; + private final RepositoriesService repositoriesService; + + @Inject + public RepositoryAnalyzeAction( + TransportService transportService, + ActionFilters actionFilters, + ClusterService clusterService, + RepositoriesService repositoriesService + ) { + super(INSTANCE.name(), transportService, actionFilters, RepositoryAnalyzeAction.Request::new, EsExecutors.DIRECT_EXECUTOR_SERVICE); + this.transportService = transportService; + this.clusterService = clusterService; + this.repositoriesService = repositoriesService; + + // construct (and therefore implicitly register) the subsidiary actions + new BlobAnalyzeAction(transportService, actionFilters, repositoriesService); + new GetBlobChecksumAction(transportService, actionFilters, repositoriesService); + new ContendedRegisterAnalyzeAction(transportService, actionFilters, repositoriesService); + new UncontendedRegisterAnalyzeAction(transportService, actionFilters, repositoriesService); } - public static class TransportAction extends HandledTransportAction { + @Override + protected void doExecute(Task task, Request request, ActionListener listener) { + final ClusterState state = clusterService.state(); - private final TransportService transportService; - private final ClusterService clusterService; - private final RepositoriesService repositoriesService; - - @Inject - public TransportAction( - TransportService transportService, - ActionFilters actionFilters, - ClusterService clusterService, - RepositoriesService repositoriesService - ) { - super(NAME, transportService, actionFilters, RepositoryAnalyzeAction.Request::new, EsExecutors.DIRECT_EXECUTOR_SERVICE); - this.transportService = transportService; - this.clusterService = clusterService; - this.repositoriesService = repositoriesService; - } - - @Override - protected void doExecute(Task task, Request request, ActionListener listener) { - final ClusterState state = clusterService.state(); + final ThreadPool threadPool = transportService.getThreadPool(); + request.reseed(threadPool.relativeTimeInMillis()); - final ThreadPool threadPool = transportService.getThreadPool(); - request.reseed(threadPool.relativeTimeInMillis()); - - final DiscoveryNode localNode = transportService.getLocalNode(); - if (isSnapshotNode(localNode)) { - final Repository repository = repositoriesService.repository(request.getRepositoryName()); - if (repository instanceof BlobStoreRepository == false) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); - } - if (repository.isReadOnly()) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); - } - - assert task instanceof CancellableTask; - new AsyncAction( - transportService, - (BlobStoreRepository) repository, - (CancellableTask) task, - request, - state.nodes(), - state.getMinTransportVersion(), - threadPool::relativeTimeInMillis, - listener - ).run(); - return; + final DiscoveryNode localNode = transportService.getLocalNode(); + if (isSnapshotNode(localNode)) { + final Repository repository = repositoriesService.repository(request.getRepositoryName()); + if (repository instanceof BlobStoreRepository == false) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); } - - if (request.getReroutedFrom() != null) { - assert false : request.getReroutedFrom(); - throw new IllegalArgumentException( - "analysis of repository [" - + request.getRepositoryName() - + "] rerouted from [" - + request.getReroutedFrom() - + "] to non-snapshot node" - ); + if (repository.isReadOnly()) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); } - request.reroutedFrom(localNode); - final List snapshotNodes = getSnapshotNodes(state.nodes()); - if (snapshotNodes.isEmpty()) { - listener.onFailure( - new IllegalArgumentException("no snapshot nodes found for analysis of repository [" + request.getRepositoryName() + "]") - ); - } else { - if (snapshotNodes.size() > 1) { - snapshotNodes.remove(state.nodes().getMasterNode()); - } - final DiscoveryNode targetNode = snapshotNodes.get(new Random(request.getSeed()).nextInt(snapshotNodes.size())); - RepositoryAnalyzeAction.logger.trace("rerouting analysis [{}] to [{}]", request.getDescription(), targetNode); - transportService.sendChildRequest( - targetNode, - NAME, - request, - task, - TransportRequestOptions.EMPTY, - new ActionListenerResponseHandler<>(listener, Response::new, TransportResponseHandler.TRANSPORT_WORKER) - ); + assert task instanceof CancellableTask; + new AsyncAction( + transportService, + (BlobStoreRepository) repository, + (CancellableTask) task, + request, + state.nodes(), + state.getMinTransportVersion(), + threadPool::relativeTimeInMillis, + listener + ).run(); + return; + } + + if (request.getReroutedFrom() != null) { + assert false : request.getReroutedFrom(); + throw new IllegalArgumentException( + "analysis of repository [" + + request.getRepositoryName() + + "] rerouted from [" + + request.getReroutedFrom() + + "] to non-snapshot node" + ); + } + + request.reroutedFrom(localNode); + final List snapshotNodes = getSnapshotNodes(state.nodes()); + if (snapshotNodes.isEmpty()) { + listener.onFailure( + new IllegalArgumentException("no snapshot nodes found for analysis of repository [" + request.getRepositoryName() + "]") + ); + } else { + if (snapshotNodes.size() > 1) { + snapshotNodes.remove(state.nodes().getMasterNode()); } + final DiscoveryNode targetNode = snapshotNodes.get(new Random(request.getSeed()).nextInt(snapshotNodes.size())); + RepositoryAnalyzeAction.logger.trace("rerouting analysis [{}] to [{}]", request.getDescription(), targetNode); + transportService.sendChildRequest( + targetNode, + INSTANCE.name(), + request, + task, + TransportRequestOptions.EMPTY, + new ActionListenerResponseHandler<>(listener, Response::new, TransportResponseHandler.TRANSPORT_WORKER) + ); } } diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java index 439542ead868c..96a4d05d2fb4b 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/SnapshotRepositoryTestKit.java @@ -30,13 +30,7 @@ public class SnapshotRepositoryTestKit extends Plugin implements ActionPlugin { @Override public List> getActions() { - return List.of( - new ActionHandler<>(RepositoryAnalyzeAction.INSTANCE, RepositoryAnalyzeAction.TransportAction.class), - new ActionHandler<>(BlobAnalyzeAction.INSTANCE, BlobAnalyzeAction.TransportAction.class), - new ActionHandler<>(GetBlobChecksumAction.INSTANCE, GetBlobChecksumAction.TransportAction.class), - new ActionHandler<>(ContendedRegisterAnalyzeAction.INSTANCE, ContendedRegisterAnalyzeAction.TransportAction.class), - new ActionHandler<>(UncontendedRegisterAnalyzeAction.INSTANCE, UncontendedRegisterAnalyzeAction.TransportAction.class) - ); + return List.of(new ActionHandler<>(RepositoryAnalyzeAction.INSTANCE, RepositoryAnalyzeAction.class)); } @Override diff --git a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java index 9fdb0d7f5228a..5a279aaf6a96f 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java +++ b/x-pack/plugin/snapshot-repo-test-kit/src/main/java/org/elasticsearch/repositories/blobstore/testkit/UncontendedRegisterAnalyzeAction.java @@ -14,7 +14,6 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.ActionType; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.common.Strings; @@ -22,7 +21,6 @@ import org.elasticsearch.common.blobstore.BlobPath; import org.elasticsearch.common.blobstore.OperationPurpose; import org.elasticsearch.common.blobstore.OptionalBytesReference; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.repositories.RepositoriesService; @@ -41,115 +39,102 @@ import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.bytesFromLong; import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes; -public class UncontendedRegisterAnalyzeAction extends ActionType { +class UncontendedRegisterAnalyzeAction extends HandledTransportAction { private static final Logger logger = LogManager.getLogger(UncontendedRegisterAnalyzeAction.class); - public static final UncontendedRegisterAnalyzeAction INSTANCE = new UncontendedRegisterAnalyzeAction(); - public static final String NAME = "cluster:admin/repository/analyze/register/uncontended"; + static final String NAME = "cluster:admin/repository/analyze/register/uncontended"; - private UncontendedRegisterAnalyzeAction() { - super(NAME, in -> ActionResponse.Empty.INSTANCE); - } - - public static class TransportAction extends HandledTransportAction { - - private static final Logger logger = UncontendedRegisterAnalyzeAction.logger; + private final RepositoriesService repositoriesService; - private final RepositoriesService repositoriesService; + UncontendedRegisterAnalyzeAction( + TransportService transportService, + ActionFilters actionFilters, + RepositoriesService repositoriesService + ) { + super(NAME, transportService, actionFilters, Request::new, transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT)); + this.repositoriesService = repositoriesService; + } - @Inject - public TransportAction(TransportService transportService, ActionFilters actionFilters, RepositoriesService repositoriesService) { - super( - NAME, - transportService, - actionFilters, - Request::new, - transportService.getThreadPool().executor(ThreadPool.Names.SNAPSHOT) - ); - this.repositoriesService = repositoriesService; + @Override + protected void doExecute(Task task, Request request, ActionListener outerListener) { + final ActionListener listener = ActionListener.assertOnce(outerListener.map(ignored -> ActionResponse.Empty.INSTANCE)); + final Repository repository = repositoriesService.repository(request.getRepositoryName()); + if (repository instanceof BlobStoreRepository == false) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); } + if (repository.isReadOnly()) { + throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); + } + final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; + final BlobPath path = blobStoreRepository.basePath().add(request.getContainerPath()); + final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path); + + logger.trace("handling [{}]", request); + + assert task instanceof CancellableTask; + blobContainer.compareAndExchangeRegister( + OperationPurpose.REPOSITORY_ANALYSIS, + request.getRegisterName(), + bytesFromLong(request.getExpectedValue()), + bytesFromLong(request.getExpectedValue() + 1), + new ActionListener<>() { + @Override + public void onResponse(OptionalBytesReference optionalBytesReference) { + ActionListener.completeWith(listener, () -> { + if (optionalBytesReference.isPresent() == false) { + throw new RepositoryVerificationException( + repository.getMetadata().name(), + Strings.format( + "uncontended register operation failed: expected [%d] but did not observe any value", + request.getExpectedValue() + ) + ); + } - @Override - protected void doExecute(Task task, Request request, ActionListener outerListener) { - final ActionListener listener = ActionListener.assertOnce(outerListener.map(ignored -> ActionResponse.Empty.INSTANCE)); - final Repository repository = repositoriesService.repository(request.getRepositoryName()); - if (repository instanceof BlobStoreRepository == false) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository"); - } - if (repository.isReadOnly()) { - throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is read-only"); - } - final BlobStoreRepository blobStoreRepository = (BlobStoreRepository) repository; - final BlobPath path = blobStoreRepository.basePath().add(request.getContainerPath()); - final BlobContainer blobContainer = blobStoreRepository.blobStore().blobContainer(path); - - logger.trace("handling [{}]", request); - - assert task instanceof CancellableTask; - blobContainer.compareAndExchangeRegister( - OperationPurpose.REPOSITORY_ANALYSIS, - request.getRegisterName(), - bytesFromLong(request.getExpectedValue()), - bytesFromLong(request.getExpectedValue() + 1), - new ActionListener<>() { - @Override - public void onResponse(OptionalBytesReference optionalBytesReference) { - ActionListener.completeWith(listener, () -> { - if (optionalBytesReference.isPresent() == false) { - throw new RepositoryVerificationException( - repository.getMetadata().name(), - Strings.format( - "uncontended register operation failed: expected [%d] but did not observe any value", - request.getExpectedValue() - ) - ); - } - - final var witness = longFromBytes(optionalBytesReference.bytesReference()); - if (witness != request.getExpectedValue()) { - throw new RepositoryVerificationException( - repository.getMetadata().name(), - Strings.format( - "uncontended register operation failed: expected [%d] but observed [%d]", - request.getExpectedValue(), - witness - ) - ); - } - - return null; - }); - } - - @Override - public void onFailure(Exception e) { - if (e instanceof UnsupportedOperationException) { - // Registers are not supported on all repository types, and that's ok. - listener.onResponse(null); - } else { - listener.onFailure(e); + final var witness = longFromBytes(optionalBytesReference.bytesReference()); + if (witness != request.getExpectedValue()) { + throw new RepositoryVerificationException( + repository.getMetadata().name(), + Strings.format( + "uncontended register operation failed: expected [%d] but observed [%d]", + request.getExpectedValue(), + witness + ) + ); } + + return null; + }); + } + + @Override + public void onFailure(Exception e) { + if (e instanceof UnsupportedOperationException) { + // Registers are not supported on all repository types, and that's ok. + listener.onResponse(null); + } else { + listener.onFailure(e); } } - ); - } + } + ); } - public static class Request extends ActionRequest { + static class Request extends ActionRequest { private final String repositoryName; private final String containerPath; private final String registerName; private final long expectedValue; - public Request(String repositoryName, String containerPath, String registerName, long expectedValue) { + Request(String repositoryName, String containerPath, String registerName, long expectedValue) { this.repositoryName = repositoryName; this.containerPath = containerPath; this.registerName = registerName; this.expectedValue = expectedValue; } - public Request(StreamInput in) throws IOException { + Request(StreamInput in) throws IOException { super(in); assert in.getTransportVersion().onOrAfter(TransportVersions.UNCONTENDED_REGISTER_ANALYSIS_ADDED); repositoryName = in.readString(); @@ -173,19 +158,19 @@ public ActionRequestValidationException validate() { return null; } - public String getRepositoryName() { + String getRepositoryName() { return repositoryName; } - public String getContainerPath() { + String getContainerPath() { return containerPath; } - public String getRegisterName() { + String getRegisterName() { return registerName; } - public long getExpectedValue() { + long getExpectedValue() { return expectedValue; } From 29dbeb469333c2992d9bd629355397b9fa637d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20FOUCRET?= Date: Tue, 24 Oct 2023 14:22:41 +0200 Subject: [PATCH 079/203] Rewrite flaky tests (#101164) --- .../ingest/BulkProcessorFactory.java | 13 ++- .../ingest/BulkProcessorFactoryTests.java | 100 ++++-------------- 2 files changed, 29 insertions(+), 84 deletions(-) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactory.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactory.java index 1b776bd993398..d00fa191b49d0 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactory.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactory.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; import java.util.stream.Collectors; import static org.elasticsearch.xpack.core.ClientHelper.ENT_SEARCH_ORIGIN; @@ -31,16 +32,22 @@ public class BulkProcessorFactory { private final AnalyticsEventIngestConfig config; - private final Client client; + private final Supplier builderSupplier; @Inject public BulkProcessorFactory(Client client, AnalyticsEventIngestConfig config) { - this.client = new OriginSettingClient(client, ENT_SEARCH_ORIGIN); + Client originClient = new OriginSettingClient(client, ENT_SEARCH_ORIGIN); + this.builderSupplier = () -> BulkProcessor2.builder(originClient::bulk, new BulkProcessorListener(), originClient.threadPool()); + this.config = config; + } + + protected BulkProcessorFactory(AnalyticsEventIngestConfig config, Supplier builderSupplier) { + this.builderSupplier = builderSupplier; this.config = config; } public BulkProcessor2 create() { - return BulkProcessor2.builder(client::bulk, new BulkProcessorListener(), client.threadPool()) + return builderSupplier.get() .setMaxNumberOfRetries(config.maxNumberOfRetries()) .setBulkActions(config.maxNumberOfEventsPerBulk()) .setFlushInterval(config.flushDelay()) diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactoryTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactoryTests.java index 3a6899e06c54f..aac7a4212fb7d 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactoryTests.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/analytics/ingest/BulkProcessorFactoryTests.java @@ -7,33 +7,23 @@ package org.elasticsearch.xpack.application.analytics.ingest; -import org.elasticsearch.ElasticsearchStatusException; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.bulk.BulkAction; import org.elasticsearch.action.bulk.BulkProcessor2; -import org.elasticsearch.action.bulk.BulkRequest; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.internal.Client; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.mockito.InOrder; -import org.mockito.Mockito; import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.doAnswer; +import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; public class BulkProcessorFactoryTests extends ESTestCase { @@ -49,83 +39,31 @@ public static void afterClass() { ThreadPool.terminate(testThreadPool, 30, TimeUnit.SECONDS); } - public void testFlushDelay() throws Exception { - AnalyticsEventIngestConfig config = mock(AnalyticsEventIngestConfig.class); - doReturn(ByteSizeValue.ofMb(10)).when(config).maxBytesInFlight(); - doReturn(TimeValue.timeValueSeconds(1)).when(config).flushDelay(); - doReturn(10).when(config).maxNumberOfEventsPerBulk(); - - Client client = mock(Client.class); - - doReturn(testThreadPool).when(client).threadPool(); - BulkProcessor2 bulkProcessor = new BulkProcessorFactory(client, config).create(); - IndexRequest indexRequest = mock(IndexRequest.class); - bulkProcessor.add(indexRequest); - - assertBusy(() -> verify(client).execute(any(BulkAction.class), argThat((BulkRequest bulkRequest) -> { - assertThat(bulkRequest.numberOfActions(), equalTo(1)); - assertThat(bulkRequest.requests().stream().findFirst().get(), equalTo(indexRequest)); - return true; - }), any()), 1, TimeUnit.SECONDS); + public void testDefaultConstructor() throws Exception { + BulkProcessorFactory factory = new BulkProcessorFactory(mock(Client.class), mock(AnalyticsEventIngestConfig.class)); + assertThat(factory.create(), instanceOf(BulkProcessor2.class)); } - public void testMaxBulkActions() throws InterruptedException { + public void testConfigValueAreUsed() throws Exception { + TimeValue flushDelay = TimeValue.parseTimeValue(randomTimeValue(), "random time value"); int maxBulkActions = randomIntBetween(1, 10); - int totalEvents = randomIntBetween(1, 5) * maxBulkActions + randomIntBetween(1, maxBulkActions); + int numberOfRetries = between(0, 5); + ByteSizeValue maxBytesInFlight = randomByteSizeValue(); AnalyticsEventIngestConfig config = mock(AnalyticsEventIngestConfig.class); + doReturn(flushDelay).when(config).flushDelay(); doReturn(maxBulkActions).when(config).maxNumberOfEventsPerBulk(); - doReturn(ByteSizeValue.ofMb(10)).when(config).maxBytesInFlight(); - - Client client = mock(Client.class); - InOrder inOrder = Mockito.inOrder(client); - - doReturn(testThreadPool).when(client).threadPool(); - BulkProcessor2 bulkProcessor = new BulkProcessorFactory(client, config).create(); - - for (int i = 0; i < totalEvents; i++) { - bulkProcessor.add(mock(IndexRequest.class)); - } - - inOrder.verify(client, times(totalEvents / maxBulkActions)).execute(any(BulkAction.class), argThat((BulkRequest bulkRequest) -> { - // Verify a bulk is executed immediately with maxNumberOfEventsPerBulk is reached. - assertThat(bulkRequest.numberOfActions(), equalTo(maxBulkActions)); - return true; - }), any()); - - bulkProcessor.awaitClose(1, TimeUnit.SECONDS); - - if (totalEvents % maxBulkActions > 0) { - inOrder.verify(client).execute(any(BulkAction.class), argThat((BulkRequest bulkRequest) -> { - // Verify another bulk with only 1 event (the remaining) is executed when closing the processor. - assertThat(bulkRequest.numberOfActions(), equalTo(totalEvents % maxBulkActions)); - return true; - }), any()); - } - } - - public void testMaxRetries() { - int numberOfRetries = between(0, 5); - AnalyticsEventIngestConfig config = mock(AnalyticsEventIngestConfig.class); - doReturn(1).when(config).maxNumberOfEventsPerBulk(); + doReturn(maxBytesInFlight).when(config).maxBytesInFlight(); doReturn(numberOfRetries).when(config).maxNumberOfRetries(); - doReturn(ByteSizeValue.ofMb(10)).when(config).maxBytesInFlight(); - Client client = mock(Client.class); - doAnswer(i -> { - i.getArgument(2, ActionListener.class).onFailure(new ElasticsearchStatusException("", RestStatus.TOO_MANY_REQUESTS)); - return null; - }).when(client).execute(any(), any(), any()); - doReturn(testThreadPool).when(client).threadPool(); - BulkProcessor2 bulkProcessor = new BulkProcessorFactory(client, config).create(); + BulkProcessor2.Builder baseBuilder = spy(BulkProcessor2.builder(mock(), new BulkProcessorFactory.BulkProcessorListener(), mock())); + BulkProcessorFactory factory = new BulkProcessorFactory(config, () -> baseBuilder); - IndexRequest indexRequest = mock(IndexRequest.class); - bulkProcessor.add(indexRequest); + assertThat(factory.create(), instanceOf(BulkProcessor2.class)); - verify(client, times(numberOfRetries + 1)).execute(any(BulkAction.class), argThat((BulkRequest bulkRequest) -> { - assertThat(bulkRequest.numberOfActions(), equalTo(1)); - assertThat(bulkRequest.requests().stream().findFirst().get(), equalTo(indexRequest)); - return true; - }), any()); + verify(baseBuilder).setFlushInterval(eq(flushDelay)); + verify(baseBuilder).setBulkActions(eq(maxBulkActions)); + verify(baseBuilder).setMaxNumberOfRetries(numberOfRetries); + verify(baseBuilder).setMaxBytesInFlight(maxBytesInFlight); } } From 4679b095a044ff44baa65969c2093a53026daf04 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Tue, 24 Oct 2023 16:18:51 +0300 Subject: [PATCH 080/203] ESQL: mv_expand pushes down a limit copy and keeps the limit after it untouched (#100782) - allow mv_expand to push down limit and project past it - accept a limit after mv_expand when there is also a second limit before the mv_expand - adds a default TopN for cases when there is only a sort at Lucene level - adds OrderBy node type to the exceptions for duplicating the limit after mv_expand --- docs/changelog/100782.yaml | 8 + .../src/main/resources/mv_expand.csv-spec | 127 ++++++ .../xpack/esql/execution/PlanExecutor.java | 5 +- .../LocalLogicalOptimizerContext.java | 34 +- .../optimizer/LocalLogicalPlanOptimizer.java | 5 +- .../optimizer/LogicalOptimizerContext.java | 43 ++ .../esql/optimizer/LogicalPlanOptimizer.java | 152 ++++++- .../elasticsearch/xpack/esql/CsvTests.java | 4 +- .../LocalLogicalPlanOptimizerTests.java | 2 +- .../LocalPhysicalPlanOptimizerTests.java | 2 +- .../optimizer/LogicalPlanOptimizerTests.java | 386 +++++++++++++++++- .../optimizer/PhysicalPlanOptimizerTests.java | 2 +- .../xpack/esql/planner/FilterTests.java | 3 +- .../esql/plugin/DataNodeRequestTests.java | 3 +- 14 files changed, 755 insertions(+), 21 deletions(-) create mode 100644 docs/changelog/100782.yaml create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalOptimizerContext.java diff --git a/docs/changelog/100782.yaml b/docs/changelog/100782.yaml new file mode 100644 index 0000000000000..c6007bfb4d9ba --- /dev/null +++ b/docs/changelog/100782.yaml @@ -0,0 +1,8 @@ +pr: 100782 +summary: "ESQL: `mv_expand` pushes down limit and project and keep the limit after\ + \ it untouched" +area: ES|QL +type: bug +issues: + - 99971 + - 100774 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec index ae27e8f56f9f7..dd1abf59f8348 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec @@ -98,3 +98,130 @@ sum_a:long | b:integer 12555000 | 29 12555000 | 30 ; + +expandAfterSort1 +from employees | keep job_positions, emp_no | sort emp_no | mv_expand job_positions | limit 10 | sort job_positions; + + job_positions:keyword |emp_no:integer +Accountant |10001 +Head Human Resources |10004 +Principal Support Engineer|10006 +Reporting Analyst |10004 +Senior Python Developer |10001 +Senior Team Lead |10002 +Support Engineer |10004 +Tech Lead |10004 +null |10005 +null |10003 +; + +expandAfterSort2 +from employees | sort emp_no | mv_expand job_positions | keep job_positions, emp_no | limit 5; + + job_positions:keyword |emp_no:integer +Accountant |10001 +Senior Python Developer|10001 +Senior Team Lead |10002 +null |10003 +Head Human Resources |10004 +; + +expandWithMultiSort +from employees | keep emp_no, job_positions | sort emp_no | mv_expand job_positions | limit 10 | where emp_no <= 10006 | sort job_positions nulls first; + +emp_no:integer | job_positions:keyword +10003 |null +10005 |null +10001 |Accountant +10004 |Head Human Resources +10006 |Principal Support Engineer +10004 |Reporting Analyst +10001 |Senior Python Developer +10002 |Senior Team Lead +10004 |Support Engineer +10004 |Tech Lead +; + +filterMvExpanded +from employees | keep emp_no, job_positions | mv_expand job_positions | where job_positions like "A*" | sort job_positions, emp_no; + +emp_no:integer | job_positions:keyword +10001 |Accountant +10012 |Accountant +10016 |Accountant +10023 |Accountant +10025 |Accountant +10028 |Accountant +10034 |Accountant +10037 |Accountant +10044 |Accountant +10045 |Accountant +10050 |Accountant +10051 |Accountant +10066 |Accountant +10081 |Accountant +10085 |Accountant +10089 |Accountant +10092 |Accountant +10094 |Accountant +10010 |Architect +10011 |Architect +10031 |Architect +10032 |Architect +10042 |Architect +10047 |Architect +10059 |Architect +10068 |Architect +10072 |Architect +10076 |Architect +10078 |Architect +10096 |Architect +10098 |Architect +; + +doubleSort_OnDifferentThan_MvExpandedFields +from employees | sort emp_no | mv_expand job_positions | keep emp_no, job_positions, salary | sort salary, job_positions | limit 5; + +emp_no:integer | job_positions:keyword |salary:integer +10015 |Head Human Resources |25324 +10015 |Junior Developer |25324 +10015 |Principal Support Engineer|25324 +10015 |Support Engineer |25324 +10035 |Data Scientist |25945 +; + +doubleLimit_expandLimitLowerThanAvailable +from employees | where emp_no == 10004 | limit 1 | keep emp_no, job_positions | mv_expand job_positions | limit 2; + +emp_no:integer | job_positions:keyword +10004 |Head Human Resources +10004 |Reporting Analyst +; + +doubleLimit_expandLimitGreaterThanAvailable +from employees | where emp_no == 10004 | limit 1 | keep emp_no, job_positions | mv_expand job_positions | limit 5; + +emp_no:integer | job_positions:keyword +10004 |Head Human Resources +10004 |Reporting Analyst +10004 |Support Engineer +10004 |Tech Lead +; + +doubleLimitWithSort +from employees | where emp_no == 10004 | limit 1 | keep emp_no, job_positions | mv_expand job_positions | limit 5 | sort job_positions desc; + +emp_no:integer | job_positions:keyword +10004 |Tech Lead +10004 |Support Engineer +10004 |Reporting Analyst +10004 |Head Human Resources +; + +tripleLimit_WithWhere_InBetween_MvExpand_And_Limit +from employees | where emp_no == 10004 | limit 1 | keep emp_no, job_positions | mv_expand job_positions | where job_positions LIKE "*a*" | limit 2 | where job_positions LIKE "*a*" | limit 3; + +emp_no:integer | job_positions:keyword +10004 |Head Human Resources +10004 |Reporting Analyst +; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java index eaf148c27c3ee..e90510461551f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java @@ -13,6 +13,7 @@ import org.elasticsearch.xpack.esql.analysis.Verifier; import org.elasticsearch.xpack.esql.enrich.EnrichPolicyResolver; import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry; +import org.elasticsearch.xpack.esql.optimizer.LogicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizer; import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan; import org.elasticsearch.xpack.esql.planner.Mapper; @@ -30,7 +31,6 @@ public class PlanExecutor { private final IndexResolver indexResolver; private final PreAnalyzer preAnalyzer; private final FunctionRegistry functionRegistry; - private final LogicalPlanOptimizer logicalPlanOptimizer; private final Mapper mapper; private final Metrics metrics; private final Verifier verifier; @@ -39,7 +39,6 @@ public PlanExecutor(IndexResolver indexResolver) { this.indexResolver = indexResolver; this.preAnalyzer = new PreAnalyzer(); this.functionRegistry = new EsqlFunctionRegistry(); - this.logicalPlanOptimizer = new LogicalPlanOptimizer(); this.mapper = new Mapper(functionRegistry); this.metrics = new Metrics(); this.verifier = new Verifier(metrics); @@ -59,7 +58,7 @@ public void esql( enrichPolicyResolver, preAnalyzer, functionRegistry, - logicalPlanOptimizer, + new LogicalPlanOptimizer(new LogicalOptimizerContext(cfg)), mapper, verifier ); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalOptimizerContext.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalOptimizerContext.java index 36d275909b47a..f0bc5697d4002 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalOptimizerContext.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalOptimizerContext.java @@ -10,4 +10,36 @@ import org.elasticsearch.xpack.esql.session.EsqlConfiguration; import org.elasticsearch.xpack.esql.stats.SearchStats; -public record LocalLogicalOptimizerContext(EsqlConfiguration configuration, SearchStats searchStats) {} +import java.util.Objects; + +public final class LocalLogicalOptimizerContext extends LogicalOptimizerContext { + private final SearchStats searchStats; + + public LocalLogicalOptimizerContext(EsqlConfiguration configuration, SearchStats searchStats) { + super(configuration); + this.searchStats = searchStats; + } + + public SearchStats searchStats() { + return searchStats; + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj)) { + var that = (LocalLogicalOptimizerContext) obj; + return Objects.equals(this.searchStats, that.searchStats); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), searchStats); + } + + @Override + public String toString() { + return "LocalLogicalOptimizerContext[" + "configuration=" + configuration() + ", " + "searchStats=" + searchStats + ']'; + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizer.java index 9a49849b34e98..3451a3981d3e3 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizer.java @@ -116,10 +116,7 @@ else if (plan instanceof Project project) { } } - public abstract static class ParameterizedOptimizerRule extends ParameterizedRule< - SubPlan, - LogicalPlan, - P> { + abstract static class ParameterizedOptimizerRule extends ParameterizedRule { public final LogicalPlan apply(LogicalPlan plan, P context) { return plan.transformUp(typeToken(), t -> rule(t, context)); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalOptimizerContext.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalOptimizerContext.java new file mode 100644 index 0000000000000..bfd3a5569b0e9 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalOptimizerContext.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.optimizer; + +import org.elasticsearch.xpack.esql.session.EsqlConfiguration; + +import java.util.Objects; + +public class LogicalOptimizerContext { + private final EsqlConfiguration configuration; + + public LogicalOptimizerContext(EsqlConfiguration configuration) { + this.configuration = configuration; + } + + public EsqlConfiguration configuration() { + return configuration; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (LogicalOptimizerContext) obj; + return Objects.equals(this.configuration, that.configuration); + } + + @Override + public int hashCode() { + return Objects.hash(configuration); + } + + @Override + public String toString() { + return "LogicalOptimizerContext[" + "configuration=" + configuration + ']'; + } + +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java index ed215efc4c066..7557a36d4fe30 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java @@ -19,6 +19,7 @@ import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In; import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.Eval; +import org.elasticsearch.xpack.esql.plan.logical.MvExpand; import org.elasticsearch.xpack.esql.plan.logical.RegexExtract; import org.elasticsearch.xpack.esql.plan.logical.TopN; import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; @@ -50,14 +51,17 @@ import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.SetAsOptimized; import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.SimplifyComparisonsArithmetics; import org.elasticsearch.xpack.ql.plan.logical.Aggregate; +import org.elasticsearch.xpack.ql.plan.logical.EsRelation; import org.elasticsearch.xpack.ql.plan.logical.Filter; import org.elasticsearch.xpack.ql.plan.logical.Limit; import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.ql.plan.logical.OrderBy; import org.elasticsearch.xpack.ql.plan.logical.Project; import org.elasticsearch.xpack.ql.plan.logical.UnaryPlan; +import org.elasticsearch.xpack.ql.rule.ParameterizedRule; +import org.elasticsearch.xpack.ql.rule.ParameterizedRuleExecutor; import org.elasticsearch.xpack.ql.rule.Rule; -import org.elasticsearch.xpack.ql.rule.RuleExecutor; +import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.util.CollectionUtils; import org.elasticsearch.xpack.ql.util.Holder; @@ -80,7 +84,11 @@ import static org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PropagateNullable; import static org.elasticsearch.xpack.ql.optimizer.OptimizerRules.TransformDirection; -public class LogicalPlanOptimizer extends RuleExecutor { +public class LogicalPlanOptimizer extends ParameterizedRuleExecutor { + + public LogicalPlanOptimizer(LogicalOptimizerContext optimizerContext) { + super(optimizerContext); + } public LogicalPlan optimize(LogicalPlan verified) { return verified.optimized() ? verified : execute(verified); @@ -128,6 +136,7 @@ protected static List> rules() { new PruneColumns(), new PruneLiteralsInOrderBy(), new PushDownAndCombineLimits(), + new DuplicateLimitAfterMvExpand(), new PushDownAndCombineFilters(), new PushDownEval(), new PushDownRegexExtract(), @@ -139,9 +148,10 @@ protected static List> rules() { var skip = new Batch<>("Skip Compute", new SkipQueryOnLimitZero()); var cleanup = new Batch<>("Clean Up", new ReplaceLimitAndSortAsTopN()); + var defaultTopN = new Batch<>("Add default TopN", new AddDefaultTopN()); var label = new Batch<>("Set as Optimized", Limiter.ONCE, new SetAsOptimized()); - return asList(substitutions, operators, skip, cleanup, label); + return asList(substitutions, operators, skip, cleanup, defaultTopN, label); } // TODO: currently this rule only works for aggregate functions (AVG) @@ -413,6 +423,10 @@ private static Limit descendantLimit(UnaryPlan unary) { while (plan instanceof Aggregate == false) { if (plan instanceof Limit limit) { return limit; + } else if (plan instanceof MvExpand) { + // the limit that applies to mv_expand shouldn't be changed + // ie "| limit 1 | mv_expand x | limit 20" where we want that last "limit" to apply on expand results + return null; } if (plan.child() instanceof UnaryPlan unaryPlan) { plan = unaryPlan; @@ -424,6 +438,92 @@ private static Limit descendantLimit(UnaryPlan unary) { } } + static class DuplicateLimitAfterMvExpand extends OptimizerRules.OptimizerRule { + + @Override + protected LogicalPlan rule(Limit limit) { + var child = limit.child(); + var shouldSkip = child instanceof Eval + || child instanceof Project + || child instanceof RegexExtract + || child instanceof Enrich + || child instanceof Limit; + + if (shouldSkip == false && child instanceof UnaryPlan unary) { + MvExpand mvExpand = descendantMvExpand(unary); + if (mvExpand != null) { + Limit limitBeforeMvExpand = limitBeforeMvExpand(mvExpand); + // if there is no "appropriate" limit before mv_expand, then push down a copy of the one after it so that: + // - a possible TopN is properly built as low as possible in the tree (closed to Lucene) + // - the input of mv_expand is as small as possible before it is expanded (less rows to inflate and occupy memory) + if (limitBeforeMvExpand == null) { + var duplicateLimit = new Limit(limit.source(), limit.limit(), mvExpand.child()); + return limit.replaceChild(propagateDuplicateLimitUntilMvExpand(duplicateLimit, mvExpand, unary)); + } + } + } + return limit; + } + + private static MvExpand descendantMvExpand(UnaryPlan unary) { + UnaryPlan plan = unary; + AttributeSet filterReferences = new AttributeSet(); + while (plan instanceof Aggregate == false) { + if (plan instanceof MvExpand mve) { + // don't return the mv_expand that has a filter after it which uses the expanded values + // since this will trigger the use of a potentially incorrect (too restrictive) limit further down in the tree + if (filterReferences.isEmpty() == false) { + if (filterReferences.contains(mve.target()) // the same field or reference attribute is used in mv_expand AND filter + || mve.target() instanceof ReferenceAttribute // or the mv_expand attr hasn't yet been resolved to a field attr + // or not all filter references have been resolved to field attributes + || filterReferences.stream().anyMatch(ref -> ref instanceof ReferenceAttribute)) { + return null; + } + } + return mve; + } else if (plan instanceof Filter filter) { + // gather all the filters' references to be checked later when a mv_expand is found + filterReferences.addAll(filter.references()); + } else if (plan instanceof OrderBy) { + // ordering after mv_expand COULD break the order of the results, so the limit shouldn't be copied past mv_expand + // something like from test | sort emp_no | mv_expand job_positions | sort first_name | limit 5 + // (the sort first_name likely changes the order of the docs after sort emp_no, so "limit 5" shouldn't be copied down + return null; + } + + if (plan.child() instanceof UnaryPlan unaryPlan) { + plan = unaryPlan; + } else { + break; + } + } + return null; + } + + private static Limit limitBeforeMvExpand(MvExpand mvExpand) { + UnaryPlan plan = mvExpand; + while (plan instanceof Aggregate == false) { + if (plan instanceof Limit limit) { + return limit; + } + if (plan.child() instanceof UnaryPlan unaryPlan) { + plan = unaryPlan; + } else { + break; + } + } + return null; + } + + private LogicalPlan propagateDuplicateLimitUntilMvExpand(Limit duplicateLimit, MvExpand mvExpand, UnaryPlan child) { + if (child == mvExpand) { + return mvExpand.replaceChild(duplicateLimit); + } else { + return child.replaceChild(propagateDuplicateLimitUntilMvExpand(duplicateLimit, mvExpand, (UnaryPlan) child.child())); + } + } + } + // 3 in (field, 4, 5) --> 3 in (field) or 3 in (4, 5) public static class SplitInWithFoldableValue extends OptimizerRules.OptimizerExpressionRule { @@ -668,7 +768,6 @@ protected LogicalPlan rule(Enrich re) { } protected static class PushDownAndCombineOrderBy extends OptimizerRules.OptimizerRule { - @Override protected LogicalPlan rule(OrderBy orderBy) { LogicalPlan child = orderBy.child(); @@ -864,6 +963,40 @@ protected LogicalPlan rule(Limit plan) { } } + /** + * This adds an explicit TopN node to a plan that only has an OrderBy right before Lucene. + * To date, the only known use case that "needs" this is a query of the form + * from test + * | sort emp_no + * | mv_expand first_name + * | rename first_name AS x + * | where x LIKE "*a*" + * | limit 15 + * + * or + * + * from test + * | sort emp_no + * | mv_expand first_name + * | sort first_name + * | limit 15 + * + * PushDownAndCombineLimits rule will copy the "limit 15" after "sort emp_no" if there is no filter on the expanded values + * OR if there is no sort between "limit" and "mv_expand". + * But, since this type of query has such a filter, the "sort emp_no" will have no limit when it reaches the current rule. + */ + static class AddDefaultTopN extends ParameterizedOptimizerRule { + + @Override + protected LogicalPlan rule(LogicalPlan plan, LogicalOptimizerContext context) { + if (plan instanceof UnaryPlan unary && unary.child() instanceof OrderBy order && order.child() instanceof EsRelation relation) { + var limit = new Literal(Source.EMPTY, context.configuration().resultTruncationMaxSize(), DataTypes.INTEGER); + return unary.replaceChild(new TopN(plan.source(), relation, order.order(), limit)); + } + return plan; + } + } + public static class ReplaceRegexMatch extends OptimizerRules.ReplaceRegexMatch { protected Expression regexToEquals(RegexMatch regexMatch, Literal literal) { @@ -949,7 +1082,18 @@ private LogicalPlan rule(Eval eval) { return plan; } + } + + private abstract static class ParameterizedOptimizerRule extends ParameterizedRule< + SubPlan, + LogicalPlan, + P> { + + public final LogicalPlan apply(LogicalPlan plan, P context) { + return plan.transformDown(typeToken(), t -> rule(t, context)); + } + protected abstract LogicalPlan rule(SubPlan plan, P context); } /** diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index b0b7bf17d2ab4..accd7f222b040 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.xpack.esql.optimizer.LocalLogicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.LocalLogicalPlanOptimizer; import org.elasticsearch.xpack.esql.optimizer.LocalPhysicalOptimizerContext; +import org.elasticsearch.xpack.esql.optimizer.LogicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizer; import org.elasticsearch.xpack.esql.optimizer.PhysicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.PhysicalPlanOptimizer; @@ -153,7 +154,6 @@ public class CsvTests extends ESTestCase { ); private final FunctionRegistry functionRegistry = new EsqlFunctionRegistry(); private final EsqlParser parser = new EsqlParser(); - private final LogicalPlanOptimizer logicalPlanOptimizer = new LogicalPlanOptimizer(); private final Mapper mapper = new Mapper(functionRegistry); private final PhysicalPlanOptimizer physicalPlanOptimizer = new TestPhysicalPlanOptimizer(new PhysicalOptimizerContext(configuration)); private ThreadPool threadPool; @@ -286,7 +286,7 @@ private PhysicalPlan physicalPlan(LogicalPlan parsed, CsvTestsDataLoader.TestsDa var enrichPolicies = loadEnrichPolicies(); var analyzer = new Analyzer(new AnalyzerContext(configuration, functionRegistry, indexResolution, enrichPolicies), TEST_VERIFIER); var analyzed = analyzer.analyze(parsed); - var logicalOptimized = logicalPlanOptimizer.optimize(analyzed); + var logicalOptimized = new LogicalPlanOptimizer(new LogicalOptimizerContext(configuration)).optimize(analyzed); var physicalPlan = mapper.map(logicalOptimized); var optimizedPlan = EstimatesRowSize.estimateRowSize(0, physicalPlanOptimizer.optimize(physicalPlan)); opportunisticallyAssertPlanSerialization(physicalPlan, optimizedPlan); // comment out to disable serialization diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java index ae9d7b7d1b5f0..bc46189e13827 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java @@ -61,7 +61,7 @@ public static void init() { mapping = loadMapping("mapping-basic.json"); EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - logicalOptimizer = new LogicalPlanOptimizer(); + logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); analyzer = new Analyzer( new AnalyzerContext(EsqlTestUtils.TEST_CFG, new EsqlFunctionRegistry(), getIndexResult, EsqlTestUtils.emptyPolicyResolution()), diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java index 72e12697488df..8b185e013a8a5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java @@ -127,7 +127,7 @@ public void init() { .sum(); EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - logicalOptimizer = new LogicalPlanOptimizer(); + logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); physicalPlanOptimizer = new PhysicalPlanOptimizer(new PhysicalOptimizerContext(config)); FunctionRegistry functionRegistry = new EsqlFunctionRegistry(); mapper = new Mapper(functionRegistry); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 285ad7021e83f..17429d3a6b1c3 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -44,6 +44,7 @@ import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.Grok; +import org.elasticsearch.xpack.esql.plan.logical.MvExpand; import org.elasticsearch.xpack.esql.plan.logical.TopN; import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; import org.elasticsearch.xpack.esql.plan.logical.local.LocalRelation; @@ -125,7 +126,7 @@ public static void init() { EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - logicalOptimizer = new LogicalPlanOptimizer(); + logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); EnrichPolicyResolution policy = AnalyzerTestUtils.loadEnrichPolicyResolution( "languages_idx", "id", @@ -306,7 +307,10 @@ public void testMultipleCombineLimits() { var value = i == limitWithMinimum ? minimum : randomIntBetween(100, 1000); plan = new Limit(EMPTY, L(value), plan); } - assertEquals(new Limit(EMPTY, L(minimum), relation), new LogicalPlanOptimizer().optimize(plan)); + assertEquals( + new Limit(EMPTY, L(minimum), relation), + new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)).optimize(plan) + ); } public static GreaterThan greaterThanOf(Expression left, Expression right) { @@ -890,6 +894,384 @@ public void testCombineOrderByThroughFilter() { as(filter.child(), EsRelation.class); } + /** + * Expected + * TopN[[Order[first_name{f}#170,ASC,LAST]],500[INTEGER]] + * \_MvExpand[first_name{f}#170] + * \_TopN[[Order[emp_no{f}#169,ASC,LAST]],500[INTEGER]] + * \_EsRelation[test][avg_worked_seconds{f}#167, birth_date{f}#168, emp_n..] + */ + public void testDontCombineOrderByThroughMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort emp_no + | mv_expand first_name + | sort first_name"""); + + var topN = as(plan, TopN.class); + assertThat(orderNames(topN), contains("first_name")); + var mvExpand = as(topN.child(), MvExpand.class); + topN = as(mvExpand.child(), TopN.class); + assertThat(orderNames(topN), contains("emp_no")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * Limit[500[INTEGER]] + * \_MvExpand[x{r}#159] + * \_EsqlProject[[first_name{f}#162 AS x]] + * \_Limit[500[INTEGER]] + * \_EsRelation[test][first_name{f}#162] + */ + public void testCopyDefaultLimitPastMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | rename first_name as x + | keep x + | mv_expand x + """); + + var limit = as(plan, Limit.class); + var mvExpand = as(limit.child(), MvExpand.class); + var keep = as(mvExpand.child(), EsqlProject.class); + var limitPastMvExpand = as(keep.child(), Limit.class); + assertThat(limitPastMvExpand.limit(), equalTo(limit.limit())); + as(limitPastMvExpand.child(), EsRelation.class); + } + + /** + * Expected + * Limit[10[INTEGER]] + * \_MvExpand[first_name{f}#155] + * \_EsqlProject[[first_name{f}#155, last_name{f}#156]] + * \_Limit[1[INTEGER]] + * \_EsRelation[test][first_name{f}#155, last_name{f}#156] + */ + public void testDontPushDownLimitPastMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | limit 1 + | keep first_name, last_name + | mv_expand first_name + | limit 10"""); + + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(10)); + var mvExpand = as(limit.child(), MvExpand.class); + var project = as(mvExpand.child(), EsqlProject.class); + limit = as(project.child(), Limit.class); + assertThat(limit.limit().fold(), equalTo(1)); + as(limit.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[emp_no{f}#141, first_name{f}#142, languages{f}#143, lll{r}#132, salary{f}#147]] + * \_TopN[[Order[salary{f}#147,DESC,FIRST], Order[first_name{f}#142,ASC,LAST]],5[INTEGER]] + * \_Limit[5[INTEGER]] + * \_MvExpand[salary{f}#147] + * \_Eval[[languages{f}#143 + 5[INTEGER] AS lll]] + * \_Filter[languages{f}#143 > 1[INTEGER]] + * \_Limit[10[INTEGER]] + * \_MvExpand[first_name{f}#142] + * \_TopN[[Order[emp_no{f}#141,DESC,FIRST]],10[INTEGER]] + * \_Filter[emp_no{f}#141 < 10006[INTEGER]] + * \_EsRelation[test][emp_no{f}#141, first_name{f}#142, languages{f}#1..] + */ + public void testMultipleMvExpandWithSortAndLimit() { + LogicalPlan plan = optimizedPlan(""" + from test + | where emp_no <= 10006 + | sort emp_no desc + | mv_expand first_name + | limit 10 + | where languages > 1 + | eval lll = languages + 5 + | mv_expand salary + | limit 5 + | sort first_name + | keep emp_no, first_name, languages, lll, salary + | sort salary desc"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(5)); + assertThat(orderNames(topN), contains("salary", "first_name")); + var limit = as(topN.child(), Limit.class); + assertThat(limit.limit().fold(), equalTo(5)); + var mvExp = as(limit.child(), MvExpand.class); + var eval = as(mvExp.child(), Eval.class); + var filter = as(eval.child(), Filter.class); + limit = as(filter.child(), Limit.class); + assertThat(limit.limit().fold(), equalTo(10)); + mvExp = as(limit.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(10)); + filter = as(topN.child(), Filter.class); + as(filter.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[emp_no{f}#350, first_name{f}#351, salary{f}#352]] + * \_TopN[[Order[salary{f}#352,ASC,LAST], Order[first_name{f}#351,ASC,LAST]],5[INTEGER]] + * \_MvExpand[first_name{f}#351] + * \_TopN[[Order[emp_no{f}#350,ASC,LAST]],10000[INTEGER]] + * \_EsRelation[employees][emp_no{f}#350, first_name{f}#351, salary{f}#352] + */ + public void testPushDownLimitThroughMultipleSort_AfterMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort emp_no + | mv_expand first_name + | keep emp_no, first_name, salary + | sort salary, first_name + | limit 5"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(5)); + assertThat(orderNames(topN), contains("salary", "first_name")); + var mvExp = as(topN.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(10000)); + assertThat(orderNames(topN), contains("emp_no")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[emp_no{f}#361, first_name{f}#362, salary{f}#363]] + * \_TopN[[Order[first_name{f}#362,ASC,LAST]],5[INTEGER]] + * \_TopN[[Order[salary{f}#363,ASC,LAST]],5[INTEGER]] + * \_MvExpand[first_name{f}#362] + * \_TopN[[Order[emp_no{f}#361,ASC,LAST]],10000[INTEGER]] + * \_EsRelation[employees][emp_no{f}#361, first_name{f}#362, salary{f}#363] + */ + public void testPushDownLimitThroughMultipleSort_AfterMvExpand2() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort emp_no + | mv_expand first_name + | keep emp_no, first_name, salary + | sort salary + | limit 5 + | sort first_name"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(5)); + assertThat(orderNames(topN), contains("first_name")); + topN = as(topN.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(5)); + assertThat(orderNames(topN), contains("salary")); + var mvExp = as(topN.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(10000)); + assertThat(orderNames(topN), contains("emp_no")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * Limit[5[INTEGER]] + * \_Aggregate[[first_name{f}#232],[MAX(salary{f}#233) AS max_s, first_name{f}#232]] + * \_Filter[ISNOTNULL(first_name{f}#232)] + * \_MvExpand[first_name{f}#232] + * \_TopN[[Order[emp_no{f}#231,ASC,LAST]],50[INTEGER]] + * \_EsRelation[employees][emp_no{f}#231, first_name{f}#232, salary{f}#233] + */ + public void testDontPushDownLimitPastAggregate_AndMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort emp_no + | limit 50 + | mv_expand first_name + | keep emp_no, first_name, salary + | stats max_s = max(salary) by first_name + | where first_name is not null + | limit 5"""); + + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(5)); + var agg = as(limit.child(), Aggregate.class); + var filter = as(agg.child(), Filter.class); + var mvExp = as(filter.child(), MvExpand.class); + var topN = as(mvExp.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(50)); + assertThat(orderNames(topN), contains("emp_no")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * Limit[5[INTEGER]] + * \_Aggregate[[first_name{f}#262],[MAX(salary{f}#263) AS max_s, first_name{f}#262]] + * \_Filter[ISNOTNULL(first_name{f}#262)] + * \_Limit[50[INTEGER]] + * \_MvExpand[first_name{f}#262] + * \_Limit[50[INTEGER]] + * \_EsRelation[employees][emp_no{f}#261, first_name{f}#262, salary{f}#263] + */ + public void testPushDown_TheRightLimit_PastMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | mv_expand first_name + | limit 50 + | keep emp_no, first_name, salary + | stats max_s = max(salary) by first_name + | where first_name is not null + | limit 5"""); + + var limit = as(plan, Limit.class); + assertThat(limit.limit().fold(), equalTo(5)); + var agg = as(limit.child(), Aggregate.class); + var filter = as(agg.child(), Filter.class); + limit = as(filter.child(), Limit.class); + assertThat(limit.limit().fold(), equalTo(50)); + var mvExp = as(limit.child(), MvExpand.class); + limit = as(mvExp.child(), Limit.class); + assertThat(limit.limit().fold(), equalTo(50)); + as(limit.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[first_name{f}#11, emp_no{f}#10, salary{f}#12, b{r}#4]] + * \_TopN[[Order[salary{f}#12,ASC,LAST]],5[INTEGER]] + * \_Eval[[100[INTEGER] AS b]] + * \_MvExpand[first_name{f}#11] + * \_TopN[[Order[first_name{f}#11,ASC,LAST]],10000[INTEGER]] + * \_EsRelation[employees][emp_no{f}#10, first_name{f}#11, salary{f}#12] + */ + public void testPushDownLimit_PastEvalAndMvExpand() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort first_name + | mv_expand first_name + | eval b = 100 + | sort salary + | limit 5 + | keep first_name, emp_no, salary, b"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(5)); + assertThat(orderNames(topN), contains("salary")); + var eval = as(topN.child(), Eval.class); + var mvExp = as(eval.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(10000)); + assertThat(orderNames(topN), contains("first_name")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[emp_no{f}#104, first_name{f}#105, salary{f}#106]] + * \_TopN[[Order[salary{f}#106,ASC,LAST], Order[first_name{f}#105,ASC,LAST]],15[INTEGER]] + * \_Filter[gender{f}#215 == [46][KEYWORD] AND WILDCARDLIKE(first_name{f}#105)] + * \_MvExpand[first_name{f}#105] + * \_TopN[[Order[emp_no{f}#104,ASC,LAST]],10000[INTEGER]] + * \_EsRelation[employees][emp_no{f}#104, first_name{f}#105, salary{f}#106] + */ + public void testAddDefaultLimit_BeforeMvExpand_WithFilterOnExpandedField() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort emp_no + | mv_expand first_name + | where gender == "F" + | where first_name LIKE "R*" + | keep emp_no, first_name, salary + | sort salary, first_name + | limit 15"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(15)); + assertThat(orderNames(topN), contains("salary", "first_name")); + var filter = as(topN.child(), Filter.class); + assertThat(filter.condition(), instanceOf(And.class)); + var mvExp = as(filter.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + // the filter acts on first_name (the one used in mv_expand), so the limit 15 is not pushed down past mv_expand + // instead the default limit is added + assertThat(topN.limit().fold(), equalTo(10000)); + assertThat(orderNames(topN), contains("emp_no")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[emp_no{f}#104, first_name{f}#105, salary{f}#106]] + * \_TopN[[Order[salary{f}#106,ASC,LAST], Order[first_name{f}#105,ASC,LAST]],15[INTEGER]] + * \_Filter[gender{f}#215 == [46][KEYWORD] AND salary{f}#106 > 60000[INTEGER]] + * \_MvExpand[first_name{f}#105] + * \_TopN[[Order[emp_no{f}#104,ASC,LAST]],10000[INTEGER]] + * \_EsRelation[employees][emp_no{f}#104, first_name{f}#105, salary{f}#106] + */ + public void testAddDefaultLimit_BeforeMvExpand_WithFilter_NOT_OnExpandedField() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort emp_no + | mv_expand first_name + | where gender == "F" + | where salary > 60000 + | keep emp_no, first_name, salary + | sort salary, first_name + | limit 15"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(15)); + assertThat(orderNames(topN), contains("salary", "first_name")); + var filter = as(topN.child(), Filter.class); + assertThat(filter.condition(), instanceOf(And.class)); + var mvExp = as(filter.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + // the filters after mv_expand do not act on the expanded field values, as such the limit 15 is the one being pushed down + // otherwise that limit wouldn't have pushed down and the default limit was instead being added by default before mv_expanded + assertThat(topN.limit().fold(), equalTo(10000)); + assertThat(orderNames(topN), contains("emp_no")); + as(topN.child(), EsRelation.class); + } + + /** + * Expected + * EsqlProject[[emp_no{f}#116, first_name{f}#117 AS x, salary{f}#119]] + * \_TopN[[Order[salary{f}#119,ASC,LAST], Order[first_name{f}#117,ASC,LAST]],15[INTEGER]] + * \_Filter[gender{f}#118 == [46][KEYWORD] AND WILDCARDLIKE(first_name{f}#117)] + * \_MvExpand[first_name{f}#117] + * \_TopN[[Order[gender{f}#118,ASC,LAST]],10000[INTEGER]] + * \_EsRelation[employees][emp_no{f}#116, first_name{f}#117, gender{f}#118, sa..] + */ + public void testAddDefaultLimit_BeforeMvExpand_WithFilterOnExpandedFieldAlias() { + LogicalPlan plan = optimizedPlan(""" + from test + | sort gender + | mv_expand first_name + | rename first_name AS x + | where gender == "F" + | where x LIKE "A*" + | keep emp_no, x, salary + | sort salary, x + | limit 15"""); + + var keep = as(plan, EsqlProject.class); + var topN = as(keep.child(), TopN.class); + assertThat(topN.limit().fold(), equalTo(15)); + assertThat(orderNames(topN), contains("salary", "first_name")); + var filter = as(topN.child(), Filter.class); + assertThat(filter.condition(), instanceOf(And.class)); + var mvExp = as(filter.child(), MvExpand.class); + topN = as(mvExp.child(), TopN.class); + // the filter uses an alias ("x") to the expanded field ("first_name"), so the default limit is used and not the one provided + assertThat(topN.limit().fold(), equalTo(10000)); + assertThat(orderNames(topN), contains("gender")); + as(topN.child(), EsRelation.class); + } + private static List orderNames(TopN topN) { return topN.order().stream().map(o -> as(o.child(), NamedExpression.class).name()).toList(); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java index f0c3b9d541b16..1f2bde2526fab 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java @@ -158,7 +158,7 @@ public void init() { .sum(); EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - logicalOptimizer = new LogicalPlanOptimizer(); + logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); physicalPlanOptimizer = new PhysicalPlanOptimizer(new PhysicalOptimizerContext(config)); FunctionRegistry functionRegistry = new EsqlFunctionRegistry(); mapper = new Mapper(functionRegistry); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/FilterTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/FilterTests.java index db3d2419ee2ee..774ac24d3cd02 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/FilterTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/FilterTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.xpack.esql.analysis.Analyzer; import org.elasticsearch.xpack.esql.analysis.AnalyzerContext; import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry; +import org.elasticsearch.xpack.esql.optimizer.LogicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizer; import org.elasticsearch.xpack.esql.optimizer.PhysicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.PhysicalPlanOptimizer; @@ -70,7 +71,7 @@ public static void init() { mapping = loadMapping("mapping-basic.json"); EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - logicalOptimizer = new LogicalPlanOptimizer(); + logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); physicalPlanOptimizer = new PhysicalPlanOptimizer(new PhysicalOptimizerContext(EsqlTestUtils.TEST_CFG)); mapper = new Mapper(false); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java index cceb3a2ab835b..8970617548016 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.xpack.esql.analysis.Analyzer; import org.elasticsearch.xpack.esql.analysis.AnalyzerContext; import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry; +import org.elasticsearch.xpack.esql.optimizer.LogicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizer; import org.elasticsearch.xpack.esql.optimizer.PhysicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.PhysicalPlanOptimizer; @@ -165,7 +166,7 @@ static LogicalPlan parse(String query) { Map mapping = loadMapping("mapping-basic.json"); EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - var logicalOptimizer = new LogicalPlanOptimizer(); + var logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(TEST_CFG)); var analyzer = new Analyzer( new AnalyzerContext(EsqlTestUtils.TEST_CFG, new EsqlFunctionRegistry(), getIndexResult, emptyPolicyResolution()), TEST_VERIFIER From 9b29fa60d474a988514691d86b4c3f0fce26ffe4 Mon Sep 17 00:00:00 2001 From: Daniel Mitterdorfer Date: Tue, 24 Oct 2023 16:03:27 +0200 Subject: [PATCH 081/203] Align look-back with client-side cache (#101264) We have recently introduced a 6 hour cache for executables (previously lifetime in the cache was unbounded) in the host agent. With this commit we align the look-back so it matched the client-side cache lifetime. --- docs/changelog/101264.yaml | 5 +++++ .../xpack/profiling/TransportGetStackTracesAction.java | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 docs/changelog/101264.yaml diff --git a/docs/changelog/101264.yaml b/docs/changelog/101264.yaml new file mode 100644 index 0000000000000..7160240b2f3a0 --- /dev/null +++ b/docs/changelog/101264.yaml @@ -0,0 +1,5 @@ +pr: 101264 +summary: Align look-back with client-side cache +area: Application +type: bug +issues: [] diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java index 3f019f01b7c77..e15792adc489d 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/TransportGetStackTracesAction.java @@ -79,12 +79,13 @@ public class TransportGetStackTracesAction extends HandledTransportActionelfInfoCacheTTL for executables (default: 6 hours) and traceExpirationTimeout for stack + * traces (default: 3 hours). */ public static final Setting PROFILING_KV_INDEX_OVERLAP = Setting.positiveTimeSetting( "xpack.profiling.kv_index.overlap", - TimeValue.timeValueHours(4), + TimeValue.timeValueHours(6), Setting.Property.NodeScope ); From ecd13e3f118d34e151081cd1e3d432a609295dad Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Tue, 24 Oct 2023 09:05:16 -0500 Subject: [PATCH 082/203] Metrics test framework (#101168) Adds a test framework that validates instruments are registered before they are called and are not double registered. Also records all invocations of Instruments and allows test authors to add validation to instruments. --- .../s3/S3BlobStoreRepositoryTests.java | 98 ++++------ .../telemetry/DelegatingMeterRegistry.java | 108 ---------- .../telemetry/InstrumentType.java | 69 +++++++ .../elasticsearch/telemetry/Measurement.java | 64 ++++++ .../telemetry/MetricRecorder.java | 109 +++++++++++ .../telemetry/RecordingInstruments.java | 185 ++++++++++++++++++ .../telemetry/RecordingMeterRegistry.java | 161 +++++++++++++++ .../elasticsearch/telemetry/Registration.java | 22 +++ .../telemetry/TestTelemetryPlugin.java | 82 ++++++++ 9 files changed, 733 insertions(+), 165 deletions(-) delete mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/Measurement.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/Registration.java create mode 100644 test/framework/src/main/java/org/elasticsearch/telemetry/TestTelemetryPlugin.java diff --git a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java index 8805955d905c4..b320364877e98 100644 --- a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java +++ b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java @@ -31,14 +31,12 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.PluginsService; -import org.elasticsearch.plugins.TelemetryPlugin; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.RepositoryData; @@ -51,11 +49,12 @@ import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.snapshots.mockstore.BlobStoreWrapper; -import org.elasticsearch.telemetry.DelegatingMeterRegistry; -import org.elasticsearch.telemetry.TelemetryProvider; +import org.elasticsearch.telemetry.Measurement; +import org.elasticsearch.telemetry.RecordingInstruments; +import org.elasticsearch.telemetry.RecordingMeterRegistry; +import org.elasticsearch.telemetry.TestTelemetryPlugin; import org.elasticsearch.telemetry.metric.LongCounter; import org.elasticsearch.telemetry.metric.MeterRegistry; -import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.test.BackgroundIndexer; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.junit.annotations.TestLogging; @@ -75,7 +74,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -141,7 +139,7 @@ protected Settings repositorySettings(String repoName) { @Override protected Collection> nodePlugins() { - return List.of(TestS3RepositoryPlugin.class, TestTelemetryPlugin.class); + return List.of(TestS3RepositoryPlugin.class, TestS3BlobTelemetryPlugin.class); } @Override @@ -267,21 +265,25 @@ public void testMetrics() throws Exception { final Map statsCollectors = s3BlobStore .getStatsCollectors().collectors; - final var plugins = internalCluster().getInstance(PluginsService.class, nodeName).filterPlugins(TestTelemetryPlugin.class); + final var plugins = internalCluster().getInstance(PluginsService.class, nodeName) + .filterPlugins(TestS3BlobTelemetryPlugin.class); assertThat(plugins, hasSize(1)); - final Map, AtomicLong> metrics = plugins.get(0).metrics; + final List metrics = Measurement.combine(plugins.get(0).getLongCounterMeasurement(METRIC_REQUESTS_COUNT)); - assertThat(statsCollectors.size(), equalTo(metrics.size())); - metrics.forEach((attributes, counter) -> { - final S3BlobStore.Operation operation = S3BlobStore.Operation.parse((String) attributes.get("operation")); + assertThat( + statsCollectors.size(), + equalTo(metrics.stream().map(m -> m.attributes().get("operation")).collect(Collectors.toSet()).size()) + ); + metrics.forEach(metric -> { + final S3BlobStore.Operation operation = S3BlobStore.Operation.parse((String) metric.attributes().get("operation")); final S3BlobStore.StatsKey statsKey = new S3BlobStore.StatsKey( operation, - OperationPurpose.parse((String) attributes.get("purpose")) + OperationPurpose.parse((String) metric.attributes().get("purpose")) ); assertThat(statsCollectors, hasKey(statsKey)); - assertThat(counter.get(), equalTo(statsCollectors.get(statsKey).counter.sum())); + assertThat(metric.getLong(), equalTo(statsCollectors.get(statsKey).counter.sum())); - aggregatedMetrics.compute(operation.getKey(), (k, v) -> v == null ? counter.get() : v + counter.get()); + aggregatedMetrics.compute(operation.getKey(), (k, v) -> v == null ? metric.getLong() : v + metric.getLong()); }); } @@ -554,63 +556,45 @@ private boolean isMultiPartUpload(String request) { } } - public static class TestTelemetryPlugin extends Plugin implements TelemetryPlugin { - - private final Map, AtomicLong> metrics = ConcurrentCollections.newConcurrentMap(); - - private final LongCounter longCounter = new LongCounter() { - @Override - public void increment() { - throw new UnsupportedOperationException(); - } + public static class TestS3BlobTelemetryPlugin extends TestTelemetryPlugin { + protected final MeterRegistry meter = new RecordingMeterRegistry() { + private final LongCounter longCounter = new RecordingInstruments.RecordingLongCounter(METRIC_REQUESTS_COUNT, recorder) { + @Override + public void increment() { + throw new UnsupportedOperationException(); + } - @Override - public void incrementBy(long inc) { - throw new UnsupportedOperationException(); - } + @Override + public void incrementBy(long inc) { + throw new UnsupportedOperationException(); + } - @Override - public void incrementBy(long inc, Map attributes) { - assertThat( - attributes, - allOf(hasEntry("repo_type", S3Repository.TYPE), hasKey("repo_name"), hasKey("operation"), hasKey("purpose")) - ); - metrics.computeIfAbsent(attributes, k -> new AtomicLong()).addAndGet(inc); - } + @Override + public void incrementBy(long inc, Map attributes) { + assertThat( + attributes, + allOf(hasEntry("repo_type", S3Repository.TYPE), hasKey("repo_name"), hasKey("operation"), hasKey("purpose")) + ); + super.incrementBy(inc, attributes); + } + }; @Override - public String getName() { - return METRIC_REQUESTS_COUNT; + protected LongCounter buildLongCounter(String name, String description, String unit) { + return longCounter; } - }; - private final MeterRegistry meterRegistry = new DelegatingMeterRegistry(MeterRegistry.NOOP) { @Override public LongCounter registerLongCounter(String name, String description, String unit) { assertThat(name, equalTo(METRIC_REQUESTS_COUNT)); - return longCounter; + return super.registerLongCounter(name, description, unit); } @Override public LongCounter getLongCounter(String name) { assertThat(name, equalTo(METRIC_REQUESTS_COUNT)); - return longCounter; + return super.getLongCounter(name); } }; - - @Override - public TelemetryProvider getTelemetryProvider(Settings settings) { - return new TelemetryProvider() { - @Override - public Tracer getTracer() { - return Tracer.NOOP; - } - - @Override - public MeterRegistry getMeterRegistry() { - return meterRegistry; - } - }; - } } } diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java b/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java deleted file mode 100644 index ad54ac769e86b..0000000000000 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/DelegatingMeterRegistry.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -package org.elasticsearch.telemetry; - -import org.elasticsearch.telemetry.metric.DoubleCounter; -import org.elasticsearch.telemetry.metric.DoubleGauge; -import org.elasticsearch.telemetry.metric.DoubleHistogram; -import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; -import org.elasticsearch.telemetry.metric.LongCounter; -import org.elasticsearch.telemetry.metric.LongGauge; -import org.elasticsearch.telemetry.metric.LongHistogram; -import org.elasticsearch.telemetry.metric.LongUpDownCounter; -import org.elasticsearch.telemetry.metric.MeterRegistry; - -public class DelegatingMeterRegistry implements MeterRegistry { - - private final MeterRegistry delegate; - - public DelegatingMeterRegistry(MeterRegistry delegate) { - this.delegate = delegate; - } - - @Override - public DoubleCounter registerDoubleCounter(String name, String description, String unit) { - return delegate.registerDoubleCounter(name, description, unit); - } - - @Override - public DoubleCounter getDoubleCounter(String name) { - return delegate.getDoubleCounter(name); - } - - @Override - public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) { - return delegate.registerDoubleUpDownCounter(name, description, unit); - } - - @Override - public DoubleUpDownCounter getDoubleUpDownCounter(String name) { - return delegate.getDoubleUpDownCounter(name); - } - - @Override - public DoubleGauge registerDoubleGauge(String name, String description, String unit) { - return delegate.registerDoubleGauge(name, description, unit); - } - - @Override - public DoubleGauge getDoubleGauge(String name) { - return delegate.getDoubleGauge(name); - } - - @Override - public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) { - return delegate.registerDoubleHistogram(name, description, unit); - } - - @Override - public DoubleHistogram getDoubleHistogram(String name) { - return delegate.getDoubleHistogram(name); - } - - @Override - public LongCounter registerLongCounter(String name, String description, String unit) { - return delegate.registerLongCounter(name, description, unit); - } - - @Override - public LongCounter getLongCounter(String name) { - return delegate.getLongCounter(name); - } - - @Override - public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) { - return delegate.registerLongUpDownCounter(name, description, unit); - } - - @Override - public LongUpDownCounter getLongUpDownCounter(String name) { - return delegate.getLongUpDownCounter(name); - } - - @Override - public LongGauge registerLongGauge(String name, String description, String unit) { - return delegate.registerLongGauge(name, description, unit); - } - - @Override - public LongGauge getLongGauge(String name) { - return delegate.getLongGauge(name); - } - - @Override - public LongHistogram registerLongHistogram(String name, String description, String unit) { - return delegate.registerLongHistogram(name, description, unit); - } - - @Override - public LongHistogram getLongHistogram(String name) { - return delegate.getLongHistogram(name); - } -} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java b/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java new file mode 100644 index 0000000000000..405e12d277307 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import org.elasticsearch.telemetry.metric.DoubleCounter; +import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.DoubleHistogram; +import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.Instrument; +import org.elasticsearch.telemetry.metric.LongCounter; +import org.elasticsearch.telemetry.metric.LongGauge; +import org.elasticsearch.telemetry.metric.LongHistogram; +import org.elasticsearch.telemetry.metric.LongUpDownCounter; + +import java.util.Objects; + +/** + * Enum with the different types for use as keys. This enum acts a bridge between the Otel and Elasticsearch versions of each + * of the instruments. + */ +enum InstrumentType { + DOUBLE_COUNTER(true), + LONG_COUNTER(false), + DOUBLE_UP_DOWN_COUNTER(true), + LONG_UP_DOWN_COUNTER(false), + DOUBLE_HISTOGRAM(true), + LONG_HISTOGRAM(false), + DOUBLE_GAUGE(true), + LONG_GAUGE(false), + DOUBLE_GAUGE_OBSERVER(true), + LONG_GAUGE_OBSERVER(false); + + public final boolean isDouble; + public final boolean isLong; + + InstrumentType(boolean isDouble) { + this.isDouble = isDouble; + this.isLong = isDouble == false; + } + + public static InstrumentType fromInstrument(Instrument instrument) { + Objects.requireNonNull(instrument); + if (instrument instanceof DoubleCounter) { + return InstrumentType.DOUBLE_COUNTER; + } else if (instrument instanceof LongCounter) { + return InstrumentType.LONG_COUNTER; + } else if (instrument instanceof DoubleUpDownCounter) { + return InstrumentType.DOUBLE_UP_DOWN_COUNTER; + } else if (instrument instanceof LongUpDownCounter) { + return InstrumentType.LONG_UP_DOWN_COUNTER; + } else if (instrument instanceof DoubleHistogram) { + return InstrumentType.DOUBLE_HISTOGRAM; + } else if (instrument instanceof LongHistogram) { + return InstrumentType.LONG_HISTOGRAM; + } else if (instrument instanceof DoubleGauge) { + return InstrumentType.DOUBLE_GAUGE_OBSERVER; + } else if (instrument instanceof LongGauge) { + return InstrumentType.LONG_GAUGE_OBSERVER; + } else { + throw new IllegalArgumentException("unknown instrument [" + instrument.getClass().getName() + "]"); + } + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/Measurement.java b/test/framework/src/main/java/org/elasticsearch/telemetry/Measurement.java new file mode 100644 index 0000000000000..76f94f54fbad9 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/Measurement.java @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * A single measurement from an {@link org.elasticsearch.telemetry.metric.Instrument}. + */ +public record Measurement(Number value, Map attributes, boolean isDouble) { + public Measurement { + Objects.requireNonNull(value); + } + + public boolean isLong() { + return isDouble == false; + } + + public double getDouble() { + assert isDouble; + return value.doubleValue(); + } + + public long getLong() { + assert isLong(); + return value.longValue(); + } + + /** + * Add measurements with the same attributes together. All measurements must be from the + * same instrument. If some measurements differ on {@link #isDouble}, @throws IllegalArgumentException + */ + public static List combine(List measurements) { + if (measurements == null || measurements.isEmpty()) { + return Collections.emptyList(); + } + boolean isDouble = measurements.get(0).isDouble; + Map, Number> byAttr = new HashMap<>(); + measurements.forEach(m -> { + if (m.isDouble != isDouble) { + throw new IllegalArgumentException("cannot combine measurements of different types"); + } + byAttr.compute( + m.attributes, + (k, v) -> (v == null) ? m.value : isDouble ? v.doubleValue() + m.getDouble() : v.longValue() + m.getLong() + ); + }); + return byAttr.entrySet() + .stream() + .map(entry -> new Measurement(entry.getValue(), entry.getKey(), isDouble)) + .collect(Collectors.toList()); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java b/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java new file mode 100644 index 0000000000000..b94ca0919c89a --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import org.elasticsearch.core.Strings; +import org.elasticsearch.telemetry.metric.Instrument; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Container for registered Instruments (either {@link Instrument} or Otel's versions). + * Records invocations of the Instruments as {@link Measurement}s. + * @param The supertype of the registered instrument. + */ +class MetricRecorder { + + /** + * Container for Instrument of a given type, such as DoubleGauge, LongHistogram, etc. + * @param registered - registration records for each named metrics + * @param called - one instance per invocation of the instance + * @param instruments - the instrument instance + */ + private record RegisteredMetric( + Map registered, + Map> called, + Map instruments + ) { + void register(String name, String description, String unit, I instrument) { + assert registered.containsKey(name) == false + : Strings.format("unexpected [{}]: [{}][{}], already registered[{}]", name, description, unit, registered.get(name)); + registered.put(name, new Registration(name, description, unit)); + instruments.put(name, instrument); + } + + void call(String name, Measurement call) { + assert registered.containsKey(name) : Strings.format("call for unregistered metric [{}]: [{}]", name, call); + called.computeIfAbsent(Objects.requireNonNull(name), k -> new ArrayList<>()).add(call); + } + } + + /** + * The containers for each metric type. + */ + private final Map> metrics; + + MetricRecorder() { + metrics = new HashMap<>(InstrumentType.values().length); + for (var instrument : InstrumentType.values()) { + metrics.put(instrument, new RegisteredMetric<>(new HashMap<>(), new HashMap<>(), new HashMap<>())); + } + } + + /** + * Register an instrument. Instruments must be registered before they are used. + */ + void register(I instrument, InstrumentType instrumentType, String name, String description, String unit) { + metrics.get(instrumentType).register(name, description, unit, instrument); + } + + /** + * Record a call made to a registered Elasticsearch {@link Instrument}. + */ + void call(Instrument instrument, Number value, Map attributes) { + call(InstrumentType.fromInstrument(instrument), instrument.getName(), value, attributes); + } + + /** + * Record a call made to the registered instrument represented by the {@link InstrumentType} enum. + */ + void call(InstrumentType instrumentType, String name, Number value, Map attributes) { + metrics.get(instrumentType).call(name, new Measurement(value, attributes, instrumentType.isDouble)); + } + + /** + * Get the {@link Measurement}s for each call of the given registered Elasticsearch {@link Instrument}. + */ + public List getMeasurements(Instrument instrument) { + return getMeasurements(InstrumentType.fromInstrument(instrument), instrument.getName()); + } + + List getMeasurements(InstrumentType instrumentType, String name) { + return metrics.get(instrumentType).called.getOrDefault(Objects.requireNonNull(name), Collections.emptyList()); + } + + /** + * Get the {@link Registration} for a given elasticsearch {@link Instrument}. + */ + Registration getRegistration(Instrument instrument) { + return metrics.get(InstrumentType.fromInstrument(instrument)).registered().get(instrument.getName()); + } + + /** + * Fetch the instrument instance given the type and registered name. + */ + I getInstrument(InstrumentType instrumentType, String name) { + return metrics.get(instrumentType).instruments.get(name); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java new file mode 100644 index 0000000000000..3878c7f9e7da1 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import org.elasticsearch.telemetry.metric.DoubleCounter; +import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.DoubleHistogram; +import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.Instrument; +import org.elasticsearch.telemetry.metric.LongCounter; +import org.elasticsearch.telemetry.metric.LongGauge; +import org.elasticsearch.telemetry.metric.LongHistogram; +import org.elasticsearch.telemetry.metric.LongUpDownCounter; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Recording versions of Elasticsearch {@link Instrument}s. All invocations are recorded via {@link MetricRecorder}. + */ +public class RecordingInstruments { + protected abstract static class RecordingInstrument implements Instrument { + protected final String name; + private final MetricRecorder recorder; + + public RecordingInstrument(String name, MetricRecorder recorder) { + this.name = Objects.requireNonNull(name); + this.recorder = Objects.requireNonNull(recorder); + } + + protected void call(Number value, Map attributes) { + recorder.call(this, value, attributes); + } + + @Override + public String getName() { + return name; + } + } + + public static class RecordingDoubleCounter extends RecordingInstrument implements DoubleCounter { + public RecordingDoubleCounter(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void increment() { + incrementBy(1.0, Collections.emptyMap()); + } + + @Override + public void incrementBy(double inc) { + incrementBy(inc, Collections.emptyMap()); + } + + @Override + public void incrementBy(double inc, Map attributes) { + call(inc, attributes); + } + } + + public static class RecordingDoubleGauge extends RecordingInstrument implements DoubleGauge { + public RecordingDoubleGauge(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void record(double value) { + record(value, Collections.emptyMap()); + } + + @Override + public void record(double value, Map attributes) { + call(value, attributes); + } + } + + public static class RecordingDoubleHistogram extends RecordingInstrument implements DoubleHistogram { + public RecordingDoubleHistogram(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void record(double value) { + record(value, Collections.emptyMap()); + } + + @Override + public void record(double value, Map attributes) { + call(value, attributes); + } + } + + public static class RecordingDoubleUpDownCounter extends RecordingInstrument implements DoubleUpDownCounter { + public RecordingDoubleUpDownCounter(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void add(double inc) { + add(inc, Collections.emptyMap()); + } + + @Override + public void add(double inc, Map attributes) { + call(inc, attributes); + } + } + + public static class RecordingLongCounter extends RecordingInstrument implements LongCounter { + public RecordingLongCounter(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void increment() { + incrementBy(1L, Collections.emptyMap()); + } + + @Override + public void incrementBy(long inc) { + incrementBy(inc, Collections.emptyMap()); + } + + @Override + public void incrementBy(long inc, Map attributes) { + call(inc, attributes); + } + } + + public static class RecordingLongGauge extends RecordingInstrument implements LongGauge { + public RecordingLongGauge(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void record(long value) { + record(value, Collections.emptyMap()); + } + + @Override + public void record(long value, Map attributes) { + call(value, attributes); + } + } + + public static class RecordingLongHistogram extends RecordingInstrument implements LongHistogram { + public RecordingLongHistogram(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void record(long value) { + record(value, Collections.emptyMap()); + } + + @Override + public void record(long value, Map attributes) { + call(value, attributes); + } + } + + public static class RecordingLongUpDownCounter extends RecordingInstrument implements LongUpDownCounter { + public RecordingLongUpDownCounter(String name, MetricRecorder recorder) { + super(name, recorder); + } + + @Override + public void add(long inc) { + add(inc, Collections.emptyMap()); + } + + @Override + public void add(long inc, Map attributes) { + call(inc, attributes); + } + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java new file mode 100644 index 0000000000000..1ca34758c8ffb --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import org.elasticsearch.telemetry.metric.DoubleCounter; +import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.DoubleHistogram; +import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.Instrument; +import org.elasticsearch.telemetry.metric.LongCounter; +import org.elasticsearch.telemetry.metric.LongGauge; +import org.elasticsearch.telemetry.metric.LongHistogram; +import org.elasticsearch.telemetry.metric.LongUpDownCounter; +import org.elasticsearch.telemetry.metric.MeterRegistry; + +/** + * A {@link MeterRegistry} that records all instrument invocations. + * Tests can subclass this class and extend the build[Instrument] methods to do their + * own validations at instrument registration time and/or provide their own instruments. + */ +public class RecordingMeterRegistry implements MeterRegistry { + protected final MetricRecorder recorder = new MetricRecorder<>(); + + MetricRecorder getRecorder() { + return recorder; + } + + @Override + public DoubleCounter registerDoubleCounter(String name, String description, String unit) { + DoubleCounter instrument = buildDoubleCounter(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public DoubleCounter getDoubleCounter(String name) { + return (DoubleCounter) recorder.getInstrument(InstrumentType.DOUBLE_COUNTER, name); + } + + protected DoubleCounter buildDoubleCounter(String name, String description, String unit) { + return new RecordingInstruments.RecordingDoubleCounter(name, recorder); + } + + @Override + public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) { + DoubleUpDownCounter instrument = buildDoubleUpDownCounter(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public DoubleUpDownCounter getDoubleUpDownCounter(String name) { + return (DoubleUpDownCounter) recorder.getInstrument(InstrumentType.DOUBLE_UP_DOWN_COUNTER, name); + } + + protected DoubleUpDownCounter buildDoubleUpDownCounter(String name, String description, String unit) { + return new RecordingInstruments.RecordingDoubleUpDownCounter(name, recorder); + } + + @Override + public DoubleGauge registerDoubleGauge(String name, String description, String unit) { + DoubleGauge instrument = buildDoubleGauge(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public DoubleGauge getDoubleGauge(String name) { + return (DoubleGauge) recorder.getInstrument(InstrumentType.DOUBLE_GAUGE, name); + } + + protected DoubleGauge buildDoubleGauge(String name, String description, String unit) { + return new RecordingInstruments.RecordingDoubleGauge(name, recorder); + } + + @Override + public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) { + DoubleHistogram instrument = buildDoubleHistogram(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public DoubleHistogram getDoubleHistogram(String name) { + return (DoubleHistogram) recorder.getInstrument(InstrumentType.DOUBLE_HISTOGRAM, name); + } + + protected DoubleHistogram buildDoubleHistogram(String name, String description, String unit) { + return new RecordingInstruments.RecordingDoubleHistogram(name, recorder); + } + + @Override + public LongCounter registerLongCounter(String name, String description, String unit) { + LongCounter instrument = buildLongCounter(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public LongCounter getLongCounter(String name) { + return (LongCounter) recorder.getInstrument(InstrumentType.LONG_COUNTER, name); + } + + protected LongCounter buildLongCounter(String name, String description, String unit) { + return new RecordingInstruments.RecordingLongCounter(name, recorder); + } + + @Override + public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) { + LongUpDownCounter instrument = buildLongUpDownCounter(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public LongUpDownCounter getLongUpDownCounter(String name) { + return (LongUpDownCounter) recorder.getInstrument(InstrumentType.LONG_UP_DOWN_COUNTER, name); + } + + protected LongUpDownCounter buildLongUpDownCounter(String name, String description, String unit) { + return new RecordingInstruments.RecordingLongUpDownCounter(name, recorder); + } + + @Override + public LongGauge registerLongGauge(String name, String description, String unit) { + LongGauge instrument = buildLongGauge(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public LongGauge getLongGauge(String name) { + return (LongGauge) recorder.getInstrument(InstrumentType.LONG_GAUGE, name); + } + + protected LongGauge buildLongGauge(String name, String description, String unit) { + return new RecordingInstruments.RecordingLongGauge(name, recorder); + } + + @Override + public LongHistogram registerLongHistogram(String name, String description, String unit) { + LongHistogram instrument = buildLongHistogram(name, description, unit); + recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); + return instrument; + } + + @Override + public LongHistogram getLongHistogram(String name) { + return (LongHistogram) recorder.getInstrument(InstrumentType.LONG_HISTOGRAM, name); + } + + protected LongHistogram buildLongHistogram(String name, String description, String unit) { + return new RecordingInstruments.RecordingLongHistogram(name, recorder); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/Registration.java b/test/framework/src/main/java/org/elasticsearch/telemetry/Registration.java new file mode 100644 index 0000000000000..8a54d9b1476a4 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/Registration.java @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import java.util.Objects; + +/** + * A record of the arguments for a registered instrument. + */ +record Registration(String name, String description, String unit) { + Registration { + Objects.requireNonNull(name); + Objects.requireNonNull(description); + Objects.requireNonNull(unit); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/TestTelemetryPlugin.java b/test/framework/src/main/java/org/elasticsearch/telemetry/TestTelemetryPlugin.java new file mode 100644 index 0000000000000..53aef542f0d1a --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/TestTelemetryPlugin.java @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.TelemetryPlugin; +import org.elasticsearch.telemetry.metric.Instrument; +import org.elasticsearch.telemetry.metric.MeterRegistry; +import org.elasticsearch.telemetry.tracing.Tracer; + +import java.util.List; + +/** + * TelemetryPlugin that uses RecordingMeterRegistry to record meter calls + * and exposes measurement getters. + */ +public class TestTelemetryPlugin extends Plugin implements TelemetryPlugin { + + protected final RecordingMeterRegistry meter = new RecordingMeterRegistry(); + + Registration getRegistration(Instrument instrument) { + return meter.getRecorder().getRegistration(instrument); + } + + public List getMetrics(Instrument instrument) { + return meter.getRecorder().getMeasurements(instrument); + } + + public List getDoubleCounterMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.DOUBLE_COUNTER, name); + } + + public List getLongCounterMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.LONG_COUNTER, name); + } + + public List getDoubleUpDownCounterMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.DOUBLE_UP_DOWN_COUNTER, name); + } + + public List getLongUpDownCounterMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.LONG_UP_DOWN_COUNTER, name); + } + + public List getDoubleGaugeMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.DOUBLE_GAUGE, name); + } + + public List getLongGaugeMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.LONG_GAUGE, name); + } + + public List getDoubleHistogramMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.DOUBLE_HISTOGRAM, name); + } + + public List getLongHistogramMeasurement(String name) { + return meter.getRecorder().getMeasurements(InstrumentType.LONG_HISTOGRAM, name); + } + + @Override + public TelemetryProvider getTelemetryProvider(Settings settings) { + return new TelemetryProvider() { + @Override + public Tracer getTracer() { + return Tracer.NOOP; + } + + @Override + public MeterRegistry getMeterRegistry() { + return meter; + } + }; + } +} From 8a9f4fed55642fa936beffd61a2fa2105e46d70a Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Tue, 24 Oct 2023 17:46:25 +0200 Subject: [PATCH 083/203] Remove explicit SearchResponse references from LegacyGeo, Aggregations and parent-join modules (#101250) --- .../bucket/AdjacencyMatrixIT.java | 399 ++--- .../bucket/TimeSeriesAggregationsIT.java | 265 +-- .../TimeSeriesNestedAggregationsIT.java | 47 +- .../pipeline/DateDerivativeIT.java | 775 ++++----- .../aggregations/pipeline/SerialDiffIT.java | 80 +- .../legacygeo/search/LegacyGeoShapeIT.java | 6 +- .../search/LegacyGeoShapeQueryTests.java | 14 +- .../join/aggregations/ChildrenIT.java | 401 ++--- .../join/aggregations/ParentIT.java | 334 ++-- .../join/query/ChildQuerySearchIT.java | 1517 +++++++++-------- .../elasticsearch/join/query/InnerHitsIT.java | 534 +++--- 11 files changed, 2275 insertions(+), 2097 deletions(-) diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java index 3ff0dc99bdc71..37e782cd7c611 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/AdjacencyMatrixIT.java @@ -12,7 +12,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.aggregations.AggregationIntegTestCase; import org.elasticsearch.aggregations.bucket.adjacency.AdjacencyMatrix; import org.elasticsearch.aggregations.bucket.adjacency.AdjacencyMatrix.Bucket; @@ -35,7 +34,9 @@ import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.avg; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -98,181 +99,180 @@ public void setupSuiteScopeCluster() throws Exception { } public void testSimple() throws Exception { - SearchResponse response = prepareSearch("idx").addAggregation( - adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))) - ).get(); - - assertNoFailures(response); - - AdjacencyMatrix matrix = response.getAggregations().get("tags"); - assertThat(matrix, notNullValue()); - assertThat(matrix.getName(), equalTo("tags")); - - int expected = numMultiTagDocs > 0 ? 3 : 2; - assertThat(matrix.getBuckets().size(), equalTo(expected)); - - AdjacencyMatrix.Bucket bucket = matrix.getBucketByKey("tag1"); - assertThat(bucket, Matchers.notNullValue()); - assertThat(bucket.getDocCount(), equalTo((long) numTag1Docs)); - - bucket = matrix.getBucketByKey("tag2"); - assertThat(bucket, Matchers.notNullValue()); - assertThat(bucket.getDocCount(), equalTo((long) numTag2Docs)); - - bucket = matrix.getBucketByKey("tag1&tag2"); - if (numMultiTagDocs == 0) { - assertThat(bucket, Matchers.nullValue()); - } else { - assertThat(bucket, Matchers.notNullValue()); - assertThat(bucket.getDocCount(), equalTo((long) numMultiTagDocs)); - } - + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))) + ), + response -> { + AdjacencyMatrix matrix = response.getAggregations().get("tags"); + assertThat(matrix, notNullValue()); + assertThat(matrix.getName(), equalTo("tags")); + + int expected = numMultiTagDocs > 0 ? 3 : 2; + assertThat(matrix.getBuckets().size(), equalTo(expected)); + + AdjacencyMatrix.Bucket bucket = matrix.getBucketByKey("tag1"); + assertThat(bucket, Matchers.notNullValue()); + assertThat(bucket.getDocCount(), equalTo((long) numTag1Docs)); + + bucket = matrix.getBucketByKey("tag2"); + assertThat(bucket, Matchers.notNullValue()); + assertThat(bucket.getDocCount(), equalTo((long) numTag2Docs)); + + bucket = matrix.getBucketByKey("tag1&tag2"); + if (numMultiTagDocs == 0) { + assertThat(bucket, Matchers.nullValue()); + } else { + assertThat(bucket, Matchers.notNullValue()); + assertThat(bucket.getDocCount(), equalTo((long) numMultiTagDocs)); + } + } + ); } public void testCustomSeparator() throws Exception { - SearchResponse response = prepareSearch("idx").addAggregation( - adjacencyMatrix("tags", "\t", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))) - ).get(); - - assertNoFailures(response); - - AdjacencyMatrix matrix = response.getAggregations().get("tags"); - assertThat(matrix, notNullValue()); - - AdjacencyMatrix.Bucket bucket = matrix.getBucketByKey("tag1\ttag2"); - if (numMultiTagDocs == 0) { - assertThat(bucket, Matchers.nullValue()); - } else { - assertThat(bucket, Matchers.notNullValue()); - assertThat(bucket.getDocCount(), equalTo((long) numMultiTagDocs)); - } - + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", "\t", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2"))) + ), + response -> { + AdjacencyMatrix matrix = response.getAggregations().get("tags"); + assertThat(matrix, notNullValue()); + + AdjacencyMatrix.Bucket bucket = matrix.getBucketByKey("tag1\ttag2"); + if (numMultiTagDocs == 0) { + assertThat(bucket, Matchers.nullValue()); + } else { + assertThat(bucket, Matchers.notNullValue()); + assertThat(bucket.getDocCount(), equalTo((long) numMultiTagDocs)); + } + } + ); } // See NullPointer issue when filters are empty: // https://github.com/elastic/elasticsearch/issues/8438 public void testEmptyFilterDeclarations() throws Exception { QueryBuilder emptyFilter = new BoolQueryBuilder(); - SearchResponse response = prepareSearch("idx").addAggregation( - adjacencyMatrix("tags", newMap("all", emptyFilter).add("tag1", termQuery("tag", "tag1"))) - ).get(); - - assertNoFailures(response); - - AdjacencyMatrix filters = response.getAggregations().get("tags"); - assertThat(filters, notNullValue()); - AdjacencyMatrix.Bucket allBucket = filters.getBucketByKey("all"); - assertThat(allBucket.getDocCount(), equalTo((long) numDocs)); - - AdjacencyMatrix.Bucket bucket = filters.getBucketByKey("tag1"); - assertThat(bucket, Matchers.notNullValue()); - assertThat(bucket.getDocCount(), equalTo((long) numTag1Docs)); + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation(adjacencyMatrix("tags", newMap("all", emptyFilter).add("tag1", termQuery("tag", "tag1")))), + response -> { + AdjacencyMatrix filters = response.getAggregations().get("tags"); + assertThat(filters, notNullValue()); + AdjacencyMatrix.Bucket allBucket = filters.getBucketByKey("all"); + assertThat(allBucket.getDocCount(), equalTo((long) numDocs)); + + AdjacencyMatrix.Bucket bucket = filters.getBucketByKey("tag1"); + assertThat(bucket, Matchers.notNullValue()); + assertThat(bucket.getDocCount(), equalTo((long) numTag1Docs)); + } + ); } public void testWithSubAggregation() throws Exception { BoolQueryBuilder boolQ = new BoolQueryBuilder(); boolQ.must(termQuery("tag", "tag1")); boolQ.must(termQuery("tag", "tag2")); - SearchResponse response = prepareSearch("idx").addAggregation( - adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")).add("both", boolQ)) - .subAggregation(avg("avg_value").field("value")) - ).get(); - - assertNoFailures(response); - - AdjacencyMatrix matrix = response.getAggregations().get("tags"); - assertThat(matrix, notNullValue()); - assertThat(matrix.getName(), equalTo("tags")); - - int expectedBuckets = 0; - if (numTag1Docs > 0) { - expectedBuckets++; - } - if (numTag2Docs > 0) { - expectedBuckets++; - } - if (numMultiTagDocs > 0) { - // both, both&tag1, both&tag2, tag1&tag2 - expectedBuckets += 4; - } - - assertThat(matrix.getBuckets().size(), equalTo(expectedBuckets)); - assertThat(((InternalAggregation) matrix).getProperty("_bucket_count"), equalTo(expectedBuckets)); - - Object[] propertiesKeys = (Object[]) ((InternalAggregation) matrix).getProperty("_key"); - Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) matrix).getProperty("_count"); - Object[] propertiesCounts = (Object[]) ((InternalAggregation) matrix).getProperty("avg_value.value"); - - assertEquals(expectedBuckets, propertiesKeys.length); - assertEquals(propertiesKeys.length, propertiesDocCounts.length); - assertEquals(propertiesKeys.length, propertiesCounts.length); - - for (int i = 0; i < propertiesCounts.length; i++) { - AdjacencyMatrix.Bucket bucket = matrix.getBucketByKey(propertiesKeys[i].toString()); - assertThat(bucket, Matchers.notNullValue()); - Avg avgValue = bucket.getAggregations().get("avg_value"); - assertThat(avgValue, notNullValue()); - assertThat((long) propertiesDocCounts[i], equalTo(bucket.getDocCount())); - assertThat((double) propertiesCounts[i], equalTo(avgValue.getValue())); - } - - AdjacencyMatrix.Bucket tag1Bucket = matrix.getBucketByKey("tag1"); - assertThat(tag1Bucket, Matchers.notNullValue()); - assertThat(tag1Bucket.getDocCount(), equalTo((long) numTag1Docs)); - long sum = 0; - for (int i = 0; i < numSingleTag1Docs; i++) { - sum += i + 1; - } - for (int i = numSingleTag1Docs + numSingleTag2Docs; i < numDocs; i++) { - sum += i + 1; - } - assertThat(tag1Bucket.getAggregations().asList().isEmpty(), is(false)); - Avg avgBucket1Value = tag1Bucket.getAggregations().get("avg_value"); - assertThat(avgBucket1Value, notNullValue()); - assertThat(avgBucket1Value.getName(), equalTo("avg_value")); - assertThat(avgBucket1Value.getValue(), equalTo((double) sum / numTag1Docs)); - - Bucket tag2Bucket = matrix.getBucketByKey("tag2"); - assertThat(tag2Bucket, Matchers.notNullValue()); - assertThat(tag2Bucket.getDocCount(), equalTo((long) numTag2Docs)); - sum = 0; - for (int i = numSingleTag1Docs; i < numDocs; i++) { - sum += i + 1; - } - assertThat(tag2Bucket.getAggregations().asList().isEmpty(), is(false)); - Avg avgBucket2Value = tag2Bucket.getAggregations().get("avg_value"); - assertThat(avgBucket2Value, notNullValue()); - assertThat(avgBucket2Value.getName(), equalTo("avg_value")); - assertThat(avgBucket2Value.getValue(), equalTo((double) sum / numTag2Docs)); - - // Check intersection buckets are computed correctly by comparing with - // ANDed query bucket results - Bucket bucketBothQ = matrix.getBucketByKey("both"); - if (numMultiTagDocs == 0) { - // Empty intersections are not returned. - assertThat(bucketBothQ, Matchers.nullValue()); - Bucket bucketIntersectQ = matrix.getBucketByKey("tag1&tag2"); - assertThat(bucketIntersectQ, Matchers.nullValue()); - Bucket tag1Both = matrix.getBucketByKey("both&tag1"); - assertThat(tag1Both, Matchers.nullValue()); - } else { - assertThat(bucketBothQ, Matchers.notNullValue()); - assertThat(bucketBothQ.getDocCount(), equalTo((long) numMultiTagDocs)); - Avg avgValueBothQ = bucketBothQ.getAggregations().get("avg_value"); - - Bucket bucketIntersectQ = matrix.getBucketByKey("tag1&tag2"); - assertThat(bucketIntersectQ, Matchers.notNullValue()); - assertThat(bucketIntersectQ.getDocCount(), equalTo((long) numMultiTagDocs)); - Avg avgValueIntersectQ = bucketBothQ.getAggregations().get("avg_value"); - assertThat(avgValueIntersectQ.getValue(), equalTo(avgValueBothQ.getValue())); - - Bucket tag1Both = matrix.getBucketByKey("both&tag1"); - assertThat(tag1Both, Matchers.notNullValue()); - assertThat(tag1Both.getDocCount(), equalTo((long) numMultiTagDocs)); - Avg avgValueTag1BothIntersectQ = tag1Both.getAggregations().get("avg_value"); - assertThat(avgValueTag1BothIntersectQ.getValue(), equalTo(avgValueBothQ.getValue())); - } - + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + adjacencyMatrix("tags", newMap("tag1", termQuery("tag", "tag1")).add("tag2", termQuery("tag", "tag2")).add("both", boolQ)) + .subAggregation(avg("avg_value").field("value")) + ), + response -> { + AdjacencyMatrix matrix = response.getAggregations().get("tags"); + assertThat(matrix, notNullValue()); + assertThat(matrix.getName(), equalTo("tags")); + + int expectedBuckets = 0; + if (numTag1Docs > 0) { + expectedBuckets++; + } + if (numTag2Docs > 0) { + expectedBuckets++; + } + if (numMultiTagDocs > 0) { + // both, both&tag1, both&tag2, tag1&tag2 + expectedBuckets += 4; + } + + assertThat(matrix.getBuckets().size(), equalTo(expectedBuckets)); + assertThat(((InternalAggregation) matrix).getProperty("_bucket_count"), equalTo(expectedBuckets)); + + Object[] propertiesKeys = (Object[]) ((InternalAggregation) matrix).getProperty("_key"); + Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) matrix).getProperty("_count"); + Object[] propertiesCounts = (Object[]) ((InternalAggregation) matrix).getProperty("avg_value.value"); + + assertEquals(expectedBuckets, propertiesKeys.length); + assertEquals(propertiesKeys.length, propertiesDocCounts.length); + assertEquals(propertiesKeys.length, propertiesCounts.length); + + for (int i = 0; i < propertiesCounts.length; i++) { + AdjacencyMatrix.Bucket bucket = matrix.getBucketByKey(propertiesKeys[i].toString()); + assertThat(bucket, Matchers.notNullValue()); + Avg avgValue = bucket.getAggregations().get("avg_value"); + assertThat(avgValue, notNullValue()); + assertThat((long) propertiesDocCounts[i], equalTo(bucket.getDocCount())); + assertThat((double) propertiesCounts[i], equalTo(avgValue.getValue())); + } + + AdjacencyMatrix.Bucket tag1Bucket = matrix.getBucketByKey("tag1"); + assertThat(tag1Bucket, Matchers.notNullValue()); + assertThat(tag1Bucket.getDocCount(), equalTo((long) numTag1Docs)); + long sum = 0; + for (int i = 0; i < numSingleTag1Docs; i++) { + sum += i + 1; + } + for (int i = numSingleTag1Docs + numSingleTag2Docs; i < numDocs; i++) { + sum += i + 1; + } + assertThat(tag1Bucket.getAggregations().asList().isEmpty(), is(false)); + Avg avgBucket1Value = tag1Bucket.getAggregations().get("avg_value"); + assertThat(avgBucket1Value, notNullValue()); + assertThat(avgBucket1Value.getName(), equalTo("avg_value")); + assertThat(avgBucket1Value.getValue(), equalTo((double) sum / numTag1Docs)); + + Bucket tag2Bucket = matrix.getBucketByKey("tag2"); + assertThat(tag2Bucket, Matchers.notNullValue()); + assertThat(tag2Bucket.getDocCount(), equalTo((long) numTag2Docs)); + sum = 0; + for (int i = numSingleTag1Docs; i < numDocs; i++) { + sum += i + 1; + } + assertThat(tag2Bucket.getAggregations().asList().isEmpty(), is(false)); + Avg avgBucket2Value = tag2Bucket.getAggregations().get("avg_value"); + assertThat(avgBucket2Value, notNullValue()); + assertThat(avgBucket2Value.getName(), equalTo("avg_value")); + assertThat(avgBucket2Value.getValue(), equalTo((double) sum / numTag2Docs)); + + // Check intersection buckets are computed correctly by comparing with + // ANDed query bucket results + Bucket bucketBothQ = matrix.getBucketByKey("both"); + if (numMultiTagDocs == 0) { + // Empty intersections are not returned. + assertThat(bucketBothQ, Matchers.nullValue()); + Bucket bucketIntersectQ = matrix.getBucketByKey("tag1&tag2"); + assertThat(bucketIntersectQ, Matchers.nullValue()); + Bucket tag1Both = matrix.getBucketByKey("both&tag1"); + assertThat(tag1Both, Matchers.nullValue()); + } else { + assertThat(bucketBothQ, Matchers.notNullValue()); + assertThat(bucketBothQ.getDocCount(), equalTo((long) numMultiTagDocs)); + Avg avgValueBothQ = bucketBothQ.getAggregations().get("avg_value"); + + Bucket bucketIntersectQ = matrix.getBucketByKey("tag1&tag2"); + assertThat(bucketIntersectQ, Matchers.notNullValue()); + assertThat(bucketIntersectQ.getDocCount(), equalTo((long) numMultiTagDocs)); + Avg avgValueIntersectQ = bucketBothQ.getAggregations().get("avg_value"); + assertThat(avgValueIntersectQ.getValue(), equalTo(avgValueBothQ.getValue())); + + Bucket tag1Both = matrix.getBucketByKey("both&tag1"); + assertThat(tag1Both, Matchers.notNullValue()); + assertThat(tag1Both.getDocCount(), equalTo((long) numMultiTagDocs)); + Avg avgValueTag1BothIntersectQ = tag1Both.getAggregations().get("avg_value"); + assertThat(avgValueTag1BothIntersectQ.getValue(), equalTo(avgValueBothQ.getValue())); + } + } + ); } public void testTooLargeMatrix() { @@ -301,23 +301,24 @@ public void testTooLargeMatrix() { } public void testAsSubAggregation() { - SearchResponse response = prepareSearch("idx").addAggregation( - histogram("histo").field("value").interval(2L).subAggregation(adjacencyMatrix("matrix", newMap("all", matchAllQuery()))) - ).get(); - - assertNoFailures(response); - - Histogram histo = response.getAggregations().get("histo"); - assertThat(histo, notNullValue()); - assertThat(histo.getBuckets().size(), greaterThanOrEqualTo(1)); - - for (Histogram.Bucket bucket : histo.getBuckets()) { - AdjacencyMatrix matrix = bucket.getAggregations().get("matrix"); - assertThat(matrix, notNullValue()); - assertThat(matrix.getBuckets().size(), equalTo(1)); - AdjacencyMatrix.Bucket filterBucket = matrix.getBuckets().get(0); - assertEquals(bucket.getDocCount(), filterBucket.getDocCount()); - } + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + histogram("histo").field("value").interval(2L).subAggregation(adjacencyMatrix("matrix", newMap("all", matchAllQuery()))) + ), + response -> { + Histogram histo = response.getAggregations().get("histo"); + assertThat(histo, notNullValue()); + assertThat(histo.getBuckets().size(), greaterThanOrEqualTo(1)); + + for (Histogram.Bucket bucket : histo.getBuckets()) { + AdjacencyMatrix matrix = bucket.getAggregations().get("matrix"); + assertThat(matrix, notNullValue()); + assertThat(matrix.getBuckets().size(), equalTo(1)); + AdjacencyMatrix.Bucket filterBucket = matrix.getBuckets().get(0); + assertEquals(bucket.getDocCount(), filterBucket.getDocCount()); + } + } + ); } public void testWithContextBasedSubAggregation() throws Exception { @@ -340,25 +341,27 @@ public void testWithContextBasedSubAggregation() throws Exception { } public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) - .addAggregation( - histogram("histo").field("value") - .interval(1L) - .minDocCount(0) - .subAggregation(adjacencyMatrix("matrix", newMap("all", matchAllQuery()))) - ) - .get(); - - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - Histogram histo = searchResponse.getAggregations().get("histo"); - assertThat(histo, Matchers.notNullValue()); - Histogram.Bucket bucket = histo.getBuckets().get(1); - assertThat(bucket, Matchers.notNullValue()); - - AdjacencyMatrix matrix = bucket.getAggregations().get("matrix"); - assertThat(matrix, notNullValue()); - AdjacencyMatrix.Bucket all = matrix.getBucketByKey("all"); - assertThat(all, Matchers.nullValue()); + assertResponse( + prepareSearch("empty_bucket_idx").setQuery(matchAllQuery()) + .addAggregation( + histogram("histo").field("value") + .interval(1L) + .minDocCount(0) + .subAggregation(adjacencyMatrix("matrix", newMap("all", matchAllQuery()))) + ), + response -> { + assertHitCount(response, 2L); + Histogram histo = response.getAggregations().get("histo"); + assertThat(histo, Matchers.notNullValue()); + Histogram.Bucket bucket = histo.getBuckets().get(1); + assertThat(bucket, Matchers.notNullValue()); + + AdjacencyMatrix matrix = bucket.getAggregations().get("matrix"); + assertThat(matrix, notNullValue()); + AdjacencyMatrix.Bucket all = matrix.getBucketByKey("all"); + assertThat(all, Matchers.nullValue()); + } + ); } // Helper methods for building maps of QueryBuilders diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java index 53d5fb9c00991..3f7d52c32e8df 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesAggregationsIT.java @@ -13,7 +13,6 @@ import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.aggregations.AggregationIntegTestCase; import org.elasticsearch.aggregations.bucket.timeseries.InternalTimeSeries; @@ -58,6 +57,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -176,84 +176,92 @@ public void setupSuiteScopeCluster() throws Exception { } public void testStandAloneTimeSeriesAgg() { - SearchResponse response = prepareSearch("index").setSize(0).addAggregation(timeSeries("by_ts")).get(); - assertNoFailures(response); - Aggregations aggregations = response.getAggregations(); - assertNotNull(aggregations); - InternalTimeSeries timeSeries = aggregations.get("by_ts"); - assertThat( - timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), - equalTo(data.keySet()) - ); - for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { - @SuppressWarnings("unchecked") - Map key = (Map) bucket.getKey(); - assertThat((long) data.get(key).size(), equalTo(bucket.getDocCount())); - } - } - - public void testTimeSeriesGroupedByADimension() { - String groupBy = "dim_" + randomIntBetween(0, numberOfDimensions - 1); - SearchResponse response = prepareSearch("index").setSize(0) - .addAggregation( - terms("by_dim").field(groupBy) - .size(data.size()) - .collectMode(randomFrom(Aggregator.SubAggCollectionMode.values())) - .subAggregation(timeSeries("by_ts")) - ) - .get(); - assertNoFailures(response); - Aggregations aggregations = response.getAggregations(); - assertNotNull(aggregations); - Terms terms = aggregations.get("by_dim"); - Set> keys = new HashSet<>(); - for (Terms.Bucket term : terms.getBuckets()) { - InternalTimeSeries timeSeries = term.getAggregations().get("by_ts"); + assertNoFailuresAndResponse(prepareSearch("index").setSize(0).addAggregation(timeSeries("by_ts")), response -> { + Aggregations aggregations = response.getAggregations(); + assertNotNull(aggregations); + InternalTimeSeries timeSeries = aggregations.get("by_ts"); + assertThat( + timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), + equalTo(data.keySet()) + ); for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { @SuppressWarnings("unchecked") Map key = (Map) bucket.getKey(); assertThat((long) data.get(key).size(), equalTo(bucket.getDocCount())); - assertTrue("key is not unique", keys.add(key)); - assertThat("time series doesn't contain dimensions we grouped by", key.get(groupBy), equalTo(term.getKeyAsString())); } - } - assertThat(keys, equalTo(data.keySet())); + }); + } + + public void testTimeSeriesGroupedByADimension() { + String groupBy = "dim_" + randomIntBetween(0, numberOfDimensions - 1); + assertNoFailuresAndResponse( + prepareSearch("index").setSize(0) + .addAggregation( + terms("by_dim").field(groupBy) + .size(data.size()) + .collectMode(randomFrom(Aggregator.SubAggCollectionMode.values())) + .subAggregation(timeSeries("by_ts")) + ), + response -> { + Aggregations aggregations = response.getAggregations(); + assertNotNull(aggregations); + Terms terms = aggregations.get("by_dim"); + Set> keys = new HashSet<>(); + for (Terms.Bucket term : terms.getBuckets()) { + InternalTimeSeries timeSeries = term.getAggregations().get("by_ts"); + for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { + @SuppressWarnings("unchecked") + Map key = (Map) bucket.getKey(); + assertThat((long) data.get(key).size(), equalTo(bucket.getDocCount())); + assertTrue("key is not unique", keys.add(key)); + assertThat( + "time series doesn't contain dimensions we grouped by", + key.get(groupBy), + equalTo(term.getKeyAsString()) + ); + } + } + assertThat(keys, equalTo(data.keySet())); + } + ); } public void testTimeSeriesGroupedByDateHistogram() { DateHistogramInterval fixedInterval = DateHistogramInterval.days(randomIntBetween(10, 100)); - SearchResponse response = prepareSearch("index").setSize(0) - .addAggregation( - dateHistogram("by_time").field("@timestamp") - .fixedInterval(fixedInterval) - .subAggregation(timeSeries("by_ts").subAggregation(stats("timestamp").field("@timestamp"))) - ) - .get(); - assertNoFailures(response); - Aggregations aggregations = response.getAggregations(); - assertNotNull(aggregations); - Histogram histogram = aggregations.get("by_time"); - Map, Long> keys = new HashMap<>(); - for (Histogram.Bucket interval : histogram.getBuckets()) { - long intervalStart = ((ZonedDateTime) interval.getKey()).toEpochSecond() * 1000; - long intervalEnd = intervalStart + fixedInterval.estimateMillis(); - InternalTimeSeries timeSeries = interval.getAggregations().get("by_ts"); - for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { - @SuppressWarnings("unchecked") - Map key = (Map) bucket.getKey(); - keys.compute(key, (k, v) -> (v == null ? 0 : v) + bucket.getDocCount()); - assertThat(bucket.getDocCount(), lessThanOrEqualTo((long) data.get(key).size())); - Stats stats = bucket.getAggregations().get("timestamp"); - long minTimestamp = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parseMillis(stats.getMinAsString()); - long maxTimestamp = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parseMillis(stats.getMaxAsString()); - assertThat(minTimestamp, greaterThanOrEqualTo(intervalStart)); - assertThat(maxTimestamp, lessThan(intervalEnd)); + assertNoFailuresAndResponse( + prepareSearch("index").setSize(0) + .addAggregation( + dateHistogram("by_time").field("@timestamp") + .fixedInterval(fixedInterval) + .subAggregation(timeSeries("by_ts").subAggregation(stats("timestamp").field("@timestamp"))) + ), + response -> { + Aggregations aggregations = response.getAggregations(); + assertNotNull(aggregations); + Histogram histogram = aggregations.get("by_time"); + Map, Long> keys = new HashMap<>(); + for (Histogram.Bucket interval : histogram.getBuckets()) { + long intervalStart = ((ZonedDateTime) interval.getKey()).toEpochSecond() * 1000; + long intervalEnd = intervalStart + fixedInterval.estimateMillis(); + InternalTimeSeries timeSeries = interval.getAggregations().get("by_ts"); + for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { + @SuppressWarnings("unchecked") + Map key = (Map) bucket.getKey(); + keys.compute(key, (k, v) -> (v == null ? 0 : v) + bucket.getDocCount()); + assertThat(bucket.getDocCount(), lessThanOrEqualTo((long) data.get(key).size())); + Stats stats = bucket.getAggregations().get("timestamp"); + long minTimestamp = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parseMillis(stats.getMinAsString()); + long maxTimestamp = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parseMillis(stats.getMaxAsString()); + assertThat(minTimestamp, greaterThanOrEqualTo(intervalStart)); + assertThat(maxTimestamp, lessThan(intervalEnd)); + } + } + assertThat(keys.keySet(), equalTo(data.keySet())); + for (Map.Entry, Long> entry : keys.entrySet()) { + assertThat(entry.getValue(), equalTo((long) data.get(entry.getKey()).size())); + } } - } - assertThat(keys.keySet(), equalTo(data.keySet())); - for (Map.Entry, Long> entry : keys.entrySet()) { - assertThat(entry.getValue(), equalTo((long) data.get(entry.getKey()).size())); - } + ); } public void testStandAloneTimeSeriesAggWithDimFilter() { @@ -264,21 +272,24 @@ public void testStandAloneTimeSeriesAggWithDimFilter() { if (include == false) { queryBuilder = QueryBuilders.boolQuery().mustNot(queryBuilder); } - SearchResponse response = prepareSearch("index").setQuery(queryBuilder).setSize(0).addAggregation(timeSeries("by_ts")).get(); - assertNoFailures(response); - Aggregations aggregations = response.getAggregations(); - assertNotNull(aggregations); - InternalTimeSeries timeSeries = aggregations.get("by_ts"); - Map, Map>> filteredData = dataFilteredByDimension("dim_" + dim, val, include); - assertThat( - timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), - equalTo(filteredData.keySet()) + assertNoFailuresAndResponse( + prepareSearch("index").setQuery(queryBuilder).setSize(0).addAggregation(timeSeries("by_ts")), + response -> { + Aggregations aggregations = response.getAggregations(); + assertNotNull(aggregations); + InternalTimeSeries timeSeries = aggregations.get("by_ts"); + Map, Map>> filteredData = dataFilteredByDimension("dim_" + dim, val, include); + assertThat( + timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), + equalTo(filteredData.keySet()) + ); + for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { + @SuppressWarnings("unchecked") + Map key = (Map) bucket.getKey(); + assertThat(bucket.getDocCount(), equalTo((long) filteredData.get(key).size())); + } + } ); - for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { - @SuppressWarnings("unchecked") - Map key = (Map) bucket.getKey(); - assertThat(bucket.getDocCount(), equalTo((long) filteredData.get(key).size())); - } } public void testStandAloneTimeSeriesAggWithGlobalAggregation() { @@ -290,32 +301,34 @@ public void testStandAloneTimeSeriesAggWithGlobalAggregation() { if (include == false) { queryBuilder = QueryBuilders.boolQuery().mustNot(queryBuilder); } - SearchResponse response = prepareSearch("index").setQuery(queryBuilder) - .setSize(0) - .addAggregation(timeSeries("by_ts").subAggregation(sum("filter_sum").field("metric_" + metric))) - .addAggregation(global("everything").subAggregation(sum("all_sum").field("metric_" + metric))) - .addAggregation(PipelineAggregatorBuilders.sumBucket("total_filter_sum", "by_ts>filter_sum")) - .get(); - assertNoFailures(response); - Aggregations aggregations = response.getAggregations(); - assertNotNull(aggregations); - InternalTimeSeries timeSeries = aggregations.get("by_ts"); - Map, Map>> filteredData = dataFilteredByDimension("dim_" + dim, val, include); - assertThat( - timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), - equalTo(filteredData.keySet()) - ); - for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { - @SuppressWarnings("unchecked") - Map key = (Map) bucket.getKey(); - assertThat(bucket.getDocCount(), equalTo((long) filteredData.get(key).size())); - } - SimpleValue obj = aggregations.get("total_filter_sum"); - assertThat(obj.value(), closeTo(sumByMetric(filteredData, "metric_" + metric), obj.value() * 0.0001)); + assertNoFailuresAndResponse( + prepareSearch("index").setQuery(queryBuilder) + .setSize(0) + .addAggregation(timeSeries("by_ts").subAggregation(sum("filter_sum").field("metric_" + metric))) + .addAggregation(global("everything").subAggregation(sum("all_sum").field("metric_" + metric))) + .addAggregation(PipelineAggregatorBuilders.sumBucket("total_filter_sum", "by_ts>filter_sum")), + response -> { + Aggregations aggregations = response.getAggregations(); + assertNotNull(aggregations); + InternalTimeSeries timeSeries = aggregations.get("by_ts"); + Map, Map>> filteredData = dataFilteredByDimension("dim_" + dim, val, include); + assertThat( + timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), + equalTo(filteredData.keySet()) + ); + for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { + @SuppressWarnings("unchecked") + Map key = (Map) bucket.getKey(); + assertThat(bucket.getDocCount(), equalTo((long) filteredData.get(key).size())); + } + SimpleValue obj = aggregations.get("total_filter_sum"); + assertThat(obj.value(), closeTo(sumByMetric(filteredData, "metric_" + metric), obj.value() * 0.0001)); - Global global = aggregations.get("everything"); - Sum allSum = global.getAggregations().get("all_sum"); - assertThat(allSum.value(), closeTo(sumByMetric(data, "metric_" + metric), allSum.value() * 0.0001)); + Global global = aggregations.get("everything"); + Sum allSum = global.getAggregations().get("all_sum"); + assertThat(allSum.value(), closeTo(sumByMetric(data, "metric_" + metric), allSum.value() * 0.0001)); + } + ); ElasticsearchException e = expectThrows( ElasticsearchException.class, @@ -337,21 +350,29 @@ public void testStandAloneTimeSeriesAggWithMetricFilter() { } else { queryBuilder.lte(val); } - SearchResponse response = prepareSearch("index").setQuery(queryBuilder).setSize(0).addAggregation(timeSeries("by_ts")).get(); - assertNoFailures(response); - Aggregations aggregations = response.getAggregations(); - assertNotNull(aggregations); - InternalTimeSeries timeSeries = aggregations.get("by_ts"); - Map, Map>> filteredData = dataFilteredByMetric(data, "metric_" + metric, val, above); - assertThat( - timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), - equalTo(filteredData.keySet()) + assertNoFailuresAndResponse( + prepareSearch("index").setQuery(queryBuilder).setSize(0).addAggregation(timeSeries("by_ts")), + response -> { + Aggregations aggregations = response.getAggregations(); + assertNotNull(aggregations); + InternalTimeSeries timeSeries = aggregations.get("by_ts"); + Map, Map>> filteredData = dataFilteredByMetric( + data, + "metric_" + metric, + val, + above + ); + assertThat( + timeSeries.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKey).collect(Collectors.toSet()), + equalTo(filteredData.keySet()) + ); + for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { + @SuppressWarnings("unchecked") + Map key = (Map) bucket.getKey(); + assertThat(bucket.getDocCount(), equalTo((long) filteredData.get(key).size())); + } + } ); - for (InternalTimeSeries.Bucket bucket : timeSeries.getBuckets()) { - @SuppressWarnings("unchecked") - Map key = (Map) bucket.getKey(); - assertThat(bucket.getDocCount(), equalTo((long) filteredData.get(key).size())); - } } public void testRetrievingHits() { diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java index b282b54088123..7fddc65ac3e03 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/bucket/TimeSeriesNestedAggregationsIT.java @@ -12,7 +12,6 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.aggregations.AggregationIntegTestCase; import org.elasticsearch.aggregations.bucket.timeseries.InternalTimeSeries; @@ -42,6 +41,8 @@ import java.util.TreeSet; import java.util.function.Supplier; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse; + public class TimeSeriesNestedAggregationsIT extends AggregationIntegTestCase { private static int numberOfDimensions; private static int numberOfDocuments; @@ -155,48 +156,56 @@ private static String formatDim(int dimId) { public void testTimeSeriesAggregation() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts"); - final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); - final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); - assertTimeSeriesAggregation(ts); + assertResponse(prepareSearch("index").addAggregation(timeSeries).setSize(0), response -> { + final InternalTimeSeries ts = (InternalTimeSeries) response.getAggregations().asList().get(0); + assertTimeSeriesAggregation(ts); + }); } public void testSumByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new SumAggregationBuilder("sum").field("gauge_metric") ); - final SearchResponse searchResponse = prepareSearch("index").setQuery(new MatchAllQueryBuilder()).get(); - assertNotEquals(numberOfDocuments, searchResponse.getHits().getHits().length); - final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); - final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); - assertTimeSeriesAggregation(ts); + assertResponse( + prepareSearch("index").setQuery(new MatchAllQueryBuilder()), + response -> assertNotEquals(numberOfDocuments, response.getHits().getHits().length) + ); + + assertResponse(prepareSearch("index").addAggregation(timeSeries).setSize(0), response -> { + final InternalTimeSeries ts = (InternalTimeSeries) response.getAggregations().asList().get(0); + assertTimeSeriesAggregation(ts); + }); } public void testTermsByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new TermsAggregationBuilder("terms").field("dim_0") ); - final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); - final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); - assertTimeSeriesAggregation(ts); + assertResponse(prepareSearch("index").addAggregation(timeSeries).setSize(0), response -> { + final InternalTimeSeries ts = (InternalTimeSeries) response.getAggregations().asList().get(0); + assertTimeSeriesAggregation(ts); + }); } public void testDateHistogramByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new DateHistogramAggregationBuilder("date_histogram").field("@timestamp").calendarInterval(DateHistogramInterval.HOUR) ); - final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); - final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); - assertTimeSeriesAggregation(ts); + assertResponse(prepareSearch("index").addAggregation(timeSeries).setSize(0), response -> { + final InternalTimeSeries ts = (InternalTimeSeries) response.getAggregations().asList().get(0); + assertTimeSeriesAggregation(ts); + }); } public void testCardinalityByTsid() { final TimeSeriesAggregationBuilder timeSeries = new TimeSeriesAggregationBuilder("ts").subAggregation( new CardinalityAggregationBuilder("dim_n_cardinality").field(formatDim(numberOfDimensions - 1)) ); - final SearchResponse aggregationResponse = prepareSearch("index").addAggregation(timeSeries).setSize(0).get(); - final InternalTimeSeries ts = (InternalTimeSeries) aggregationResponse.getAggregations().asList().get(0); - assertTimeSeriesAggregation(ts); - ts.getBuckets().forEach(bucket -> { assertCardinality(bucket.getAggregations().get("dim_n_cardinality"), 1); }); + assertResponse(prepareSearch("index").addAggregation(timeSeries).setSize(0), response -> { + final InternalTimeSeries ts = (InternalTimeSeries) response.getAggregations().asList().get(0); + assertTimeSeriesAggregation(ts); + ts.getBuckets().forEach(bucket -> { assertCardinality(bucket.getAggregations().get("dim_n_cardinality"), 1); }); + }); } private static void assertTimeSeriesAggregation(final InternalTimeSeries timeSeriesAggregation) { diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java index f375d341c5ae3..14bae46e1e00f 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/DateDerivativeIT.java @@ -9,7 +9,6 @@ package org.elasticsearch.aggregations.pipeline; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.aggregations.AggregationsPlugin; import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.common.time.DateFormatters; @@ -39,7 +38,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; @@ -116,91 +115,93 @@ public void afterEachTest() throws IOException { } public void testSingleValuedField() throws Exception { - SearchResponse response = prepareSearch("idx").addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1L)); - SimpleValue docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, nullValue()); - - key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(2L)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(1d)); - - key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3L)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(1d)); + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(3)); + + ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + Histogram.Bucket bucket = buckets.get(0); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(1L)); + SimpleValue docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, nullValue()); + + key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(1); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(2L)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(1d)); + + key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(2); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(3L)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(1d)); + } + ); } public void testSingleValuedFieldNormalised() throws Exception { - SearchResponse response = prepareSearch("idx").addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.DAY)) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1L)); - Derivative docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, nullValue()); - - key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(2L)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), closeTo(1d, 0.00001)); - assertThat(docCountDeriv.normalizedValue(), closeTo(1d / 31d, 0.00001)); - - key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3L)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), closeTo(1d, 0.00001)); - assertThat(docCountDeriv.normalizedValue(), closeTo(1d / 29d, 0.00001)); + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.DAY)) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(3)); + + ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + Histogram.Bucket bucket = buckets.get(0); + assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(1L)); + Derivative docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, nullValue()); + + key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(1); + assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(2L)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), closeTo(1d, 0.00001)); + assertThat(docCountDeriv.normalizedValue(), closeTo(1d / 31d, 0.00001)); + + key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(2); + assertThat(bucket, notNullValue()); + assertThat(bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(3L)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), closeTo(1d, 0.00001)); + assertThat(docCountDeriv.normalizedValue(), closeTo(1d / 29d, 0.00001)); + } + ); } /** @@ -221,43 +222,44 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstStart() throws Excep indexRandom(true, builders); ensureSearchable(); - SearchResponse response = prepareSearch(IDX_DST_START).addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .timeZone(timezone) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(4)); - - DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd"); - ZonedDateTime expectedKeyFirstBucket = LocalDate.from(dateFormatter.parse("2012-03-24")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null); - - ZonedDateTime expectedKeySecondBucket = LocalDate.from(dateFormatter.parse("2012-03-25")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(1), expectedKeySecondBucket, 2L, notNullValue(), 1d, 1d / 24d); - - // the following is normalized using a 23h bucket width - ZonedDateTime expectedKeyThirdBucket = LocalDate.from(dateFormatter.parse("2012-03-26")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(2), expectedKeyThirdBucket, 3L, notNullValue(), 1d, 1d / 23d); - - ZonedDateTime expectedKeyFourthBucket = LocalDate.from(dateFormatter.parse("2012-03-27")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(3), expectedKeyFourthBucket, 4L, notNullValue(), 1d, 1d / 24d); + assertNoFailuresAndResponse( + prepareSearch(IDX_DST_START).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.DAY) + .timeZone(timezone) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(4)); + + DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd"); + ZonedDateTime expectedKeyFirstBucket = LocalDate.from(dateFormatter.parse("2012-03-24")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null); + + ZonedDateTime expectedKeySecondBucket = LocalDate.from(dateFormatter.parse("2012-03-25")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(1), expectedKeySecondBucket, 2L, notNullValue(), 1d, 1d / 24d); + + // the following is normalized using a 23h bucket width + ZonedDateTime expectedKeyThirdBucket = LocalDate.from(dateFormatter.parse("2012-03-26")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(2), expectedKeyThirdBucket, 3L, notNullValue(), 1d, 1d / 23d); + + ZonedDateTime expectedKeyFourthBucket = LocalDate.from(dateFormatter.parse("2012-03-27")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(3), expectedKeyFourthBucket, 4L, notNullValue(), 1d, 1d / 24d); + } + ); } /** @@ -277,44 +279,45 @@ public void testSingleValuedFieldNormalised_timeZone_CET_DstEnd() throws Excepti indexRandom(true, builders); ensureSearchable(); - SearchResponse response = prepareSearch(IDX_DST_END).addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.DAY) - .timeZone(timezone) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(4)); - - DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd").withZone(ZoneOffset.UTC); - - ZonedDateTime expectedKeyFirstBucket = LocalDate.from(dateFormatter.parse("2012-10-27")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null); - - ZonedDateTime expectedKeySecondBucket = LocalDate.from(dateFormatter.parse("2012-10-28")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(1), expectedKeySecondBucket, 2L, notNullValue(), 1d, 1d / 24d); - - // the following is normalized using a 25h bucket width - ZonedDateTime expectedKeyThirdBucket = LocalDate.from(dateFormatter.parse("2012-10-29")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(2), expectedKeyThirdBucket, 3L, notNullValue(), 1d, 1d / 25d); - - ZonedDateTime expectedKeyFourthBucket = LocalDate.from(dateFormatter.parse("2012-10-30")) - .atStartOfDay(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(3), expectedKeyFourthBucket, 4L, notNullValue(), 1d, 1d / 24d); + assertNoFailuresAndResponse( + prepareSearch(IDX_DST_END).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.DAY) + .timeZone(timezone) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.HOUR)) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(4)); + + DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd").withZone(ZoneOffset.UTC); + + ZonedDateTime expectedKeyFirstBucket = LocalDate.from(dateFormatter.parse("2012-10-27")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null); + + ZonedDateTime expectedKeySecondBucket = LocalDate.from(dateFormatter.parse("2012-10-28")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(1), expectedKeySecondBucket, 2L, notNullValue(), 1d, 1d / 24d); + + // the following is normalized using a 25h bucket width + ZonedDateTime expectedKeyThirdBucket = LocalDate.from(dateFormatter.parse("2012-10-29")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(2), expectedKeyThirdBucket, 3L, notNullValue(), 1d, 1d / 25d); + + ZonedDateTime expectedKeyFourthBucket = LocalDate.from(dateFormatter.parse("2012-10-30")) + .atStartOfDay(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(3), expectedKeyFourthBucket, 4L, notNullValue(), 1d, 1d / 24d); + } + ); } /** @@ -335,44 +338,45 @@ public void testSingleValuedFieldNormalised_timeZone_AsiaKathmandu() throws Exce indexRandom(true, builders); ensureSearchable(); - SearchResponse response = prepareSearch(IDX_DST_KATHMANDU).addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.HOUR) - .timeZone(timezone) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.MINUTE)) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(4)); - - DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); - - ZonedDateTime expectedKeyFirstBucket = LocalDateTime.from(dateFormatter.parse("1985-12-31T22:00:00")) - .atZone(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null); - - ZonedDateTime expectedKeySecondBucket = LocalDateTime.from(dateFormatter.parse("1985-12-31T23:00:00")) - .atZone(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(1), expectedKeySecondBucket, 2L, notNullValue(), 1d, 1d / 60d); - - // the following is normalized using a 105min bucket width - ZonedDateTime expectedKeyThirdBucket = LocalDateTime.from(dateFormatter.parse("1986-01-01T01:00:00")) - .atZone(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(2), expectedKeyThirdBucket, 3L, notNullValue(), 1d, 1d / 105d); - - ZonedDateTime expectedKeyFourthBucket = LocalDateTime.from(dateFormatter.parse("1986-01-01T02:00:00")) - .atZone(timezone) - .withZoneSameInstant(ZoneOffset.UTC); - assertBucket(buckets.get(3), expectedKeyFourthBucket, 4L, notNullValue(), 1d, 1d / 60d); + assertNoFailuresAndResponse( + prepareSearch(IDX_DST_KATHMANDU).addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.HOUR) + .timeZone(timezone) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count").unit(DateHistogramInterval.MINUTE)) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(4)); + + DateFormatter dateFormatter = DateFormatter.forPattern("uuuu-MM-dd'T'HH:mm:ss").withZone(ZoneOffset.UTC); + + ZonedDateTime expectedKeyFirstBucket = LocalDateTime.from(dateFormatter.parse("1985-12-31T22:00:00")) + .atZone(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(0), expectedKeyFirstBucket, 1L, nullValue(), null, null); + + ZonedDateTime expectedKeySecondBucket = LocalDateTime.from(dateFormatter.parse("1985-12-31T23:00:00")) + .atZone(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(1), expectedKeySecondBucket, 2L, notNullValue(), 1d, 1d / 60d); + + // the following is normalized using a 105min bucket width + ZonedDateTime expectedKeyThirdBucket = LocalDateTime.from(dateFormatter.parse("1986-01-01T01:00:00")) + .atZone(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(2), expectedKeyThirdBucket, 3L, notNullValue(), 1d, 1d / 105d); + + ZonedDateTime expectedKeyFourthBucket = LocalDateTime.from(dateFormatter.parse("1986-01-01T02:00:00")) + .atZone(timezone) + .withZoneSameInstant(ZoneOffset.UTC); + assertBucket(buckets.get(3), expectedKeyFourthBucket, 4L, notNullValue(), 1d, 1d / 60d); + } + ); } private static void addNTimes(int amount, String index, ZonedDateTime dateTime, List builders) throws Exception { @@ -401,203 +405,206 @@ private static void assertBucket( } public void testSingleValuedFieldWithSubAggregation() throws Exception { - SearchResponse response = prepareSearch("idx").addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(sum("sum").field("value")) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "sum")) - ).get(); - - assertNoFailures(response); - - Histogram histo = response.getAggregations().get("histo"); - assertThat(histo, notNullValue()); - assertThat(histo.getName(), equalTo("histo")); - List buckets = histo.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - Object[] propertiesKeys = (Object[]) ((InternalAggregation) histo).getProperty("_key"); - Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) histo).getProperty("_count"); - Object[] propertiesCounts = (Object[]) ((InternalAggregation) histo).getProperty("sum.value"); - - ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - Sum sum = bucket.getAggregations().get("sum"); - assertThat(sum, notNullValue()); - assertThat(sum.value(), equalTo(1.0)); - SimpleValue deriv = bucket.getAggregations().get("deriv"); - assertThat(deriv, nullValue()); - assertThat((ZonedDateTime) propertiesKeys[0], equalTo(key)); - assertThat((long) propertiesDocCounts[0], equalTo(1L)); - assertThat((double) propertiesCounts[0], equalTo(1.0)); - - key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(2L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - sum = bucket.getAggregations().get("sum"); - assertThat(sum, notNullValue()); - assertThat(sum.value(), equalTo(5.0)); - deriv = bucket.getAggregations().get("deriv"); - assertThat(deriv, notNullValue()); - assertThat(deriv.value(), equalTo(4.0)); - assertThat( - ((InternalMultiBucketAggregation.InternalBucket) bucket).getProperty( - "histo", - AggregationPath.parse("deriv.value").getPathElementsAsStringList() - ), - equalTo(4.0) - ); - assertThat((ZonedDateTime) propertiesKeys[1], equalTo(key)); - assertThat((long) propertiesDocCounts[1], equalTo(2L)); - assertThat((double) propertiesCounts[1], equalTo(5.0)); - - key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - sum = bucket.getAggregations().get("sum"); - assertThat(sum, notNullValue()); - assertThat(sum.value(), equalTo(15.0)); - deriv = bucket.getAggregations().get("deriv"); - assertThat(deriv, notNullValue()); - assertThat(deriv.value(), equalTo(10.0)); - assertThat( - ((InternalMultiBucketAggregation.InternalBucket) bucket).getProperty( - "histo", - AggregationPath.parse("deriv.value").getPathElementsAsStringList() + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(sum("sum").field("value")) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "sum")) ), - equalTo(10.0) + response -> { + Histogram histo = response.getAggregations().get("histo"); + assertThat(histo, notNullValue()); + assertThat(histo.getName(), equalTo("histo")); + List buckets = histo.getBuckets(); + assertThat(buckets.size(), equalTo(3)); + Object[] propertiesKeys = (Object[]) ((InternalAggregation) histo).getProperty("_key"); + Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) histo).getProperty("_count"); + Object[] propertiesCounts = (Object[]) ((InternalAggregation) histo).getProperty("sum.value"); + + ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + Histogram.Bucket bucket = buckets.get(0); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(1L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + Sum sum = bucket.getAggregations().get("sum"); + assertThat(sum, notNullValue()); + assertThat(sum.value(), equalTo(1.0)); + SimpleValue deriv = bucket.getAggregations().get("deriv"); + assertThat(deriv, nullValue()); + assertThat((ZonedDateTime) propertiesKeys[0], equalTo(key)); + assertThat((long) propertiesDocCounts[0], equalTo(1L)); + assertThat((double) propertiesCounts[0], equalTo(1.0)); + + key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(1); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(2L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + sum = bucket.getAggregations().get("sum"); + assertThat(sum, notNullValue()); + assertThat(sum.value(), equalTo(5.0)); + deriv = bucket.getAggregations().get("deriv"); + assertThat(deriv, notNullValue()); + assertThat(deriv.value(), equalTo(4.0)); + assertThat( + ((InternalMultiBucketAggregation.InternalBucket) bucket).getProperty( + "histo", + AggregationPath.parse("deriv.value").getPathElementsAsStringList() + ), + equalTo(4.0) + ); + assertThat((ZonedDateTime) propertiesKeys[1], equalTo(key)); + assertThat((long) propertiesDocCounts[1], equalTo(2L)); + assertThat((double) propertiesCounts[1], equalTo(5.0)); + + key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(2); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(3L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + sum = bucket.getAggregations().get("sum"); + assertThat(sum, notNullValue()); + assertThat(sum.value(), equalTo(15.0)); + deriv = bucket.getAggregations().get("deriv"); + assertThat(deriv, notNullValue()); + assertThat(deriv.value(), equalTo(10.0)); + assertThat( + ((InternalMultiBucketAggregation.InternalBucket) bucket).getProperty( + "histo", + AggregationPath.parse("deriv.value").getPathElementsAsStringList() + ), + equalTo(10.0) + ); + assertThat((ZonedDateTime) propertiesKeys[2], equalTo(key)); + assertThat((long) propertiesDocCounts[2], equalTo(3L)); + assertThat((double) propertiesCounts[2], equalTo(15.0)); + } ); - assertThat((ZonedDateTime) propertiesKeys[2], equalTo(key)); - assertThat((long) propertiesDocCounts[2], equalTo(3L)); - assertThat((double) propertiesCounts[2], equalTo(15.0)); } public void testMultiValuedField() throws Exception { - SearchResponse response = prepareSearch("idx").addAggregation( - dateHistogram("histo").field("dates") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(4)); - - ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(true)); - SimpleValue docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, nullValue()); - - key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(2.0)); - - key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(5L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(2.0)); - - key = ZonedDateTime.of(2012, 4, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(3); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(-2.0)); + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + dateHistogram("histo").field("dates") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(4)); + + ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + Histogram.Bucket bucket = buckets.get(0); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(1L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(true)); + SimpleValue docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, nullValue()); + + key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(1); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(3L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(2.0)); + + key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(2); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(5L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(2.0)); + + key = ZonedDateTime.of(2012, 4, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(3); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(3L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(-2.0)); + } + ); } public void testUnmapped() throws Exception { - SearchResponse response = prepareSearch("idx_unmapped").addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - assertThat(deriv.getBuckets().size(), equalTo(0)); + assertNoFailuresAndResponse( + prepareSearch("idx_unmapped").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + assertThat(deriv.getBuckets().size(), equalTo(0)); + } + ); } public void testPartiallyUnmapped() throws Exception { - SearchResponse response = prepareSearch("idx", "idx_unmapped").addAggregation( - dateHistogram("histo").field("date") - .calendarInterval(DateHistogramInterval.MONTH) - .minDocCount(0) - .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) - ).get(); - - assertNoFailures(response); - - Histogram deriv = response.getAggregations().get("histo"); - assertThat(deriv, notNullValue()); - assertThat(deriv.getName(), equalTo("histo")); - List buckets = deriv.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(true)); - SimpleValue docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, nullValue()); - - key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(2L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(1.0)); - - key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3L)); - assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); - docCountDeriv = bucket.getAggregations().get("deriv"); - assertThat(docCountDeriv, notNullValue()); - assertThat(docCountDeriv.value(), equalTo(1.0)); + assertNoFailuresAndResponse( + prepareSearch("idx", "idx_unmapped").addAggregation( + dateHistogram("histo").field("date") + .calendarInterval(DateHistogramInterval.MONTH) + .minDocCount(0) + .subAggregation(new DerivativePipelineAggregationBuilder("deriv", "_count")) + ), + response -> { + Histogram deriv = response.getAggregations().get("histo"); + assertThat(deriv, notNullValue()); + assertThat(deriv.getName(), equalTo("histo")); + List buckets = deriv.getBuckets(); + assertThat(buckets.size(), equalTo(3)); + + ZonedDateTime key = ZonedDateTime.of(2012, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + Histogram.Bucket bucket = buckets.get(0); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(1L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(true)); + SimpleValue docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, nullValue()); + + key = ZonedDateTime.of(2012, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(1); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(2L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(1.0)); + + key = ZonedDateTime.of(2012, 3, 1, 0, 0, 0, 0, ZoneOffset.UTC); + bucket = buckets.get(2); + assertThat(bucket, notNullValue()); + assertThat((ZonedDateTime) bucket.getKey(), equalTo(key)); + assertThat(bucket.getDocCount(), equalTo(3L)); + assertThat(bucket.getAggregations().asList().isEmpty(), is(false)); + docCountDeriv = bucket.getAggregations().get("deriv"); + assertThat(docCountDeriv, notNullValue()); + assertThat(docCountDeriv.value(), equalTo(1.0)); + } + ); } - } diff --git a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java index c918220d19e51..e0c91689b333d 100644 --- a/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java +++ b/modules/aggregations/src/internalClusterTest/java/org/elasticsearch/aggregations/pipeline/SerialDiffIT.java @@ -9,7 +9,6 @@ package org.elasticsearch.aggregations.pipeline; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.aggregations.AggregationIntegTestCase; import org.elasticsearch.common.collect.EvictingQueue; import org.elasticsearch.common.util.Maps; @@ -31,7 +30,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.max; import static org.elasticsearch.search.aggregations.AggregationBuilders.min; import static org.elasticsearch.search.aggregations.PipelineAggregatorBuilders.diff; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; @@ -220,44 +219,45 @@ private void setupExpected(MetricTarget target) { } public void testBasicDiff() { - SearchResponse response = prepareSearch("idx").addAggregation( - histogram("histo").field(INTERVAL_FIELD) - .interval(interval) - .extendedBounds(0L, (long) (interval * (numBuckets - 1))) - .subAggregation(metric) - .subAggregation(diff("diff_counts", "_count").lag(lag).gapPolicy(gapPolicy)) - .subAggregation(diff("diff_values", "the_metric").lag(lag).gapPolicy(gapPolicy)) - ).get(); - - assertNoFailures(response); - - Histogram histo = response.getAggregations().get("histo"); - assertThat(histo, notNullValue()); - assertThat(histo.getName(), equalTo("histo")); - List buckets = histo.getBuckets(); - assertThat("Size of buckets array is not correct.", buckets.size(), equalTo(mockHisto.size())); - - List expectedCounts = testValues.get(MetricTarget.COUNT.toString()); - List expectedValues = testValues.get(MetricTarget.VALUE.toString()); - - Iterator actualIter = buckets.iterator(); - Iterator expectedBucketIter = mockHisto.iterator(); - Iterator expectedCountsIter = expectedCounts.iterator(); - Iterator expectedValuesIter = expectedValues.iterator(); - - while (actualIter.hasNext()) { - assertValidIterators(expectedBucketIter, expectedCountsIter, expectedValuesIter); - - Histogram.Bucket actual = actualIter.next(); - PipelineAggregationHelperTests.MockBucket expected = expectedBucketIter.next(); - Double expectedCount = expectedCountsIter.next(); - Double expectedValue = expectedValuesIter.next(); - - assertThat("keys do not match", ((Number) actual.getKey()).longValue(), equalTo(expected.key)); - assertThat("doc counts do not match", actual.getDocCount(), equalTo((long) expected.count)); - - assertBucketContents(actual, expectedCount, expectedValue); - } + assertNoFailuresAndResponse( + prepareSearch("idx").addAggregation( + histogram("histo").field(INTERVAL_FIELD) + .interval(interval) + .extendedBounds(0L, (long) (interval * (numBuckets - 1))) + .subAggregation(metric) + .subAggregation(diff("diff_counts", "_count").lag(lag).gapPolicy(gapPolicy)) + .subAggregation(diff("diff_values", "the_metric").lag(lag).gapPolicy(gapPolicy)) + ), + response -> { + Histogram histo = response.getAggregations().get("histo"); + assertThat(histo, notNullValue()); + assertThat(histo.getName(), equalTo("histo")); + List buckets = histo.getBuckets(); + assertThat("Size of buckets array is not correct.", buckets.size(), equalTo(mockHisto.size())); + + List expectedCounts = testValues.get(MetricTarget.COUNT.toString()); + List expectedValues = testValues.get(MetricTarget.VALUE.toString()); + + Iterator actualIter = buckets.iterator(); + Iterator expectedBucketIter = mockHisto.iterator(); + Iterator expectedCountsIter = expectedCounts.iterator(); + Iterator expectedValuesIter = expectedValues.iterator(); + + while (actualIter.hasNext()) { + assertValidIterators(expectedBucketIter, expectedCountsIter, expectedValuesIter); + + Histogram.Bucket actual = actualIter.next(); + PipelineAggregationHelperTests.MockBucket expected = expectedBucketIter.next(); + Double expectedCount = expectedCountsIter.next(); + Double expectedValue = expectedValuesIter.next(); + + assertThat("keys do not match", ((Number) actual.getKey()).longValue(), equalTo(expected.key)); + assertThat("doc counts do not match", actual.getDocCount(), equalTo((long) expected.count)); + + assertBucketContents(actual, expectedCount, expectedValue); + } + } + ); } public void testInvalidLagSize() { diff --git a/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java b/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java index ee588740c340e..a7fe63eb34ce6 100644 --- a/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java +++ b/modules/legacy-geo/src/internalClusterTest/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeIT.java @@ -8,7 +8,6 @@ package org.elasticsearch.legacygeo.search; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.geometry.Circle; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.legacygeo.test.TestLegacyGeoShapeFieldMapperPlugin; @@ -24,7 +23,7 @@ import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.hamcrest.Matchers.equalTo; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; public class LegacyGeoShapeIT extends GeoShapeIntegTestCase { @@ -73,7 +72,6 @@ public void testLegacyCircle() throws Exception { })); // test self crossing of circles - SearchResponse searchResponse = prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCount(prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))), 1L); } } diff --git a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeQueryTests.java b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeQueryTests.java index 56856ec571274..6ef1f4c8a99b6 100644 --- a/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeQueryTests.java +++ b/modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeQueryTests.java @@ -8,7 +8,6 @@ package org.elasticsearch.legacygeo.search; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.Strings; import org.elasticsearch.common.geo.GeoJson; import org.elasticsearch.common.settings.Settings; @@ -32,6 +31,7 @@ import static org.elasticsearch.index.query.QueryBuilders.geoIntersectionQuery; import static org.elasticsearch.index.query.QueryBuilders.geoShapeQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; @@ -101,9 +101,7 @@ public void testPointsOnlyExplicit() throws Exception { .get(); // test that point was inserted - SearchResponse response = client().prepareSearch("geo_points_only").setQuery(matchAllQuery()).get(); - - assertEquals(2, response.getHits().getTotalHits().value); + assertHitCount(client().prepareSearch("geo_points_only").setQuery(matchAllQuery()).get(), 2L); } public void testPointsOnly() throws Exception { @@ -139,10 +137,7 @@ public void testPointsOnly() throws Exception { } // test that point was inserted - SearchResponse response = client().prepareSearch("geo_points_only") - .setQuery(geoIntersectionQuery(defaultFieldName, geometry)) - .get(); - assertEquals(1, response.getHits().getTotalHits().value); + assertHitCount(client().prepareSearch("geo_points_only").setQuery(geoIntersectionQuery(defaultFieldName, geometry)), 1L); } public void testFieldAlias() throws IOException { @@ -172,8 +167,7 @@ public void testFieldAlias() throws IOException { .setRefreshPolicy(IMMEDIATE) .get(); - SearchResponse response = client().prepareSearch(defaultIndexName).setQuery(geoShapeQuery("alias", multiPoint)).get(); - assertEquals(1, response.getHits().getTotalHits().value); + assertHitCount(client().prepareSearch(defaultIndexName).setQuery(geoShapeQuery("alias", multiPoint)), 1L); } /** diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java index cafcf11a1438d..595d845d40b3d 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ChildrenIT.java @@ -9,8 +9,6 @@ import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.internal.Requests; import org.elasticsearch.search.SearchHit; @@ -35,7 +33,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; @@ -44,113 +42,119 @@ public class ChildrenIT extends AbstractParentChildTestCase { public void testSimpleChildrenAgg() { - final SearchRequestBuilder searchRequest = prepareSearch("test").setQuery(matchQuery("randomized", true)) - .addAggregation(children("to_comment", "comment")); - final SearchResponse searchResponse = searchRequest.get(); long count = categoryToControl.values().stream().mapToLong(control -> control.commentIds.size()).sum(); - assertNoFailures(searchResponse); - Children childrenAgg = searchResponse.getAggregations().get("to_comment"); - assertThat("Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", childrenAgg.getDocCount(), equalTo(count)); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(matchQuery("randomized", true)).addAggregation(children("to_comment", "comment")), + response -> { + Children childrenAgg = response.getAggregations().get("to_comment"); + assertThat("Response: " + response + "\n", childrenAgg.getDocCount(), equalTo(count)); + } + ); } public void testChildrenAggs() { - SearchResponse searchResponse = prepareSearch("test").setQuery(matchQuery("randomized", true)) - .addAggregation( - terms("category").field("category") - .size(10000) - .subAggregation( - children("to_comment", "comment").subAggregation( - terms("commenters").field("commenter").size(10000).subAggregation(topHits("top_comments")) + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(matchQuery("randomized", true)) + .addAggregation( + terms("category").field("category") + .size(10000) + .subAggregation( + children("to_comment", "comment").subAggregation( + terms("commenters").field("commenter").size(10000).subAggregation(topHits("top_comments")) + ) ) - ) - ) - .get(); - assertNoFailures(searchResponse); - - Terms categoryTerms = searchResponse.getAggregations().get("category"); - assertThat(categoryTerms.getBuckets().size(), equalTo(categoryToControl.size())); - for (Map.Entry entry1 : categoryToControl.entrySet()) { - Terms.Bucket categoryBucket = categoryTerms.getBucketByKey(entry1.getKey()); - assertThat(categoryBucket.getKeyAsString(), equalTo(entry1.getKey())); - assertThat(categoryBucket.getDocCount(), equalTo((long) entry1.getValue().articleIds.size())); - - Children childrenBucket = categoryBucket.getAggregations().get("to_comment"); - assertThat(childrenBucket.getName(), equalTo("to_comment")); - assertThat(childrenBucket.getDocCount(), equalTo((long) entry1.getValue().commentIds.size())); - assertThat(((InternalAggregation) childrenBucket).getProperty("_count"), equalTo((long) entry1.getValue().commentIds.size())); - - Terms commentersTerms = childrenBucket.getAggregations().get("commenters"); - assertThat(((InternalAggregation) childrenBucket).getProperty("commenters"), sameInstance(commentersTerms)); - assertThat(commentersTerms.getBuckets().size(), equalTo(entry1.getValue().commenterToCommentId.size())); - for (Map.Entry> entry2 : entry1.getValue().commenterToCommentId.entrySet()) { - Terms.Bucket commentBucket = commentersTerms.getBucketByKey(entry2.getKey()); - assertThat(commentBucket.getKeyAsString(), equalTo(entry2.getKey())); - assertThat(commentBucket.getDocCount(), equalTo((long) entry2.getValue().size())); - - TopHits topHits = commentBucket.getAggregations().get("top_comments"); - for (SearchHit searchHit : topHits.getHits().getHits()) { - assertThat(entry2.getValue().contains(searchHit.getId()), is(true)); + ), + response -> { + Terms categoryTerms = response.getAggregations().get("category"); + assertThat(categoryTerms.getBuckets().size(), equalTo(categoryToControl.size())); + for (Map.Entry entry1 : categoryToControl.entrySet()) { + Terms.Bucket categoryBucket = categoryTerms.getBucketByKey(entry1.getKey()); + assertThat(categoryBucket.getKeyAsString(), equalTo(entry1.getKey())); + assertThat(categoryBucket.getDocCount(), equalTo((long) entry1.getValue().articleIds.size())); + + Children childrenBucket = categoryBucket.getAggregations().get("to_comment"); + assertThat(childrenBucket.getName(), equalTo("to_comment")); + assertThat(childrenBucket.getDocCount(), equalTo((long) entry1.getValue().commentIds.size())); + assertThat( + ((InternalAggregation) childrenBucket).getProperty("_count"), + equalTo((long) entry1.getValue().commentIds.size()) + ); + + Terms commentersTerms = childrenBucket.getAggregations().get("commenters"); + assertThat(((InternalAggregation) childrenBucket).getProperty("commenters"), sameInstance(commentersTerms)); + assertThat(commentersTerms.getBuckets().size(), equalTo(entry1.getValue().commenterToCommentId.size())); + for (Map.Entry> entry2 : entry1.getValue().commenterToCommentId.entrySet()) { + Terms.Bucket commentBucket = commentersTerms.getBucketByKey(entry2.getKey()); + assertThat(commentBucket.getKeyAsString(), equalTo(entry2.getKey())); + assertThat(commentBucket.getDocCount(), equalTo((long) entry2.getValue().size())); + + TopHits topHits = commentBucket.getAggregations().get("top_comments"); + for (SearchHit searchHit : topHits.getHits().getHits()) { + assertThat(entry2.getValue().contains(searchHit.getId()), is(true)); + } + } } } - } + ); } public void testParentWithMultipleBuckets() { - SearchResponse searchResponse = prepareSearch("test").setQuery(matchQuery("randomized", false)) - .addAggregation( - terms("category").field("category") - .size(10000) - .subAggregation(children("to_comment", "comment").subAggregation(topHits("top_comments").sort("id", SortOrder.ASC))) - ) - .get(); - assertNoFailures(searchResponse); - - Terms categoryTerms = searchResponse.getAggregations().get("category"); - assertThat(categoryTerms.getBuckets().size(), equalTo(3)); - - for (Terms.Bucket bucket : categoryTerms.getBuckets()) { - logger.info("bucket={}", bucket.getKey()); - Children childrenBucket = bucket.getAggregations().get("to_comment"); - TopHits topHits = childrenBucket.getAggregations().get("top_comments"); - logger.info("total_hits={}", topHits.getHits().getTotalHits().value); - for (SearchHit searchHit : topHits.getHits()) { - logger.info("hit= {} {}", searchHit.getSortValues()[0], searchHit.getId()); - } - } + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(matchQuery("randomized", false)) + .addAggregation( + terms("category").field("category") + .size(10000) + .subAggregation(children("to_comment", "comment").subAggregation(topHits("top_comments").sort("id", SortOrder.ASC))) + ), + response -> { + Terms categoryTerms = response.getAggregations().get("category"); + assertThat(categoryTerms.getBuckets().size(), equalTo(3)); + + for (Terms.Bucket bucket : categoryTerms.getBuckets()) { + logger.info("bucket={}", bucket.getKey()); + Children childrenBucket = bucket.getAggregations().get("to_comment"); + TopHits topHits = childrenBucket.getAggregations().get("top_comments"); + logger.info("total_hits={}", topHits.getHits().getTotalHits().value); + for (SearchHit searchHit : topHits.getHits()) { + logger.info("hit= {} {}", searchHit.getSortValues()[0], searchHit.getId()); + } + } - Terms.Bucket categoryBucket = categoryTerms.getBucketByKey("a"); - assertThat(categoryBucket.getKeyAsString(), equalTo("a")); - assertThat(categoryBucket.getDocCount(), equalTo(3L)); - - Children childrenBucket = categoryBucket.getAggregations().get("to_comment"); - assertThat(childrenBucket.getName(), equalTo("to_comment")); - assertThat(childrenBucket.getDocCount(), equalTo(2L)); - TopHits topHits = childrenBucket.getAggregations().get("top_comments"); - assertThat(topHits.getHits().getTotalHits().value, equalTo(2L)); - assertThat(topHits.getHits().getAt(0).getId(), equalTo("e")); - assertThat(topHits.getHits().getAt(1).getId(), equalTo("f")); - - categoryBucket = categoryTerms.getBucketByKey("b"); - assertThat(categoryBucket.getKeyAsString(), equalTo("b")); - assertThat(categoryBucket.getDocCount(), equalTo(2L)); - - childrenBucket = categoryBucket.getAggregations().get("to_comment"); - assertThat(childrenBucket.getName(), equalTo("to_comment")); - assertThat(childrenBucket.getDocCount(), equalTo(1L)); - topHits = childrenBucket.getAggregations().get("top_comments"); - assertThat(topHits.getHits().getTotalHits().value, equalTo(1L)); - assertThat(topHits.getHits().getAt(0).getId(), equalTo("f")); - - categoryBucket = categoryTerms.getBucketByKey("c"); - assertThat(categoryBucket.getKeyAsString(), equalTo("c")); - assertThat(categoryBucket.getDocCount(), equalTo(2L)); - - childrenBucket = categoryBucket.getAggregations().get("to_comment"); - assertThat(childrenBucket.getName(), equalTo("to_comment")); - assertThat(childrenBucket.getDocCount(), equalTo(1L)); - topHits = childrenBucket.getAggregations().get("top_comments"); - assertThat(topHits.getHits().getTotalHits().value, equalTo(1L)); - assertThat(topHits.getHits().getAt(0).getId(), equalTo("f")); + Terms.Bucket categoryBucket = categoryTerms.getBucketByKey("a"); + assertThat(categoryBucket.getKeyAsString(), equalTo("a")); + assertThat(categoryBucket.getDocCount(), equalTo(3L)); + + Children childrenBucket = categoryBucket.getAggregations().get("to_comment"); + assertThat(childrenBucket.getName(), equalTo("to_comment")); + assertThat(childrenBucket.getDocCount(), equalTo(2L)); + TopHits topHits = childrenBucket.getAggregations().get("top_comments"); + assertThat(topHits.getHits().getTotalHits().value, equalTo(2L)); + assertThat(topHits.getHits().getAt(0).getId(), equalTo("e")); + assertThat(topHits.getHits().getAt(1).getId(), equalTo("f")); + + categoryBucket = categoryTerms.getBucketByKey("b"); + assertThat(categoryBucket.getKeyAsString(), equalTo("b")); + assertThat(categoryBucket.getDocCount(), equalTo(2L)); + + childrenBucket = categoryBucket.getAggregations().get("to_comment"); + assertThat(childrenBucket.getName(), equalTo("to_comment")); + assertThat(childrenBucket.getDocCount(), equalTo(1L)); + topHits = childrenBucket.getAggregations().get("top_comments"); + assertThat(topHits.getHits().getTotalHits().value, equalTo(1L)); + assertThat(topHits.getHits().getAt(0).getId(), equalTo("f")); + + categoryBucket = categoryTerms.getBucketByKey("c"); + assertThat(categoryBucket.getKeyAsString(), equalTo("c")); + assertThat(categoryBucket.getDocCount(), equalTo(2L)); + + childrenBucket = categoryBucket.getAggregations().get("to_comment"); + assertThat(childrenBucket.getName(), equalTo("to_comment")); + assertThat(childrenBucket.getDocCount(), equalTo(1L)); + topHits = childrenBucket.getAggregations().get("top_comments"); + assertThat(topHits.getHits().getTotalHits().value, equalTo(1L)); + assertThat(topHits.getHits().getAt(0).getId(), equalTo("f")); + } + ); } public void testWithDeletes() throws Exception { @@ -170,16 +174,16 @@ public void testWithDeletes() throws Exception { indexRandom(true, requests); for (int i = 0; i < 10; i++) { - SearchResponse searchResponse = prepareSearch(indexName).addAggregation( - children("children", "child").subAggregation(sum("counts").field("count")) - ).get(); - - assertNoFailures(searchResponse); - Children children = searchResponse.getAggregations().get("children"); - assertThat(children.getDocCount(), equalTo(4L)); - - Sum count = children.getAggregations().get("counts"); - assertThat(count.value(), equalTo(4.)); + assertNoFailuresAndResponse( + prepareSearch(indexName).addAggregation(children("children", "child").subAggregation(sum("counts").field("count"))), + response -> { + Children children = response.getAggregations().get("children"); + assertThat(children.getDocCount(), equalTo(4L)); + + Sum count = children.getAggregations().get("counts"); + assertThat(count.value(), equalTo(4.)); + } + ); String idToUpdate = Integer.toString(2 + randomInt(3)); /* @@ -199,12 +203,11 @@ public void testWithDeletes() throws Exception { } public void testNonExistingChildType() throws Exception { - SearchResponse searchResponse = prepareSearch("test").addAggregation(children("non-existing", "xyz")).get(); - assertNoFailures(searchResponse); - - Children children = searchResponse.getAggregations().get("non-existing"); - assertThat(children.getName(), equalTo("non-existing")); - assertThat(children.getDocCount(), equalTo(0L)); + assertNoFailuresAndResponse(prepareSearch("test").addAggregation(children("non-existing", "xyz")), response -> { + Children children = response.getAggregations().get("non-existing"); + assertThat(children.getName(), equalTo("non-existing")); + assertThat(children.getDocCount(), equalTo(0L)); + }); } public void testPostCollection() throws Exception { @@ -251,33 +254,35 @@ public void testPostCollection() throws Exception { requests.add(createIndexRequest(indexName, childType, "16", "2", "color", "green", "size", "44")); indexRandom(true, requests); - SearchResponse response = prepareSearch(indexName).setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None)) - .addAggregation( - children("my-refinements", childType).subAggregation(terms("my-colors").field("color")) - .subAggregation(terms("my-sizes").field("size")) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - - Children childrenAgg = response.getAggregations().get("my-refinements"); - assertThat(childrenAgg.getDocCount(), equalTo(7L)); - - Terms termsAgg = childrenAgg.getAggregations().get("my-colors"); - assertThat(termsAgg.getBuckets().size(), equalTo(4)); - assertThat(termsAgg.getBucketByKey("black").getDocCount(), equalTo(3L)); - assertThat(termsAgg.getBucketByKey("blue").getDocCount(), equalTo(2L)); - assertThat(termsAgg.getBucketByKey("green").getDocCount(), equalTo(1L)); - assertThat(termsAgg.getBucketByKey("orange").getDocCount(), equalTo(1L)); - - termsAgg = childrenAgg.getAggregations().get("my-sizes"); - assertThat(termsAgg.getBuckets().size(), equalTo(6)); - assertThat(termsAgg.getBucketByKey("36").getDocCount(), equalTo(2L)); - assertThat(termsAgg.getBucketByKey("32").getDocCount(), equalTo(1L)); - assertThat(termsAgg.getBucketByKey("34").getDocCount(), equalTo(1L)); - assertThat(termsAgg.getBucketByKey("38").getDocCount(), equalTo(1L)); - assertThat(termsAgg.getBucketByKey("40").getDocCount(), equalTo(1L)); - assertThat(termsAgg.getBucketByKey("44").getDocCount(), equalTo(1L)); + assertNoFailuresAndResponse( + prepareSearch(indexName).setQuery(hasChildQuery(childType, termQuery("color", "orange"), ScoreMode.None)) + .addAggregation( + children("my-refinements", childType).subAggregation(terms("my-colors").field("color")) + .subAggregation(terms("my-sizes").field("size")) + ), + response -> { + assertHitCount(response, 1L); + + Children childrenAgg = response.getAggregations().get("my-refinements"); + assertThat(childrenAgg.getDocCount(), equalTo(7L)); + + Terms termsAgg = childrenAgg.getAggregations().get("my-colors"); + assertThat(termsAgg.getBuckets().size(), equalTo(4)); + assertThat(termsAgg.getBucketByKey("black").getDocCount(), equalTo(3L)); + assertThat(termsAgg.getBucketByKey("blue").getDocCount(), equalTo(2L)); + assertThat(termsAgg.getBucketByKey("green").getDocCount(), equalTo(1L)); + assertThat(termsAgg.getBucketByKey("orange").getDocCount(), equalTo(1L)); + + termsAgg = childrenAgg.getAggregations().get("my-sizes"); + assertThat(termsAgg.getBuckets().size(), equalTo(6)); + assertThat(termsAgg.getBucketByKey("36").getDocCount(), equalTo(2L)); + assertThat(termsAgg.getBucketByKey("32").getDocCount(), equalTo(1L)); + assertThat(termsAgg.getBucketByKey("34").getDocCount(), equalTo(1L)); + assertThat(termsAgg.getBucketByKey("38").getDocCount(), equalTo(1L)); + assertThat(termsAgg.getBucketByKey("40").getDocCount(), equalTo(1L)); + assertThat(termsAgg.getBucketByKey("44").getDocCount(), equalTo(1L)); + } + ); } public void testHierarchicalChildrenAggs() { @@ -300,24 +305,28 @@ public void testHierarchicalChildrenAggs() { createIndexRequest(indexName, childType, "3", "2", "name", "brussels").setRouting("1").get(); refresh(); - SearchResponse response = prepareSearch(indexName).setQuery(matchQuery("name", "europe")) - .addAggregation( - children(parentType, parentType).subAggregation(children(childType, childType).subAggregation(terms("name").field("name"))) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 1); - - Children children = response.getAggregations().get(parentType); - assertThat(children.getName(), equalTo(parentType)); - assertThat(children.getDocCount(), equalTo(1L)); - children = children.getAggregations().get(childType); - assertThat(children.getName(), equalTo(childType)); - assertThat(children.getDocCount(), equalTo(1L)); - Terms terms = children.getAggregations().get("name"); - assertThat(terms.getBuckets().size(), equalTo(1)); - assertThat(terms.getBuckets().get(0).getKey().toString(), equalTo("brussels")); - assertThat(terms.getBuckets().get(0).getDocCount(), equalTo(1L)); + assertNoFailuresAndResponse( + prepareSearch(indexName).setQuery(matchQuery("name", "europe")) + .addAggregation( + children(parentType, parentType).subAggregation( + children(childType, childType).subAggregation(terms("name").field("name")) + ) + ), + response -> { + assertHitCount(response, 1L); + + Children children = response.getAggregations().get(parentType); + assertThat(children.getName(), equalTo(parentType)); + assertThat(children.getDocCount(), equalTo(1L)); + children = children.getAggregations().get(childType); + assertThat(children.getName(), equalTo(childType)); + assertThat(children.getDocCount(), equalTo(1L)); + Terms terms = children.getAggregations().get("name"); + assertThat(terms.getBuckets().size(), equalTo(1)); + assertThat(terms.getBuckets().get(0).getKey().toString(), equalTo("brussels")); + assertThat(terms.getBuckets().get(0).getDocCount(), equalTo(1L)); + } + ); } public void testPostCollectAllLeafReaders() throws Exception { @@ -350,40 +359,42 @@ public void testPostCollectAllLeafReaders() throws Exception { requests.add(createIndexRequest("index", "childType", "8", "3", "name", "Dan", "age", 1)); indexRandom(true, requests); - SearchResponse response = prepareSearch("index").setSize(0) - .addAggregation( - AggregationBuilders.terms("towns") - .field("town") - .subAggregation( - AggregationBuilders.terms("parent_names").field("name").subAggregation(children("child_docs", "childType")) - ) - ) - .get(); - - Terms towns = response.getAggregations().get("towns"); - assertThat(towns.getBuckets().size(), equalTo(2)); - assertThat(towns.getBuckets().get(0).getKeyAsString(), equalTo("Chicago")); - assertThat(towns.getBuckets().get(0).getDocCount(), equalTo(2L)); - - Terms parents = towns.getBuckets().get(0).getAggregations().get("parent_names"); - assertThat(parents.getBuckets().size(), equalTo(2)); - assertThat(parents.getBuckets().get(0).getKeyAsString(), equalTo("Alice")); - assertThat(parents.getBuckets().get(0).getDocCount(), equalTo(1L)); - Children children = parents.getBuckets().get(0).getAggregations().get("child_docs"); - assertThat(children.getDocCount(), equalTo(1L)); - - assertThat(parents.getBuckets().get(1).getKeyAsString(), equalTo("Bill")); - assertThat(parents.getBuckets().get(1).getDocCount(), equalTo(1L)); - children = parents.getBuckets().get(1).getAggregations().get("child_docs"); - assertThat(children.getDocCount(), equalTo(2L)); - - assertThat(towns.getBuckets().get(1).getKeyAsString(), equalTo("Memphis")); - assertThat(towns.getBuckets().get(1).getDocCount(), equalTo(1L)); - parents = towns.getBuckets().get(1).getAggregations().get("parent_names"); - assertThat(parents.getBuckets().size(), equalTo(1)); - assertThat(parents.getBuckets().get(0).getKeyAsString(), equalTo("Bob")); - assertThat(parents.getBuckets().get(0).getDocCount(), equalTo(1L)); - children = parents.getBuckets().get(0).getAggregations().get("child_docs"); - assertThat(children.getDocCount(), equalTo(2L)); + assertNoFailuresAndResponse( + prepareSearch("index").setSize(0) + .addAggregation( + AggregationBuilders.terms("towns") + .field("town") + .subAggregation( + AggregationBuilders.terms("parent_names").field("name").subAggregation(children("child_docs", "childType")) + ) + ), + response -> { + Terms towns = response.getAggregations().get("towns"); + assertThat(towns.getBuckets().size(), equalTo(2)); + assertThat(towns.getBuckets().get(0).getKeyAsString(), equalTo("Chicago")); + assertThat(towns.getBuckets().get(0).getDocCount(), equalTo(2L)); + + Terms parents = towns.getBuckets().get(0).getAggregations().get("parent_names"); + assertThat(parents.getBuckets().size(), equalTo(2)); + assertThat(parents.getBuckets().get(0).getKeyAsString(), equalTo("Alice")); + assertThat(parents.getBuckets().get(0).getDocCount(), equalTo(1L)); + Children children = parents.getBuckets().get(0).getAggregations().get("child_docs"); + assertThat(children.getDocCount(), equalTo(1L)); + + assertThat(parents.getBuckets().get(1).getKeyAsString(), equalTo("Bill")); + assertThat(parents.getBuckets().get(1).getDocCount(), equalTo(1L)); + children = parents.getBuckets().get(1).getAggregations().get("child_docs"); + assertThat(children.getDocCount(), equalTo(2L)); + + assertThat(towns.getBuckets().get(1).getKeyAsString(), equalTo("Memphis")); + assertThat(towns.getBuckets().get(1).getDocCount(), equalTo(1L)); + parents = towns.getBuckets().get(1).getAggregations().get("parent_names"); + assertThat(parents.getBuckets().size(), equalTo(1)); + assertThat(parents.getBuckets().get(0).getKeyAsString(), equalTo("Bob")); + assertThat(parents.getBuckets().get(0).getDocCount(), equalTo(1L)); + children = parents.getBuckets().get(0).getAggregations().get("child_docs"); + assertThat(children.getDocCount(), equalTo(2L)); + } + ); } } diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java index 23043650d589c..65c162e0b78bc 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/aggregations/ParentIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.join.aggregations; -import org.elasticsearch.action.search.SearchRequestBuilder; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.search.aggregations.Aggregation; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.bucket.terms.Terms; @@ -27,155 +25,144 @@ import static org.elasticsearch.join.aggregations.JoinAggregationBuilders.parent; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.aggregations.AggregationBuilders.topHits; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; import static org.hamcrest.Matchers.equalTo; public class ParentIT extends AbstractParentChildTestCase { public void testSimpleParentAgg() { - final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(0) - .setQuery(matchQuery("randomized", true)) - .addAggregation(parent("to_article", "comment")); - SearchResponse searchResponse = searchRequest.get(); - - assertNoFailures(searchResponse); long articlesWithComment = articleToControl.values() .stream() .filter(parentControl -> parentControl.commentIds.isEmpty() == false) .count(); - Parent parentAgg = searchResponse.getAggregations().get("to_article"); - assertThat( - "Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", - parentAgg.getDocCount(), - equalTo(articlesWithComment) + assertNoFailuresAndResponse( + prepareSearch("test").setSize(0).setQuery(matchQuery("randomized", true)).addAggregation(parent("to_article", "comment")), + response -> { + Parent parentAgg = response.getAggregations().get("to_article"); + assertThat("\nResponse: " + response + "\n", parentAgg.getDocCount(), equalTo(articlesWithComment)); + } ); } public void testSimpleParentAggWithSubAgg() { - final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(10000) - .setQuery(matchQuery("randomized", true)) - .addAggregation(parent("to_article", "comment").subAggregation(terms("category").field("category").size(10000))); - SearchResponse searchResponse = searchRequest.get(); - assertNoFailures(searchResponse); - long articlesWithComment = articleToControl.values() .stream() .filter(parentControl -> parentControl.commentIds.isEmpty() == false) .count(); - - Parent parentAgg = searchResponse.getAggregations().get("to_article"); - assertThat( - "Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", - parentAgg.getDocCount(), - equalTo(articlesWithComment) - ); - Terms categoryTerms = parentAgg.getAggregations().get("category"); long categoriesWithComments = categoryToControl.values().stream().filter(control -> control.commentIds.isEmpty() == false).count(); - assertThat( - "Buckets: " - + categoryTerms.getBuckets() - .stream() - .map((Function) MultiBucketsAggregation.Bucket::getKeyAsString) - .collect(Collectors.toList()) - + "\nCategories: " - + categoryToControl.keySet(), - (long) categoryTerms.getBuckets().size(), - equalTo(categoriesWithComments) - ); - for (Map.Entry entry : categoryToControl.entrySet()) { - // no children for this category -> no entry in the child to parent-aggregation - if (entry.getValue().commentIds.isEmpty()) { - assertNull(categoryTerms.getBucketByKey(entry.getKey())); - continue; + assertNoFailuresAndResponse( + prepareSearch("test").setSize(10000) + .setQuery(matchQuery("randomized", true)) + .addAggregation(parent("to_article", "comment").subAggregation(terms("category").field("category").size(10000))), + response -> { + Parent parentAgg = response.getAggregations().get("to_article"); + assertThat("Response: " + response + "\n", parentAgg.getDocCount(), equalTo(articlesWithComment)); + Terms categoryTerms = parentAgg.getAggregations().get("category"); + assertThat( + "Buckets: " + + categoryTerms.getBuckets() + .stream() + .map((Function) MultiBucketsAggregation.Bucket::getKeyAsString) + .collect(Collectors.toList()) + + "\nCategories: " + + categoryToControl.keySet(), + (long) categoryTerms.getBuckets().size(), + equalTo(categoriesWithComments) + ); + for (Map.Entry entry : categoryToControl.entrySet()) { + // no children for this category -> no entry in the child to parent-aggregation + if (entry.getValue().commentIds.isEmpty()) { + assertNull(categoryTerms.getBucketByKey(entry.getKey())); + continue; + } + + final Terms.Bucket categoryBucket = categoryTerms.getBucketByKey(entry.getKey()); + assertNotNull("Failed for category " + entry.getKey(), categoryBucket); + assertThat("Failed for category " + entry.getKey(), categoryBucket.getKeyAsString(), equalTo(entry.getKey())); + + // count all articles in this category which have at least one comment + long articlesForCategory = articleToControl.values() + .stream() + // only articles with this category + .filter(parentControl -> parentControl.category.equals(entry.getKey())) + // only articles which have comments + .filter(parentControl -> parentControl.commentIds.isEmpty() == false) + .count(); + assertThat("Failed for category " + entry.getKey(), categoryBucket.getDocCount(), equalTo(articlesForCategory)); + } } - - final Terms.Bucket categoryBucket = categoryTerms.getBucketByKey(entry.getKey()); - assertNotNull("Failed for category " + entry.getKey(), categoryBucket); - assertThat("Failed for category " + entry.getKey(), categoryBucket.getKeyAsString(), equalTo(entry.getKey())); - - // count all articles in this category which have at least one comment - long articlesForCategory = articleToControl.values() - .stream() - // only articles with this category - .filter(parentControl -> parentControl.category.equals(entry.getKey())) - // only articles which have comments - .filter(parentControl -> parentControl.commentIds.isEmpty() == false) - .count(); - assertThat("Failed for category " + entry.getKey(), categoryBucket.getDocCount(), equalTo(articlesForCategory)); - } + ); } public void testParentAggs() throws Exception { - final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(10000) - .setQuery(matchQuery("randomized", true)) - .addAggregation( - terms("to_commenter").field("commenter") - .size(10000) - .subAggregation( - parent("to_article", "comment").subAggregation( - terms("to_category").field("category").size(10000).subAggregation(topHits("top_category")) + assertNoFailuresAndResponse( + prepareSearch("test").setSize(10000) + .setQuery(matchQuery("randomized", true)) + .addAggregation( + terms("to_commenter").field("commenter") + .size(10000) + .subAggregation( + parent("to_article", "comment").subAggregation( + terms("to_category").field("category").size(10000).subAggregation(topHits("top_category")) + ) ) - ) - ); - SearchResponse searchResponse = searchRequest.get(); - assertNoFailures(searchResponse); - - final Set commenters = getCommenters(); - final Map> commenterToComments = getCommenterToComments(); + ), + response -> { + final Set commenters = getCommenters(); + final Map> commenterToComments = getCommenterToComments(); + + Terms categoryTerms = response.getAggregations().get("to_commenter"); + assertThat("Response: " + response + "\n", categoryTerms.getBuckets().size(), equalTo(commenters.size())); + for (Terms.Bucket commenterBucket : categoryTerms.getBuckets()) { + Set comments = commenterToComments.get(commenterBucket.getKeyAsString()); + assertNotNull(comments); + assertThat( + "Failed for commenter " + commenterBucket.getKeyAsString(), + commenterBucket.getDocCount(), + equalTo((long) comments.size()) + ); + + Parent articleAgg = commenterBucket.getAggregations().get("to_article"); + assertThat(articleAgg.getName(), equalTo("to_article")); + // find all articles for the comments for the current commenter + Set articles = articleToControl.values() + .stream() + .flatMap( + (Function>) parentControl -> parentControl.commentIds.stream() + .filter(comments::contains) + ) + .collect(Collectors.toSet()); - Terms categoryTerms = searchResponse.getAggregations().get("to_commenter"); - assertThat( - "Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", - categoryTerms.getBuckets().size(), - equalTo(commenters.size()) - ); - for (Terms.Bucket commenterBucket : categoryTerms.getBuckets()) { - Set comments = commenterToComments.get(commenterBucket.getKeyAsString()); - assertNotNull(comments); - assertThat( - "Failed for commenter " + commenterBucket.getKeyAsString(), - commenterBucket.getDocCount(), - equalTo((long) comments.size()) - ); + assertThat(articleAgg.getDocCount(), equalTo((long) articles.size())); - Parent articleAgg = commenterBucket.getAggregations().get("to_article"); - assertThat(articleAgg.getName(), equalTo("to_article")); - // find all articles for the comments for the current commenter - Set articles = articleToControl.values() - .stream() - .flatMap( - (Function>) parentControl -> parentControl.commentIds.stream().filter(comments::contains) - ) - .collect(Collectors.toSet()); + Terms categoryAgg = articleAgg.getAggregations().get("to_category"); + assertNotNull(categoryAgg); - assertThat(articleAgg.getDocCount(), equalTo((long) articles.size())); + List categories = categoryToControl.entrySet() + .stream() + .filter(entry -> entry.getValue().commenterToCommentId.containsKey(commenterBucket.getKeyAsString())) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); - Terms categoryAgg = articleAgg.getAggregations().get("to_category"); - assertNotNull(categoryAgg); + for (String category : categories) { + Terms.Bucket categoryBucket = categoryAgg.getBucketByKey(category); + assertNotNull(categoryBucket); - List categories = categoryToControl.entrySet() - .stream() - .filter(entry -> entry.getValue().commenterToCommentId.containsKey(commenterBucket.getKeyAsString())) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); + Aggregation topCategory = categoryBucket.getAggregations().get("top_category"); + assertNotNull(topCategory); + } + } - for (String category : categories) { - Terms.Bucket categoryBucket = categoryAgg.getBucketByKey(category); - assertNotNull(categoryBucket); + for (String commenter : commenters) { + Terms.Bucket categoryBucket = categoryTerms.getBucketByKey(commenter); + assertThat(categoryBucket.getKeyAsString(), equalTo(commenter)); + assertThat(categoryBucket.getDocCount(), equalTo((long) commenterToComments.get(commenter).size())); - Aggregation topCategory = categoryBucket.getAggregations().get("top_category"); - assertNotNull(topCategory); + Parent childrenBucket = categoryBucket.getAggregations().get("to_article"); + assertThat(childrenBucket.getName(), equalTo("to_article")); + } } - } - - for (String commenter : commenters) { - Terms.Bucket categoryBucket = categoryTerms.getBucketByKey(commenter); - assertThat(categoryBucket.getKeyAsString(), equalTo(commenter)); - assertThat(categoryBucket.getDocCount(), equalTo((long) commenterToComments.get(commenter).size())); - - Parent childrenBucket = categoryBucket.getAggregations().get("to_article"); - assertThat(childrenBucket.getName(), equalTo("to_article")); - } + ); } private Set getCommenters() { @@ -197,68 +184,65 @@ private Map> getCommenterToComments() { } public void testNonExistingParentType() throws Exception { - SearchResponse searchResponse = prepareSearch("test").addAggregation(parent("non-existing", "xyz")).get(); - assertNoFailures(searchResponse); - - Parent parent = searchResponse.getAggregations().get("non-existing"); - assertThat(parent.getName(), equalTo("non-existing")); - assertThat(parent.getDocCount(), equalTo(0L)); + assertNoFailuresAndResponse(prepareSearch("test").addAggregation(parent("non-existing", "xyz")), response -> { + Parent parent = response.getAggregations().get("non-existing"); + assertThat(parent.getName(), equalTo("non-existing")); + assertThat(parent.getDocCount(), equalTo(0L)); + }); } public void testTermsParentAggTerms() throws Exception { - final SearchRequestBuilder searchRequest = prepareSearch("test").setSize(10000) - .setQuery(matchQuery("randomized", true)) - .addAggregation( - terms("to_commenter").field("commenter") - .size(10000) - .subAggregation(parent("to_article", "comment").subAggregation(terms("to_category").field("category").size(10000))) - ); - SearchResponse searchResponse = searchRequest.get(); - assertNoFailures(searchResponse); - - final Set commenters = getCommenters(); - final Map> commenterToComments = getCommenterToComments(); - - Terms commentersAgg = searchResponse.getAggregations().get("to_commenter"); - assertThat( - "Request: " + searchRequest + "\nResponse: " + searchResponse + "\n", - commentersAgg.getBuckets().size(), - equalTo(commenters.size()) - ); - for (Terms.Bucket commenterBucket : commentersAgg.getBuckets()) { - Set comments = commenterToComments.get(commenterBucket.getKeyAsString()); - assertNotNull(comments); - assertThat( - "Failed for commenter " + commenterBucket.getKeyAsString(), - commenterBucket.getDocCount(), - equalTo((long) comments.size()) - ); - - Parent articleAgg = commenterBucket.getAggregations().get("to_article"); - assertThat(articleAgg.getName(), equalTo("to_article")); - // find all articles for the comments for the current commenter - Set articles = articleToControl.values() - .stream() - .flatMap( - (Function>) parentControl -> parentControl.commentIds.stream().filter(comments::contains) - ) - .collect(Collectors.toSet()); + assertNoFailuresAndResponse( + prepareSearch("test").setSize(10000) + .setQuery(matchQuery("randomized", true)) + .addAggregation( + terms("to_commenter").field("commenter") + .size(10000) + .subAggregation(parent("to_article", "comment").subAggregation(terms("to_category").field("category").size(10000))) + ), + response -> { + final Set commenters = getCommenters(); + final Map> commenterToComments = getCommenterToComments(); + + Terms commentersAgg = response.getAggregations().get("to_commenter"); + assertThat("Response: " + response + "\n", commentersAgg.getBuckets().size(), equalTo(commenters.size())); + for (Terms.Bucket commenterBucket : commentersAgg.getBuckets()) { + Set comments = commenterToComments.get(commenterBucket.getKeyAsString()); + assertNotNull(comments); + assertThat( + "Failed for commenter " + commenterBucket.getKeyAsString(), + commenterBucket.getDocCount(), + equalTo((long) comments.size()) + ); + + Parent articleAgg = commenterBucket.getAggregations().get("to_article"); + assertThat(articleAgg.getName(), equalTo("to_article")); + // find all articles for the comments for the current commenter + Set articles = articleToControl.values() + .stream() + .flatMap( + (Function>) parentControl -> parentControl.commentIds.stream() + .filter(comments::contains) + ) + .collect(Collectors.toSet()); - assertThat(articleAgg.getDocCount(), equalTo((long) articles.size())); + assertThat(articleAgg.getDocCount(), equalTo((long) articles.size())); - Terms categoryAgg = articleAgg.getAggregations().get("to_category"); - assertNotNull(categoryAgg); + Terms categoryAgg = articleAgg.getAggregations().get("to_category"); + assertNotNull(categoryAgg); - List categories = categoryToControl.entrySet() - .stream() - .filter(entry -> entry.getValue().commenterToCommentId.containsKey(commenterBucket.getKeyAsString())) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); + List categories = categoryToControl.entrySet() + .stream() + .filter(entry -> entry.getValue().commenterToCommentId.containsKey(commenterBucket.getKeyAsString())) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); - for (String category : categories) { - Terms.Bucket categoryBucket = categoryAgg.getBucketByKey(category); - assertNotNull(categoryBucket); + for (String category : categories) { + Terms.Bucket categoryBucket = categoryAgg.getBucketByKey(category); + assertNotNull(categoryBucket); + } + } } - } + ); } } diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java index 1915df55a7836..34ead2c21480b 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/ChildQuerySearchIT.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.explain.ExplainResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; @@ -64,7 +65,10 @@ import static org.elasticsearch.join.query.JoinQueryBuilders.parentId; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHits; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; @@ -89,48 +93,59 @@ public void testMultiLevelChild() throws Exception { createIndexRequest("test", "grandchild", "gc1", "c1", "gc_field", "gc_value1").setRouting("p1").get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()) - .filter( - hasChildQuery( - "child", - boolQuery().must(termQuery("c_field", "c_value1")) - .filter(hasChildQuery("grandchild", termQuery("gc_field", "gc_value1"), ScoreMode.None)), - ScoreMode.None + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()) + .filter( + hasChildQuery( + "child", + boolQuery().must(termQuery("c_field", "c_value1")) + .filter(hasChildQuery("grandchild", termQuery("gc_field", "gc_value1"), ScoreMode.None)), + ScoreMode.None + ) ) - ) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)) - ).execute().actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false)) - ).execute().actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("gc1")); - - searchResponse = prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)) - .execute() - .actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - - searchResponse = prepareSearch("test").setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)) - .execute() - .actionGet(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("gc1")); + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("c1")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("child", termQuery("c_field", "c_value1"), false)) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("gc1")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "p_value1"), false)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("c1")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasParentQuery("child", termQuery("c_field", "c_value1"), false)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("gc1")); + } + ); } // see #2744 @@ -142,11 +157,13 @@ public void test2744() throws IOException { createIndexRequest("test", "foo", "1", null, "foo", 1).get(); createIndexRequest("test", "test", "2", "1", "foo", 1).get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery(hasChildQuery("test", matchQuery("foo", 1), ScoreMode.None)).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); - + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("test", matchQuery("foo", 1), ScoreMode.None)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + } + ); } public void testSimpleChildQuery() throws Exception { @@ -163,53 +180,60 @@ public void testSimpleChildQuery() throws Exception { refresh(); // TEST FETCHING _parent from child - SearchResponse searchResponse; - searchResponse = prepareSearch("test").setQuery(idsQuery().addIds("c1")).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - assertThat(extractValue("join_field.name", searchResponse.getHits().getAt(0).getSourceAsMap()), equalTo("child")); - assertThat(extractValue("join_field.parent", searchResponse.getHits().getAt(0).getSourceAsMap()), equalTo("p1")); + assertNoFailuresAndResponse(prepareSearch("test").setQuery(idsQuery().addIds("c1")), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("c1")); + assertThat(extractValue("join_field.name", response.getHits().getAt(0).getSourceAsMap()), equalTo("child")); + assertThat(extractValue("join_field.parent", response.getHits().getAt(0).getSourceAsMap()), equalTo("p1")); + + }); // TEST matching on parent - searchResponse = prepareSearch("test").setQuery( - boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child")) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); - assertThat(extractValue("join_field.name", searchResponse.getHits().getAt(0).getSourceAsMap()), equalTo("child")); - assertThat(extractValue("join_field.parent", searchResponse.getHits().getAt(0).getSourceAsMap()), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); - assertThat(extractValue("join_field.name", searchResponse.getHits().getAt(1).getSourceAsMap()), equalTo("child")); - assertThat(extractValue("join_field.parent", searchResponse.getHits().getAt(1).getSourceAsMap()), equalTo("p1")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().filter(termQuery("join_field#parent", "p1")).filter(termQuery("join_field", "child")) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(extractValue("join_field.name", response.getHits().getAt(0).getSourceAsMap()), equalTo("child")); + assertThat(extractValue("join_field.parent", response.getHits().getAt(0).getSourceAsMap()), equalTo("p1")); + assertThat(response.getHits().getAt(1).getId(), anyOf(equalTo("c1"), equalTo("c2"))); + assertThat(extractValue("join_field.name", response.getHits().getAt(1).getSourceAsMap()), equalTo("child")); + assertThat(extractValue("join_field.parent", response.getHits().getAt(1).getSourceAsMap()), equalTo("p1")); + } + ); // HAS CHILD - searchResponse = prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")).get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - - searchResponse = prepareSearch("test").setQuery(randomHasChild("child", "c_field", "blue")).execute().actionGet(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); - - searchResponse = prepareSearch("test").setQuery(randomHasChild("child", "c_field", "red")).get(); - assertHitCount(searchResponse, 2L); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertNoFailuresAndResponse(prepareSearch("test").setQuery(randomHasChild("child", "c_field", "yellow")), response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + }); + + assertNoFailuresAndResponse(prepareSearch("test").setQuery(randomHasChild("child", "c_field", "blue")), response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getId(), equalTo("p2")); + }); + + assertNoFailuresAndResponse(prepareSearch("test").setQuery(randomHasChild("child", "c_field", "red")), response -> { + assertHitCount(response, 2L); + assertThat(response.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(response.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + }); // HAS PARENT - searchResponse = prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value2")).get(); - assertNoFailures(searchResponse); - assertHitCount(searchResponse, 2L); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c3")); - assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("c4")); - - searchResponse = prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value1")).get(); - assertHitCount(searchResponse, 2L); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c1")); - assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("c2")); + assertNoFailuresAndResponse(prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value2")), response -> { + assertHitCount(response, 2L); + assertThat(response.getHits().getAt(0).getId(), equalTo("c3")); + assertThat(response.getHits().getAt(1).getId(), equalTo("c4")); + }); + + assertNoFailuresAndResponse(prepareSearch("test").setQuery(randomHasParent("parent", "p_field", "p_value1")), response -> { + assertHitCount(response, 2L); + assertThat(response.getHits().getAt(0).getId(), equalTo("c1")); + assertThat(response.getHits().getAt(1).getId(), equalTo("c2")); + }); } // Issue #3290 @@ -276,18 +300,21 @@ public void testHasParentFilter() throws Exception { assertThat(parentToChildren.isEmpty(), equalTo(false)); for (Map.Entry> parentToChildrenEntry : parentToChildren.entrySet()) { - SearchResponse searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasParentQuery("parent", termQuery("p_field", parentToChildrenEntry.getKey()), false)) - ).setSize(numChildDocsPerParent).get(); - - assertNoFailures(searchResponse); - Set childIds = parentToChildrenEntry.getValue(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo((long) childIds.size())); - for (int i = 0; i < searchResponse.getHits().getTotalHits().value; i++) { - assertThat(childIds.remove(searchResponse.getHits().getAt(i).getId()), is(true)); - assertThat(searchResponse.getHits().getAt(i).getScore(), is(1.0f)); - } - assertThat(childIds.size(), is(0)); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + constantScoreQuery(hasParentQuery("parent", termQuery("p_field", parentToChildrenEntry.getKey()), false)) + ).setSize(numChildDocsPerParent), + response -> { + assertNoFailures(response); + Set childIds = parentToChildrenEntry.getValue(); + assertThat(response.getHits().getTotalHits().value, equalTo((long) childIds.size())); + for (int i = 0; i < response.getHits().getTotalHits().value; i++) { + assertThat(childIds.remove(response.getHits().getAt(i).getId()), is(true)); + assertThat(response.getHits().getAt(i).getScore(), is(1.0f)); + } + assertThat(childIds.size(), is(0)); + } + ); } } @@ -311,47 +338,56 @@ public void testSimpleChildQueryWithFlush() throws Exception { refresh(); // HAS CHILD QUERY + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + } + ); - SearchResponse searchResponse = prepareSearch("test").setQuery( - hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - - searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p2")); + } + ); - searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None)).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(response.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + } + ); // HAS CHILD FILTER - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None))), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + } + ); - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None))), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p2")); + } + ); - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "red"), ScoreMode.None))), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(response.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + } + ); } public void testScopedFacet() throws Exception { @@ -372,32 +408,38 @@ public void testScopedFacet() throws Exception { refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - hasChildQuery("child", boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), ScoreMode.None) - ) - .addAggregation( - AggregationBuilders.global("global") - .subAggregation( - AggregationBuilders.filter( - "filter", - boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")) - ).subAggregation(AggregationBuilders.terms("facet1").field("c_field")) - ) + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + hasChildQuery( + "child", + boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")), + ScoreMode.None + ) ) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); - assertThat(searchResponse.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); - - Global global = searchResponse.getAggregations().get("global"); - Filter filter = global.getAggregations().get("filter"); - Terms termsFacet = filter.getAggregations().get("facet1"); - assertThat(termsFacet.getBuckets().size(), equalTo(2)); - assertThat(termsFacet.getBuckets().get(0).getKeyAsString(), equalTo("red")); - assertThat(termsFacet.getBuckets().get(0).getDocCount(), equalTo(2L)); - assertThat(termsFacet.getBuckets().get(1).getKeyAsString(), equalTo("yellow")); - assertThat(termsFacet.getBuckets().get(1).getDocCount(), equalTo(1L)); + .addAggregation( + AggregationBuilders.global("global") + .subAggregation( + AggregationBuilders.filter( + "filter", + boolQuery().should(termQuery("c_field", "red")).should(termQuery("c_field", "yellow")) + ).subAggregation(AggregationBuilders.terms("facet1").field("c_field")) + ) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + assertThat(response.getHits().getAt(1).getId(), anyOf(equalTo("p2"), equalTo("p1"))); + + Global global = response.getAggregations().get("global"); + Filter filter = global.getAggregations().get("filter"); + Terms termsFacet = filter.getAggregations().get("facet1"); + assertThat(termsFacet.getBuckets().size(), equalTo(2)); + assertThat(termsFacet.getBuckets().get(0).getKeyAsString(), equalTo("red")); + assertThat(termsFacet.getBuckets().get(0).getDocCount(), equalTo(2L)); + assertThat(termsFacet.getBuckets().get(1).getKeyAsString(), equalTo("yellow")); + assertThat(termsFacet.getBuckets().get(1).getDocCount(), equalTo(1L)); + } + ); } public void testDeletedParent() throws Exception { @@ -413,26 +455,28 @@ public void testDeletedParent() throws Exception { refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None))), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + assertThat(response.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); + } + ); // update p1 and see what that we get updated values... createIndexRequest("test", "parent", "p1", null, "p_field", "p_value1_updated").get(); indicesAdmin().prepareRefresh().get(); - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1_updated\"")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.None))), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + assertThat(response.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1_updated\"")); + } + ); } public void testDfsSearchType() throws Exception { @@ -470,17 +514,17 @@ public void testHasChildAndHasParentFailWhenSomeSegmentsDontContainAnyParentOrCh client().prepareIndex("test").setId("3").setSource("p_field", 1).get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchAllQuery(), ScoreMode.None)) + ), + 1L + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchAllQuery(), false)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery(boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchAllQuery(), false))), + 1L + ); } public void testCountApiUsage() throws Exception { @@ -517,15 +561,21 @@ public void testExplainUsage() throws Exception { createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setExplain(true) - .setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)) - .get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); + assertResponse( + prepareSearch("test").setExplain(true).setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)), + response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); + } + ); - searchResponse = prepareSearch("test").setExplain(true).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)).get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); + assertResponse( + prepareSearch("test").setExplain(true).setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true)), + response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getExplanation().getDescription(), containsString("join value p1")); + } + ); ExplainResponse explainResponse = client().prepareExplain("test", parentId) .setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max)) @@ -587,81 +637,93 @@ public void testScoreForParentChildQueriesWithFunctionScore() throws Exception { ensureGreen(); indexRandom(true, createDocBuilders().toArray(new IndexRequestBuilder[0])); - SearchResponse response = prepareSearch("test").setQuery( - hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), - ScoreMode.Total - ) - ).get(); + assertResponse( + prepareSearch("test").setQuery( + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), + ScoreMode.Total + ) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("1")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(4f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(3f)); + } + ); - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("1")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(4f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(3f)); - - response = prepareSearch("test").setQuery( - hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), - ScoreMode.Max - ) - ).get(); + assertResponse( + prepareSearch("test").setQuery( + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), + ScoreMode.Max + ) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(4f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("1")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(2f)); + } + ); - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(4f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("1")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(2f)); - - response = prepareSearch("test").setQuery( - hasChildQuery( - "child", - QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) - .boostMode(CombineFunction.REPLACE), - ScoreMode.Avg - ) - ).get(); + assertResponse( + prepareSearch("test").setQuery( + hasChildQuery( + "child", + QueryBuilders.functionScoreQuery(matchQuery("c_field2", 0), fieldValueFactorFunction("c_field1")) + .boostMode(CombineFunction.REPLACE), + ScoreMode.Avg + ) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(4f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("1")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1.5f)); + } + ); - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(4f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("1")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1.5f)); - - response = prepareSearch("test").setQuery( - hasParentQuery( - "parent", - QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), fieldValueFactorFunction("p_field2")) - .boostMode(CombineFunction.REPLACE), - true - ) - ).addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()).get(); - - assertThat(response.getHits().getTotalHits().value, equalTo(7L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("16")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("17")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("18")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[3].getId(), equalTo("19")); - assertThat(response.getHits().getHits()[3].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[4].getId(), equalTo("20")); - assertThat(response.getHits().getHits()[4].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[5].getId(), equalTo("21")); - assertThat(response.getHits().getHits()[5].getScore(), equalTo(5f)); - assertThat(response.getHits().getHits()[6].getId(), equalTo("22")); - assertThat(response.getHits().getHits()[6].getScore(), equalTo(5f)); + assertResponse( + prepareSearch("test").setQuery( + hasParentQuery( + "parent", + QueryBuilders.functionScoreQuery(matchQuery("p_field1", "p_value3"), fieldValueFactorFunction("p_field2")) + .boostMode(CombineFunction.REPLACE), + true + ) + ).addSort(SortBuilders.fieldSort("c_field3")).addSort(SortBuilders.scoreSort()), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(7L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("16")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(5f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("17")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(5f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("18")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(5f)); + assertThat(response.getHits().getHits()[3].getId(), equalTo("19")); + assertThat(response.getHits().getHits()[3].getScore(), equalTo(5f)); + assertThat(response.getHits().getHits()[4].getId(), equalTo("20")); + assertThat(response.getHits().getHits()[4].getScore(), equalTo(5f)); + assertThat(response.getHits().getHits()[5].getId(), equalTo("21")); + assertThat(response.getHits().getHits()[5].getScore(), equalTo(5f)); + assertThat(response.getHits().getHits()[6].getId(), equalTo("22")); + assertThat(response.getHits().getHits()[6].getScore(), equalTo(5f)); + } + ); } // Issue #2536 @@ -669,30 +731,26 @@ public void testParentChildQueriesCanHandleNoRelevantTypesInIndex() throws Excep assertAcked(prepareCreate("test").setMapping(buildParentJoinFieldMappingFromSimplifiedDef("join_field", true, "parent", "child"))); ensureGreen(); - SearchResponse response = prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); - assertNoFailures(response); - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)), + 0L + ); client().prepareIndex("test") .setSource(jsonBuilder().startObject().field("text", "value").endObject()) .setRefreshPolicy(RefreshPolicy.IMMEDIATE) .get(); - response = prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)).get(); - assertNoFailures(response); - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.None)), + 0L + ); - response = prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)).get(); - assertNoFailures(response); - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(hasChildQuery("child", matchQuery("text", "value"), ScoreMode.Max)), 0L); - response = prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), false)).get(); - assertNoFailures(response); - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), false)), 0L); - response = prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), true)).get(); - assertNoFailures(response); - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); + assertHitCountAndNoFailures(prepareSearch("test").setQuery(hasParentQuery("parent", matchQuery("text", "value"), true)), 0L); } public void testHasChildAndHasParentFilter_withFilter() throws Exception { @@ -706,19 +764,25 @@ public void testHasChildAndHasParentFilter_withFilter() throws Exception { client().prepareIndex("test").setId("3").setSource("p_field", 2).get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", termQuery("c_field", 1), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", termQuery("c_field", 1), ScoreMode.None)) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("1")); + } + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", 1), false)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", termQuery("p_field", 1), false)) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); + } + ); } public void testHasChildInnerHitsHighlighting() throws Exception { @@ -729,20 +793,23 @@ public void testHasChildInnerHitsHighlighting() throws Exception { createIndexRequest("test", "child", "2", "1", "c_field", "foo bar").get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - hasChildQuery("child", matchQuery("c_field", "foo"), ScoreMode.None).innerHit( - new InnerHitBuilder().setHighlightBuilder( - new HighlightBuilder().field(new Field("c_field").highlightQuery(QueryBuilders.matchQuery("c_field", "bar"))) + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + hasChildQuery("child", matchQuery("c_field", "foo"), ScoreMode.None).innerHit( + new InnerHitBuilder().setHighlightBuilder( + new HighlightBuilder().field(new Field("c_field").highlightQuery(QueryBuilders.matchQuery("c_field", "bar"))) + ) ) - ) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1")); - SearchHit[] searchHits = searchResponse.getHits().getHits()[0].getInnerHits().get("child").getHits(); - assertThat(searchHits.length, equalTo(1)); - assertThat(searchHits[0].getHighlightFields().get("c_field").getFragments().length, equalTo(1)); - assertThat(searchHits[0].getHighlightFields().get("c_field").getFragments()[0].string(), equalTo("foo bar")); + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("1")); + SearchHit[] searchHits = response.getHits().getHits()[0].getInnerHits().get("child").getHits(); + assertThat(searchHits.length, equalTo(1)); + assertThat(searchHits[0].getHighlightFields().get("c_field").getFragments().length, equalTo(1)); + assertThat(searchHits[0].getHighlightFields().get("c_field").getFragments()[0].string(), equalTo("foo bar")); + } + ); } public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { @@ -755,25 +822,33 @@ public void testHasChildAndHasParentWrappedInAQueryFilter() throws Exception { createIndexRequest("test", "child", "2", "1", "c_field", 1).get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)) - ).get(); - assertSearchHit(searchResponse, 1, hasId("1")); + assertResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None)) + ), + response -> assertSearchHit(response, 1, hasId("1")) + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchQuery("p_field", 1), false)) - ).get(); - assertSearchHit(searchResponse, 1, hasId("2")); + assertResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(hasParentQuery("parent", matchQuery("p_field", 1), false)) + ), + response -> assertSearchHit(response, 1, hasId("2")) + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None))) - ).get(); - assertSearchHit(searchResponse, 1, hasId("1")); + assertResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", 1), ScoreMode.None))) + ), + response -> assertSearchHit(response, 1, hasId("1")) + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false))) - ).get(); - assertSearchHit(searchResponse, 1, hasId("2")); + assertResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchAllQuery()).filter(boolQuery().must(hasParentQuery("parent", matchQuery("p_field", 1), false))) + ), + response -> assertSearchHit(response, 1, hasId("2")) + ); } public void testSimpleQueryRewrite() throws Exception { @@ -806,31 +881,35 @@ public void testSimpleQueryRewrite() throws Exception { SearchType[] searchTypes = new SearchType[] { SearchType.QUERY_THEN_FETCH, SearchType.DFS_QUERY_THEN_FETCH }; for (SearchType searchType : searchTypes) { - SearchResponse searchResponse = prepareSearch("test").setSearchType(searchType) - .setQuery(hasChildQuery("child", prefixQuery("c_field", "c"), ScoreMode.Max)) - .addSort("p_field", SortOrder.ASC) - .setSize(5) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(10L)); - assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("p000")); - assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("p001")); - assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("p002")); - assertThat(searchResponse.getHits().getHits()[3].getId(), equalTo("p003")); - assertThat(searchResponse.getHits().getHits()[4].getId(), equalTo("p004")); - - searchResponse = prepareSearch("test").setSearchType(searchType) - .setQuery(hasParentQuery("parent", prefixQuery("p_field", "p"), true)) - .addSort("c_field", SortOrder.ASC) - .setSize(5) - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(500L)); - assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("c000")); - assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("c001")); - assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("c002")); - assertThat(searchResponse.getHits().getHits()[3].getId(), equalTo("c003")); - assertThat(searchResponse.getHits().getHits()[4].getId(), equalTo("c004")); + assertNoFailuresAndResponse( + prepareSearch("test").setSearchType(searchType) + .setQuery(hasChildQuery("child", prefixQuery("c_field", "c"), ScoreMode.Max)) + .addSort("p_field", SortOrder.ASC) + .setSize(5), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(10L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("p000")); + assertThat(response.getHits().getHits()[1].getId(), equalTo("p001")); + assertThat(response.getHits().getHits()[2].getId(), equalTo("p002")); + assertThat(response.getHits().getHits()[3].getId(), equalTo("p003")); + assertThat(response.getHits().getHits()[4].getId(), equalTo("p004")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("test").setSearchType(searchType) + .setQuery(hasParentQuery("parent", prefixQuery("p_field", "p"), true)) + .addSort("c_field", SortOrder.ASC) + .setSize(5), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(500L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("c000")); + assertThat(response.getHits().getHits()[1].getId(), equalTo("c001")); + assertThat(response.getHits().getHits()[2].getId(), equalTo("c002")); + assertThat(response.getHits().getHits()[3].getId(), equalTo("c003")); + assertThat(response.getHits().getHits()[4].getId(), equalTo("c004")); + } + ); } } @@ -849,21 +928,25 @@ public void testReIndexingParentAndChildDocuments() throws Exception { refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + assertThat(response.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); + } + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("c3")); - assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("c4")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true)) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("c3")); + assertThat(response.getHits().getAt(1).getId(), equalTo("c4")); + } + ); // re-index for (int i = 0; i < 10; i++) { @@ -874,19 +957,25 @@ public void testReIndexingParentAndChildDocuments() throws Exception { indicesAdmin().prepareRefresh("test").get(); } - searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p1")); - assertThat(searchResponse.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "yellow"), ScoreMode.Total)), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p1")); + assertThat(response.getHits().getAt(0).getSourceAsString(), containsString("\"p_value1\"")); + } + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); - assertThat(searchResponse.getHits().getAt(0).getId(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); - assertThat(searchResponse.getHits().getAt(1).getId(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); + assertNoFailuresAndResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchQuery("c_field", "x")).must(hasParentQuery("parent", termQuery("p_field", "p_value2"), true)) + ), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getId(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); + assertThat(response.getHits().getAt(1).getId(), Matchers.anyOf(equalTo("c3"), equalTo("c4"))); + } + ); } // Issue #3203 @@ -902,14 +991,15 @@ public void testHasChildQueryWithMinimumScore() throws Exception { createIndexRequest("test", "child", "c4", "p2", "c_field", "x").get(); createIndexRequest("test", "child", "c5", "p2", "c_field", "x").get(); refresh(); - - SearchResponse searchResponse = prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)) - .setMinScore(3) // Score needs to be 3 or above! - .get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("p2")); - assertThat(searchResponse.getHits().getAt(0).getScore(), equalTo(3.0f)); + // Score needs to be 3 or above! + assertNoFailuresAndResponse( + prepareSearch("test").setQuery(hasChildQuery("child", matchAllQuery(), ScoreMode.Total)).setMinScore(3), + response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("p2")); + assertThat(response.getHits().getAt(0).getScore(), equalTo(3.0f)); + } + ); } public void testParentFieldQuery() throws Exception { @@ -984,20 +1074,18 @@ public void testHasChildNotBeingCached() throws IOException { indicesAdmin().prepareFlush("test").get(); indicesAdmin().prepareRefresh("test").get(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None))), + 1L + ); createIndexRequest("test", "child", "c2", "p2", "c_field", "blue").get(); indicesAdmin().prepareRefresh("test").get(); - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None)) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery(constantScoreQuery(hasChildQuery("child", termQuery("c_field", "blue"), ScoreMode.None))), + 2L + ); } private QueryBuilder randomHasChild(String type, String field, String value) { @@ -1141,19 +1229,21 @@ public void testHasChildQueryWithNestedInnerObjects() throws Exception { refresh(); ScoreMode scoreMode = randomFrom(ScoreMode.values()); - SearchResponse searchResponse = prepareSearch("test").setQuery( - boolQuery().must(hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)) - .filter(boolQuery().mustNot(termQuery("p_field", "3"))) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery( + boolQuery().must(hasChildQuery("child", termQuery("c_field", "blue"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3"))) + ), + 1L + ); - searchResponse = prepareSearch("test").setQuery( - boolQuery().must(hasChildQuery("child", termQuery("c_field", "red"), scoreMode)) - .filter(boolQuery().mustNot(termQuery("p_field", "3"))) - ).get(); - assertNoFailures(searchResponse); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); + assertHitCountAndNoFailures( + prepareSearch("test").setQuery( + boolQuery().must(hasChildQuery("child", termQuery("c_field", "red"), scoreMode)) + .filter(boolQuery().mustNot(termQuery("p_field", "3"))) + ), + 2L + ); } public void testNamedFilters() throws Exception { @@ -1165,31 +1255,45 @@ public void testNamedFilters() throws Exception { createIndexRequest("test", "child", "c1", parentId, "c_field", "1").get(); refresh(); - SearchResponse searchResponse = prepareSearch("test").setQuery( - hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max).queryName("test") - ).get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + assertResponse( + prepareSearch("test").setQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.Max).queryName("test")), + response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + } + ); - searchResponse = prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true).queryName("test")).get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + assertResponse( + prepareSearch("test").setQuery(hasParentQuery("parent", termQuery("p_field", "1"), true).queryName("test")), + response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + } + ); - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None).queryName("test")) - ).get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + assertResponse( + prepareSearch("test").setQuery( + constantScoreQuery(hasChildQuery("child", termQuery("c_field", "1"), ScoreMode.None).queryName("test")) + ), + response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + } + ); - searchResponse = prepareSearch("test").setQuery( - constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false).queryName("test")) - ).get(); - assertHitCount(searchResponse, 1L); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(searchResponse.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + assertResponse( + prepareSearch("test").setQuery( + constantScoreQuery(hasParentQuery("parent", termQuery("p_field", "1"), false).queryName("test")) + ), + response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getMatchedQueries()[0], equalTo("test")); + } + ); } public void testParentChildQueriesNoParentType() throws Exception { @@ -1259,22 +1363,27 @@ public void testParentChildCaching() throws Exception { indicesAdmin().prepareRefresh("test").get(); for (int i = 0; i < 2; i++) { - SearchResponse searchResponse = prepareSearch().setQuery( - boolQuery().must(matchAllQuery()) - .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery())) - ).get(); - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(2L)); + assertHitCount( + prepareSearch().setQuery( + boolQuery().must(matchAllQuery()) + .filter( + boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery()) + ) + ), + 2L + ); } createIndexRequest("test", "child", "c3", "p2", "c_field", "blue").get(); indicesAdmin().prepareRefresh("test").get(); - SearchResponse searchResponse = prepareSearch().setQuery( - boolQuery().must(matchAllQuery()) - .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery())) - ).get(); - - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + assertHitCount( + prepareSearch().setQuery( + boolQuery().must(matchAllQuery()) + .filter(boolQuery().must(hasChildQuery("child", matchQuery("c_field", "red"), ScoreMode.None)).must(matchAllQuery())) + ), + 1L + ); } public void testParentChildQueriesViaScrollApi() throws Exception { @@ -1341,7 +1450,8 @@ private List createMinMaxDocBuilders() { return indexBuilders; } - private SearchResponse minMaxQuery(ScoreMode scoreMode, int minChildren, Integer maxChildren) throws SearchPhaseExecutionException { + private SearchRequestBuilder minMaxQuery(ScoreMode scoreMode, int minChildren, Integer maxChildren) + throws SearchPhaseExecutionException { HasChildQueryBuilder hasChildQuery = hasChildQuery( "child", QueryBuilders.functionScoreQuery( @@ -1354,7 +1464,7 @@ private SearchResponse minMaxQuery(ScoreMode scoreMode, int minChildren, Integer scoreMode ).minMaxChildren(minChildren, maxChildren != null ? maxChildren : HasChildQueryBuilder.DEFAULT_MAX_CHILDREN); - return prepareSearch("test").setQuery(hasChildQuery).addSort("_score", SortOrder.DESC).addSort("id", SortOrder.ASC).get(); + return prepareSearch("test").setQuery(hasChildQuery).addSort("_score", SortOrder.DESC).addSort("id", SortOrder.ASC); } public void testMinMaxChildren() throws Exception { @@ -1362,268 +1472,259 @@ public void testMinMaxChildren() throws Exception { ensureGreen(); indexRandom(true, createMinMaxDocBuilders().toArray(new IndexRequestBuilder[0])); - SearchResponse response; // Score mode = NONE - response = minMaxQuery(ScoreMode.None, 1, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.None, 2, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.None, 3, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.None, 4, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - - response = minMaxQuery(ScoreMode.None, 1, 4); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.None, 1, 3); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.None, 1, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.None, 2, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + assertResponse(minMaxQuery(ScoreMode.None, 1, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.None, 2, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.None, 3, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + }); + + assertHitCount(minMaxQuery(ScoreMode.None, 4, null), 0L); + + assertResponse(minMaxQuery(ScoreMode.None, 1, 4), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.None, 1, 3), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.None, 1, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.None, 2, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1f)); + }); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> minMaxQuery(ScoreMode.None, 3, 2)); assertThat(e.getMessage(), equalTo("[has_child] 'max_children' is less than 'min_children'")); // Score mode = SUM - response = minMaxQuery(ScoreMode.Total, 1, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Total, 2, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); - - response = minMaxQuery(ScoreMode.Total, 3, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); - - response = minMaxQuery(ScoreMode.Total, 4, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - - response = minMaxQuery(ScoreMode.Total, 1, 4); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Total, 1, 3); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Total, 1, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Total, 2, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + assertResponse(minMaxQuery(ScoreMode.Total, 1, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Total, 2, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Total, 3, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); + }); + + assertHitCount(minMaxQuery(ScoreMode.Total, 4, null), 0L); + + assertResponse(minMaxQuery(ScoreMode.Total, 1, 4), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Total, 1, 3), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(6f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Total, 1, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Total, 2, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + }); e = expectThrows(IllegalArgumentException.class, () -> minMaxQuery(ScoreMode.Total, 3, 2)); assertThat(e.getMessage(), equalTo("[has_child] 'max_children' is less than 'min_children'")); // Score mode = MAX - response = minMaxQuery(ScoreMode.Max, 1, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Max, 2, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); - - response = minMaxQuery(ScoreMode.Max, 3, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); - - response = minMaxQuery(ScoreMode.Max, 4, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - - response = minMaxQuery(ScoreMode.Max, 1, 4); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Max, 1, 3); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Max, 1, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Max, 2, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + assertResponse(minMaxQuery(ScoreMode.Max, 1, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Max, 2, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Max, 3, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + }); + + assertHitCount(minMaxQuery(ScoreMode.Max, 4, null), 0L); + + assertResponse(minMaxQuery(ScoreMode.Max, 1, 4), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Max, 1, 3), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(3f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Max, 1, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Max, 2, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + }); e = expectThrows(IllegalArgumentException.class, () -> minMaxQuery(ScoreMode.Max, 3, 2)); assertThat(e.getMessage(), equalTo("[has_child] 'max_children' is less than 'min_children'")); // Score mode = AVG - response = minMaxQuery(ScoreMode.Avg, 1, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Avg, 2, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); - - response = minMaxQuery(ScoreMode.Avg, 3, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); - - response = minMaxQuery(ScoreMode.Avg, 4, null); - - assertThat(response.getHits().getTotalHits().value, equalTo(0L)); - - response = minMaxQuery(ScoreMode.Avg, 1, 4); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Avg, 1, 3); - - assertThat(response.getHits().getTotalHits().value, equalTo(3L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); - assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Avg, 1, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(2L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1.5f)); - assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); - assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); - - response = minMaxQuery(ScoreMode.Avg, 2, 2); - - assertThat(response.getHits().getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); - assertThat(response.getHits().getHits()[0].getScore(), equalTo(1.5f)); + assertResponse(minMaxQuery(ScoreMode.Avg, 1, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Avg, 2, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Avg, 3, null), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + }); + + assertHitCount(minMaxQuery(ScoreMode.Avg, 4, null), 0L); + + assertResponse(minMaxQuery(ScoreMode.Avg, 1, 4), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Avg, 1, 3), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("4")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(2f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1.5f)); + assertThat(response.getHits().getHits()[2].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[2].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Avg, 1, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1.5f)); + assertThat(response.getHits().getHits()[1].getId(), equalTo("2")); + assertThat(response.getHits().getHits()[1].getScore(), equalTo(1f)); + }); + + assertResponse(minMaxQuery(ScoreMode.Avg, 2, 2), response -> { + assertThat(response.getHits().getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getHits()[0].getId(), equalTo("3")); + assertThat(response.getHits().getHits()[0].getScore(), equalTo(1.5f)); + }); e = expectThrows(IllegalArgumentException.class, () -> minMaxQuery(ScoreMode.Avg, 3, 2)); assertThat(e.getMessage(), equalTo("[has_child] 'max_children' is less than 'min_children'")); @@ -1676,23 +1777,31 @@ public void testHighlightersIgnoreParentChild() throws IOException { String[] highlightTypes = new String[] { "plain", "fvh", "unified" }; for (String highlightType : highlightTypes) { logger.info("Testing with highlight type [{}]", highlightType); - SearchResponse searchResponse = prepareSearch("test").setQuery( - new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) - .must(new HasChildQueryBuilder("child-type", new MatchAllQueryBuilder(), ScoreMode.None)) - ).highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))).get(); - assertHitCount(searchResponse, 1); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("parent-id")); - HighlightField highlightField = searchResponse.getHits().getAt(0).getHighlightFields().get("searchText"); - assertThat(highlightField.getFragments()[0].string(), equalTo("quick brown fox")); - - searchResponse = prepareSearch("test").setQuery( - new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) - .must(new HasParentQueryBuilder("parent-type", new MatchAllQueryBuilder(), false)) - ).highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))).get(); - assertHitCount(searchResponse, 1); - assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("child-id")); - highlightField = searchResponse.getHits().getAt(0).getHighlightFields().get("searchText"); - assertThat(highlightField.getFragments()[0].string(), equalTo("quick brown fox")); + assertResponse( + prepareSearch("test").setQuery( + new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) + .must(new HasChildQueryBuilder("child-type", new MatchAllQueryBuilder(), ScoreMode.None)) + ).highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))), + response -> { + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("parent-id")); + HighlightField highlightField = response.getHits().getAt(0).getHighlightFields().get("searchText"); + assertThat(highlightField.getFragments()[0].string(), equalTo("quick brown fox")); + } + ); + + assertResponse( + prepareSearch("test").setQuery( + new BoolQueryBuilder().must(new MatchQueryBuilder("searchText", "fox")) + .must(new HasParentQueryBuilder("parent-type", new MatchAllQueryBuilder(), false)) + ).highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("searchText").highlighterType(highlightType))), + response -> { + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("child-id")); + HighlightField highlightField = response.getHits().getAt(0).getHighlightFields().get("searchText"); + assertThat(highlightField.getFragments()[0].string(), equalTo("quick brown fox")); + } + ); } } @@ -1709,12 +1818,14 @@ public void testAliasesFilterWithHasChildQuery() throws Exception { ); assertAcked(indicesAdmin().prepareAliases().addAlias("my-index", "filter2", hasParentQuery("parent", matchAllQuery(), false))); - SearchResponse response = prepareSearch("filter1").get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - response = prepareSearch("filter2").get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("2")); + assertResponse(prepareSearch("filter1"), response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + }); + assertResponse(prepareSearch("filter2"), response -> { + assertHitCount(response, 1L); + assertThat(response.getHits().getAt(0).getId(), equalTo("2")); + }); } } diff --git a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java index a857316319a63..39a84f2d16d7f 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/elasticsearch/join/query/InnerHitsIT.java @@ -12,7 +12,6 @@ import org.apache.lucene.util.ArrayUtil; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.index.IndexSettings; @@ -54,6 +53,8 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCountAndNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertResponse; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchHitsWithoutFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasId; @@ -114,81 +115,96 @@ public void testSimpleParentChild() throws Exception { requests.add(createIndexRequest("articles", "comment", "c6", "p2", "message", "elephant scared by mice x y")); indexRandom(true, requests); - SearchResponse response = prepareSearch("articles").setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder()) - ).get(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("p1")); - assertThat(response.getHits().getAt(0).getShard(), notNullValue()); + assertNoFailuresAndResponse( + prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit(new InnerHitBuilder()) + ), + response -> { + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("p1")); + assertThat(response.getHits().getAt(0).getShard(), notNullValue()); - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits().value, equalTo(2L)); + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits().value, equalTo(2L)); - assertThat(innerHits.getAt(0).getId(), equalTo("c1")); - assertThat(innerHits.getAt(1).getId(), equalTo("c2")); + assertThat(innerHits.getAt(0).getId(), equalTo("c1")); + assertThat(innerHits.getAt(1).getId(), equalTo("c2")); + } + ); final boolean seqNoAndTerm = randomBoolean(); - response = prepareSearch("articles").setQuery( - hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit( - new InnerHitBuilder().setSeqNoAndPrimaryTerm(seqNoAndTerm) - ) - ).get(); - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("p2")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits().value, equalTo(3L)); - - assertThat(innerHits.getAt(0).getId(), equalTo("c4")); - assertThat(innerHits.getAt(1).getId(), equalTo("c5")); - assertThat(innerHits.getAt(2).getId(), equalTo("c6")); - - if (seqNoAndTerm) { - assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(1L)); - assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(1L)); - assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(1L)); - assertThat(innerHits.getAt(0).getSeqNo(), greaterThanOrEqualTo(0L)); - assertThat(innerHits.getAt(1).getSeqNo(), greaterThanOrEqualTo(0L)); - assertThat(innerHits.getAt(2).getSeqNo(), greaterThanOrEqualTo(0L)); - } else { - assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM)); - assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM)); - assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM)); - assertThat(innerHits.getAt(0).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); - assertThat(innerHits.getAt(1).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); - assertThat(innerHits.getAt(2).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); - } + assertNoFailuresAndResponse( + prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "elephant"), ScoreMode.None).innerHit( + new InnerHitBuilder().setSeqNoAndPrimaryTerm(seqNoAndTerm) + ) + ), + response -> { + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("p2")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits().value, equalTo(3L)); + + assertThat(innerHits.getAt(0).getId(), equalTo("c4")); + assertThat(innerHits.getAt(1).getId(), equalTo("c5")); + assertThat(innerHits.getAt(2).getId(), equalTo("c6")); + + if (seqNoAndTerm) { + assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(1L)); + assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(1L)); + assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(1L)); + assertThat(innerHits.getAt(0).getSeqNo(), greaterThanOrEqualTo(0L)); + assertThat(innerHits.getAt(1).getSeqNo(), greaterThanOrEqualTo(0L)); + assertThat(innerHits.getAt(2).getSeqNo(), greaterThanOrEqualTo(0L)); + } else { + assertThat(innerHits.getAt(0).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM)); + assertThat(innerHits.getAt(1).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM)); + assertThat(innerHits.getAt(2).getPrimaryTerm(), equalTo(UNASSIGNED_PRIMARY_TERM)); + assertThat(innerHits.getAt(0).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); + assertThat(innerHits.getAt(1).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); + assertThat(innerHits.getAt(2).getSeqNo(), equalTo(UNASSIGNED_SEQ_NO)); + } + } + ); - response = prepareSearch("articles").setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder().addFetchField("message") - .setHighlightBuilder(new HighlightBuilder().field("message")) - .setExplain(true) - .setSize(1) - .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) - ) - ).get(); - assertNoFailures(response); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getHits().length, equalTo(1)); - assertThat(innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), equalTo("fox eat quick")); - assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(message:fox")); - assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("fox eat quick")); - assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); - - response = prepareSearch("articles").setQuery( - hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( - new InnerHitBuilder().addDocValueField("message").setSize(1) - ) - ).get(); - assertNoFailures(response); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getHits().length, equalTo(1)); - assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); + assertNoFailuresAndResponse( + prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder().addFetchField("message") + .setHighlightBuilder(new HighlightBuilder().field("message")) + .setExplain(true) + .setSize(1) + .addScriptField("script", new Script(ScriptType.INLINE, MockScriptEngine.NAME, "5", Collections.emptyMap())) + ) + ), + response -> { + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getHits().length, equalTo(1)); + assertThat( + innerHits.getAt(0).getHighlightFields().get("message").getFragments()[0].string(), + equalTo("fox eat quick") + ); + assertThat(innerHits.getAt(0).getExplanation().toString(), containsString("weight(message:fox")); + assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("fox eat quick")); + assertThat(innerHits.getAt(0).getFields().get("script").getValue().toString(), equalTo("5")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("articles").setQuery( + hasChildQuery("comment", matchQuery("message", "fox"), ScoreMode.None).innerHit( + new InnerHitBuilder().addDocValueField("message").setSize(1) + ) + ), + response -> { + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getHits().length, equalTo(1)); + assertThat(innerHits.getAt(0).getFields().get("message").getValue().toString(), equalTo("eat")); + } + ); } public void testRandomParentChild() throws Exception { @@ -251,39 +267,39 @@ public void testRandomParentChild() throws Exception { ) ) ); - SearchResponse searchResponse = prepareSearch("idx").setSize(numDocs).addSort("id", SortOrder.ASC).setQuery(boolQuery).get(); - - assertNoFailures(searchResponse); - assertHitCount(searchResponse, numDocs); - assertThat(searchResponse.getHits().getHits().length, equalTo(numDocs)); - int offset1 = 0; - int offset2 = 0; - for (int parent = 0; parent < numDocs; parent++) { - SearchHit searchHit = searchResponse.getHits().getAt(parent); - assertThat(searchHit.getId(), equalTo(String.format(Locale.ENGLISH, "p_%03d", parent))); - assertThat(searchHit.getShard(), notNullValue()); - - SearchHits inner = searchHit.getInnerHits().get("a"); - assertThat(inner.getTotalHits().value, equalTo((long) child1InnerObjects[parent])); - for (int child = 0; child < child1InnerObjects[parent] && child < size; child++) { - SearchHit innerHit = inner.getAt(child); - String childId = String.format(Locale.ENGLISH, "c1_%04d", offset1 + child); - assertThat(innerHit.getId(), equalTo(childId)); - assertThat(innerHit.getNestedIdentity(), nullValue()); - } - offset1 += child1InnerObjects[parent]; - - inner = searchHit.getInnerHits().get("b"); - assertThat(inner.getTotalHits().value, equalTo((long) child2InnerObjects[parent])); - for (int child = 0; child < child2InnerObjects[parent] && child < size; child++) { - SearchHit innerHit = inner.getAt(child); - String childId = String.format(Locale.ENGLISH, "c2_%04d", offset2 + child); - assertThat(innerHit.getId(), equalTo(childId)); - assertThat(innerHit.getNestedIdentity(), nullValue()); + assertNoFailuresAndResponse(prepareSearch("idx").setSize(numDocs).addSort("id", SortOrder.ASC).setQuery(boolQuery), response -> { + assertHitCount(response, numDocs); + assertThat(response.getHits().getHits().length, equalTo(numDocs)); + + int offset1 = 0; + int offset2 = 0; + for (int parent = 0; parent < numDocs; parent++) { + SearchHit searchHit = response.getHits().getAt(parent); + assertThat(searchHit.getId(), equalTo(String.format(Locale.ENGLISH, "p_%03d", parent))); + assertThat(searchHit.getShard(), notNullValue()); + + SearchHits inner = searchHit.getInnerHits().get("a"); + assertThat(inner.getTotalHits().value, equalTo((long) child1InnerObjects[parent])); + for (int child = 0; child < child1InnerObjects[parent] && child < size; child++) { + SearchHit innerHit = inner.getAt(child); + String childId = String.format(Locale.ENGLISH, "c1_%04d", offset1 + child); + assertThat(innerHit.getId(), equalTo(childId)); + assertThat(innerHit.getNestedIdentity(), nullValue()); + } + offset1 += child1InnerObjects[parent]; + + inner = searchHit.getInnerHits().get("b"); + assertThat(inner.getTotalHits().value, equalTo((long) child2InnerObjects[parent])); + for (int child = 0; child < child2InnerObjects[parent] && child < size; child++) { + SearchHit innerHit = inner.getAt(child); + String childId = String.format(Locale.ENGLISH, "c2_%04d", offset2 + child); + assertThat(innerHit.getId(), equalTo(childId)); + assertThat(innerHit.getNestedIdentity(), nullValue()); + } + offset2 += child2InnerObjects[parent]; } - offset2 += child2InnerObjects[parent]; - } + }); } public void testInnerHitsOnHasParent() throws Exception { @@ -320,24 +336,26 @@ public void testInnerHitsOnHasParent() throws Exception { ); indexRandom(true, requests); - SearchResponse response = prepareSearch("stack").addSort("id", SortOrder.ASC) - .setQuery( - boolQuery().must(matchQuery("body", "fail2ban")) - .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder())) - ) - .get(); - assertNoFailures(response); - assertHitCount(response, 2); - - SearchHit searchHit = response.getHits().getAt(0); - assertThat(searchHit.getId(), equalTo("3")); - assertThat(searchHit.getInnerHits().get("question").getTotalHits().value, equalTo(1L)); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("1")); - - searchHit = response.getHits().getAt(1); - assertThat(searchHit.getId(), equalTo("4")); - assertThat(searchHit.getInnerHits().get("question").getTotalHits().value, equalTo(1L)); - assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("2")); + assertNoFailuresAndResponse( + prepareSearch("stack").addSort("id", SortOrder.ASC) + .setQuery( + boolQuery().must(matchQuery("body", "fail2ban")) + .must(hasParentQuery("question", matchAllQuery(), false).innerHit(new InnerHitBuilder())) + ), + response -> { + assertHitCount(response, 2); + + SearchHit searchHit = response.getHits().getAt(0); + assertThat(searchHit.getId(), equalTo("3")); + assertThat(searchHit.getInnerHits().get("question").getTotalHits().value, equalTo(1L)); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("1")); + + searchHit = response.getHits().getAt(1); + assertThat(searchHit.getId(), equalTo("4")); + assertThat(searchHit.getInnerHits().get("question").getTotalHits().value, equalTo(1L)); + assertThat(searchHit.getInnerHits().get("question").getAt(0).getId(), equalTo("2")); + } + ); } public void testParentChildMultipleLayers() throws Exception { @@ -362,47 +380,51 @@ public void testParentChildMultipleLayers() throws Exception { requests.add(createIndexRequest("articles", "remark", "6", "4", "message", "bad").setRouting("2")); indexRandom(true, requests); - SearchResponse response = prepareSearch("articles").setQuery( - hasChildQuery( - "comment", - hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder()), - ScoreMode.None - ).innerHit(new InnerHitBuilder()) - ).get(); - - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("1")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("3")); - - innerHits = innerHits.getAt(0).getInnerHits().get("remark"); - assertThat(innerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("5")); - - response = prepareSearch("articles").setQuery( - hasChildQuery( - "comment", - hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder()), - ScoreMode.None - ).innerHit(new InnerHitBuilder()) - ).get(); - - assertNoFailures(response); - assertHitCount(response, 1); - assertSearchHit(response, 1, hasId("2")); - - assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); - innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); - assertThat(innerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("4")); - - innerHits = innerHits.getAt(0).getInnerHits().get("remark"); - assertThat(innerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("6")); + assertNoFailuresAndResponse( + prepareSearch("articles").setQuery( + hasChildQuery( + "comment", + hasChildQuery("remark", matchQuery("message", "good"), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None + ).innerHit(new InnerHitBuilder()) + ), + response -> { + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("1")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("3")); + + innerHits = innerHits.getAt(0).getInnerHits().get("remark"); + assertThat(innerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("5")); + } + ); + + assertNoFailuresAndResponse( + prepareSearch("articles").setQuery( + hasChildQuery( + "comment", + hasChildQuery("remark", matchQuery("message", "bad"), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None + ).innerHit(new InnerHitBuilder()) + ), + response -> { + assertHitCount(response, 1); + assertSearchHit(response, 1, hasId("2")); + + assertThat(response.getHits().getAt(0).getInnerHits().size(), equalTo(1)); + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("comment"); + assertThat(innerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("4")); + + innerHits = innerHits.getAt(0).getInnerHits().get("remark"); + assertThat(innerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("6")); + } + ); } public void testRoyals() throws Exception { @@ -436,56 +458,61 @@ public void testRoyals() throws Exception { requests.add(createIndexRequest("royals", "baron", "baron3", "earl3").setRouting("king")); requests.add(createIndexRequest("royals", "baron", "baron4", "earl4").setRouting("king")); indexRandom(true, requests); - - SearchResponse response = prepareSearch("royals").setQuery( - boolQuery().filter( - hasParentQuery( - "prince", - hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings")), - false - ).innerHit(new InnerHitBuilder().setName("princes")) - ) - .filter( - hasChildQuery( - "earl", - hasChildQuery("baron", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("barons")), - ScoreMode.None - ).innerHit(new InnerHitBuilder().addSort(SortBuilders.fieldSort("id").order(SortOrder.ASC)).setName("earls").setSize(4)) + assertResponse( + prepareSearch("royals").setQuery( + boolQuery().filter( + hasParentQuery( + "prince", + hasParentQuery("king", matchAllQuery(), false).innerHit(new InnerHitBuilder().setName("kings")), + false + ).innerHit(new InnerHitBuilder().setName("princes")) ) - ).get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); - - SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("earls"); - assertThat(innerHits.getTotalHits().value, equalTo(4L)); - assertThat(innerHits.getAt(0).getId(), equalTo("earl1")); - assertThat(innerHits.getAt(1).getId(), equalTo("earl2")); - assertThat(innerHits.getAt(2).getId(), equalTo("earl3")); - assertThat(innerHits.getAt(3).getId(), equalTo("earl4")); - - SearchHits innerInnerHits = innerHits.getAt(0).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron1")); - - innerInnerHits = innerHits.getAt(1).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron2")); - - innerInnerHits = innerHits.getAt(2).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron3")); - - innerInnerHits = innerHits.getAt(3).getInnerHits().get("barons"); - assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron4")); - - innerHits = response.getHits().getAt(0).getInnerHits().get("princes"); - assertThat(innerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerHits.getAt(0).getId(), equalTo("prince")); - - innerInnerHits = innerHits.getAt(0).getInnerHits().get("kings"); - assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); - assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); + .filter( + hasChildQuery( + "earl", + hasChildQuery("baron", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder().setName("barons")), + ScoreMode.None + ).innerHit( + new InnerHitBuilder().addSort(SortBuilders.fieldSort("id").order(SortOrder.ASC)).setName("earls").setSize(4) + ) + ) + ), + response -> { + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("duke")); + + SearchHits innerHits = response.getHits().getAt(0).getInnerHits().get("earls"); + assertThat(innerHits.getTotalHits().value, equalTo(4L)); + assertThat(innerHits.getAt(0).getId(), equalTo("earl1")); + assertThat(innerHits.getAt(1).getId(), equalTo("earl2")); + assertThat(innerHits.getAt(2).getId(), equalTo("earl3")); + assertThat(innerHits.getAt(3).getId(), equalTo("earl4")); + + SearchHits innerInnerHits = innerHits.getAt(0).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron1")); + + innerInnerHits = innerHits.getAt(1).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron2")); + + innerInnerHits = innerHits.getAt(2).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron3")); + + innerInnerHits = innerHits.getAt(3).getInnerHits().get("barons"); + assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("baron4")); + + innerHits = response.getHits().getAt(0).getInnerHits().get("princes"); + assertThat(innerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerHits.getAt(0).getId(), equalTo("prince")); + + innerInnerHits = innerHits.getAt(0).getInnerHits().get("kings"); + assertThat(innerInnerHits.getTotalHits().value, equalTo(1L)); + assertThat(innerInnerHits.getAt(0).getId(), equalTo("king")); + } + ); } public void testMatchesQueriesParentChildInnerHits() throws Exception { @@ -498,29 +525,34 @@ public void testMatchesQueriesParentChildInnerHits() throws Exception { requests.add(createIndexRequest("index", "child", "5", "2", "field", "value1")); indexRandom(true, requests); - SearchResponse response = prepareSearch("index").setQuery( - hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None).innerHit(new InnerHitBuilder()) - ).addSort("id", SortOrder.ASC).get(); - assertHitCount(response, 2); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); - - assertThat(response.getHits().getAt(1).getId(), equalTo("2")); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + assertResponse( + prepareSearch("index").setQuery( + hasChildQuery("child", matchQuery("field", "value1").queryName("_name1"), ScoreMode.None).innerHit(new InnerHitBuilder()) + ).addSort("id", SortOrder.ASC), + response -> { + assertHitCount(response, 2); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + + assertThat(response.getHits().getAt(1).getId(), equalTo("2")); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(1).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name1")); + } + ); QueryBuilder query = hasChildQuery("child", matchQuery("field", "value2").queryName("_name2"), ScoreMode.None).innerHit( new InnerHitBuilder() ); - response = prepareSearch("index").setQuery(query).addSort("id", SortOrder.ASC).get(); - assertHitCount(response, 1); - assertThat(response.getHits().getAt(0).getId(), equalTo("1")); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); - assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); + assertResponse(prepareSearch("index").setQuery(query).addSort("id", SortOrder.ASC), response -> { + assertHitCount(response, 1); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getTotalHits().value, equalTo(1L)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries().length, equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("child").getAt(0).getMatchedQueries()[0], equalTo("_name2")); + }); } public void testUseMaxDocInsteadOfSize() throws Exception { @@ -539,9 +571,7 @@ public void testUseMaxDocInsteadOfSize() throws Exception { QueryBuilder query = hasChildQuery("child", matchQuery("field", "value1"), ScoreMode.None).innerHit( new InnerHitBuilder().setSize(ArrayUtil.MAX_ARRAY_LENGTH - 1) ); - SearchResponse response = prepareSearch("index1").setQuery(query).get(); - assertNoFailures(response); - assertHitCount(response, 1); + assertHitCountAndNoFailures(prepareSearch("index1").setQuery(query), 1L); } public void testNestedInnerHitWrappedInParentChildInnerhit() { @@ -557,21 +587,31 @@ public void testNestedInnerHitWrappedInParentChildInnerhit() { createIndexRequest("test", "parent_type", "1", null, "key", "value").get(); createIndexRequest("test", "child_type", "2", "1", "nested_type", Collections.singletonMap("key", "value")).get(); refresh(); - SearchResponse response = prepareSearch("test").setQuery( - boolQuery().must(matchQuery("key", "value")) - .should( - hasChildQuery( - "child_type", - nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder()), - ScoreMode.None - ).innerHit(new InnerHitBuilder()) - ) - ).get(); - assertHitCount(response, 1); - SearchHit hit = response.getHits().getAt(0); - String parentId = (String) extractValue("join_field.parent", hit.getInnerHits().get("child_type").getAt(0).getSourceAsMap()); - assertThat(parentId, equalTo("1")); - assertThat(hit.getInnerHits().get("child_type").getAt(0).getInnerHits().get("nested_type").getAt(0).field("_parent"), nullValue()); + assertResponse( + prepareSearch("test").setQuery( + boolQuery().must(matchQuery("key", "value")) + .should( + hasChildQuery( + "child_type", + nestedQuery("nested_type", matchAllQuery(), ScoreMode.None).innerHit(new InnerHitBuilder()), + ScoreMode.None + ).innerHit(new InnerHitBuilder()) + ) + ), + response -> { + assertHitCount(response, 1); + SearchHit hit = response.getHits().getAt(0); + String parentId = (String) extractValue( + "join_field.parent", + hit.getInnerHits().get("child_type").getAt(0).getSourceAsMap() + ); + assertThat(parentId, equalTo("1")); + assertThat( + hit.getInnerHits().get("child_type").getAt(0).getInnerHits().get("nested_type").getAt(0).field("_parent"), + nullValue() + ); + } + ); } public void testInnerHitsWithIgnoreUnmapped() { From 9f0a9bc94907e0c3f1d30b4b1f18cf4996e252f8 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Tue, 24 Oct 2023 17:01:54 +0100 Subject: [PATCH 084/203] Add an extra check to DeprecationHttpIT that the index is actually deleted (#101273) The tests in DeprecationHttpIT are affecting each other - this adds a check the index is actually deleted between each test. This should stop the regular CI failures we see in DeprecationHttpIT. --- .../xpack/deprecation/DeprecationHttpIT.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 9d5ea622568c9..b382d103d6001 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -36,6 +36,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -48,6 +49,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.hasEntry; @@ -108,8 +110,13 @@ private void resetDeprecationIndexAndCache() throws Exception { } catch (Exception e) { throw new AssertionError(e); } - }, 30, TimeUnit.SECONDS); + + assertBusy(() -> { + // wait for the data stream to really be deleted + var response = ESRestTestCase.entityAsMap(client().performRequest(new Request("GET", "/_data_stream"))); + assertThat((Collection) response.get("data_streams"), empty()); + }); } /** From 46f95a67b46036d50bb1a8d76404552ba91f88ac Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Tue, 24 Oct 2023 16:55:17 -0400 Subject: [PATCH 085/203] ESQL: More MV_* tests (#100564) This adds more tests for some of the `MV_` functions and updates their docs now that the railroad diagram and table generated by the tests covers all of the types. --- .../esql/functions/mv_concat.asciidoc | 6 + .../esql/functions/mv_count.asciidoc | 7 +- .../esql/functions/mv_dedupe.asciidoc | 7 + docs/reference/esql/functions/mv_max.asciidoc | 7 + docs/reference/esql/functions/mv_min.asciidoc | 7 + .../esql/functions/signature/mv_concat.svg | 2 +- .../esql/functions/signature/mv_count.svg | 2 +- .../esql/functions/signature/mv_dedupe.svg | 2 +- .../esql/functions/signature/mv_max.svg | 2 +- .../esql/functions/signature/mv_min.svg | 2 +- .../esql/functions/types/mv_concat.asciidoc | 2 +- .../esql/functions/types/mv_count.asciidoc | 6 +- .../esql/functions/types/mv_dedupe.asciidoc | 6 +- .../esql/functions/types/mv_max.asciidoc | 6 +- .../esql/functions/types/mv_min.asciidoc | 6 +- .../src/main/resources/show.csv-spec | 26 +-- .../AbstractMultivalueFunction.java | 10 +- .../function/scalar/multivalue/MvConcat.java | 12 +- .../function/scalar/multivalue/MvCount.java | 18 +- .../function/scalar/multivalue/MvDedupe.java | 11 +- .../function/scalar/multivalue/MvMax.java | 13 +- .../function/scalar/multivalue/MvMin.java | 11 +- .../elasticsearch/xpack/esql/CsvTests.java | 15 +- .../function/AbstractFunctionTestCase.java | 45 +++-- .../expression/function/TestCaseSupplier.java | 4 +- .../AbstractMultivalueFunctionTestCase.java | 169 ++++++++++++------ .../scalar/multivalue/MvAvgTests.java | 2 +- .../scalar/multivalue/MvCountTests.java | 5 +- .../scalar/multivalue/MvDedupeTests.java | 1 + .../scalar/multivalue/MvMaxTests.java | 3 +- .../scalar/multivalue/MvMedianTests.java | 4 +- .../scalar/multivalue/MvMinTests.java | 3 +- 32 files changed, 309 insertions(+), 113 deletions(-) diff --git a/docs/reference/esql/functions/mv_concat.asciidoc b/docs/reference/esql/functions/mv_concat.asciidoc index d4be458455131..a13cef4c1e67a 100644 --- a/docs/reference/esql/functions/mv_concat.asciidoc +++ b/docs/reference/esql/functions/mv_concat.asciidoc @@ -1,6 +1,9 @@ [discrete] [[esql-mv_concat]] === `MV_CONCAT` +[.text-center] +image::esql/functions/signature/mv_concat.svg[Embedded,opts=inline] + Converts a multivalued string field into a single valued field containing the concatenation of all values separated by a delimiter: @@ -24,3 +27,6 @@ include::{esql-specs}/string.csv-spec[tag=mv_concat-to_string] include::{esql-specs}/string.csv-spec[tag=mv_concat-to_string-result] |=== +Supported types: + +include::types/mv_concat.asciidoc[] diff --git a/docs/reference/esql/functions/mv_count.asciidoc b/docs/reference/esql/functions/mv_count.asciidoc index 5bcda53ca5a9b..e6a61cd6e9c63 100644 --- a/docs/reference/esql/functions/mv_count.asciidoc +++ b/docs/reference/esql/functions/mv_count.asciidoc @@ -1,6 +1,9 @@ [discrete] [[esql-mv_count]] === `MV_COUNT` +[.text-center] +image::esql/functions/signature/mv_count.svg[Embedded,opts=inline] + Converts a multivalued field into a single valued field containing a count of the number of values: @@ -13,4 +16,6 @@ include::{esql-specs}/string.csv-spec[tag=mv_count] include::{esql-specs}/string.csv-spec[tag=mv_count-result] |=== -NOTE: This function accepts all types and always returns an `integer`. +Supported types: + +include::types/mv_count.asciidoc[] diff --git a/docs/reference/esql/functions/mv_dedupe.asciidoc b/docs/reference/esql/functions/mv_dedupe.asciidoc index c6af3f2d1aa3f..c85c6ddff4354 100644 --- a/docs/reference/esql/functions/mv_dedupe.asciidoc +++ b/docs/reference/esql/functions/mv_dedupe.asciidoc @@ -1,6 +1,9 @@ [discrete] [[esql-mv_dedupe]] === `MV_DEDUPE` +[.text-center] +image::esql/functions/signature/mv_dedupe.svg[Embedded,opts=inline] + Removes duplicates from a multivalued field. For example: [source.merge.styled,esql] @@ -12,4 +15,8 @@ include::{esql-specs}/string.csv-spec[tag=mv_dedupe] include::{esql-specs}/string.csv-spec[tag=mv_dedupe-result] |=== +Supported types: + +include::types/mv_dedupe.asciidoc[] + NOTE: `MV_DEDUPE` may, but won't always, sort the values in the field. diff --git a/docs/reference/esql/functions/mv_max.asciidoc b/docs/reference/esql/functions/mv_max.asciidoc index e8ef951f168f5..ed433b64a2813 100644 --- a/docs/reference/esql/functions/mv_max.asciidoc +++ b/docs/reference/esql/functions/mv_max.asciidoc @@ -1,6 +1,9 @@ [discrete] [[esql-mv_max]] === `MV_MAX` +[.text-center] +image::esql/functions/signature/mv_max.svg[Embedded,opts=inline] + Converts a multivalued field into a single valued field containing the maximum value. For example: [source.merge.styled,esql] @@ -23,3 +26,7 @@ include::{esql-specs}/string.csv-spec[tag=mv_max] |=== include::{esql-specs}/string.csv-spec[tag=mv_max-result] |=== + +Supported types: + +include::types/mv_max.asciidoc[] diff --git a/docs/reference/esql/functions/mv_min.asciidoc b/docs/reference/esql/functions/mv_min.asciidoc index 235e5c3c2bb5e..b0c8dd51c97fc 100644 --- a/docs/reference/esql/functions/mv_min.asciidoc +++ b/docs/reference/esql/functions/mv_min.asciidoc @@ -1,6 +1,9 @@ [discrete] [[esql-mv_min]] === `MV_MIN` +[.text-center] +image::esql/functions/signature/mv_min.svg[Embedded,opts=inline] + Converts a multivalued field into a single valued field containing the minimum value. For example: [source.merge.styled,esql] @@ -23,3 +26,7 @@ include::{esql-specs}/string.csv-spec[tag=mv_min] |=== include::{esql-specs}/string.csv-spec[tag=mv_min-result] |=== + +Supported types: + +include::types/mv_min.asciidoc[] diff --git a/docs/reference/esql/functions/signature/mv_concat.svg b/docs/reference/esql/functions/signature/mv_concat.svg index d12153de7241c..ec3a3aa4ae750 100644 --- a/docs/reference/esql/functions/signature/mv_concat.svg +++ b/docs/reference/esql/functions/signature/mv_concat.svg @@ -1 +1 @@ -MV_CONCAT(arg1,arg2) \ No newline at end of file +MV_CONCAT(v,delim) \ No newline at end of file diff --git a/docs/reference/esql/functions/signature/mv_count.svg b/docs/reference/esql/functions/signature/mv_count.svg index 23d1f3a9f5bea..48e60f26e394d 100644 --- a/docs/reference/esql/functions/signature/mv_count.svg +++ b/docs/reference/esql/functions/signature/mv_count.svg @@ -1 +1 @@ -MV_COUNT(arg1) \ No newline at end of file +MV_COUNT(v) \ No newline at end of file diff --git a/docs/reference/esql/functions/signature/mv_dedupe.svg b/docs/reference/esql/functions/signature/mv_dedupe.svg index 460dcae11e46c..92be3210ce895 100644 --- a/docs/reference/esql/functions/signature/mv_dedupe.svg +++ b/docs/reference/esql/functions/signature/mv_dedupe.svg @@ -1 +1 @@ -MV_DEDUPE(arg1) \ No newline at end of file +MV_DEDUPE(v) \ No newline at end of file diff --git a/docs/reference/esql/functions/signature/mv_max.svg b/docs/reference/esql/functions/signature/mv_max.svg index aec9dbf82a445..6c64809be0720 100644 --- a/docs/reference/esql/functions/signature/mv_max.svg +++ b/docs/reference/esql/functions/signature/mv_max.svg @@ -1 +1 @@ -MV_MAX(arg1) \ No newline at end of file +MV_MAX(v) \ No newline at end of file diff --git a/docs/reference/esql/functions/signature/mv_min.svg b/docs/reference/esql/functions/signature/mv_min.svg index 386057b5aa287..c6ef5e30c289c 100644 --- a/docs/reference/esql/functions/signature/mv_min.svg +++ b/docs/reference/esql/functions/signature/mv_min.svg @@ -1 +1 @@ -MV_MIN(arg1) \ No newline at end of file +MV_MIN(v) \ No newline at end of file diff --git a/docs/reference/esql/functions/types/mv_concat.asciidoc b/docs/reference/esql/functions/types/mv_concat.asciidoc index 2836799f335e8..e3ea8b0830f47 100644 --- a/docs/reference/esql/functions/types/mv_concat.asciidoc +++ b/docs/reference/esql/functions/types/mv_concat.asciidoc @@ -1,6 +1,6 @@ [%header.monospaced.styled,format=dsv,separator=|] |=== -arg1 | arg2 | result +v | delim | result keyword | keyword | keyword keyword | text | keyword text | keyword | keyword diff --git a/docs/reference/esql/functions/types/mv_count.asciidoc b/docs/reference/esql/functions/types/mv_count.asciidoc index 2fcdfc65fa63b..21794bcb1b959 100644 --- a/docs/reference/esql/functions/types/mv_count.asciidoc +++ b/docs/reference/esql/functions/types/mv_count.asciidoc @@ -1,10 +1,14 @@ [%header.monospaced.styled,format=dsv,separator=|] |=== -arg1 | result +v | result boolean | integer +datetime | integer double | integer integer | integer +ip | integer keyword | integer long | integer +text | integer unsigned_long | integer +version | integer |=== diff --git a/docs/reference/esql/functions/types/mv_dedupe.asciidoc b/docs/reference/esql/functions/types/mv_dedupe.asciidoc index 4e12c68422662..dc1175ccdd951 100644 --- a/docs/reference/esql/functions/types/mv_dedupe.asciidoc +++ b/docs/reference/esql/functions/types/mv_dedupe.asciidoc @@ -1,9 +1,13 @@ [%header.monospaced.styled,format=dsv,separator=|] |=== -arg1 | result +v | result boolean | boolean +datetime | datetime double | double integer | integer +ip | ip keyword | keyword long | long +text | text +version | version |=== diff --git a/docs/reference/esql/functions/types/mv_max.asciidoc b/docs/reference/esql/functions/types/mv_max.asciidoc index 50740a71e4b49..1a9a1bee08388 100644 --- a/docs/reference/esql/functions/types/mv_max.asciidoc +++ b/docs/reference/esql/functions/types/mv_max.asciidoc @@ -1,10 +1,14 @@ [%header.monospaced.styled,format=dsv,separator=|] |=== -arg1 | result +v | result boolean | boolean +datetime | datetime double | double integer | integer +ip | ip keyword | keyword long | long +text | text unsigned_long | unsigned_long +version | version |=== diff --git a/docs/reference/esql/functions/types/mv_min.asciidoc b/docs/reference/esql/functions/types/mv_min.asciidoc index 50740a71e4b49..1a9a1bee08388 100644 --- a/docs/reference/esql/functions/types/mv_min.asciidoc +++ b/docs/reference/esql/functions/types/mv_min.asciidoc @@ -1,10 +1,14 @@ [%header.monospaced.styled,format=dsv,separator=|] |=== -arg1 | result +v | result boolean | boolean +datetime | datetime double | double integer | integer +ip | ip keyword | keyword long | long +text | text unsigned_long | unsigned_long +version | version |=== diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec index 60c5fc94ba0d6..5c045ea7efc48 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec @@ -36,22 +36,22 @@ greatest |"? greatest(first:integer|long|double|boolean|keyword| is_finite |? is_finite(arg1:?) |arg1 |? | "" |? | "" | false | false is_infinite |? is_infinite(arg1:?) |arg1 |? | "" |? | "" | false | false is_nan |? is_nan(arg1:?) |arg1 |? | "" |? | "" | false | false -least |"? least(first:integer|long|double|boolean|keyword|text|ip|version, rest...:integer|long|double|boolean|keyword|text|ip|version)" |[first, rest] |["integer|long|double|boolean|keyword|text|ip|version", "integer|long|double|boolean|keyword|text|ip|version"] |["", ""] |? | "" | [false, false] | true -left |"? left(string:keyword, length:integer)" |[string, length] |["keyword", "integer"] |["", ""] |? | "" | [false, false] | false +least |"? least(first:integer|long|double|boolean|keyword|text|ip|version, rest...:integer|long|double|boolean|keyword|text|ip|version)" |[first, rest] |["integer|long|double|boolean|keyword|text|ip|version", "integer|long|double|boolean|keyword|text|ip|version"] |["", ""] |? | "" | [false, false] | true +left |"? left(string:keyword, length:integer)" |[string, length] |["keyword", "integer"] |["", ""] |? | "" | [false, false] | false length |? length(arg1:?) |arg1 |? | "" |? | "" | false | false -log10 |"? log10(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false +log10 |"? log10(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false ltrim |? ltrim(arg1:?) |arg1 |? | "" |? | "" | false | false max |? max(arg1:?) |arg1 |? | "" |? | "" | false | false median |? median(arg1:?) |arg1 |? | "" |? | "" | false | false median_absolute_deviation|? median_absolute_deviation(arg1:?) |arg1 |? | "" |? | "" | false | false min |? min(arg1:?) |arg1 |? | "" |? | "" | false | false mv_avg |? mv_avg(arg1:?) |arg1 |? | "" |? | "" | false | false -mv_concat |? mv_concat(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false -mv_count |? mv_count(arg1:?) |arg1 |? | "" |? | "" | false | false -mv_dedupe |? mv_dedupe(arg1:?) |arg1 |? | "" |? | "" | false | false -mv_max |? mv_max(arg1:?) |arg1 |? | "" |? | "" | false | false +mv_concat |"keyword mv_concat(v:text|keyword, delim:text|keyword)" |[v, delim] |["text|keyword", "text|keyword"] |["values to join", "delimiter"] |keyword | "Reduce a multivalued string field to a single valued field by concatenating all values." | [false, false] | false +mv_count |"integer mv_count(v:unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long)" |v | "unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long" | "" | integer | "Reduce a multivalued field to a single valued field containing the count of values." | false | false +mv_dedupe |"? mv_dedupe(v:boolean|date|double|ip|text|integer|keyword|version|long)" |v | "boolean|date|double|ip|text|integer|keyword|version|long" | "" |? | "Remove duplicate values from a multivalued field." | false | false +mv_max |"? mv_max(v:unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long)" |v | "unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long" | "" |? | "Reduce a multivalued field to a single valued field containing the maximum value." | false | false mv_median |? mv_median(arg1:?) |arg1 |? | "" |? | "" | false | false -mv_min |? mv_min(arg1:?) |arg1 |? | "" |? | "" | false | false +mv_min |"? mv_min(v:unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long)" |v | "unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long" | "" |? | "Reduce a multivalued field to a single valued field containing the minimum value." | false | false mv_sum |? mv_sum(arg1:?) |arg1 |? | "" |? | "" | false | false now |? now() | null |null | null |? | "" | null | false percentile |? percentile(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false @@ -135,12 +135,12 @@ synopsis:keyword ? median_absolute_deviation(arg1:?) ? min(arg1:?) ? mv_avg(arg1:?) -? mv_concat(arg1:?, arg2:?) -? mv_count(arg1:?) -? mv_dedupe(arg1:?) -? mv_max(arg1:?) +"keyword mv_concat(v:text|keyword, delim:text|keyword)" +"integer mv_count(v:unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long)" +"? mv_dedupe(v:boolean|date|double|ip|text|integer|keyword|version|long)" +"? mv_max(v:unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long)" ? mv_median(arg1:?) -? mv_min(arg1:?) +"? mv_min(v:unsigned_long|date|boolean|double|ip|text|integer|keyword|version|long)" ? mv_sum(arg1:?) ? now() ? percentile(arg1:?, arg2:?) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java index 78344f0ae51b8..ce2582c3d641b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java @@ -78,6 +78,9 @@ protected Block.Ref evalSingleValuedNotNullable(Block.Ref fieldRef) { @Override public final Block.Ref eval(Page page) { Block.Ref ref = field.eval(page); + if (ref.block().areAllValuesNull()) { + return ref; + } if (ref.block().mayHaveMultivaluedFields() == false) { if (ref.block().mayHaveNulls()) { return evalSingleValuedNullable(ref); @@ -118,8 +121,11 @@ protected Block.Ref evalSingleValuedNullable(Block.Ref fieldRef) { @Override public Block.Ref eval(Page page) { - Block.Ref fieldRef = field.eval(page); - return fieldRef.block().mayHaveMultivaluedFields() ? evalNullable(fieldRef) : evalSingleValuedNullable(fieldRef); + Block.Ref ref = field.eval(page); + if (ref.block().areAllValuesNull()) { + return ref; + } + return ref.block().mayHaveMultivaluedFields() ? evalNullable(ref) : evalSingleValuedNullable(ref); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java index 1d9108e4ba096..8ba7db4c6b551 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java @@ -16,6 +16,8 @@ import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.TypeResolutions; import org.elasticsearch.xpack.ql.expression.function.scalar.BinaryScalarFunction; @@ -32,7 +34,15 @@ * Reduce a multivalued string field to a single valued field by concatenating all values. */ public class MvConcat extends BinaryScalarFunction implements EvaluatorMapper { - public MvConcat(Source source, Expression field, Expression delim) { + @FunctionInfo( + returnType = "keyword", + description = "Reduce a multivalued string field to a single valued field by concatenating all values." + ) + public MvConcat( + Source source, + @Param(name = "v", type = { "text", "keyword" }, description = "values to join") Expression field, + @Param(name = "delim", type = { "text", "keyword" }, description = "delimiter") Expression delim + ) { super(source, field, delim); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java index 8520366ed82ce..0fdf1d8d80b77 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java @@ -13,6 +13,8 @@ import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -25,11 +27,21 @@ import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isType; /** - * Reduce a multivalued field to a single valued field containing the minimum value. + * Reduce a multivalued field to a single valued field containing the count of values. */ public class MvCount extends AbstractMultivalueFunction { - public MvCount(Source source, Expression field) { - super(source, field); + @FunctionInfo( + returnType = "integer", + description = "Reduce a multivalued field to a single valued field containing the count of values." + ) + public MvCount( + Source source, + @Param( + name = "v", + type = { "unsigned_long", "date", "boolean", "double", "ip", "text", "integer", "keyword", "version", "long" } + ) Expression v + ) { + super(source, v); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java index cec9da98d96a3..bda8faa62f7af 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java @@ -9,6 +9,8 @@ import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.compute.operator.MultivalueDedupe; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; @@ -23,7 +25,14 @@ * Removes duplicate values from a multivalued field. */ public class MvDedupe extends AbstractMultivalueFunction { - public MvDedupe(Source source, Expression field) { + @FunctionInfo(returnType = "?", description = "Remove duplicate values from a multivalued field.") + public MvDedupe( + Source source, + @Param( + name = "v", + type = { "boolean", "date", "double", "ip", "text", "integer", "keyword", "version", "long" } // TODO add unsigned_long + ) Expression field + ) { super(source, field); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java index 5f527beef4967..3ed9be9667ab9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java @@ -12,6 +12,8 @@ import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +28,15 @@ * Reduce a multivalued field to a single valued field containing the maximum value. */ public class MvMax extends AbstractMultivalueFunction { - public MvMax(Source source, Expression field) { - super(source, field); + @FunctionInfo(returnType = "?", description = "Reduce a multivalued field to a single valued field containing the maximum value.") + public MvMax( + Source source, + @Param( + name = "v", + type = { "unsigned_long", "date", "boolean", "double", "ip", "text", "integer", "keyword", "version", "long" } + ) Expression v + ) { + super(source, v); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java index 2647cbfc2e0c3..3678bf532271e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java @@ -12,6 +12,8 @@ import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,7 +28,14 @@ * Reduce a multivalued field to a single valued field containing the minimum value. */ public class MvMin extends AbstractMultivalueFunction { - public MvMin(Source source, Expression field) { + @FunctionInfo(returnType = "?", description = "Reduce a multivalued field to a single valued field containing the minimum value.") + public MvMin( + Source source, + @Param( + name = "v", + type = { "unsigned_long", "date", "boolean", "double", "ip", "text", "integer", "keyword", "version", "long" } + ) Expression field + ) { super(source, field); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index accd7f222b040..be5f2e8315aa6 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -231,13 +231,16 @@ public boolean logResults() { private void doTest() throws Exception { BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, ByteSizeValue.ofGb(1)).withCircuitBreaking(); var actualResults = executePlan(bigArrays); - var expected = loadCsvSpecValues(testCase.expectedResults); + try { + var expected = loadCsvSpecValues(testCase.expectedResults); - var log = logResults() ? LOGGER : null; - assertResults(expected, actualResults, testCase.ignoreOrder, log); - assertWarnings(actualResults.responseHeaders().getOrDefault("Warning", List.of())); - Releasables.close(() -> Iterators.map(actualResults.pages().iterator(), p -> p::releaseBlocks)); - assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); + var log = logResults() ? LOGGER : null; + assertResults(expected, actualResults, testCase.ignoreOrder, log); + assertWarnings(actualResults.responseHeaders().getOrDefault("Warning", List.of())); + } finally { + Releasables.close(() -> Iterators.map(actualResults.pages().iterator(), p -> p::releaseBlocks)); + assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); + } } protected void assertResults(ExpectedResults expected, ActualResults actual, boolean ignoreOrder, Logger logger) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index a139c21473f47..3adb40152d51c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -61,12 +61,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -107,7 +109,7 @@ public static Literal randomLiteral(DataType type) { case "ip" -> new BytesRef(InetAddressPoint.encode(randomIp(randomBoolean()))); case "time_duration" -> Duration.ofMillis(randomLongBetween(-604800000L, 604800000L)); // plus/minus 7 days case "text" -> new BytesRef(randomAlphaOfLength(50)); - case "version" -> new Version(randomIdentifier()).toBytesRef(); + case "version" -> randomVersion().toBytesRef(); case "null" -> null; default -> throw new IllegalArgumentException("can't make random values for [" + type.typeName() + "]"); }, type); @@ -508,7 +510,7 @@ public static void testFunctionInfo() { } for (int i = 0; i < args.size(); i++) { - Set annotationTypes = Arrays.stream(args.get(i).type()).collect(Collectors.toSet()); + Set annotationTypes = Arrays.stream(args.get(i).type()).collect(Collectors.toCollection(() -> new TreeSet<>())); if (annotationTypes.equals(Set.of("?"))) { continue; // TODO remove this eventually, so that all the functions will have to provide signature info } @@ -516,9 +518,9 @@ public static void testFunctionInfo() { assertEquals(annotationTypes, signatureTypes); } - Set returnTypes = Arrays.stream(description.returnType()).collect(Collectors.toSet()); - if (returnTypes.equals(Set.of("?")) == false) { // TODO remove this eventually, so that all the functions will have to provide - // singature info + Set returnTypes = Arrays.stream(description.returnType()).collect(Collectors.toCollection(() -> new TreeSet<>())); + if (returnTypes.equals(Set.of("?")) == false) { + // TODO remove this eventually, so that all the functions will have to provide signature info assertEquals(returnTypes, returnFromSignature); } } @@ -536,11 +538,7 @@ public static void testFunctionInfo() { * on input types like {@link Greatest} or {@link Coalesce}. */ protected static List anyNullIsNull(boolean entirelyNullPreservesType, List testCaseSuppliers) { - for (TestCaseSupplier s : testCaseSuppliers) { - if (s.types() == null) { - throw new IllegalArgumentException("types required"); - } - } + typesRequired(testCaseSuppliers); List suppliers = new ArrayList<>(testCaseSuppliers.size()); suppliers.addAll(testCaseSuppliers); @@ -615,11 +613,7 @@ protected static List anyNullIsNull(boolean entirelyNullPreser * that they throw type errors. */ protected static List errorsForCasesWithoutExamples(List testCaseSuppliers) { - for (TestCaseSupplier s : testCaseSuppliers) { - if (s.types() == null) { - throw new IllegalArgumentException("types required"); - } - } + typesRequired(testCaseSuppliers); List suppliers = new ArrayList<>(testCaseSuppliers.size()); suppliers.addAll(testCaseSuppliers); @@ -644,6 +638,13 @@ protected static List errorsForCasesWithoutExamples(List suppliers) { + String bad = suppliers.stream().filter(s -> s.types() == null).map(s -> s.name()).collect(Collectors.joining("\n")); + if (bad.equals("") == false) { + throw new IllegalArgumentException("types required but not found for these tests:\n" + bad); + } + } + private static List> validPerPosition(Set> valid) { int max = valid.stream().mapToInt(List::size).max().getAsInt(); List> result = new ArrayList<>(max); @@ -735,7 +736,9 @@ private static String expectedType(Set validTypes) { * don't have a test case covering explicit `null` arguments in * this position. Generally you can get that with anyNullIsNull. */ - throw new UnsupportedOperationException("can't guess expected types for " + validTypes); + throw new UnsupportedOperationException( + "can't guess expected types for " + validTypes.stream().sorted(Comparator.comparing(t -> t.typeName())).toList() + ); } return named; } @@ -883,4 +886,14 @@ public void allMemoryReleased() { assertThat(breaker.getUsed(), equalTo(0L)); } } + + static Version randomVersion() { + // TODO degenerate versions and stuff + return switch (between(0, 2)) { + case 0 -> new Version(Integer.toString(between(0, 100))); + case 1 -> new Version(between(0, 100) + "." + between(0, 100)); + case 2 -> new Version(between(0, 100) + "." + between(0, 100) + "." + between(0, 100)); + default -> throw new IllegalArgumentException(); + }; + } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java index 8ca09494fb06c..e49776db1edea 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java @@ -70,7 +70,9 @@ public TestCase get() { if (types != null) { for (int i = 0; i < types.size(); i++) { if (supplied.getData().get(i).type() != types.get(i)) { - throw new IllegalStateException("supplier/data type mismatch " + supplied.getData().get(i).type() + "/" + types.get(i)); + throw new IllegalStateException( + name + ": supplier/data type mismatch " + supplied.getData().get(i).type() + "/" + types.get(i) + ); } } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java index 2519b41fdac2e..a87cc379e8c3f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunctionTestCase.java @@ -25,6 +25,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Function; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; @@ -32,7 +33,7 @@ public abstract class AbstractMultivalueFunctionTestCase extends AbstractScalarFunctionTestCase { /** - * Build a test case with {@code boolean} values. + * Build many test cases with {@code boolean} values. */ protected static void booleans( List cases, @@ -44,7 +45,7 @@ protected static void booleans( } /** - * Build a test case with {@code boolean} values. + * Build many test cases with {@code boolean} values. */ protected static void booleans( List cases, @@ -56,6 +57,7 @@ protected static void booleans( cases.add( new TestCaseSupplier( name + "(false)", + List.of(DataTypes.BOOLEAN), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(false), DataTypes.BOOLEAN, "field")), evaluatorName + "[field=Attribute[channel=0]]", @@ -67,6 +69,7 @@ protected static void booleans( cases.add( new TestCaseSupplier( name + "(true)", + List.of(DataTypes.BOOLEAN), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(true), DataTypes.BOOLEAN, "field")), evaluatorName + "[field=Attribute[channel=0]]", @@ -76,7 +79,7 @@ protected static void booleans( ) ); for (Block.MvOrdering ordering : Block.MvOrdering.values()) { - cases.add(new TestCaseSupplier(name + "() " + ordering, () -> { + cases.add(new TestCaseSupplier(name + "() " + ordering, List.of(DataTypes.BOOLEAN), () -> { List mvData = randomList(2, 100, ESTestCase::randomBoolean); putInOrder(mvData, ordering); return new TestCaseSupplier.TestCase( @@ -90,7 +93,7 @@ protected static void booleans( } /** - * Build a test case with {@link BytesRef} values. + * Build many test cases with {@link BytesRef} values. */ protected static void bytesRefs( List cases, @@ -98,55 +101,60 @@ protected static void bytesRefs( String evaluatorName, BiFunction, Matcher> matcher ) { - bytesRefs(cases, name, evaluatorName, DataTypes.KEYWORD, matcher); + bytesRefs(cases, name, evaluatorName, t -> t, matcher); } /** - * Build a test case with {@link BytesRef} values. + * Build many test cases with {@link BytesRef} values. */ protected static void bytesRefs( List cases, String name, String evaluatorName, - DataType expectedDataType, + Function expectedDataType, BiFunction, Matcher> matcher ) { - cases.add( - new TestCaseSupplier( - name + "(empty string)", - () -> new TestCaseSupplier.TestCase( - List.of(new TestCaseSupplier.TypedData(List.of(new BytesRef("")), DataTypes.KEYWORD, "field")), - evaluatorName + "[field=Attribute[channel=0]]", - expectedDataType, - matcher.apply(1, Stream.of(new BytesRef(""))) - ) - ) - ); - cases.add(new TestCaseSupplier(name + "(BytesRef)", () -> { - BytesRef data = new BytesRef(randomAlphaOfLength(10)); - return new TestCaseSupplier.TestCase( - List.of(new TestCaseSupplier.TypedData(List.of(data), DataTypes.KEYWORD, "field")), - evaluatorName + "[field=Attribute[channel=0]]", - expectedDataType, - matcher.apply(1, Stream.of(data)) - ); - })); - for (Block.MvOrdering ordering : Block.MvOrdering.values()) { - cases.add(new TestCaseSupplier(name + "() " + ordering, () -> { - List mvData = randomList(1, 100, () -> new BytesRef(randomAlphaOfLength(10))); - putInOrder(mvData, ordering); + for (DataType type : new DataType[] { DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.IP, DataTypes.VERSION }) { + if (type != DataTypes.IP) { + cases.add( + new TestCaseSupplier( + name + "(empty " + type.typeName() + ")", + List.of(type), + () -> new TestCaseSupplier.TestCase( + List.of(new TestCaseSupplier.TypedData(List.of(new BytesRef("")), type, "field")), + evaluatorName + "[field=Attribute[channel=0]]", + expectedDataType.apply(type), + matcher.apply(1, Stream.of(new BytesRef(""))) + ) + ) + ); + } + cases.add(new TestCaseSupplier(name + "(" + type.typeName() + ")", List.of(type), () -> { + BytesRef data = (BytesRef) randomLiteral(type).value(); return new TestCaseSupplier.TestCase( - List.of(new TestCaseSupplier.TypedData(mvData, DataTypes.KEYWORD, "field")), + List.of(new TestCaseSupplier.TypedData(List.of(data), type, "field")), evaluatorName + "[field=Attribute[channel=0]]", - expectedDataType, - matcher.apply(mvData.size(), mvData.stream()) + expectedDataType.apply(type), + matcher.apply(1, Stream.of(data)) ); })); + for (Block.MvOrdering ordering : Block.MvOrdering.values()) { + cases.add(new TestCaseSupplier(name + "(<" + type.typeName() + "s>) " + ordering, List.of(type), () -> { + List mvData = randomList(1, 100, () -> (BytesRef) randomLiteral(type).value()); + putInOrder(mvData, ordering); + return new TestCaseSupplier.TestCase( + List.of(new TestCaseSupplier.TypedData(mvData, type, "field")), + evaluatorName + "[field=Attribute[channel=0]]", + expectedDataType.apply(type), + matcher.apply(mvData.size(), mvData.stream()) + ); + })); + } } } /** - * Build a test case with {@code double} values. + * Build many test cases with {@code double} values. */ protected static void doubles( List cases, @@ -158,7 +166,7 @@ protected static void doubles( } /** - * Build a test case with {@code double} values. + * Build many test cases with {@code double} values. */ protected static void doubles( List cases, @@ -170,6 +178,7 @@ protected static void doubles( cases.add( new TestCaseSupplier( name + "(0.0)", + List.of(DataTypes.DOUBLE), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(0.0), DataTypes.DOUBLE, "field")), evaluatorName + "[field=Attribute[channel=0]]", @@ -178,7 +187,7 @@ protected static void doubles( ) ) ); - cases.add(new TestCaseSupplier(name + "(double)", () -> { + cases.add(new TestCaseSupplier(name + "(double)", List.of(DataTypes.DOUBLE), () -> { double mvData = randomDouble(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(mvData), DataTypes.DOUBLE, "field")), @@ -188,7 +197,7 @@ protected static void doubles( ); })); for (Block.MvOrdering ordering : Block.MvOrdering.values()) { - cases.add(new TestCaseSupplier(name + "() " + ordering, () -> { + cases.add(new TestCaseSupplier(name + "() " + ordering, List.of(DataTypes.DOUBLE), () -> { List mvData = randomList(1, 100, ESTestCase::randomDouble); putInOrder(mvData, ordering); return new TestCaseSupplier.TestCase( @@ -202,7 +211,7 @@ protected static void doubles( } /** - * Build a test case with {@code int} values. + * Build many test cases with {@code int} values. */ protected static void ints( List cases, @@ -214,7 +223,7 @@ protected static void ints( } /** - * Build a test case with {@code int} values. + * Build many test cases with {@code int} values. */ protected static void ints( List cases, @@ -226,6 +235,7 @@ protected static void ints( cases.add( new TestCaseSupplier( name + "(0)", + List.of(DataTypes.INTEGER), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(0), DataTypes.INTEGER, "field")), evaluatorName + "[field=Attribute[channel=0]]", @@ -234,7 +244,7 @@ protected static void ints( ) ) ); - cases.add(new TestCaseSupplier(name + "(int)", () -> { + cases.add(new TestCaseSupplier(name + "(int)", List.of(DataTypes.INTEGER), () -> { int data = randomInt(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(data), DataTypes.INTEGER, "field")), @@ -244,7 +254,7 @@ protected static void ints( ); })); for (Block.MvOrdering ordering : Block.MvOrdering.values()) { - cases.add(new TestCaseSupplier(name + "() " + ordering, () -> { + cases.add(new TestCaseSupplier(name + "() " + ordering, List.of(DataTypes.INTEGER), () -> { List mvData = randomList(1, 100, ESTestCase::randomInt); putInOrder(mvData, ordering); return new TestCaseSupplier.TestCase( @@ -258,7 +268,7 @@ protected static void ints( } /** - * Build a test case with {@code long} values. + * Build many test cases with {@code long} values. */ protected static void longs( List cases, @@ -270,7 +280,7 @@ protected static void longs( } /** - * Build a test case with {@code long} values. + * Build many test cases with {@code long} values. */ protected static void longs( List cases, @@ -282,6 +292,7 @@ protected static void longs( cases.add( new TestCaseSupplier( name + "(0L)", + List.of(DataTypes.LONG), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(0L), DataTypes.LONG, "field")), evaluatorName + "[field=Attribute[channel=0]]", @@ -290,7 +301,7 @@ protected static void longs( ) ) ); - cases.add(new TestCaseSupplier(name + "(long)", () -> { + cases.add(new TestCaseSupplier(name + "(long)", List.of(DataTypes.LONG), () -> { long data = randomLong(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(data), DataTypes.LONG, "field")), @@ -300,7 +311,7 @@ protected static void longs( ); })); for (Block.MvOrdering ordering : Block.MvOrdering.values()) { - cases.add(new TestCaseSupplier(name + "() " + ordering, () -> { + cases.add(new TestCaseSupplier(name + "() " + ordering, List.of(DataTypes.LONG), () -> { List mvData = randomList(1, 100, ESTestCase::randomLong); putInOrder(mvData, ordering); return new TestCaseSupplier.TestCase( @@ -314,7 +325,64 @@ protected static void longs( } /** - * Build a test case with unsigned {@code long} values. + * Build many test cases with {@code date} values. + */ + protected static void dateTimes( + List cases, + String name, + String evaluatorName, + BiFunction> matcher + ) { + dateTimes(cases, name, evaluatorName, DataTypes.DATETIME, matcher); + } + + /** + * Build many test cases with {@code date} values. + */ + protected static void dateTimes( + List cases, + String name, + String evaluatorName, + DataType expectedDataType, + BiFunction> matcher + ) { + cases.add( + new TestCaseSupplier( + name + "(epoch)", + List.of(DataTypes.DATETIME), + () -> new TestCaseSupplier.TestCase( + List.of(new TestCaseSupplier.TypedData(List.of(0L), DataTypes.DATETIME, "field")), + evaluatorName + "[field=Attribute[channel=0]]", + expectedDataType, + matcher.apply(1, LongStream.of(0L)) + ) + ) + ); + cases.add(new TestCaseSupplier(name + "(date)", List.of(DataTypes.DATETIME), () -> { + long data = randomLong(); + return new TestCaseSupplier.TestCase( + List.of(new TestCaseSupplier.TypedData(List.of(data), DataTypes.DATETIME, "field")), + evaluatorName + "[field=Attribute[channel=0]]", + expectedDataType, + matcher.apply(1, LongStream.of(data)) + ); + })); + for (Block.MvOrdering ordering : Block.MvOrdering.values()) { + cases.add(new TestCaseSupplier(name + "() " + ordering, List.of(DataTypes.DATETIME), () -> { + List mvData = randomList(1, 100, ESTestCase::randomLong); + putInOrder(mvData, ordering); + return new TestCaseSupplier.TestCase( + List.of(new TestCaseSupplier.TypedData(mvData, DataTypes.DATETIME, "field")), + evaluatorName + "[field=Attribute[channel=0]]", + expectedDataType, + matcher.apply(mvData.size(), mvData.stream().mapToLong(Long::longValue)) + ); + })); + } + } + + /** + * Build many test cases with unsigned {@code long} values. */ protected static void unsignedLongs( List cases, @@ -326,7 +394,7 @@ protected static void unsignedLongs( } /** - * Build a test case with unsigned {@code long} values. + * Build many test cases with unsigned {@code long} values. */ protected static void unsignedLongs( List cases, @@ -338,6 +406,7 @@ protected static void unsignedLongs( cases.add( new TestCaseSupplier( name + "(0UL)", + List.of(DataTypes.UNSIGNED_LONG), () -> new TestCaseSupplier.TestCase( List.of( new TestCaseSupplier.TypedData( @@ -352,7 +421,7 @@ protected static void unsignedLongs( ) ) ); - cases.add(new TestCaseSupplier(name + "(unsigned long)", () -> { + cases.add(new TestCaseSupplier(name + "(unsigned long)", List.of(DataTypes.UNSIGNED_LONG), () -> { long data = randomLong(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(data), DataTypes.UNSIGNED_LONG, "field")), @@ -362,7 +431,7 @@ protected static void unsignedLongs( ); })); for (Block.MvOrdering ordering : Block.MvOrdering.values()) { - cases.add(new TestCaseSupplier(name + "() " + ordering, () -> { + cases.add(new TestCaseSupplier(name + "() " + ordering, List.of(DataTypes.UNSIGNED_LONG), () -> { List mvData = randomList(1, 100, ESTestCase::randomLong); putInOrder(mvData, ordering); return new TestCaseSupplier.TestCase( diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgTests.java index b0e459164de71..b1070cb7eb12b 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgTests.java @@ -55,7 +55,7 @@ public static Iterable parameters() { */ (size, data) -> avg.apply(size, data.mapToDouble(v -> NumericUtils.unsignedLongToDouble(NumericUtils.asLongUnsigned(v)))) ); - return parameterSuppliersFromTypedData(cases); + return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, cases))); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCountTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCountTests.java index a13a43bdee75c..deffc42244c10 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCountTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCountTests.java @@ -31,12 +31,13 @@ public MvCountTests(@Name("TestCase") Supplier testCa public static Iterable parameters() { List cases = new ArrayList<>(); booleans(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); - bytesRefs(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); + bytesRefs(cases, "mv_count", "MvCount", t -> DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); doubles(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); ints(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); longs(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); unsignedLongs(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); - return parameterSuppliersFromTypedData(cases); + dateTimes(cases, "mv_count", "MvCount", DataTypes.INTEGER, (size, values) -> equalTo(Math.toIntExact(values.count()))); + return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, cases))); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupeTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupeTests.java index 713ae263705d3..375a7a769ccfd 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupeTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupeTests.java @@ -38,6 +38,7 @@ public static Iterable parameters() { List cases = new ArrayList<>(); booleans(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values)); bytesRefs(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values)); + dateTimes(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Long::valueOf))); doubles(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Double::valueOf))); ints(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Integer::valueOf))); longs(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Long::valueOf))); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java index 556cedf259a86..8f7292adb86a4 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java @@ -43,7 +43,8 @@ public static Iterable parameters() { "MvMax", (size, values) -> equalTo(NumericUtils.asLongUnsigned(values.reduce(BigInteger::max).get())) ); - return parameterSuppliersFromTypedData(cases); + dateTimes(cases, "mv_max", "MvMax", (size, values) -> equalTo(values.max().getAsLong())); + return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(false, cases))); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java index 047dc4fe64641..ce83c4bb8f786 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java @@ -73,6 +73,7 @@ public static Iterable parameters() { cases.add( new TestCaseSupplier( "mv_median(<1, 2>)", + List.of(DataTypes.INTEGER), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(1, 2), DataTypes.INTEGER, "field")), "MvMedian[field=Attribute[channel=0]]", @@ -84,6 +85,7 @@ public static Iterable parameters() { cases.add( new TestCaseSupplier( "mv_median(<-1, -2>)", + List.of(DataTypes.INTEGER), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(List.of(-1, -2), DataTypes.INTEGER, "field")), "MvMedian[field=Attribute[channel=0]]", @@ -92,7 +94,7 @@ public static Iterable parameters() { ) ) ); - return parameterSuppliersFromTypedData(cases); + return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(false, cases))); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java index c1dd713e6639c..750d5d4cb89ce 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java @@ -43,7 +43,8 @@ public static Iterable parameters() { "MvMin", (size, values) -> equalTo(NumericUtils.asLongUnsigned(values.reduce(BigInteger::min).get())) ); - return parameterSuppliersFromTypedData(cases); + dateTimes(cases, "mv_min", "MvMin", (size, values) -> equalTo(values.min().getAsLong())); + return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(false, cases))); } @Override From 5446d88e3b0bb346fc7b9a9f9189bada27d94d00 Mon Sep 17 00:00:00 2001 From: Mark Vieira Date: Tue, 24 Oct 2023 14:08:21 -0700 Subject: [PATCH 086/203] Include branch information in build scans for buildkite jobs (#101284) --- .../src/main/groovy/elasticsearch.build-scan.gradle | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/build-tools-internal/src/main/groovy/elasticsearch.build-scan.gradle b/build-tools-internal/src/main/groovy/elasticsearch.build-scan.gradle index d6984e40a5ea1..e6bbaeb19e495 100644 --- a/build-tools-internal/src/main/groovy/elasticsearch.build-scan.gradle +++ b/build-tools-internal/src/main/groovy/elasticsearch.build-scan.gradle @@ -95,10 +95,10 @@ buildScan { // Disable async upload in CI to ensure scan upload completes before CI agent is terminated uploadInBackground = false - def branch = System.getenv('BUILDKITE_BRANCH') + def branch = System.getenv('BUILDKITE_PULL_REQUEST_BASE_BRANCH') ?: System.getenv('BUILDKITE_BRANCH') def repoMatcher = System.getenv('BUILDKITE_REPO') =~ /(https:\/\/github\.com\/|git@github\.com:)(\S+)\.git/ def repository = repoMatcher.matches() ? repoMatcher.group(2) : "" - def jobName = (System.getenv('BUILDKITE_LABEL') ?: '').replaceAll(/[^a-zA-Z0-9_\-]+/, '_').toLowerCase() + def jobName = (System.getenv('BUILDKITE_LABEL') ?: '').replaceAll(/[^a-zA-Z0-9_\-]+/, ' ').trim().replaceAll(' ', '_').toLowerCase() tag 'CI' link 'CI Build', buildKiteUrl @@ -110,6 +110,11 @@ buildScan { value 'Job Name', jobName tag jobName + if (branch) { + tag branch + value 'Git Branch', branch + } + // Add SCM information def prId = System.getenv('BUILDKITE_PULL_REQUEST') if (prId != 'false') { @@ -122,7 +127,6 @@ buildScan { } else { value 'Git Commit ID', BuildParams.gitRevision link 'Source', "https://github.com/${repository}/tree/${BuildParams.gitRevision}" - tag branch } buildScanPublished { scan -> From 66f7298e83f96d3ecb39e30cd3f967803fd64915 Mon Sep 17 00:00:00 2001 From: Volodymyr Krasnikov <129072588+volodk85@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:17:34 -0700 Subject: [PATCH 087/203] Update doc as per SDH finding (#101285) --- .../bi-directional-disaster-recovery.asciidoc | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/reference/ccr/bi-directional-disaster-recovery.asciidoc b/docs/reference/ccr/bi-directional-disaster-recovery.asciidoc index 614af8846230e..b491e90053031 100644 --- a/docs/reference/ccr/bi-directional-disaster-recovery.asciidoc +++ b/docs/reference/ccr/bi-directional-disaster-recovery.asciidoc @@ -10,7 +10,7 @@ ---- PUT _data_stream/logs-generic-default ---- -// TESTSETUP +// TESTSETUP [source,console] ---- @@ -20,12 +20,12 @@ DELETE /_data_stream/* //// Learn how to set up disaster recovery between two clusters based on -bi-directional {ccr}. The following tutorial is designed for data streams which support -<> and <>. You can only perform these actions on the leader index. +bi-directional {ccr}. The following tutorial is designed for data streams which support +<> and <>. You can only perform these actions on the leader index. -This tutorial works with {ls} as the source of ingestion. It takes advantage of a {ls} feature where {logstash-ref}/plugins-outputs-elasticsearch.html[the {ls} output to {es}] can be load balanced across an array of hosts specified. {beats} and {agents} currently do not -support multiple outputs. It should also be possible to set up a proxy -(load balancer) to redirect traffic without {ls} in this tutorial. +This tutorial works with {ls} as the source of ingestion. It takes advantage of a {ls} feature where {logstash-ref}/plugins-outputs-elasticsearch.html[the {ls} output to {es}] can be load balanced across an array of hosts specified. {beats} and {agents} currently do not +support multiple outputs. It should also be possible to set up a proxy +(load balancer) to redirect traffic without {ls} in this tutorial. * Setting up a remote cluster on `clusterA` and `clusterB`. * Setting up bi-directional cross-cluster replication with exclusion patterns. @@ -92,7 +92,7 @@ PUT /_ccr/auto_follow/logs-generic-default "leader_index_patterns": [ ".ds-logs-generic-default-20*" ], - "leader_index_exclusion_patterns":"{{leader_index}}-replicated_from_clustera", + "leader_index_exclusion_patterns":"*-replicated_from_clustera", "follow_index_pattern": "{{leader_index}}-replicated_from_clusterb" } @@ -103,7 +103,7 @@ PUT /_ccr/auto_follow/logs-generic-default "leader_index_patterns": [ ".ds-logs-generic-default-20*" ], - "leader_index_exclusion_patterns":"{{leader_index}}-replicated_from_clusterb", + "leader_index_exclusion_patterns":"*-replicated_from_clusterb", "follow_index_pattern": "{{leader_index}}-replicated_from_clustera" } ---- @@ -126,7 +126,7 @@ pattern in the UI. Use the API in this step. + This example uses the input generator to demonstrate the document count in the clusters. Reconfigure this section -to suit your own use case. +to suit your own use case. + [source,logstash] ---- @@ -171,7 +171,7 @@ Bi-directional {ccr} will create one more data stream on each of the clusters with the `-replication_from_cluster{a|b}` suffix. At the end of this step: + * data streams on cluster A contain: -** 50 documents in `logs-generic-default-replicated_from_clusterb` +** 50 documents in `logs-generic-default-replicated_from_clusterb` ** 50 documents in `logs-generic-default` * data streams on cluster B contain: ** 50 documents in `logs-generic-default-replicated_from_clustera` @@ -179,7 +179,7 @@ with the `-replication_from_cluster{a|b}` suffix. At the end of this step: . Queries should be set up to search across both data streams. A query on `logs*`, on either of the clusters, returns 100 -hits in total. +hits in total. + [source,console] ---- @@ -199,27 +199,27 @@ use cases where {ls} ingests continuously.) bin/logstash -f multiple_hosts.conf ---- -. Observe all {ls} traffic will be redirected to `cluster B` automatically. +. Observe all {ls} traffic will be redirected to `cluster B` automatically. + -TIP: You should also redirect all search traffic to the `clusterB` cluster during this time. +TIP: You should also redirect all search traffic to the `clusterB` cluster during this time. -. The two data streams on `cluster B` now contain a different number of documents. +. The two data streams on `cluster B` now contain a different number of documents. + -* data streams on cluster A (down) -** 50 documents in `logs-generic-default-replicated_from_clusterb` +* data streams on cluster A (down) +** 50 documents in `logs-generic-default-replicated_from_clusterb` ** 50 documents in `logs-generic-default` -* data streams On cluster B (up) +* data streams On cluster B (up) ** 50 documents in `logs-generic-default-replicated_from_clustera` ** 150 documents in `logs-generic-default` ==== Failback when `clusterA` comes back -. You can simulate this by turning `cluster A` back on. +. You can simulate this by turning `cluster A` back on. . Data ingested to `cluster B` during `cluster A` 's downtime will be -automatically replicated. +automatically replicated. + * data streams on cluster A -** 150 documents in `logs-generic-default-replicated_from_clusterb` +** 150 documents in `logs-generic-default-replicated_from_clusterb` ** 50 documents in `logs-generic-default` * data streams on cluster B ** 50 documents in `logs-generic-default-replicated_from_clustera` @@ -271,5 +271,5 @@ POST logs-generic-default/_update_by_query } } ---- -+ ++ TIP: If a soft delete is merged away before it can be replicated to a follower the following process will fail due to incomplete history on the leader, see <> for more details. From 311185311f8316808f56b70abe34f6277fe8ec5a Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 25 Oct 2023 08:21:18 +0200 Subject: [PATCH 088/203] Remove index.codec setting from setting up tsdb docs. (#101276) This is not needed for tsdb, because of synthetic source and slows down indexing / refreshes. --- docs/reference/data-streams/set-up-tsds.asciidoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/reference/data-streams/set-up-tsds.asciidoc b/docs/reference/data-streams/set-up-tsds.asciidoc index a98e3c7302424..c175da2e991e9 100644 --- a/docs/reference/data-streams/set-up-tsds.asciidoc +++ b/docs/reference/data-streams/set-up-tsds.asciidoc @@ -192,8 +192,7 @@ PUT _component_template/my-weather-sensor-settings "template": { "settings": { "index.lifecycle.name": "my-lifecycle-policy", - "index.look_ahead_time": "3h", - "index.codec": "best_compression" + "index.look_ahead_time": "3h" } }, "_meta": { From 126412c8bf4214176bd2aa0f89513ef3e32f145d Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Wed, 25 Oct 2023 08:35:02 +0200 Subject: [PATCH 089/203] Don't generate bounding box touching tiles in GeoTileGridAggregatorTests (#101137) --- .../geogrid/GeoHashGridAggregatorTests.java | 2 +- .../geogrid/GeoTileGridAggregatorTests.java | 59 +++++++++++++------ .../geogrid/GeoGridAggregatorTestCase.java | 28 +++++---- .../bucket/geogrid/GeoHexAggregatorTests.java | 2 +- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java index daf97a2a49e23..7f6bedcea5277 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java @@ -30,7 +30,7 @@ protected Point randomPoint() { } @Override - protected GeoBoundingBox randomBBox() { + protected GeoBoundingBox randomBBox(int precision) { Rectangle rectangle = GeometryTestUtils.randomRectangle(); return new GeoBoundingBox( new GeoPoint(rectangle.getMaxLat(), rectangle.getMinLon()), diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java index e92a268c49efe..2cf729992cfb4 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java @@ -8,8 +8,6 @@ package org.elasticsearch.search.aggregations.bucket.geogrid; -import org.apache.lucene.geo.GeoEncodingUtils; -import org.apache.lucene.tests.util.LuceneTestCase; import org.elasticsearch.common.geo.GeoBoundingBox; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoUtils; @@ -17,7 +15,11 @@ import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Rectangle; -@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101077") +import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude; +import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude; +import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude; +import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude; + public class GeoTileGridAggregatorTests extends GeoGridAggregatorTestCase { @Override @@ -39,7 +41,7 @@ protected Point randomPoint() { } @Override - protected GeoBoundingBox randomBBox() { + protected GeoBoundingBox randomBBox(int precision) { GeoBoundingBox bbox = randomValueOtherThanMany( (b) -> b.top() > GeoTileUtils.LATITUDE_MASK || b.bottom() < -GeoTileUtils.LATITUDE_MASK, () -> { @@ -50,11 +52,42 @@ protected GeoBoundingBox randomBBox() { ); } ); - // Avoid numerical errors for sub-atomic values - double left = GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(bbox.left())); - double right = GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(bbox.right())); - double top = GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(bbox.top())); - double bottom = GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(bbox.bottom())); + final int tiles = 1 << precision; + + // Due to the way GeoTileBoundedPredicate works that adjust the given bounding box when it is touching the tiles, we need to + // adjust here in order not to generate bounding boxes touching tiles or the test will fail + + // compute tile at the top left + final Rectangle minTile = GeoTileUtils.toBoundingBox( + GeoTileUtils.getXTile(bbox.left(), tiles), + GeoTileUtils.getYTile(bbox.top(), tiles), + precision + ); + // adjust if it is touching the tile + final int encodedLeft = encodeLongitude(bbox.left()); + final double left = encodeLongitude(minTile.getMaxX()) == encodedLeft + ? decodeLongitude(encodedLeft + 1) + : decodeLongitude(encodedLeft); + final int encodedTop = encodeLatitude(bbox.top()); + final double bottom = encodeLatitude(minTile.getMinY()) == encodedTop + ? decodeLongitude(encodedTop + 1) + : decodeLatitude(encodedTop); + // compute tile at the bottom right + final Rectangle maxTile = GeoTileUtils.toBoundingBox( + GeoTileUtils.getXTile(bbox.right(), tiles), + GeoTileUtils.getYTile(bbox.bottom(), tiles), + precision + ); + // adjust if it is touching the tile + final int encodedRight = encodeLongitude(bbox.right()); + final double right = encodeLongitude(maxTile.getMinX()) == encodedRight + ? decodeLongitude(encodedRight) + : decodeLongitude(encodedRight + 1); + final int encodedBottom = encodeLatitude(bbox.bottom()); + final double top = encodeLatitude(maxTile.getMaxY()) == encodedBottom + ? decodeLatitude(encodedBottom) + : decodeLatitude(encodedBottom + 1); + bbox.topLeft().reset(top, left); bbox.bottomRight().reset(bottom, right); return bbox; @@ -62,14 +95,6 @@ protected GeoBoundingBox randomBBox() { @Override protected Rectangle getTile(double lng, double lat, int precision) { - int tiles = 1 << precision; - int x = GeoTileUtils.getXTile(lng, tiles); - int y = GeoTileUtils.getYTile(lat, tiles); - Rectangle r1 = GeoTileUtils.toBoundingBox(x, y, precision); - Rectangle r2 = GeoTileUtils.toBoundingBox(GeoTileUtils.longEncode(lng, lat, precision)); - if (r1.equals(r2) == false) { - int a = 0; - } return GeoTileUtils.toBoundingBox(GeoTileUtils.longEncode(lng, lat, precision)); } diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java index d3012c4779024..1bdc39fdc8e5f 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java @@ -74,7 +74,7 @@ public abstract class GeoGridAggregatorTestCase /** * Return a random {@link GeoBoundingBox} within the bounds of the tile grid. */ - protected abstract GeoBoundingBox randomBBox(); + protected abstract GeoBoundingBox randomBBox(int precision); /** * Return the bounding tile as a {@link Rectangle} for a given point @@ -131,23 +131,24 @@ public void testUnmappedMissing() throws IOException { } public void testSingletonDocs() throws IOException { - testWithSeveralDocs(() -> true, null); + testWithSeveralDocs(() -> true, null, randomPrecision()); } public void testBoundedSingletonDocs() throws IOException { - testWithSeveralDocs(() -> true, randomBBox()); + int precision = randomPrecision(); + testWithSeveralDocs(() -> true, randomBBox(precision), precision); } public void testMultiValuedDocs() throws IOException { - testWithSeveralDocs(LuceneTestCase::rarely, null); + testWithSeveralDocs(LuceneTestCase::rarely, null, randomPrecision()); } public void testBoundedMultiValuedDocs() throws IOException { - testWithSeveralDocs(LuceneTestCase::rarely, randomBBox()); + int precision = randomPrecision(); + testWithSeveralDocs(LuceneTestCase::rarely, randomBBox(precision), precision); } - private void testWithSeveralDocs(BooleanSupplier supplier, GeoBoundingBox bbox) throws IOException { - int precision = randomPrecision(); + private void testWithSeveralDocs(BooleanSupplier supplier, GeoBoundingBox bbox, int precision) throws IOException { int numPoints = randomIntBetween(8, 128); Map expectedCountPerGeoHash = new HashMap<>(); testCase(new MatchAllDocsQuery(), FIELD_NAME, precision, bbox, geoHashGrid -> { @@ -185,23 +186,24 @@ private void testWithSeveralDocs(BooleanSupplier supplier, GeoBoundingBox bbox) } public void testSingletonDocsAsSubAgg() throws IOException { - testWithSeveralDocsAsSubAgg(() -> true, null); + testWithSeveralDocsAsSubAgg(() -> true, null, randomPrecision()); } public void testBoundedSingletonDocsAsSubAgg() throws IOException { - testWithSeveralDocsAsSubAgg(() -> true, randomBBox()); + int precision = randomPrecision(); + testWithSeveralDocsAsSubAgg(() -> true, randomBBox(precision), precision); } public void testMultiValuedDocsAsSubAgg() throws IOException { - testWithSeveralDocsAsSubAgg(LuceneTestCase::rarely, null); + testWithSeveralDocsAsSubAgg(LuceneTestCase::rarely, null, randomPrecision()); } public void testBoundedMultiValuedDocsAsSubAgg() throws IOException { - testWithSeveralDocsAsSubAgg(LuceneTestCase::rarely, randomBBox()); + int precision = randomPrecision(); + testWithSeveralDocsAsSubAgg(LuceneTestCase::rarely, randomBBox(precision), precision); } - private void testWithSeveralDocsAsSubAgg(BooleanSupplier supplier, GeoBoundingBox bbox) throws IOException { - int precision = randomPrecision(); + private void testWithSeveralDocsAsSubAgg(BooleanSupplier supplier, GeoBoundingBox bbox, int precision) throws IOException { int numPoints = randomIntBetween(8, 128); Map> expectedCountPerTPerGeoHash = new TreeMap<>(); TermsAggregationBuilder aggregationBuilder = new TermsAggregationBuilder("t").field("t").size(numPoints); diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexAggregatorTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexAggregatorTests.java index 4aa2340eef3b6..3f75b9830a96d 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexAggregatorTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/search/aggregations/bucket/geogrid/GeoHexAggregatorTests.java @@ -72,7 +72,7 @@ protected Point randomPoint() { } @Override - protected GeoBoundingBox randomBBox() { + protected GeoBoundingBox randomBBox(int precision) { GeoBoundingBox bbox = randomValueOtherThanMany( (b) -> b.top() > GeoTileUtils.LATITUDE_MASK || b.bottom() < -GeoTileUtils.LATITUDE_MASK, () -> { From 5bbfe66b99291cdf0f49f58f04aeff4697565e48 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 25 Oct 2023 07:52:23 +0100 Subject: [PATCH 090/203] Improve string reps for snapshot debugging (#101295) Various things related to snapshots appear in debug logs, but have no useful string representation which makes it hard to follow the process. This commit adds some missing string representations. --- .../snapshots/SnapshotStressTestsIT.java | 10 +++++-- .../cluster/SnapshotDeletionsInProgress.java | 10 ++++++- .../blobstore/BlobStoreRepository.java | 29 +++++++++++++++++-- .../snapshots/SnapshotsService.java | 27 +++++++++++++++-- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java index cdf76bea1cf04..4721b1a186a99 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotStressTestsIT.java @@ -1031,11 +1031,15 @@ private void startPartialSnapshotter() { final Releasable abortReleasable = abortReleasables.transfer(); abortRunnable = mustSucceed(() -> { - logger.info("--> aborting/deleting snapshot [{}:{}]", trackedRepository.repositoryName, snapshotName); + logger.info("--> abort/delete snapshot [{}:{}] start", trackedRepository.repositoryName, snapshotName); deleteSnapshotRequestBuilder.execute(new ActionListener<>() { @Override public void onResponse(AcknowledgedResponse acknowledgedResponse) { - logger.info("--> aborted/deleted snapshot [{}:{}]", trackedRepository.repositoryName, snapshotName); + logger.info( + "--> abort/delete snapshot [{}:{}] success", + trackedRepository.repositoryName, + snapshotName + ); Releasables.close(abortReleasable); assertTrue(acknowledgedResponse.isAcknowledged()); } @@ -1046,7 +1050,7 @@ public void onFailure(Exception e) { if (ExceptionsHelper.unwrapCause(e) instanceof SnapshotMissingException) { // processed before the snapshot even started logger.info( - "--> abort/delete of [{}:{}] got snapshot missing", + "--> abort/delete snapshot [{}:{}] got snapshot missing", trackedRepository.repositoryName, snapshotName ); diff --git a/server/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java b/server/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java index a4fde4993a47e..234c9a924d8a8 100644 --- a/server/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java +++ b/server/src/main/java/org/elasticsearch/cluster/SnapshotDeletionsInProgress.java @@ -11,6 +11,7 @@ import org.elasticsearch.TransportVersion; import org.elasticsearch.TransportVersions; import org.elasticsearch.cluster.ClusterState.Custom; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; @@ -321,7 +322,14 @@ public long repositoryStateId() { @Override public String toString() { - return "SnapshotDeletionsInProgress.Entry[[" + uuid + "][" + state + "]" + snapshots + "]"; + return Strings.format( + "SnapshotDeletionsInProgress.Entry[[%s@%d][%s][%s]%s]", + repoName, + repositoryStateId, + uuid, + state, + snapshots + ); } } diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 79e2ed3c5c206..f66d406b95321 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -1673,7 +1673,17 @@ record RootBlobUpdateResult(RepositoryData oldRepositoryData, RepositoryData new ), repositoryStateId, repositoryMetaVersion, - finalizeSnapshotContext::updatedClusterState, + new Function<>() { + @Override + public ClusterState apply(ClusterState state) { + return finalizeSnapshotContext.updatedClusterState(state); + } + + @Override + public String toString() { + return "finalizing snapshot [" + metadata.name() + "][" + snapshotId + "]"; + } + }, l.map(newRepositoryData -> new RootBlobUpdateResult(existingRepositoryData, newRepositoryData)) ); // NB failure of writeIndexGen doesn't guarantee the update failed, so we cannot safely clean anything up on failure @@ -2570,6 +2580,11 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) logger.trace("[{}] successfully set pending repository generation to [{}]", metadata.name(), newGen); setPendingStep.onResponse(newGen); } + + @Override + public String toString() { + return Strings.format("start RepositoryData update from generation [%d], stateFilter=[%s]", expectedGen, stateFilter); + } }); final ListenableFuture filterRepositoryDataStep = new ListenableFuture<>(); @@ -2630,7 +2645,7 @@ public void onFailure(Exception e) { if (ensureSafeGenerationExists(expectedGen, delegate::onFailure) == false) { return; } - final String indexBlob = INDEX_FILE_PREFIX + Long.toString(newGen); + final String indexBlob = INDEX_FILE_PREFIX + newGen; logger.debug("Repository [{}] writing new index generational blob [{}]", metadata.name(), indexBlob); writeAtomic(blobContainer(), indexBlob, out -> { try (XContentBuilder xContentBuilder = XContentFactory.jsonBuilder(org.elasticsearch.core.Streams.noCloseStream(out))) { @@ -2689,6 +2704,16 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) cacheRepositoryData(newRepositoryData, version); delegate.onResponse(newRepositoryData); } + + @Override + public String toString() { + return Strings.format( + "complete RepositoryData update from generation [%d] to generation [%d], stateFilter=[%s]", + expectedGen, + newGen, + stateFilter + ); + } }); })); } diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index aabe4eeddc822..2ad9b21bcb88f 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -562,6 +562,11 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) logger.warn("Did not find expected entry [{}] in the cluster state", cloneEntry); } } + + @Override + public String toString() { + return Strings.format("start snapshot clone [%s] from [%s]", updatedEntry.snapshot(), updatedEntry.source()); + } }, "start snapshot clone", onFailure), onFailure) ); } @@ -1445,7 +1450,8 @@ public void onFailure(Exception e) { ); }, e -> handleFinalizationFailure(e, snapshot, repositoryData))); } catch (Exception e) { - assert false : new AssertionError(e); + logger.error(Strings.format("unexpected failure finalizing %s", snapshot), e); + assert false : new AssertionError("unexpected failure finalizing " + snapshot, e); handleFinalizationFailure(e, snapshot, repositoryData); } } @@ -2096,6 +2102,11 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) } } } + + @Override + public String toString() { + return Strings.format("delete snapshot task [%s]%s", repository, Arrays.toString(snapshotNames)); + } }, "delete snapshot [" + repository + "]" + Arrays.toString(snapshotNames), listener::onFailure); } @@ -2691,6 +2702,11 @@ private static void markShardReassigned(RepositoryShardId shardId, Set()) + if (snapshotsToFinalize.getOrDefault(snapshot.getRepository(), new LinkedList<>()) .stream() - .noneMatch(entry -> entry.equals(snapshot)) : "Snapshot [" + snapshot + "] is still in finalization queue"; + .anyMatch(entry -> entry.equals(snapshot))) { + + final var assertionError = new AssertionError("[" + snapshot + "] should not be in " + snapshotsToFinalize); + logger.error("assertNotQueued failure", assertionError); + throw assertionError; + } return true; } From f878a8c3085e992cee2d791c4b2ae52e0d03d25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Wed, 25 Oct 2023 10:55:15 +0200 Subject: [PATCH 091/203] Fix NodeInfo version parsing in integration tests (#100770) * Compatible version parsing in YAML tests * Compatible version parsing in various IT tests --- .../elasticsearch/upgrades/FieldCapsIT.java | 7 ++-- .../elasticsearch/upgrades/IndexingIT.java | 22 +++++------- .../ApiKeyBackwardsCompatibilityIT.java | 34 ++++++++++++------- .../TokenBackwardsCompatibilityIT.java | 13 ++++--- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java index e269b608930a8..f3971d832be3e 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FieldCapsIT.java @@ -11,7 +11,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import org.apache.http.HttpHost; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse; import org.elasticsearch.client.Request; import org.elasticsearch.client.RestClient; @@ -272,18 +272,19 @@ public void testAllIndicesWithIndexFilter() throws Exception { @SuppressWarnings("unchecked") // Returns a client connected to one of the upgraded nodes. private RestClient getUpgradedNodeClient() throws IOException { + var currentVersion = Build.current().version(); for (HttpHost host : getClusterHosts()) { RestClient client = RestClient.builder(host).build(); Request nodesRequest = new Request("GET", "_nodes/_local/_none"); Map nodeMap = (Map) entityAsMap(client.performRequest(nodesRequest)).get("nodes"); Map nameMap = (Map) nodeMap.values().iterator().next(); String version = (String) nameMap.get("version"); - if (version.equals(Version.CURRENT.toString())) { + if (version.equals(currentVersion)) { return client; } client.close(); } - throw new IllegalStateException("Couldn't find node on version " + Version.CURRENT); + throw new IllegalStateException("Couldn't find node on version " + currentVersion); } // Test field type filtering on mixed cluster diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java index 73ab8fb0c25d2..d5b5e24e2ccde 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/IndexingIT.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import static org.elasticsearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; import static org.elasticsearch.test.ListMatcher.matchesList; @@ -139,8 +140,7 @@ public void testAutoIdWithOpTypeCreate() throws IOException { Request waitForGreen = new Request("GET", "/_cluster/health"); waitForGreen.addParameter("wait_for_nodes", "3"); client().performRequest(waitForGreen); - Version minNodeVersion = minNodeVersion(); - if (minNodeVersion.before(Version.V_7_5_0)) { + if (clusterSupportsBulkApi() == false) { ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(bulk)); assertEquals(400, e.getResponse().getStatusLine().getStatusCode()); assertThat( @@ -410,19 +410,13 @@ private void assertCount(String index, int count) throws IOException { ); } - private Version minNodeVersion() throws IOException { + // TODO[lor]: replace this check with a (historical) feature check ("supports bulk requests") + private boolean clusterSupportsBulkApi() throws IOException { Map response = entityAsMap(client().performRequest(new Request("GET", "_nodes"))); Map nodes = (Map) response.get("nodes"); - Version minNodeVersion = null; - for (Map.Entry node : nodes.entrySet()) { - Map nodeInfo = (Map) node.getValue(); - Version nodeVersion = Version.fromString(nodeInfo.get("version").toString()); - if (minNodeVersion == null) { - minNodeVersion = nodeVersion; - } else if (nodeVersion.before(minNodeVersion)) { - minNodeVersion = nodeVersion; - } - } - return minNodeVersion; + + Predicate> nodeSupportsBulkApi = n -> Version.fromString(n.get("version").toString()).onOrAfter(Version.V_7_5_0); + + return nodes.values().stream().map(o -> (Map) o).allMatch(nodeSupportsBulkApi); } } diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java index f9b72a0d89024..ee50f349afddf 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java @@ -303,11 +303,18 @@ private static String randomRoleDescriptors(boolean includeRemoteIndices) { } } + boolean nodeSupportApiKeyRemoteIndices(Map nodeDetails) { + // TODO[lor]: the method can be kept, but we need to replace version check with features checks + String versionString = (String) nodeDetails.get("version"); + Version version = Version.fromString(versionString.replace("-SNAPSHOT", "")); + return version.onOrAfter(API_KEY_SUPPORT_REMOTE_INDICES_VERSION); + } + private void createClientsByVersion() throws IOException { - Map clientsByVersion = getRestClientByVersion(); - if (clientsByVersion.size() == 2) { - for (Map.Entry client : clientsByVersion.entrySet()) { - if (client.getKey().before(API_KEY_SUPPORT_REMOTE_INDICES_VERSION)) { + var clientsByCapability = getRestClientByCapability(); + if (clientsByCapability.size() == 2) { + for (Map.Entry client : clientsByCapability.entrySet()) { + if (client.getKey() == false) { oldVersionClient = client.getValue(); } else { newVersionClient = client.getValue(); @@ -316,7 +323,7 @@ private void createClientsByVersion() throws IOException { assertThat(oldVersionClient, notNullValue()); assertThat(newVersionClient, notNullValue()); } else { - fail("expected 2 versions during rolling upgrade but got: " + clientsByVersion.size()); + fail("expected 2 versions during rolling upgrade but got: " + clientsByCapability.size()); } } @@ -332,23 +339,24 @@ private void closeClientsByVersion() throws IOException { } @SuppressWarnings("unchecked") - private Map getRestClientByVersion() throws IOException { + private Map getRestClientByCapability() throws IOException { Response response = client().performRequest(new Request("GET", "_nodes")); assertOK(response); ObjectPath objectPath = ObjectPath.createFromResponse(response); Map nodesAsMap = objectPath.evaluate("nodes"); - Map> hostsByVersion = new HashMap<>(); + Map> hostsByCapability = new HashMap<>(); for (Map.Entry entry : nodesAsMap.entrySet()) { Map nodeDetails = (Map) entry.getValue(); - Version version = Version.fromString((String) nodeDetails.get("version")); + var capabilitySupported = nodeSupportApiKeyRemoteIndices(nodeDetails); Map httpInfo = (Map) nodeDetails.get("http"); - hostsByVersion.computeIfAbsent(version, k -> new ArrayList<>()).add(HttpHost.create((String) httpInfo.get("publish_address"))); + hostsByCapability.computeIfAbsent(capabilitySupported, k -> new ArrayList<>()) + .add(HttpHost.create((String) httpInfo.get("publish_address"))); } - Map clientsByVersion = new HashMap<>(); - for (Map.Entry> entry : hostsByVersion.entrySet()) { - clientsByVersion.put(entry.getKey(), buildClient(restClientSettings(), entry.getValue().toArray(new HttpHost[0]))); + Map clientsByCapability = new HashMap<>(); + for (var entry : hostsByCapability.entrySet()) { + clientsByCapability.put(entry.getKey(), buildClient(restClientSettings(), entry.getValue().toArray(new HttpHost[0]))); } - return clientsByVersion; + return clientsByCapability; } private static RoleDescriptor randomRoleDescriptor(boolean includeRemoteIndices) { diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java index d82d6d5dd6747..8b2fe0d1e2af1 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java @@ -8,7 +8,6 @@ import org.apache.http.HttpHeaders; import org.apache.http.HttpHost; -import org.elasticsearch.Version; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; @@ -43,7 +42,7 @@ public class TokenBackwardsCompatibilityIT extends AbstractUpgradeTestCase { @Before private void collectClientsByVersion() throws IOException { - Map clientsByVersion = getRestClientByVersion(); + Map clientsByVersion = getRestClientByVersion(); if (clientsByVersion.size() == 2) { // usual case, clients have different versions twoClients = clientsByVersion.values(); @@ -316,20 +315,20 @@ private void assertRefreshTokenInvalidated(String refreshToken) throws IOExcepti } @SuppressWarnings("unchecked") - private Map getRestClientByVersion() throws IOException { + private Map getRestClientByVersion() throws IOException { Response response = client().performRequest(new Request("GET", "_nodes")); assertOK(response); ObjectPath objectPath = ObjectPath.createFromResponse(response); Map nodesAsMap = objectPath.evaluate("nodes"); - Map> hostsByVersion = new HashMap<>(); + Map> hostsByVersion = new HashMap<>(); for (Map.Entry entry : nodesAsMap.entrySet()) { Map nodeDetails = (Map) entry.getValue(); - Version version = Version.fromString((String) nodeDetails.get("version")); + String version = (String) nodeDetails.get("version"); Map httpInfo = (Map) nodeDetails.get("http"); hostsByVersion.computeIfAbsent(version, k -> new ArrayList<>()).add(HttpHost.create((String) httpInfo.get("publish_address"))); } - Map clientsByVersion = new HashMap<>(); - for (Map.Entry> entry : hostsByVersion.entrySet()) { + Map clientsByVersion = new HashMap<>(); + for (Map.Entry> entry : hostsByVersion.entrySet()) { clientsByVersion.put(entry.getKey(), buildClient(restClientSettings(), entry.getValue().toArray(new HttpHost[0]))); } return clientsByVersion; From 2f162a48acd8e9352b4a262c0299d40a7a243343 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Wed, 25 Oct 2023 10:50:35 +0100 Subject: [PATCH 092/203] Unmute DeprecationHttpIT.testCompatibleMessagesCanBeIndexed (#101278) This has been fixed by #101273 --- .../org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index b382d103d6001..39cf434685b27 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -569,7 +569,6 @@ public void testDeprecationWarnMessagesCanBeIndexed() throws Exception { /** * Check that log messages about REST API compatibility are recorded to an index */ - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/96723") public void testCompatibleMessagesCanBeIndexed() throws Exception { final Request compatibleRequest = new Request("GET", "/_test_cluster/compat_only"); From 0c76b4d13597cd1119ed1e0563b9647fdb4b678f Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 25 Oct 2023 11:52:10 +0200 Subject: [PATCH 093/203] QL: Add error logging for *QL (#101057) This adds logging for the *QL query failures. Exceptions resulting in a 5xx-class response are logged on `WARN` level, otherwise `DEBUG`. --- docs/changelog/101057.yaml | 5 ++++ .../xpack/eql/plugin/RestEqlSearchAction.java | 10 ++++---- .../esql/action/EsqlResponseListener.java | 4 +++- .../xpack/ql/util/LoggingUtils.java | 24 +++++++++++++++++++ .../xpack/sql/plugin/RestSqlQueryAction.java | 13 +++++++++- 5 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 docs/changelog/101057.yaml create mode 100644 x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/LoggingUtils.java diff --git a/docs/changelog/101057.yaml b/docs/changelog/101057.yaml new file mode 100644 index 0000000000000..2024c714f58b0 --- /dev/null +++ b/docs/changelog/101057.yaml @@ -0,0 +1,5 @@ +pr: 101057 +summary: Add error logging for *QL +area: EQL +type: enhancement +issues: [] diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/RestEqlSearchAction.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/RestEqlSearchAction.java index 0f686fd066805..e24a4749f45cd 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/RestEqlSearchAction.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/RestEqlSearchAction.java @@ -6,13 +6,13 @@ */ package org.elasticsearch.xpack.eql.plugin; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.index.IndexNotFoundException; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestResponse; @@ -32,10 +32,11 @@ import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.xpack.ql.util.LoggingUtils.logOnFailure; @ServerlessScope(Scope.PUBLIC) public class RestEqlSearchAction extends BaseRestHandler { - private static Logger logger = LogManager.getLogger(RestEqlSearchAction.class); + private static final Logger LOGGER = LogManager.getLogger(RestEqlSearchAction.class); private static final String SEARCH_PATH = "/{index}/_eql/search"; @Override @@ -93,11 +94,12 @@ public void onFailure(Exception e) { finalException = new IndexNotFoundException(indices, infe.getCause()); } } + logOnFailure(LOGGER, finalException); try { channel.sendResponse(new RestResponse(channel, finalException)); } catch (Exception inner) { inner.addSuppressed(finalException); - logger.error("failed to send failure response", inner); + LOGGER.error("failed to send failure response", inner); } } }); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java index facff81033cb8..ee641cd9209a7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlResponseListener.java @@ -26,6 +26,7 @@ import static org.elasticsearch.xpack.esql.formatter.TextFormat.CSV; import static org.elasticsearch.xpack.esql.formatter.TextFormat.URL_PARAM_DELIMITER; +import static org.elasticsearch.xpack.ql.util.LoggingUtils.logOnFailure; /** * Listens for a single {@link EsqlQueryResponse}, builds a corresponding {@link RestResponse} and sends it. @@ -162,8 +163,9 @@ public ActionListener wrapWithLogging() { }, ex -> { // In case of failure, stop the time manually before sending out the response. long timeMillis = stopWatch.stop().getMillis(); - onFailure(ex); LOGGER.info("Failed execution of ESQL query.\nQuery string: [{}]\nExecution time: [{}]ms", esqlQuery, timeMillis); + logOnFailure(LOGGER, ex); + onFailure(ex); }); } } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/LoggingUtils.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/LoggingUtils.java new file mode 100644 index 0000000000000..12102e350d23e --- /dev/null +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/LoggingUtils.java @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ql.util; + +import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.logging.Level; +import org.elasticsearch.logging.Logger; +import org.elasticsearch.rest.RestStatus; + +public final class LoggingUtils { + + private LoggingUtils() {} + + public static void logOnFailure(Logger logger, Throwable throwable) { + RestStatus status = ExceptionsHelper.status(throwable); + logger.log(status.getStatus() >= 500 ? Level.WARN : Level.DEBUG, () -> "Request failed with status [" + status + "]: ", throwable); + } + +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java index 75888000384dd..43051e9e16160 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/RestSqlQueryAction.java @@ -9,6 +9,8 @@ import org.elasticsearch.client.internal.node.NodeClient; import org.elasticsearch.core.RestApiVersion; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.Scope; @@ -26,10 +28,12 @@ import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.xpack.ql.util.LoggingUtils.logOnFailure; import static org.elasticsearch.xpack.sql.proto.CoreProtocol.URL_PARAM_DELIMITER; @ServerlessScope(Scope.PUBLIC) public class RestSqlQueryAction extends BaseRestHandler { + private static final Logger LOGGER = LogManager.getLogger(RestSqlQueryAction.class); @Override public List routes() { @@ -52,7 +56,14 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli return channel -> { RestCancellableNodeClient cancellableClient = new RestCancellableNodeClient(client, request.getHttpChannel()); - cancellableClient.execute(SqlQueryAction.INSTANCE, sqlRequest, new SqlResponseListener(channel, request, sqlRequest)); + cancellableClient.execute( + SqlQueryAction.INSTANCE, + sqlRequest, + new SqlResponseListener(channel, request, sqlRequest).delegateResponse((l, ex) -> { + logOnFailure(LOGGER, ex); + l.onFailure(ex); + }) + ); }; } From 75d9bd7790839f14e2921aeeb38cd4537fe43950 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Wed, 25 Oct 2023 11:56:28 +0200 Subject: [PATCH 094/203] Rename component templates and pipelines according to the new naming conventions (#99975) - Creates a new StackTemplateRegistry that uses the new names - The new registry only respects stack.templates.enabled for index templates - Renames the old registry to LegacyStackTemplateRegistry - Component templates are not duplicated but registered under two different names - Documents the new naming convention - Index templates are not renamed, at least for now, as there are some challenges with it See 7fd0423 for more details. --- docs/changelog/99975.yaml | 5 + .../reference/ilm/apis/put-lifecycle.asciidoc | 5 + .../indices/index-templates.asciidoc | 4 +- .../indices/put-component-template.asciidoc | 33 ++- docs/reference/ingest.asciidoc | 4 +- .../ingest/apis/put-pipeline.asciidoc | 8 +- .../datastreams/DataStreamUpgradeRestIT.java | 12 +- .../datastreams/EcsLogsDataStreamIT.java | 2 +- .../datastreams/LogsDataStreamIT.java | 4 +- .../test/rest/ESRestTestCase.java | 14 +- ...s-default.json => 180-days@lifecycle.json} | 0 ...ys-default.json => 30-days@lifecycle.json} | 0 ...s-default.json => 365-days@lifecycle.json} | 0 ...ays-default.json => 7-days@lifecycle.json} | 0 ...ys-default.json => 90-days@lifecycle.json} | 0 ...ppings.json => data-streams@mappings.json} | 0 ...ynamic-mappings.json => ecs@mappings.json} | 0 ...te.json => kibana-reporting@template.json} | 0 ...peline.json => logs@default-pipeline.json} | 0 ...-pipeline.json => logs@json-pipeline.json} | 0 .../{logs-policy.json => logs@lifecycle.json} | 0 ...{logs-mappings.json => logs@mappings.json} | 0 ...{logs-settings.json => logs@settings.json} | 2 +- ...{logs-template.json => logs@template.json} | 6 +- ...ics-policy.json => metrics@lifecycle.json} | 0 ...cs-mappings.json => metrics@mappings.json} | 0 ...cs-settings.json => metrics@settings.json} | 0 ...cs-template.json => metrics@template.json} | 6 +- ...ttings.json => metrics@tsdb-settings.json} | 0 ...-policy.json => synthetics@lifecycle.json} | 0 ...mappings.json => synthetics@mappings.json} | 0 ...settings.json => synthetics@settings.json} | 0 ...template.json => synthetics@template.json} | 6 +- .../ml/integration/MlNativeIntegTestCase.java | 2 + .../rest-api-spec/test/stack/10_basic.yml | 24 ++ .../stack/LegacyStackTemplateRegistry.java | 267 ++++++++++++++++++ .../xpack/stack/StackPlugin.java | 20 +- .../xpack/stack/StackTemplateRegistry.java | 99 +++---- .../stack/StackTemplateRegistryTests.java | 35 ++- .../test/resources/non-required-template.json | 2 +- 40 files changed, 457 insertions(+), 103 deletions(-) create mode 100644 docs/changelog/99975.yaml rename x-pack/plugin/core/template-resources/src/main/resources/{180-days-default.json => 180-days@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{30-days-default.json => 30-days@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{365-days-default.json => 365-days@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{7-days-default.json => 7-days@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{90-days-default.json => 90-days@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{data-streams-mappings.json => data-streams@mappings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{ecs-dynamic-mappings.json => ecs@mappings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{kibana-reporting-template.json => kibana-reporting@template.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{logs-default-pipeline.json => logs@default-pipeline.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{logs-json-message-pipeline.json => logs@json-pipeline.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{logs-policy.json => logs@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{logs-mappings.json => logs@mappings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{logs-settings.json => logs@settings.json} (90%) rename x-pack/plugin/core/template-resources/src/main/resources/{logs-template.json => logs@template.json} (83%) rename x-pack/plugin/core/template-resources/src/main/resources/{metrics-policy.json => metrics@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{metrics-mappings.json => metrics@mappings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{metrics-settings.json => metrics@settings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{metrics-template.json => metrics@template.json} (78%) rename x-pack/plugin/core/template-resources/src/main/resources/{metrics-tsdb-settings.json => metrics@tsdb-settings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{synthetics-policy.json => synthetics@lifecycle.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{synthetics-mappings.json => synthetics@mappings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{synthetics-settings.json => synthetics@settings.json} (100%) rename x-pack/plugin/core/template-resources/src/main/resources/{synthetics-template.json => synthetics@template.json} (77%) create mode 100644 x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/LegacyStackTemplateRegistry.java diff --git a/docs/changelog/99975.yaml b/docs/changelog/99975.yaml new file mode 100644 index 0000000000000..a34746c27ec99 --- /dev/null +++ b/docs/changelog/99975.yaml @@ -0,0 +1,5 @@ +pr: 99975 +summary: Rename component templates and pipelines according to the new naming conventions +area: Indices APIs +type: enhancement +issues: [] diff --git a/docs/reference/ilm/apis/put-lifecycle.asciidoc b/docs/reference/ilm/apis/put-lifecycle.asciidoc index 674f75264662c..6d7c8ea5e297f 100644 --- a/docs/reference/ilm/apis/put-lifecycle.asciidoc +++ b/docs/reference/ilm/apis/put-lifecycle.asciidoc @@ -37,6 +37,11 @@ previous versions. ``:: (Required, string) Identifier for the policy. ++ +[IMPORTANT] +==== +To avoid naming collisions with built-in and Fleet-managed ILM policies, avoid using `@` as part of the id of your own ILM policies. +==== [[ilm-put-lifecycle-query-params]] ==== {api-query-parms-title} diff --git a/docs/reference/indices/index-templates.asciidoc b/docs/reference/indices/index-templates.asciidoc index c1903cf13654b..538fb5b97860a 100644 --- a/docs/reference/indices/index-templates.asciidoc +++ b/docs/reference/indices/index-templates.asciidoc @@ -28,7 +28,7 @@ applied. template, the settings from the <> request take precedence over settings specified in the index template and its component templates. -* Settings specified in the index template itself take precedence over the settings +* Settings specified in the index template itself take precedence over the settings in its component templates. * If a new data stream or index matches more than one index template, the index template with the highest priority is used. @@ -65,6 +65,8 @@ For example, if you don't use {fleet} or {agent} and want to create a template for the `logs-*` index pattern, assign your template a priority of `500`. This ensures your template is applied instead of the built-in template for `logs-*-*`. + +- To avoid naming collisions with built-in and Fleet-managed index templates, avoid using `@` as part of the name of your own index templates. **** [discrete] diff --git a/docs/reference/indices/put-component-template.asciidoc b/docs/reference/indices/put-component-template.asciidoc index 35b42f94640be..794f01cb7f3ae 100644 --- a/docs/reference/indices/put-component-template.asciidoc +++ b/docs/reference/indices/put-component-template.asciidoc @@ -91,23 +91,28 @@ Name of the component template to create. {es} includes the following built-in component templates: // tag::built-in-component-templates[] -- `logs-mappings` -- `logs-settings` -- `metrics-mappings` -- `metrics-settings` -- `metrics-tsdb-settings` -- `synthetics-mapping` -- `synthetics-settings` +- `logs@mappings` +- `logs@settings` +- `metrics@mappings` +- `metrics@settings` +- `metrics@tsdb-settings` +- `synthetics@mapping` +- `synthetics@settings` // end::built-in-component-templates[] {fleet-guide}/fleet-overview.html[{agent}] uses these templates to configure -backing indices for its data streams. If you use {agent} and want to overwrite -one of these templates, set the `version` for your replacement template higher -than the current version. - -If you don't use {agent} and want to disable all built-in component and index -templates, set <> to `false` -using the <>. +backing indices for its data streams. +If you want to customize these templates, don't override them as they may be reset after an update. +Instead, look for a `*@custom` component template in the `composed_of` section of the managed index template. +These custom component templates allow you to customize the mappings of managed index templates, +without having to override managed index templates or component templates. +Note that the custom component templates may not exist yet. +After you create them using the <>, they'll be picked up by the index template. +See <> on how to apply the changes to the corresponding data stream. + +To avoid naming collisions with built-in and Fleet-managed component templates, +avoid using `@` as part of your own component template names. +The exception of that rule are the `*@custom` component templates that let you safely customize managed index templates. ==== [[put-component-template-api-query-params]] diff --git a/docs/reference/ingest.asciidoc b/docs/reference/ingest.asciidoc index ddba7c4e775ce..64f327b09f182 100644 --- a/docs/reference/ingest.asciidoc +++ b/docs/reference/ingest.asciidoc @@ -307,13 +307,13 @@ matches these templates to your {fleet} data streams based on the {fleet-guide}/data-streams.html#data-streams-naming-scheme[stream's naming scheme]. -Each default integration pipeline calls a nonexistent, unversioned `@custom` ingest pipeline. +Each default integration pipeline calls a nonexistent, unversioned `*@custom` ingest pipeline. If unaltered, this pipeline call has no effect on your data. However, you can modify this call to create custom pipelines for integrations that persist across upgrades. Refer to {fleet-guide}/data-streams-pipeline-tutorial.html[Tutorial: Transform data with custom ingest pipelines] to learn more. {fleet} doesn't provide a default ingest pipeline for the **Custom logs** integration, -but you can specify a pipeline for this integration using an +but you can specify a pipeline for this integration using an <> or a <>. diff --git a/docs/reference/ingest/apis/put-pipeline.asciidoc b/docs/reference/ingest/apis/put-pipeline.asciidoc index 5b73a7803fdda..97c6a176dc256 100644 --- a/docs/reference/ingest/apis/put-pipeline.asciidoc +++ b/docs/reference/ingest/apis/put-pipeline.asciidoc @@ -43,7 +43,13 @@ PUT _ingest/pipeline/my-pipeline-id ``:: (Required, string) ID of the ingest pipeline to create or update. - ++ +[IMPORTANT] +==== +To avoid naming collisions with built-in and Fleet-managed ingest pipelines, avoid using `@` as part of your own ingest pipelines names. +The exception of that rule are the `*@custom` ingest pipelines that let you safely add a custom pipeline to managed pipelines. +See also <>. +==== [[put-pipeline-api-query-params]] ==== {api-query-parms-title} diff --git a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamUpgradeRestIT.java b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamUpgradeRestIT.java index 9656ec2744bed..f447e5b80f8c8 100644 --- a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamUpgradeRestIT.java +++ b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/DataStreamUpgradeRestIT.java @@ -65,7 +65,7 @@ public void testCompatibleMappingUpgrade() throws Exception { { "index_patterns": [ "logs-mysql-*" ], "priority": 200, - "composed_of": [ "logs-mappings", "logs-settings" ], + "composed_of": [ "logs@mappings", "logs@settings" ], "data_stream": {}, "template": { "mappings": { @@ -103,7 +103,7 @@ public void testCompatibleMappingUpgrade() throws Exception { { "index_patterns": [ "logs-mysql-*" ], "priority": 200, - "composed_of": [ "logs-mappings", "logs-settings" ], + "composed_of": [ "logs@mappings", "logs@settings" ], "data_stream": {}, "template": { "mappings": { @@ -168,7 +168,7 @@ public void testConflictingMappingUpgrade() throws Exception { { "index_patterns": [ "logs-mysql-*" ], "priority": 200, - "composed_of": [ "logs-mappings", "logs-settings" ], + "composed_of": [ "logs@mappings", "logs@settings" ], "data_stream": {}, "template": { "mappings": { @@ -205,7 +205,7 @@ public void testConflictingMappingUpgrade() throws Exception { { "index_patterns": [ "logs-mysql-*" ], "priority": 200, - "composed_of": [ "logs-mappings", "logs-settings" ], + "composed_of": [ "logs@mappings", "logs@settings" ], "data_stream": {}, "template": { "mappings": { @@ -285,7 +285,7 @@ static void verifyTotalHitCount(String index, String requestBody, int expectedTo private void waitForLogsComponentTemplateInitialization() throws Exception { assertBusy(() -> { try { - Request logsComponentTemplateRequest = new Request("GET", "/_component_template/logs-*"); + Request logsComponentTemplateRequest = new Request("GET", "/_component_template/logs@*"); Response response = client().performRequest(logsComponentTemplateRequest); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); @@ -297,7 +297,7 @@ private void waitForLogsComponentTemplateInitialization() throws Exception { List componentTemplates = (List) responseBody.get("component_templates"); assertThat(componentTemplates.size(), equalTo(2)); Set names = componentTemplates.stream().map(m -> ((Map) m).get("name")).collect(Collectors.toSet()); - assertThat(names, containsInAnyOrder("logs-mappings", "logs-settings")); + assertThat(names, containsInAnyOrder("logs@mappings", "logs@settings")); } catch (ResponseException responseException) { // Retry in case of a 404, maybe they haven't been initialized yet. if (responseException.getResponse().getStatusLine().getStatusCode() == 404) { diff --git a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/EcsLogsDataStreamIT.java b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/EcsLogsDataStreamIT.java index 7de4ed2f2843c..3802d572e04dd 100644 --- a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/EcsLogsDataStreamIT.java +++ b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/EcsLogsDataStreamIT.java @@ -47,7 +47,7 @@ public void setup() throws Exception { "processors": [ { "pipeline" : { - "name": "logs@json-message", + "name": "logs@json-pipeline", "description": "A pipeline that automatically parses JSON log events into top-level fields if they are such" } } diff --git a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java index cc8695b9e0e5b..b150c71c86122 100644 --- a/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java +++ b/modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/LogsDataStreamIT.java @@ -294,7 +294,7 @@ public void testLogsMessagePipeline() throws Exception { "processors": [ { "pipeline" : { - "name": "logs@json-message", + "name": "logs@json-pipeline", "description": "A pipeline that automatically parses JSON log events into top-level fields if they are such" } } @@ -452,7 +452,7 @@ public void testNoSubobjects() throws Exception { "priority": 200, "data_stream": {}, "index_patterns": ["logs-*-*"], - "composed_of": ["logs-test-subobjects-mappings", "ecs@dynamic_templates"] + "composed_of": ["logs-test-subobjects-mappings", "ecs@mappings"] } """); assertOK(client.performRequest(request)); diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java index 80237927655e0..f157ef5ced63f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java @@ -642,14 +642,23 @@ protected Set preserveILMPolicyIds() { "watch-history-ilm-policy-16", "ml-size-based-ilm-policy", "logs", + "logs@lifecycle", "metrics", + "metrics@lifecycle", "profiling", + "profiling@lifecycle", "synthetics", + "synthetics@lifecycle", "7-days-default", + "7-days@lifecycle", "30-days-default", + "30-days@lifecycle", "90-days-default", + "90-days@lifecycle", "180-days-default", + "180-days@lifecycle", "365-days-default", + "365-days@lifecycle", ".fleet-files-ilm-policy", ".fleet-file-data-ilm-policy", ".fleet-actions-results-ilm-policy", @@ -1869,6 +1878,10 @@ protected static boolean isXPackTemplate(String name) { if (name.startsWith("elastic-connectors")) { return true; } + if (name.contains("@")) { + // We have a naming convention that internal component templates contain `@`. See also index-templates.asciidoc. + return true; + } switch (name) { case ".watches": case "security_audit_log": @@ -1891,7 +1904,6 @@ protected static boolean isXPackTemplate(String name) { case "logstash-index-template": case "security-index-template": case "data-streams-mappings": - case "ecs@dynamic_templates": case "search-acl-filter": case ".kibana-reporting": return true; diff --git a/x-pack/plugin/core/template-resources/src/main/resources/180-days-default.json b/x-pack/plugin/core/template-resources/src/main/resources/180-days@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/180-days-default.json rename to x-pack/plugin/core/template-resources/src/main/resources/180-days@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/30-days-default.json b/x-pack/plugin/core/template-resources/src/main/resources/30-days@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/30-days-default.json rename to x-pack/plugin/core/template-resources/src/main/resources/30-days@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/365-days-default.json b/x-pack/plugin/core/template-resources/src/main/resources/365-days@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/365-days-default.json rename to x-pack/plugin/core/template-resources/src/main/resources/365-days@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/7-days-default.json b/x-pack/plugin/core/template-resources/src/main/resources/7-days@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/7-days-default.json rename to x-pack/plugin/core/template-resources/src/main/resources/7-days@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/90-days-default.json b/x-pack/plugin/core/template-resources/src/main/resources/90-days@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/90-days-default.json rename to x-pack/plugin/core/template-resources/src/main/resources/90-days@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/data-streams-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/data-streams@mappings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/data-streams-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/data-streams@mappings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/ecs-dynamic-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/ecs@mappings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/ecs-dynamic-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/ecs@mappings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/kibana-reporting-template.json b/x-pack/plugin/core/template-resources/src/main/resources/kibana-reporting@template.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/kibana-reporting-template.json rename to x-pack/plugin/core/template-resources/src/main/resources/kibana-reporting@template.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/logs-default-pipeline.json b/x-pack/plugin/core/template-resources/src/main/resources/logs@default-pipeline.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/logs-default-pipeline.json rename to x-pack/plugin/core/template-resources/src/main/resources/logs@default-pipeline.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/logs-json-message-pipeline.json b/x-pack/plugin/core/template-resources/src/main/resources/logs@json-pipeline.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/logs-json-message-pipeline.json rename to x-pack/plugin/core/template-resources/src/main/resources/logs@json-pipeline.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/logs-policy.json b/x-pack/plugin/core/template-resources/src/main/resources/logs@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/logs-policy.json rename to x-pack/plugin/core/template-resources/src/main/resources/logs@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/logs-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/logs@mappings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/logs-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/logs@mappings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/logs-settings.json b/x-pack/plugin/core/template-resources/src/main/resources/logs@settings.json similarity index 90% rename from x-pack/plugin/core/template-resources/src/main/resources/logs-settings.json rename to x-pack/plugin/core/template-resources/src/main/resources/logs@settings.json index 250d254899204..cc61f195402fe 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/logs-settings.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/logs@settings.json @@ -12,7 +12,7 @@ "mapping": { "ignore_malformed": true }, - "default_pipeline": "logs-default-pipeline" + "default_pipeline": "logs@default-pipeline" } } }, diff --git a/x-pack/plugin/core/template-resources/src/main/resources/logs-template.json b/x-pack/plugin/core/template-resources/src/main/resources/logs@template.json similarity index 83% rename from x-pack/plugin/core/template-resources/src/main/resources/logs-template.json rename to x-pack/plugin/core/template-resources/src/main/resources/logs@template.json index f232f00a8674f..b41b2d0453c89 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/logs-template.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/logs@template.json @@ -3,10 +3,10 @@ "priority": 100, "data_stream": {}, "composed_of": [ - "logs-mappings", - "logs-settings", + "logs@mappings", + "logs@settings", "logs@custom", - "ecs@dynamic_templates" + "ecs@mappings" ], "ignore_missing_component_templates": ["logs@custom"], "allow_auto_create": true, diff --git a/x-pack/plugin/core/template-resources/src/main/resources/metrics-policy.json b/x-pack/plugin/core/template-resources/src/main/resources/metrics@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/metrics-policy.json rename to x-pack/plugin/core/template-resources/src/main/resources/metrics@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/metrics-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/metrics@mappings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/metrics-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/metrics@mappings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/metrics-settings.json b/x-pack/plugin/core/template-resources/src/main/resources/metrics@settings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/metrics-settings.json rename to x-pack/plugin/core/template-resources/src/main/resources/metrics@settings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/metrics-template.json b/x-pack/plugin/core/template-resources/src/main/resources/metrics@template.json similarity index 78% rename from x-pack/plugin/core/template-resources/src/main/resources/metrics-template.json rename to x-pack/plugin/core/template-resources/src/main/resources/metrics@template.json index b0c4308722912..a596314bc9e8c 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/metrics-template.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/metrics@template.json @@ -3,9 +3,9 @@ "priority": 100, "data_stream": {}, "composed_of": [ - "metrics-mappings", - "data-streams-mappings", - "metrics-settings" + "metrics@mappings", + "data-streams@mappings", + "metrics@settings" ], "allow_auto_create": true, "_meta": { diff --git a/x-pack/plugin/core/template-resources/src/main/resources/metrics-tsdb-settings.json b/x-pack/plugin/core/template-resources/src/main/resources/metrics@tsdb-settings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/metrics-tsdb-settings.json rename to x-pack/plugin/core/template-resources/src/main/resources/metrics@tsdb-settings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/synthetics-policy.json b/x-pack/plugin/core/template-resources/src/main/resources/synthetics@lifecycle.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/synthetics-policy.json rename to x-pack/plugin/core/template-resources/src/main/resources/synthetics@lifecycle.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/synthetics-mappings.json b/x-pack/plugin/core/template-resources/src/main/resources/synthetics@mappings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/synthetics-mappings.json rename to x-pack/plugin/core/template-resources/src/main/resources/synthetics@mappings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/synthetics-settings.json b/x-pack/plugin/core/template-resources/src/main/resources/synthetics@settings.json similarity index 100% rename from x-pack/plugin/core/template-resources/src/main/resources/synthetics-settings.json rename to x-pack/plugin/core/template-resources/src/main/resources/synthetics@settings.json diff --git a/x-pack/plugin/core/template-resources/src/main/resources/synthetics-template.json b/x-pack/plugin/core/template-resources/src/main/resources/synthetics@template.json similarity index 77% rename from x-pack/plugin/core/template-resources/src/main/resources/synthetics-template.json rename to x-pack/plugin/core/template-resources/src/main/resources/synthetics@template.json index 0e292f3d8694f..6369bd5a82c15 100644 --- a/x-pack/plugin/core/template-resources/src/main/resources/synthetics-template.json +++ b/x-pack/plugin/core/template-resources/src/main/resources/synthetics@template.json @@ -3,9 +3,9 @@ "priority": 100, "data_stream": {}, "composed_of": [ - "synthetics-mappings", - "data-streams-mappings", - "synthetics-settings" + "synthetics@mappings", + "data-streams@mappings", + "synthetics@settings" ], "allow_auto_create": true, "_meta": { diff --git a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeIntegTestCase.java b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeIntegTestCase.java index bf5298337dab4..46a4e008cc752 100644 --- a/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeIntegTestCase.java +++ b/x-pack/plugin/ml/qa/native-multi-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/ml/integration/MlNativeIntegTestCase.java @@ -63,6 +63,7 @@ import org.elasticsearch.xpack.core.ilm.LifecycleSettings; import org.elasticsearch.xpack.core.ilm.LifecycleType; import org.elasticsearch.xpack.core.ilm.RolloverAction; +import org.elasticsearch.xpack.core.ilm.ShrinkAction; import org.elasticsearch.xpack.core.ilm.TimeseriesLifecycleType; import org.elasticsearch.xpack.core.ml.MachineLearningField; import org.elasticsearch.xpack.core.ml.MlMetaIndex; @@ -319,6 +320,7 @@ protected void ensureClusterStateConsistency() throws IOException { entries.add(new NamedWriteableRegistry.Entry(LifecycleAction.class, DeleteAction.NAME, DeleteAction::readFrom)); entries.add(new NamedWriteableRegistry.Entry(LifecycleAction.class, ForceMergeAction.NAME, ForceMergeAction::new)); entries.add(new NamedWriteableRegistry.Entry(LifecycleAction.class, RolloverAction.NAME, RolloverAction::read)); + entries.add(new NamedWriteableRegistry.Entry(LifecycleAction.class, ShrinkAction.NAME, ShrinkAction::new)); entries.add( new NamedWriteableRegistry.Entry( PersistentTaskParams.class, diff --git a/x-pack/plugin/stack/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/stack/10_basic.yml b/x-pack/plugin/stack/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/stack/10_basic.yml index 51d5edcb764f3..b1ac564a53715 100644 --- a/x-pack/plugin/stack/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/stack/10_basic.yml +++ b/x-pack/plugin/stack/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/stack/10_basic.yml @@ -9,34 +9,58 @@ setup: - do: ilm.get_lifecycle: policy: "logs" + - do: + ilm.get_lifecycle: + policy: "logs@lifecycle" - do: ilm.get_lifecycle: policy: "metrics" + - do: + ilm.get_lifecycle: + policy: "metrics@lifecycle" - do: cluster.get_component_template: name: data-streams-mappings + - do: + cluster.get_component_template: + name: data-streams@mappings - do: cluster.get_component_template: name: logs-mappings + - do: + cluster.get_component_template: + name: logs@mappings - do: cluster.get_component_template: name: logs-settings + - do: + cluster.get_component_template: + name: logs@settings - do: cluster.get_component_template: name: metrics-mappings + - do: + cluster.get_component_template: + name: metrics@mappings - do: cluster.get_component_template: name: metrics-settings + - do: + cluster.get_component_template: + name: metrics@settings - do: cluster.get_component_template: name: metrics-tsdb-settings + - do: + cluster.get_component_template: + name: metrics@tsdb-settings - do: indices.get_index_template: diff --git a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/LegacyStackTemplateRegistry.java b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/LegacyStackTemplateRegistry.java new file mode 100644 index 0000000000000..26bcfb66bf818 --- /dev/null +++ b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/LegacyStackTemplateRegistry.java @@ -0,0 +1,267 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.stack; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.Version; +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.metadata.ComponentTemplate; +import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.xcontent.NamedXContentRegistry; +import org.elasticsearch.xcontent.XContentParserConfiguration; +import org.elasticsearch.xcontent.json.JsonXContent; +import org.elasticsearch.xpack.core.ClientHelper; +import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; +import org.elasticsearch.xpack.core.template.IndexTemplateConfig; +import org.elasticsearch.xpack.core.template.IndexTemplateRegistry; +import org.elasticsearch.xpack.core.template.IngestPipelineConfig; +import org.elasticsearch.xpack.core.template.LifecyclePolicyConfig; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.xpack.stack.StackTemplateRegistry.STACK_TEMPLATES_ENABLED; + +@Deprecated(since = "8.12.0", forRemoval = true) +public class LegacyStackTemplateRegistry extends IndexTemplateRegistry { + private static final Logger logger = LogManager.getLogger(LegacyStackTemplateRegistry.class); + + // Current version of the registry requires all nodes to be at least 8.9.0. + public static final Version MIN_NODE_VERSION = Version.V_8_9_0; + + // The stack template registry version. This number must be incremented when we make changes + // to built-in templates. + public static final int REGISTRY_VERSION = 3; + + public static final String TEMPLATE_VERSION_VARIABLE = "xpack.stack.template.version"; + + private final ClusterService clusterService; + private volatile boolean stackTemplateEnabled; + + // General mappings conventions for any data that ends up in a data stream + public static final String DATA_STREAMS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "data-streams-mappings"; + + // ECS dynamic mappings + public static final String ECS_DYNAMIC_MAPPINGS_COMPONENT_TEMPLATE_NAME = "ecs@dynamic_templates"; + + ////////////////////////////////////////////////////////// + // Built in ILM policies for users to use + ////////////////////////////////////////////////////////// + public static final String ILM_7_DAYS_POLICY_NAME = "7-days-default"; + public static final String ILM_30_DAYS_POLICY_NAME = "30-days-default"; + public static final String ILM_90_DAYS_POLICY_NAME = "90-days-default"; + public static final String ILM_180_DAYS_POLICY_NAME = "180-days-default"; + public static final String ILM_365_DAYS_POLICY_NAME = "365-days-default"; + + ////////////////////////////////////////////////////////// + // Logs components (for matching logs-*-* indices) + ////////////////////////////////////////////////////////// + public static final String LOGS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "logs-mappings"; + public static final String LOGS_SETTINGS_COMPONENT_TEMPLATE_NAME = "logs-settings"; + public static final String LOGS_ILM_POLICY_NAME = "logs"; + + ////////////////////////////////////////////////////////// + // Metrics components (for matching metric-*-* indices) + ////////////////////////////////////////////////////////// + public static final String METRICS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "metrics-mappings"; + public static final String METRICS_SETTINGS_COMPONENT_TEMPLATE_NAME = "metrics-settings"; + public static final String METRICS_TSDB_SETTINGS_COMPONENT_TEMPLATE_NAME = "metrics-tsdb-settings"; + public static final String METRICS_ILM_POLICY_NAME = "metrics"; + + ////////////////////////////////////////////////////////// + // Synthetics components (for matching synthetics-*-* indices) + ////////////////////////////////////////////////////////// + public static final String SYNTHETICS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "synthetics-mappings"; + public static final String SYNTHETICS_SETTINGS_COMPONENT_TEMPLATE_NAME = "synthetics-settings"; + public static final String SYNTHETICS_ILM_POLICY_NAME = "synthetics"; + + public LegacyStackTemplateRegistry( + Settings nodeSettings, + ClusterService clusterService, + ThreadPool threadPool, + Client client, + NamedXContentRegistry xContentRegistry + ) { + super(nodeSettings, clusterService, threadPool, client, xContentRegistry); + this.clusterService = clusterService; + this.stackTemplateEnabled = STACK_TEMPLATES_ENABLED.get(nodeSettings); + } + + @Override + public void initialize() { + super.initialize(); + clusterService.getClusterSettings().addSettingsUpdateConsumer(STACK_TEMPLATES_ENABLED, this::updateEnabledSetting); + } + + private void updateEnabledSetting(boolean newValue) { + if (newValue) { + this.stackTemplateEnabled = true; + } else { + logger.info( + "stack composable templates [{}] and component templates [{}] will not be installed or reinstalled", + String.join(",", getComposableTemplateConfigs().keySet()), + String.join(",", getComponentTemplateConfigs().keySet()) + ); + this.stackTemplateEnabled = false; + } + } + + private static final List LIFECYCLE_POLICY_CONFIGS = List.of( + new LifecyclePolicyConfig(LOGS_ILM_POLICY_NAME, "/logs@lifecycle.json"), + new LifecyclePolicyConfig(METRICS_ILM_POLICY_NAME, "/metrics@lifecycle.json"), + new LifecyclePolicyConfig(SYNTHETICS_ILM_POLICY_NAME, "/synthetics@lifecycle.json"), + new LifecyclePolicyConfig(ILM_7_DAYS_POLICY_NAME, "/7-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_30_DAYS_POLICY_NAME, "/30-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_90_DAYS_POLICY_NAME, "/90-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_180_DAYS_POLICY_NAME, "/180-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_365_DAYS_POLICY_NAME, "/365-days@lifecycle.json") + ); + + @Override + protected List getLifecycleConfigs() { + return LIFECYCLE_POLICY_CONFIGS; + } + + @Override + protected List getLifecyclePolicies() { + if (stackTemplateEnabled) { + return lifecyclePolicies; + } else { + return Collections.emptyList(); + } + } + + private static final Map COMPONENT_TEMPLATE_CONFIGS; + + static { + final Map componentTemplates = new HashMap<>(); + for (IndexTemplateConfig config : List.of( + new IndexTemplateConfig( + DATA_STREAMS_MAPPINGS_COMPONENT_TEMPLATE_NAME, + "/data-streams@mappings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + LOGS_MAPPINGS_COMPONENT_TEMPLATE_NAME, + "/logs@mappings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + ECS_DYNAMIC_MAPPINGS_COMPONENT_TEMPLATE_NAME, + "/ecs@mappings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + LOGS_SETTINGS_COMPONENT_TEMPLATE_NAME, + "/logs@settings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + METRICS_MAPPINGS_COMPONENT_TEMPLATE_NAME, + "/metrics@mappings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + METRICS_SETTINGS_COMPONENT_TEMPLATE_NAME, + "/metrics@settings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + METRICS_TSDB_SETTINGS_COMPONENT_TEMPLATE_NAME, + "/metrics@tsdb-settings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + SYNTHETICS_MAPPINGS_COMPONENT_TEMPLATE_NAME, + "/synthetics@mappings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ), + new IndexTemplateConfig( + SYNTHETICS_SETTINGS_COMPONENT_TEMPLATE_NAME, + "/synthetics@settings.json", + REGISTRY_VERSION, + TEMPLATE_VERSION_VARIABLE + ) + )) { + try { + componentTemplates.put( + config.getTemplateName(), + ComponentTemplate.parse(JsonXContent.jsonXContent.createParser(XContentParserConfiguration.EMPTY, config.loadBytes())) + ); + } catch (IOException e) { + throw new AssertionError(e); + } + } + COMPONENT_TEMPLATE_CONFIGS = Map.copyOf(componentTemplates); + } + + @Override + protected Map getComponentTemplateConfigs() { + if (stackTemplateEnabled) { + return COMPONENT_TEMPLATE_CONFIGS; + } else { + return Map.of(); + } + } + + @Override + protected Map getComposableTemplateConfigs() { + return Map.of(); + } + + private static final List INGEST_PIPELINE_CONFIGS = List.of( + new IngestPipelineConfig("logs@json-message", "/logs@json-pipeline.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), + new IngestPipelineConfig("logs-default-pipeline", "/logs@default-pipeline.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE) + ); + + @Override + protected List getIngestPipelines() { + return INGEST_PIPELINE_CONFIGS; + } + + @Override + protected String getOrigin() { + return ClientHelper.STACK_ORIGIN; + } + + @Override + protected boolean requiresMasterNode() { + // Stack templates use the composable index template and component APIs, + // these APIs aren't supported in 7.7 and earlier and in mixed cluster + // environments this can cause a lot of ActionNotFoundTransportException + // errors in the logs during rolling upgrades. If these templates + // are only installed via elected master node then the APIs are always + // there and the ActionNotFoundTransportException errors are then prevented. + return true; + } + + @Override + protected boolean isClusterReady(ClusterChangedEvent event) { + // Ensure current version of the components are installed only once all nodes are updated to 8.9.0. + // This is necessary to prevent an error caused nby the usage of the ignore_missing_pipeline property + // in the pipeline processor, which has been introduced only in 8.9.0 + Version minNodeVersion = event.state().nodes().getMinNodeVersion(); + return minNodeVersion.onOrAfter(MIN_NODE_VERSION); + } +} diff --git a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java index de0858e59900c..a665170f8dcdb 100644 --- a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java +++ b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java @@ -59,8 +59,22 @@ public Collection createComponents( AllocationService allocationService, IndicesService indicesService ) { - StackTemplateRegistry templateRegistry = new StackTemplateRegistry(settings, clusterService, threadPool, client, xContentRegistry); - templateRegistry.initialize(); - return Collections.singleton(templateRegistry); + LegacyStackTemplateRegistry legacyStackTemplateRegistry = new LegacyStackTemplateRegistry( + settings, + clusterService, + threadPool, + client, + xContentRegistry + ); + legacyStackTemplateRegistry.initialize(); + StackTemplateRegistry stackTemplateRegistry = new StackTemplateRegistry( + settings, + clusterService, + threadPool, + client, + xContentRegistry + ); + stackTemplateRegistry.initialize(); + return List.of(legacyStackTemplateRegistry, stackTemplateRegistry); } } diff --git a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackTemplateRegistry.java b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackTemplateRegistry.java index 9b2d7439d0756..f81697982c803 100644 --- a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackTemplateRegistry.java +++ b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackTemplateRegistry.java @@ -29,7 +29,6 @@ import org.elasticsearch.xpack.core.template.LifecyclePolicyConfig; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -56,43 +55,43 @@ public class StackTemplateRegistry extends IndexTemplateRegistry { private volatile boolean stackTemplateEnabled; // General mappings conventions for any data that ends up in a data stream - public static final String DATA_STREAMS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "data-streams-mappings"; + public static final String DATA_STREAMS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "data-streams@mappings"; // ECS dynamic mappings - public static final String ECS_DYNAMIC_MAPPINGS_COMPONENT_TEMPLATE_NAME = "ecs@dynamic_templates"; + public static final String ECS_DYNAMIC_MAPPINGS_COMPONENT_TEMPLATE_NAME = "ecs@mappings"; ////////////////////////////////////////////////////////// // Built in ILM policies for users to use ////////////////////////////////////////////////////////// - public static final String ILM_7_DAYS_POLICY_NAME = "7-days-default"; - public static final String ILM_30_DAYS_POLICY_NAME = "30-days-default"; - public static final String ILM_90_DAYS_POLICY_NAME = "90-days-default"; - public static final String ILM_180_DAYS_POLICY_NAME = "180-days-default"; - public static final String ILM_365_DAYS_POLICY_NAME = "365-days-default"; + public static final String ILM_7_DAYS_POLICY_NAME = "7-days@lifecycle"; + public static final String ILM_30_DAYS_POLICY_NAME = "30-days@lifecycle"; + public static final String ILM_90_DAYS_POLICY_NAME = "90-days@lifecycle"; + public static final String ILM_180_DAYS_POLICY_NAME = "180-days@lifecycle"; + public static final String ILM_365_DAYS_POLICY_NAME = "365-days@lifecycle"; ////////////////////////////////////////////////////////// // Logs components (for matching logs-*-* indices) ////////////////////////////////////////////////////////// - public static final String LOGS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "logs-mappings"; - public static final String LOGS_SETTINGS_COMPONENT_TEMPLATE_NAME = "logs-settings"; - public static final String LOGS_ILM_POLICY_NAME = "logs"; + public static final String LOGS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "logs@mappings"; + public static final String LOGS_SETTINGS_COMPONENT_TEMPLATE_NAME = "logs@settings"; + public static final String LOGS_ILM_POLICY_NAME = "logs@lifecycle"; public static final String LOGS_INDEX_TEMPLATE_NAME = "logs"; ////////////////////////////////////////////////////////// // Metrics components (for matching metric-*-* indices) ////////////////////////////////////////////////////////// - public static final String METRICS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "metrics-mappings"; - public static final String METRICS_SETTINGS_COMPONENT_TEMPLATE_NAME = "metrics-settings"; - public static final String METRICS_TSDB_SETTINGS_COMPONENT_TEMPLATE_NAME = "metrics-tsdb-settings"; - public static final String METRICS_ILM_POLICY_NAME = "metrics"; + public static final String METRICS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "metrics@mappings"; + public static final String METRICS_SETTINGS_COMPONENT_TEMPLATE_NAME = "metrics@settings"; + public static final String METRICS_TSDB_SETTINGS_COMPONENT_TEMPLATE_NAME = "metrics@tsdb-settings"; + public static final String METRICS_ILM_POLICY_NAME = "metrics@lifecycle"; public static final String METRICS_INDEX_TEMPLATE_NAME = "metrics"; ////////////////////////////////////////////////////////// // Synthetics components (for matching synthetics-*-* indices) ////////////////////////////////////////////////////////// - public static final String SYNTHETICS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "synthetics-mappings"; - public static final String SYNTHETICS_SETTINGS_COMPONENT_TEMPLATE_NAME = "synthetics-settings"; - public static final String SYNTHETICS_ILM_POLICY_NAME = "synthetics"; + public static final String SYNTHETICS_MAPPINGS_COMPONENT_TEMPLATE_NAME = "synthetics@mappings"; + public static final String SYNTHETICS_SETTINGS_COMPONENT_TEMPLATE_NAME = "synthetics@settings"; + public static final String SYNTHETICS_ILM_POLICY_NAME = "synthetics@lifecycle"; public static final String SYNTHETICS_INDEX_TEMPLATE_NAME = "synthetics"; /////////////////////////////////// @@ -132,14 +131,14 @@ private void updateEnabledSetting(boolean newValue) { } private static final List LIFECYCLE_POLICY_CONFIGS = List.of( - new LifecyclePolicyConfig(LOGS_ILM_POLICY_NAME, "/logs-policy.json"), - new LifecyclePolicyConfig(METRICS_ILM_POLICY_NAME, "/metrics-policy.json"), - new LifecyclePolicyConfig(SYNTHETICS_ILM_POLICY_NAME, "/synthetics-policy.json"), - new LifecyclePolicyConfig(ILM_7_DAYS_POLICY_NAME, "/" + ILM_7_DAYS_POLICY_NAME + ".json"), - new LifecyclePolicyConfig(ILM_30_DAYS_POLICY_NAME, "/" + ILM_30_DAYS_POLICY_NAME + ".json"), - new LifecyclePolicyConfig(ILM_90_DAYS_POLICY_NAME, "/" + ILM_90_DAYS_POLICY_NAME + ".json"), - new LifecyclePolicyConfig(ILM_180_DAYS_POLICY_NAME, "/" + ILM_180_DAYS_POLICY_NAME + ".json"), - new LifecyclePolicyConfig(ILM_365_DAYS_POLICY_NAME, "/" + ILM_365_DAYS_POLICY_NAME + ".json") + new LifecyclePolicyConfig(LOGS_ILM_POLICY_NAME, "/logs@lifecycle.json"), + new LifecyclePolicyConfig(METRICS_ILM_POLICY_NAME, "/metrics@lifecycle.json"), + new LifecyclePolicyConfig(SYNTHETICS_ILM_POLICY_NAME, "/synthetics@lifecycle.json"), + new LifecyclePolicyConfig(ILM_7_DAYS_POLICY_NAME, "/7-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_30_DAYS_POLICY_NAME, "/30-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_90_DAYS_POLICY_NAME, "/90-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_180_DAYS_POLICY_NAME, "/180-days@lifecycle.json"), + new LifecyclePolicyConfig(ILM_365_DAYS_POLICY_NAME, "/365-days@lifecycle.json") ); @Override @@ -149,11 +148,7 @@ protected List getLifecycleConfigs() { @Override protected List getLifecyclePolicies() { - if (stackTemplateEnabled) { - return lifecyclePolicies; - } else { - return Collections.emptyList(); - } + return lifecyclePolicies; } private static final Map COMPONENT_TEMPLATE_CONFIGS; @@ -163,55 +158,55 @@ protected List getLifecyclePolicies() { for (IndexTemplateConfig config : List.of( new IndexTemplateConfig( DATA_STREAMS_MAPPINGS_COMPONENT_TEMPLATE_NAME, - "/data-streams-mappings.json", + "/data-streams@mappings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( LOGS_MAPPINGS_COMPONENT_TEMPLATE_NAME, - "/logs-mappings.json", + "/logs@mappings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( ECS_DYNAMIC_MAPPINGS_COMPONENT_TEMPLATE_NAME, - "/ecs-dynamic-mappings.json", + "/ecs@mappings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( LOGS_SETTINGS_COMPONENT_TEMPLATE_NAME, - "/logs-settings.json", + "/logs@settings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( METRICS_MAPPINGS_COMPONENT_TEMPLATE_NAME, - "/metrics-mappings.json", + "/metrics@mappings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( METRICS_SETTINGS_COMPONENT_TEMPLATE_NAME, - "/metrics-settings.json", + "/metrics@settings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( METRICS_TSDB_SETTINGS_COMPONENT_TEMPLATE_NAME, - "/metrics-tsdb-settings.json", + "/metrics@tsdb-settings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( SYNTHETICS_MAPPINGS_COMPONENT_TEMPLATE_NAME, - "/synthetics-mappings.json", + "/synthetics@mappings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ), new IndexTemplateConfig( SYNTHETICS_SETTINGS_COMPONENT_TEMPLATE_NAME, - "/synthetics-settings.json", + "/synthetics@settings.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ) @@ -230,20 +225,16 @@ protected List getLifecyclePolicies() { @Override protected Map getComponentTemplateConfigs() { - if (stackTemplateEnabled) { - return COMPONENT_TEMPLATE_CONFIGS; - } else { - return Map.of(); - } + return COMPONENT_TEMPLATE_CONFIGS; } private static final Map COMPOSABLE_INDEX_TEMPLATE_CONFIGS = parseComposableTemplates( - new IndexTemplateConfig(LOGS_INDEX_TEMPLATE_NAME, "/logs-template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), - new IndexTemplateConfig(METRICS_INDEX_TEMPLATE_NAME, "/metrics-template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), - new IndexTemplateConfig(SYNTHETICS_INDEX_TEMPLATE_NAME, "/synthetics-template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), + new IndexTemplateConfig(LOGS_INDEX_TEMPLATE_NAME, "/logs@template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), + new IndexTemplateConfig(METRICS_INDEX_TEMPLATE_NAME, "/metrics@template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), + new IndexTemplateConfig(SYNTHETICS_INDEX_TEMPLATE_NAME, "/synthetics@template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), new IndexTemplateConfig( KIBANA_REPORTING_INDEX_TEMPLATE_NAME, - "/kibana-reporting-template.json", + "/kibana-reporting@template.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE ) @@ -259,14 +250,8 @@ protected Map getComposableTemplateConfigs() { } private static final List INGEST_PIPELINE_CONFIGS = List.of( - new IngestPipelineConfig("logs@json-message", "/logs-json-message-pipeline.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), - new IngestPipelineConfig( - "logs-default-pipeline", - "/logs-default-pipeline.json", - REGISTRY_VERSION, - TEMPLATE_VERSION_VARIABLE, - Collections.singletonList("logs@json-message") - ) + new IngestPipelineConfig("logs@json-pipeline", "/logs@json-pipeline.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE), + new IngestPipelineConfig("logs@default-pipeline", "/logs@default-pipeline.json", REGISTRY_VERSION, TEMPLATE_VERSION_VARIABLE) ); @Override diff --git a/x-pack/plugin/stack/src/test/java/org/elasticsearch/xpack/stack/StackTemplateRegistryTests.java b/x-pack/plugin/stack/src/test/java/org/elasticsearch/xpack/stack/StackTemplateRegistryTests.java index 4128e6a4af7ec..8e0cbc3f82f35 100644 --- a/x-pack/plugin/stack/src/test/java/org/elasticsearch/xpack/stack/StackTemplateRegistryTests.java +++ b/x-pack/plugin/stack/src/test/java/org/elasticsearch/xpack/stack/StackTemplateRegistryTests.java @@ -63,10 +63,12 @@ import static org.hamcrest.Matchers.anEmptyMap; import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -92,7 +94,7 @@ public void tearDown() throws Exception { threadPool.shutdownNow(); } - public void testDisabledDoesNotAddTemplates() { + public void testDisabledDoesNotAddIndexTemplates() { Settings settings = Settings.builder().put(StackTemplateRegistry.STACK_TEMPLATES_ENABLED.getKey(), false).build(); StackTemplateRegistry disabledRegistry = new StackTemplateRegistry( settings, @@ -101,9 +103,34 @@ public void testDisabledDoesNotAddTemplates() { client, NamedXContentRegistry.EMPTY ); - assertThat(disabledRegistry.getComponentTemplateConfigs(), anEmptyMap()); assertThat(disabledRegistry.getComposableTemplateConfigs(), anEmptyMap()); - assertThat(disabledRegistry.getLifecyclePolicies(), hasSize(0)); + } + + public void testDisabledStillAddsComponentTemplatesAndIlmPolicies() { + Settings settings = Settings.builder().put(StackTemplateRegistry.STACK_TEMPLATES_ENABLED.getKey(), false).build(); + StackTemplateRegistry disabledRegistry = new StackTemplateRegistry( + settings, + clusterService, + threadPool, + client, + NamedXContentRegistry.EMPTY + ); + assertThat(disabledRegistry.getComponentTemplateConfigs(), not(anEmptyMap())); + assertThat( + disabledRegistry.getComponentTemplateConfigs() + .keySet() + .stream() + // We have a naming convention that internal component templates contain `@`. See also put-component-template.asciidoc. + .filter(t -> t.contains("@") == false) + .collect(Collectors.toSet()), + empty() + ); + assertThat(disabledRegistry.getLifecyclePolicies(), not(empty())); + assertThat( + // We have a naming convention that internal ILM policies contain `@`. See also put-lifecycle.asciidoc. + disabledRegistry.getLifecyclePolicies().stream().filter(p -> p.getName().contains("@") == false).collect(Collectors.toSet()), + empty() + ); } public void testThatNonExistingTemplatesAreAddedImmediately() throws Exception { @@ -356,7 +383,7 @@ public void testMissingNonRequiredTemplates() throws Exception { assertThat(putComposableTemplateRequest.name(), equalTo("syslog")); ComposableIndexTemplate composableIndexTemplate = putComposableTemplateRequest.indexTemplate(); assertThat(composableIndexTemplate.composedOf(), hasSize(2)); - assertThat(composableIndexTemplate.composedOf().get(0), equalTo("logs-settings")); + assertThat(composableIndexTemplate.composedOf().get(0), equalTo("logs@settings")); assertThat(composableIndexTemplate.composedOf().get(1), equalTo("syslog@custom")); assertThat(composableIndexTemplate.getIgnoreMissingComponentTemplates(), hasSize(1)); assertThat(composableIndexTemplate.getIgnoreMissingComponentTemplates().get(0), equalTo("syslog@custom")); diff --git a/x-pack/plugin/stack/src/test/resources/non-required-template.json b/x-pack/plugin/stack/src/test/resources/non-required-template.json index bb9731c96a765..47391a5b1d4fc 100644 --- a/x-pack/plugin/stack/src/test/resources/non-required-template.json +++ b/x-pack/plugin/stack/src/test/resources/non-required-template.json @@ -3,7 +3,7 @@ "priority": 100, "data_stream": {}, "composed_of": [ - "logs-settings", + "logs@settings", "syslog@custom" ], "ignore_missing_component_templates": ["syslog@custom"], From 74ea04fb2d1aae59c45f71c421263394ad6e1412 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Wed, 25 Oct 2023 11:12:36 +0100 Subject: [PATCH 095/203] [DOCS] document tail merging and create tutorial for migrating to DSL (#101117) This documents tail merging, the enabled flag, and adds a tutorial to migrate a data stream from ILM to DSL. --- .../lifecycle/apis/put-lifecycle.asciidoc | 6 + .../data-streams/lifecycle/index.asciidoc | 14 +- ...grate-data-stream-from-ilm-to-dsl.asciidoc | 460 ++++++++++++++++++ .../data-stream-lifecycle-settings.asciidoc | 21 + 4 files changed, 499 insertions(+), 2 deletions(-) create mode 100644 docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc diff --git a/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc b/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc index 81055379fe6da..dd893d0abe757 100644 --- a/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc +++ b/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc @@ -53,6 +53,12 @@ Defaults to `open`. (Optional, string) If defined, every document added to this data stream will be stored at least for this time frame. Any time after this duration the document could be deleted. When empty, every document in this data stream will be stored indefinitely. + +`enabled`:: +(Optional, boolean) +If defined, it turns data streqm lifecycle on/off (`true`/`false`) for this data stream. +A data stream lifecycle that's disabled (`enabled: false`) will have no effect on the +data stream. Defaults to `true`. ==== [[data-streams-put-lifecycle-example]] diff --git a/docs/reference/data-streams/lifecycle/index.asciidoc b/docs/reference/data-streams/lifecycle/index.asciidoc index da837293ab68e..5f7596087cb5b 100644 --- a/docs/reference/data-streams/lifecycle/index.asciidoc +++ b/docs/reference/data-streams/lifecycle/index.asciidoc @@ -28,7 +28,14 @@ each data stream and performs the following steps: 1. Checks if the data stream has a data stream lifecycle configured, skipping any indices not part of a managed data stream. 2. Rolls over the write index of the data stream, if it fulfills the conditions defined by <>. -3. Applies retention to the remaining backing indices. This means deleting the backing indices whose +3. After an index is not the write index anymore (i.e. the data stream has been rolled over), +automatically tail merges the index. Data stream lifecycle executes a merge operation that only targets +the long tail of small segments instead of the whole shard. As the segments are organised +into tiers of exponential sizes, merging the long tail of small segments is only a +fraction of the cost of force mergeing to a single segment. The small segments would usually +hold the most recent data so tail mergeing will focus the merging resources on the higher-value +data that is most likely to keep being queried. +4. Applies retention to the remaining backing indices. This means deleting the backing indices whose `generation_time` is longer than the configured retention period. The `generation_time` is only applicable to rolled over backing indices and it is either the time since the backing index got rolled over, or the time optionally configured in the <> setting. @@ -37,7 +44,7 @@ IMPORTANT: We use the `generation_time` instead of the creation time because thi index have passed the retention period. As a result, the retention period is not the exact time data gets deleted, but the minimum time data will be stored. -NOTE: The steps `2` and `3` apply only to backing indices that are not already managed by {ilm-init}, meaning that these indices either do +NOTE: Steps `2-4` apply only to backing indices that are not already managed by {ilm-init}, meaning that these indices either do not have an {ilm-init} policy defined, or if they do, they have <> set to `false`. @@ -55,6 +62,7 @@ that matches the name of your data stream (see <> to edit the lifecycle on the data stream itself (see <>). +* Migrate an existing {ilm-init} managed data stream to Data stream lifecycle using <>. NOTE: Updating the data stream lifecycle of an existing data stream is different from updating the settings or the mapping, because it is applied on the data stream level and not on the individual backing indices. @@ -62,3 +70,5 @@ because it is applied on the data stream level and not on the individual backing include::tutorial-manage-new-data-stream.asciidoc[] include::tutorial-manage-existing-data-stream.asciidoc[] + +include::tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc[] diff --git a/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc b/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc new file mode 100644 index 0000000000000..c96bfa75516a8 --- /dev/null +++ b/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc @@ -0,0 +1,460 @@ +[role="xpack"] +[[tutorial-migrate-data-stream-from-ilm-to-dsl]] +=== Tutorial: Migrate ILM managed data stream to Data stream lifecycle + +preview:[] + +In this tutorial we'll look at migrating an existing data stream from {ilm-init} to +Data stream lifecycle. The existing {ilm-init} managed backing indices will continue +to be managed by {ilm-init} until they age out and get deleted by {ilm-init}; however, +the new backing indices will be managed by Data stream lifecycle. +This way, a data stream is gradually migrated away from being managed by {ilm-cap} to +being managed by Data stream lifecycle. As we'll see, {ilm-cap} and Data stream lifecycle +can co-manage a data stream; however, an index can only be managed by one system at +a time. + +Let's first create a data stream with two backing indices managed by {ilm-cap}. +We first create an {ilm-cap} policy: + +[source,console] +---- +PUT _ilm/policy/pre-dsl-ilm-policy +{ + "policy": { + "phases": { + "hot": { + "actions": { + "rollover": { + "max_primary_shard_size": "50gb" + } + } + }, + "delete": { + "min_age": "7d", + "actions": { + "delete": {} + } + } + } + } +} +---- + +And let's create an index template that'll back the data stream and configures {ilm-cap}: + +[source,console] +---- +PUT _index_template/dsl-data-stream-template +{ + "index_patterns": ["dsl-data-stream*"], + "data_stream": { }, + "priority": 500, + "template": { + "settings": { + "index.lifecycle.name": "pre-dsl-ilm-policy" + } + } +} +---- +// TEST[continued] + +We'll now index a document targetting `dsl-data-stream` to create the data stream +and we'll also manually rollover the data stream to have another generation index created: + +[source,console] +---- +POST dsl-data-stream/_doc? +{ + "@timestamp": "2023-10-18T16:21:15.000Z", + "message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736" +} +---- +// TEST[continued] + +[source,console] +---- +POST dsl-data-stream/_rollover +---- +// TEST[continued] + +We'll use the <> API to inspect the state of +the data stream: + +[source,console] +-------------------------------------------------- +GET _data_stream/dsl-data-stream +-------------------------------------------------- +// TEST[continued] + +Inspecting the response we'll see that both backing indices are managed by {ilm-init} +and that the next generation index will also be managed by {ilm-init}: + +[source,console-result] +---- +{ + "data_streams": [ + { + "name": "dsl-data-stream", + "timestamp_field": { + "name": "@timestamp" + }, + "indices": [ + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000001", <1> + "index_uuid": "xCEhwsp8Tey0-FLNFYVwSg", + "prefer_ilm": true, <2> + "ilm_policy": "pre-dsl-ilm-policy", <3> + "managed_by": "Index Lifecycle Management" <4> + }, + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000002", + "index_uuid": "PA_JquKGSiKcAKBA8DJ5gw", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" + } + ], + "generation": 2, + "status": "GREEN", + "template": "dsl-data-stream-template", + "next_generation_managed_by": "Index Lifecycle Management", <5> + "prefer_ilm": true, <6> + "ilm_policy": "pre-dsl-ilm-policy", <7> + "hidden": false, + "system": false, + "allow_custom_routing": false, + "replicated": false + } + ] +} +---- +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000001"/"index_name": $body.data_streams.0.indices.0.index_name/] +// TESTRESPONSE[s/"index_uuid": "xCEhwsp8Tey0-FLNFYVwSg"/"index_uuid": $body.data_streams.0.indices.0.index_uuid/] +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000002"/"index_name": $body.data_streams.0.indices.1.index_name/] +// TESTRESPONSE[s/"index_uuid": "PA_JquKGSiKcAKBA8DJ5gw"/"index_uuid": $body.data_streams.0.indices.1.index_uuid/] +// TESTRESPONSE[s/"status": "GREEN"/"status": "YELLOW"/] + +<1> The name of the backing index. +<2> For each backing index we display the value of the <> +configuration which will indicate if {ilm-init} takes precedence over data stream lifecycle in case +both systems are configured for an index. +<3> The {ilm-ini} policy configured for this index. +<4> The system that manages this index (possible values are "Index Lifecycle Management", +"Data stream lifecycle", or "Unmanaged") +<5> The system that will manage the next generation index (the new write index of this +data stream, once the data stream is rolled over). The possible values are +"Index Lifecycle Management", "Data stream lifecycle", or "Unmanaged". +<6> The <> value configured in the index template +that's backing the data stream. This value will be configured for all the new backing indices. +If it's not configured in the index template the backing indices will receive the `true` +default value ({ilm-init} takes precedence over data stream lifecycle by default as it's +currently richer in features). +<7> The {ilm-init} policy configured in the index template that's backing this data +stream (which will be configured on all the new backing indices, as long as it exists +in the index template). + +To migrate the `dsl-data-stream` to data stream lifecycle we'll have to execute +two steps: + +1. Update the index template that's backing the index template to configure <> +to `false`, and to configure data stream lifecycle. +2. Configure the data stream lifecycle for the _existing_ `dsl-data-stream` using +the <>. + +IMPORTANT: The data stream lifecycle configuration that's added to the index template, +being a data stream configuration, will only apply to **new** data streams. +Our data stream exists already, so even though we added a data stream lifecycle +configuration in the index template it will not be applied to `dsl-data-stream`. + + +[[update-index-template-for-dsl]] +Let's update the index template: + +[source,console] +---- +PUT _index_template/dsl-data-stream-template +{ + "index_patterns": ["dsl-data-stream*"], + "data_stream": { }, + "priority": 500, + "template": { + "settings": { + "index.lifecycle.name": "pre-dsl-ilm-policy", + "index.lifecycle.prefer_ilm": false <1> + }, + "lifecycle": { + "data_retention": "7d" <2> + } + } +} +---- +// TEST[continued] + +<1> The `prefer_ilm` setting will now be configured on the **new** backing indices +(created by rolling over the data stream) such that {ilm-init} does _not_ take +precedence over Data stream lifecycle. +<2> We're configuring the data stream lifecycle so _new_ data streams will be +managed by Data stream lifecycle. + +We've now make sure that new data streams will be managed by Data stream lifecycle. + +Let's update our existing `dsl-data-stream` and configure Data stream lifecycle: + +[source,console] +---- +PUT _data_stream/dsl-data-stream/_lifecycle +{ + "data_retention": "7d" +} +---- +// TEST[continued] + +We can inspect the data stream to check that the next generation will indeed be +managed by Data stream lifecycle: + +[source,console] +-------------------------------------------------- +GET _data_stream/dsl-data-stream +-------------------------------------------------- +// TEST[continued] + +[source,console-result] +---- +{ + "data_streams": [ + { + "name": "dsl-data-stream", + "timestamp_field": { + "name": "@timestamp" + }, + "indices": [ + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000001", + "index_uuid": "xCEhwsp8Tey0-FLNFYVwSg", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" <1> + }, + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000002", + "index_uuid": "PA_JquKGSiKcAKBA8DJ5gw", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" <2> + } + ], + "generation": 2, + "status": "GREEN", + "template": "dsl-data-stream-template", + "lifecycle": { + "enabled": true, + "data_retention": "7d" + }, + "ilm_policy": "pre-dsl-ilm-policy", + "next_generation_managed_by": "Data stream lifecycle", <3> + "prefer_ilm": false, <4> + "hidden": false, + "system": false, + "allow_custom_routing": false, + "replicated": false + } + ] +} +---- +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000001"/"index_name": $body.data_streams.0.indices.0.index_name/] +// TESTRESPONSE[s/"index_uuid": "xCEhwsp8Tey0-FLNFYVwSg"/"index_uuid": $body.data_streams.0.indices.0.index_uuid/] +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000002"/"index_name": $body.data_streams.0.indices.1.index_name/] +// TESTRESPONSE[s/"index_uuid": "PA_JquKGSiKcAKBA8DJ5gw"/"index_uuid": $body.data_streams.0.indices.1.index_uuid/] +// TESTRESPONSE[s/"status": "GREEN"/"status": "YELLOW"/] + +<1> The existing backing index will continue to be managed by {ilm-init} +<2> The existing backing index will continue to be managed by {ilm-init} +<3> The next generation index will be managed by Data stream lifecycle +<4> The `prefer_ilm` setting value we configured in the index template is reflected +and will be configured accordingly for new backing indices. + +We'll now rollover the data stream to see the new generation index being managed by +Data stream lifecycle: + +[source,console] +---- +POST dsl-data-stream/_rollover +---- +// TEST[continued] + +[source,console] +---- +GET _data_stream/dsl-data-stream +---- +// TEST[continued] + +[source,console-result] +---- +{ + "data_streams": [ + { + "name": "dsl-data-stream", + "timestamp_field": { + "name": "@timestamp" + }, + "indices": [ + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000001", + "index_uuid": "xCEhwsp8Tey0-FLNFYVwSg", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" <1> + }, + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000002", + "index_uuid": "PA_JquKGSiKcAKBA8DJ5gw", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" <2> + }, + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000003", + "index_uuid": "PA_JquKGSiKcAKBA8abcd1", + "prefer_ilm": false, <3> + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Data stream lifecycle" <4> + } + ], + "generation": 3, + "status": "GREEN", + "template": "dsl-data-stream-template", + "lifecycle": { + "enabled": true, + "data_retention": "7d" + }, + "ilm_policy": "pre-dsl-ilm-policy", + "next_generation_managed_by": "Data stream lifecycle", + "prefer_ilm": false, + "hidden": false, + "system": false, + "allow_custom_routing": false, + "replicated": false + } + ] +} +---- +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000001"/"index_name": $body.data_streams.0.indices.0.index_name/] +// TESTRESPONSE[s/"index_uuid": "xCEhwsp8Tey0-FLNFYVwSg"/"index_uuid": $body.data_streams.0.indices.0.index_uuid/] +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000002"/"index_name": $body.data_streams.0.indices.1.index_name/] +// TESTRESPONSE[s/"index_uuid": "PA_JquKGSiKcAKBA8DJ5gw"/"index_uuid": $body.data_streams.0.indices.1.index_uuid/] +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000003"/"index_name": $body.data_streams.0.indices.2.index_name/] +// TESTRESPONSE[s/"index_uuid": "PA_JquKGSiKcAKBA8abcd1"/"index_uuid": $body.data_streams.0.indices.2.index_uuid/] +// TESTRESPONSE[s/"status": "GREEN"/"status": "YELLOW"/] + +<1> The backing indices that existed before rollover will continue to be managed by {ilm-init} +<2> The backing indices that existed before rollover will continue to be managed by {ilm-init} +<3> The new write index received the `false` value for the `prefer_ilm` setting, as we configured +in the index template +<4> The new write index is managed by `Data stream lifecycle` + +We can easily change this data stream to be managed by {ilm-cap} because we didn't remove +the {ilm-cap} policy when we <>. + +We can achieve this in two ways: + +1. <> from the data streams +2. Disable Data stream lifecycle by configured the `enabled` flag to `false`. + +Let's implement option 2 and disable the data stream lifecycle: + +[source,console] +---- +PUT _data_stream/dsl-data-stream/_lifecycle +{ + "data_retention": "7d", + "enabled": false <1> +} +---- +// TEST[continued] +<1> The `enabled` flag can be ommitted and defaults to `true` however, here we +explicitly configure it to `false` +Let's check the state of the data stream: + +[source,console] +---- +GET _data_stream/dsl-data-stream +---- +// TEST[continued] + +[source,console-result] +---- +{ + "data_streams": [ + { + "name": "dsl-data-stream", + "timestamp_field": { + "name": "@timestamp" + }, + "indices": [ + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000001", + "index_uuid": "xCEhwsp8Tey0-FLNFYVwSg", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" + }, + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000002", + "index_uuid": "PA_JquKGSiKcAKBA8DJ5gw", + "prefer_ilm": true, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" + }, + { + "index_name": ".ds-dsl-data-stream-2023.10.19-000003", + "index_uuid": "PA_JquKGSiKcAKBA8abcd1", + "prefer_ilm": false, + "ilm_policy": "pre-dsl-ilm-policy", + "managed_by": "Index Lifecycle Management" <1> + } + ], + "generation": 3, + "status": "GREEN", + "template": "dsl-data-stream-template", + "lifecycle": { + "enabled": false, <2> + "data_retention": "7d" + }, + "ilm_policy": "pre-dsl-ilm-policy", + "next_generation_managed_by": "Index Lifecycle Management", <3> + "prefer_ilm": false, + "hidden": false, + "system": false, + "allow_custom_routing": false, + "replicated": false + } + ] +} +---- +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000001"/"index_name": $body.data_streams.0.indices.0.index_name/] +// TESTRESPONSE[s/"index_uuid": "xCEhwsp8Tey0-FLNFYVwSg"/"index_uuid": $body.data_streams.0.indices.0.index_uuid/] +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000002"/"index_name": $body.data_streams.0.indices.1.index_name/] +// TESTRESPONSE[s/"index_uuid": "PA_JquKGSiKcAKBA8DJ5gw"/"index_uuid": $body.data_streams.0.indices.1.index_uuid/] +// TESTRESPONSE[s/"index_name": ".ds-dsl-data-stream-2023.10.19-000003"/"index_name": $body.data_streams.0.indices.2.index_name/] +// TESTRESPONSE[s/"index_uuid": "PA_JquKGSiKcAKBA8abcd1"/"index_uuid": $body.data_streams.0.indices.2.index_uuid/] +// TESTRESPONSE[s/"status": "GREEN"/"status": "YELLOW"/] +<1> The write index is now managed by {ilm-cap} +<2> The `lifecycle` configured on the data stream is now disabled. +<3> The next write index will be managed by {ilm-cap} + +Had we removed the {ilm-cap} policy from the index template when we <> +it, the write index of the data stream will now be `Unmanaged` because the index +wouldn't have the {ilm-cap} policy configured to fallback onto. + +////////////////////////// +[source,console] +-------------------------------------------------- +DELETE _data_stream/dsl-data-stream +DELETE _index_template/dsl-data-stream-template +DELETE _ilm/policy/pre-dsl-ilm-policy +-------------------------------------------------- +// TEST[continued] + +////////////////////////// + diff --git a/docs/reference/settings/data-stream-lifecycle-settings.asciidoc b/docs/reference/settings/data-stream-lifecycle-settings.asciidoc index 6860ad56fefaf..8c3f4c793e5e0 100644 --- a/docs/reference/settings/data-stream-lifecycle-settings.asciidoc +++ b/docs/reference/settings/data-stream-lifecycle-settings.asciidoc @@ -30,6 +30,27 @@ this means that your data stream will rollover if any of the following condition * or the index reaches a certain age which depends on the retention time of your data stream, * **and** has at least one document. +[[data-streams-lifecycle-target-merge-factor]] +`data_streams.lifecycle.target.merge.policy.merge_factor`:: +(<>, integer) +Data stream lifecycle implements <> by +updating the lucene merge policy factor for the target backing index. The merge factor +is both the number of segments that should be merged together, and the maximum number +of segments that we expect to find on a given tier. +This setting controls what value does <> +configures on the target index. It defaults to `16`. +The value will be visible under the `index.merge.policy.merge_factor` index setting +on the target index. + +[[data-streams-lifecycle-target-floor-segment]] +`data_streams.lifecycle.target.merge.policy.floor_segment`:: +(<>) +Data stream lifecycle implements <> by +updating the lucene merge policy floor segment for the target backing index. This floor +segment size is a way to prevent indices from having a long tail of very small segments. +This setting controls what value does <> +configures on the target index. It defaults to `100MB`. + ==== Index level settings The following index-level settings are typically configured on the backing indices of a data stream. From 77dac6576127c0ec3b95c29e6e127f5799e93d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Wed, 25 Oct 2023 12:59:10 +0200 Subject: [PATCH 096/203] Fix NodeInfo version parsing in bwc tests (#100838) * Mixed cluster tests with string NodeInfo version - Move version based feature comparison to a common, deprecated method (to be replaced with real features) - Use string comparison against old cluster version to partition new/old cluster nodes --- qa/mixed-cluster/build.gradle | 2 + .../elasticsearch/backwards/IndexingIT.java | 65 ++++++++++----- .../SearchWithMinCompatibleSearchNodeIT.java | 77 +++++++----------- .../ParameterizedRollingUpgradeTestCase.java | 10 ++- .../upgrades/SnapshotBasedRecoveryIT.java | 15 ++-- x-pack/plugin/eql/qa/mixed-node/build.gradle | 2 + .../xpack/eql/qa/mixed_node/EqlSearchIT.java | 4 +- .../org/elasticsearch/xpack/ql/TestNode.java | 3 +- .../org/elasticsearch/xpack/ql/TestNodes.java | 20 ++--- .../org/elasticsearch/xpack/ql/TestUtils.java | 17 ++-- x-pack/plugin/sql/qa/mixed-node/build.gradle | 7 +- .../xpack/sql/qa/mixed_node/SqlCompatIT.java | 4 +- .../xpack/sql/qa/mixed_node/SqlSearchIT.java | 15 ++-- .../upgrades/AbstractUpgradeTestCase.java | 8 +- .../ApiKeyBackwardsCompatibilityIT.java | 12 +-- .../upgrades/DataStreamsUpgradeIT.java | 4 +- .../upgrades/MLModelDeploymentsUpgradeIT.java | 4 +- .../upgrades/MlJobSnapshotUpgradeIT.java | 10 ++- .../upgrades/MlTrainedModelsUpgradeIT.java | 2 +- .../SearchableSnapshotsRollingUpgradeIT.java | 81 +++++++++---------- .../upgrades/TransformSurvivesUpgradeIT.java | 4 +- ...TransportVersionClusterStateUpgradeIT.java | 21 ++--- .../UpgradeClusterClientYamlTestSuiteIT.java | 2 +- 23 files changed, 211 insertions(+), 178 deletions(-) diff --git a/qa/mixed-cluster/build.gradle b/qa/mixed-cluster/build.gradle index f3edc7a90646a..e3796683d1d32 100644 --- a/qa/mixed-cluster/build.gradle +++ b/qa/mixed-cluster/build.gradle @@ -84,6 +84,8 @@ BuildParams.bwcVersions.withWireCompatible { bwcVersion, baseName -> } } systemProperty 'tests.path.repo', "${buildDir}/cluster/shared/repo/${baseName}" + systemProperty 'tests.bwc_nodes_version', bwcVersion.toString().replace('-SNAPSHOT', '') + systemProperty 'tests.new_nodes_version', project.version.toString().replace('-SNAPSHOT', '') onlyIf("BWC tests disabled") { project.bwc_tests_enabled } } diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java index b2370c6e564ef..75a55e6e69af1 100644 --- a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java @@ -8,7 +8,6 @@ package org.elasticsearch.backwards; import org.apache.http.HttpHost; -import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; @@ -32,6 +31,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.hamcrest.Matchers.contains; @@ -39,6 +40,7 @@ import static org.hamcrest.Matchers.oneOf; public class IndexingIT extends ESRestTestCase { + protected static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); private int indexDocs(String index, final int idStart, final int numDocs) throws IOException { for (int i = 0; i < numDocs; i++) { @@ -188,7 +190,7 @@ public void testSeqNoCheckpoints() throws Exception { final int numberOfInitialDocs = 1 + randomInt(5); logger.info("indexing [{}] docs initially", numberOfInitialDocs); numDocs += indexDocs(index, 0, numberOfInitialDocs); - assertSeqNoOnShards(index, nodes, nodes.getBWCVersion().major >= 6 ? numDocs : 0, newNodeClient); + assertSeqNoOnShards(index, nodes, numDocs, newNodeClient); logger.info("allowing shards on all nodes"); updateIndexSettings(index, Settings.builder().putNull("index.routing.allocation.include._name")); ensureGreen(index); @@ -199,7 +201,7 @@ public void testSeqNoCheckpoints() throws Exception { final int numberOfDocsAfterAllowingShardsOnAllNodes = 1 + randomInt(5); logger.info("indexing [{}] docs after allowing shards on all nodes", numberOfDocsAfterAllowingShardsOnAllNodes); numDocs += indexDocs(index, numDocs, numberOfDocsAfterAllowingShardsOnAllNodes); - assertSeqNoOnShards(index, nodes, nodes.getBWCVersion().major >= 6 ? numDocs : 0, newNodeClient); + assertSeqNoOnShards(index, nodes, numDocs, newNodeClient); Shard primary = buildShards(index, nodes, newNodeClient).stream().filter(Shard::primary).findFirst().get(); logger.info("moving primary to new node by excluding {}", primary.node().nodeName()); updateIndexSettings(index, Settings.builder().put("index.routing.allocation.exclude._name", primary.node().nodeName())); @@ -209,7 +211,7 @@ public void testSeqNoCheckpoints() throws Exception { logger.info("indexing [{}] docs after moving primary", numberOfDocsAfterMovingPrimary); numDocsOnNewPrimary += indexDocs(index, numDocs, numberOfDocsAfterMovingPrimary); numDocs += numberOfDocsAfterMovingPrimary; - assertSeqNoOnShards(index, nodes, nodes.getBWCVersion().major >= 6 ? numDocs : numDocsOnNewPrimary, newNodeClient); + assertSeqNoOnShards(index, nodes, numDocs, newNodeClient); /* * Dropping the number of replicas to zero, and then increasing it to one triggers a recovery thus exercising any BWC-logic in * the recovery code. @@ -228,7 +230,7 @@ public void testSeqNoCheckpoints() throws Exception { for (Shard shard : buildShards(index, nodes, newNodeClient)) { assertCount(index, "_only_nodes:" + shard.node.nodeName, numDocs); } - assertSeqNoOnShards(index, nodes, nodes.getBWCVersion().major >= 6 ? numDocs : numDocsOnNewPrimary, newNodeClient); + assertSeqNoOnShards(index, nodes, numDocs, newNodeClient); } } @@ -283,9 +285,36 @@ public void testUpdateSnapshotStatus() throws Exception { request.setJsonEntity("{\"indices\": \"" + index + "\"}"); } + /** + * Tries to extract a major version from a version string, if this is in the major.minor.revision format + * @param version a string representing a version. Can be opaque or semantic + * @return Optional.empty() if the format is not recognized, or an Optional containing the major Integer otherwise + */ + private static Optional extractLegacyMajorVersion(String version) { + var semanticVersionMatcher = Pattern.compile("^(\\d+)\\.\\d+\\.\\d+\\D?.*").matcher(version); + if (semanticVersionMatcher.matches() == false) { + return Optional.empty(); + } + var major = Integer.parseInt(semanticVersionMatcher.group(1)); + return Optional.of(major); + } + + private static boolean syncedFlushDeprecated() { + // Only versions past 8.10 can be non-semantic, so we can safely assume that non-semantic versions have this "feature" + return extractLegacyMajorVersion(BWC_NODES_VERSION).map(m -> m >= 7).orElse(true); + } + + private static boolean syncedFlushRemoved() { + // Only versions past 8.10 can be non-semantic, so we can safely assume that non-semantic versions have this "feature" + return extractLegacyMajorVersion(BWC_NODES_VERSION).map(m -> m >= 8).orElse(true); + } + public void testSyncedFlushTransition() throws Exception { Nodes nodes = buildNodeAndVersions(); - assumeTrue("bwc version is on 7.x", nodes.getBWCVersion().before(Version.V_8_0_0)); + assumeTrue( + "bwc version is on 7.x (synced flush deprecated but not removed yet)", + syncedFlushDeprecated() && syncedFlushRemoved() == false + ); assumeFalse("no new node found", nodes.getNewNodes().isEmpty()); assumeFalse("no bwc node found", nodes.getBWCNodes().isEmpty()); // Allocate shards to new nodes then verify synced flush requests processed by old nodes/new nodes @@ -496,13 +525,13 @@ static Nodes buildNodeAndVersions(RestClient client) throws IOException { Response response = client.performRequest(new Request("GET", "_nodes")); ObjectPath objectPath = ObjectPath.createFromResponse(response); Map nodesAsMap = objectPath.evaluate("nodes"); - Nodes nodes = new Nodes(); + Nodes nodes = new Nodes(BWC_NODES_VERSION); for (String id : nodesAsMap.keySet()) { nodes.add( new Node( id, objectPath.evaluate("nodes." + id + ".name"), - Version.fromString(objectPath.evaluate("nodes." + id + ".version")), + objectPath.evaluate("nodes." + id + ".version"), HttpHost.create(objectPath.evaluate("nodes." + id + ".http.publish_address")) ) ); @@ -514,8 +543,13 @@ static Nodes buildNodeAndVersions(RestClient client) throws IOException { static final class Nodes extends HashMap { + private final String bwcNodesVersion; private String masterNodeId = null; + Nodes(String bwcNodesVersion) { + this.bwcNodesVersion = bwcNodesVersion; + } + public Node getMaster() { return get(masterNodeId); } @@ -532,20 +566,11 @@ public void add(Node node) { } public List getNewNodes() { - Version bwcVersion = getBWCVersion(); - return values().stream().filter(n -> n.version().after(bwcVersion)).collect(Collectors.toList()); + return values().stream().filter(n -> n.version().equals(bwcNodesVersion) == false).collect(Collectors.toList()); } public List getBWCNodes() { - Version bwcVersion = getBWCVersion(); - return values().stream().filter(n -> n.version().equals(bwcVersion)).collect(Collectors.toList()); - } - - public Version getBWCVersion() { - if (isEmpty()) { - throw new IllegalStateException("no nodes available"); - } - return Version.fromId(values().stream().map(node -> node.version().id).min(Integer::compareTo).get()); + return values().stream().filter(n -> n.version().equals(bwcNodesVersion)).collect(Collectors.toList()); } public Node getSafe(String id) { @@ -567,7 +592,7 @@ public String toString() { } } - record Node(String id, String nodeName, Version version, HttpHost publishAddress) {} + record Node(String id, String nodeName, String version, HttpHost publishAddress) {} record Shard(Node node, boolean primary, SeqNoStats seqNoStats) {} } diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java index a0f0f1319b40d..f28e014837dda 100644 --- a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java @@ -8,7 +8,6 @@ package org.elasticsearch.backwards; import org.apache.http.HttpHost; -import org.elasticsearch.Version; import org.elasticsearch.backwards.IndexingIT.Node; import org.elasticsearch.backwards.IndexingIT.Nodes; import org.elasticsearch.client.Request; @@ -17,7 +16,6 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.core.CheckedRunnable; import org.elasticsearch.core.Strings; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.rest.ESRestTestCase; @@ -34,14 +32,15 @@ public class SearchWithMinCompatibleSearchNodeIT extends ESRestTestCase { + protected static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + protected static final String NEW_NODES_VERSION = System.getProperty("tests.new_nodes_version"); + private static String index = "test_min_version"; private static int numShards; private static int numReplicas = 1; private static int numDocs; private static Nodes nodes; private static List allNodes; - private static Version bwcVersion; - private static Version newVersion; @Before public void prepareTestData() throws IOException { @@ -51,8 +50,6 @@ public void prepareTestData() throws IOException { allNodes = new ArrayList<>(); allNodes.addAll(nodes.getBWCNodes()); allNodes.addAll(nodes.getNewNodes()); - bwcVersion = nodes.getBWCNodes().get(0).version(); - newVersion = nodes.getNewNodes().get(0).version(); if (client().performRequest(new Request("HEAD", "/" + index)).getStatusLine().getStatusCode() == 404) { createIndex( @@ -75,7 +72,7 @@ public void testMinVersionAsNewVersion() throws Exception { try (RestClient client = buildClient(restClientSettings(), allNodes.stream().map(Node::publishAddress).toArray(HttpHost[]::new))) { Request newVersionRequest = new Request( "POST", - index + "/_search?min_compatible_shard_node=" + newVersion + "&ccs_minimize_roundtrips=false" + index + "/_search?min_compatible_shard_node=" + NEW_NODES_VERSION + "&ccs_minimize_roundtrips=false" ); assertBusy(() -> { ResponseException responseException = expectThrows(ResponseException.class, () -> client.performRequest(newVersionRequest)); @@ -87,7 +84,7 @@ public void testMinVersionAsNewVersion() throws Exception { {"error":{"root_cause":[],"type":"search_phase_execution_exception\"""")); assertThat(responseException.getMessage(), containsString(Strings.format(""" caused_by":{"type":"version_mismatch_exception",\ - "reason":"One of the shards is incompatible with the required minimum version [%s]\"""", newVersion))); + "reason":"One of the shards is incompatible with the required minimum version [%s]\"""", NEW_NODES_VERSION))); }); } } @@ -96,64 +93,46 @@ public void testMinVersionAsOldVersion() throws Exception { try (RestClient client = buildClient(restClientSettings(), allNodes.stream().map(Node::publishAddress).toArray(HttpHost[]::new))) { Request oldVersionRequest = new Request( "POST", - index + "/_search?min_compatible_shard_node=" + bwcVersion + "&ccs_minimize_roundtrips=false" + index + "/_search?min_compatible_shard_node=" + BWC_NODES_VERSION + "&ccs_minimize_roundtrips=false" ); oldVersionRequest.setJsonEntity(""" {"query":{"match_all":{}},"_source":false}"""); assertBusy(() -> { - assertWithBwcVersionCheck(() -> { - Response response = client.performRequest(oldVersionRequest); - ObjectPath responseObject = ObjectPath.createFromResponse(response); - Map shardsResult = responseObject.evaluate("_shards"); - assertThat(shardsResult.get("total"), equalTo(numShards)); - assertThat(shardsResult.get("successful"), equalTo(numShards)); - assertThat(shardsResult.get("failed"), equalTo(0)); - Map hitsResult = responseObject.evaluate("hits.total"); - assertThat(hitsResult.get("value"), equalTo(numDocs)); - assertThat(hitsResult.get("relation"), equalTo("eq")); - }, client, oldVersionRequest); + Response response = client.performRequest(oldVersionRequest); + ObjectPath responseObject = ObjectPath.createFromResponse(response); + Map shardsResult = responseObject.evaluate("_shards"); + assertThat(shardsResult.get("total"), equalTo(numShards)); + assertThat(shardsResult.get("successful"), equalTo(numShards)); + assertThat(shardsResult.get("failed"), equalTo(0)); + Map hitsResult = responseObject.evaluate("hits.total"); + assertThat(hitsResult.get("value"), equalTo(numDocs)); + assertThat(hitsResult.get("relation"), equalTo("eq")); }); } } public void testCcsMinimizeRoundtripsIsFalse() throws Exception { try (RestClient client = buildClient(restClientSettings(), allNodes.stream().map(Node::publishAddress).toArray(HttpHost[]::new))) { - Version version = randomBoolean() ? newVersion : bwcVersion; + String version = randomBoolean() ? NEW_NODES_VERSION : BWC_NODES_VERSION; Request request = new Request( "POST", index + "/_search?min_compatible_shard_node=" + version + "&ccs_minimize_roundtrips=true" ); assertBusy(() -> { - assertWithBwcVersionCheck(() -> { - ResponseException responseException = expectThrows(ResponseException.class, () -> client.performRequest(request)); - assertThat( - responseException.getResponse().getStatusLine().getStatusCode(), - equalTo(RestStatus.BAD_REQUEST.getStatus()) - ); - assertThat(responseException.getMessage(), containsString(""" - {"error":{"root_cause":[{"type":"action_request_validation_exception"\ - """)); - assertThat( - responseException.getMessage(), - containsString( - "\"reason\":\"Validation Failed: 1: " - + "[ccs_minimize_roundtrips] cannot be [true] when setting a minimum compatible shard version;\"" - ) - ); - }, client, request); + ResponseException responseException = expectThrows(ResponseException.class, () -> client.performRequest(request)); + assertThat(responseException.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.BAD_REQUEST.getStatus())); + assertThat(responseException.getMessage(), containsString(""" + {"error":{"root_cause":[{"type":"action_request_validation_exception"\ + """)); + assertThat( + responseException.getMessage(), + containsString( + "\"reason\":\"Validation Failed: 1: " + + "[ccs_minimize_roundtrips] cannot be [true] when setting a minimum compatible shard version;\"" + ) + ); }); } } - - private void assertWithBwcVersionCheck(CheckedRunnable code, RestClient client, Request request) throws Exception { - if (bwcVersion.before(Version.V_7_12_0)) { - // min_compatible_shard_node support doesn't exist in older versions and there will be an "unrecognized parameter" exception - ResponseException exception = expectThrows(ResponseException.class, () -> client.performRequest(request)); - assertThat(exception.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.BAD_REQUEST.getStatus())); - assertThat(exception.getMessage(), containsString("contains unrecognized parameter: [min_compatible_shard_node]")); - } else { - code.run(); - } - } } diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java index 26b1f8a0153b6..5a2c4c783ec85 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ParameterizedRollingUpgradeTestCase.java @@ -39,7 +39,7 @@ import static org.hamcrest.Matchers.notNullValue; public abstract class ParameterizedRollingUpgradeTestCase extends ESRestTestCase { - private static final Version OLD_CLUSTER_VERSION = Version.fromString(System.getProperty("tests.old_cluster_version")); + private static final String OLD_CLUSTER_VERSION = System.getProperty("tests.old_cluster_version"); private static final TemporaryFolder repoDirectory = new TemporaryFolder(); @@ -142,7 +142,7 @@ public static void resetNodes() { } protected static org.elasticsearch.Version getOldClusterVersion() { - return org.elasticsearch.Version.fromString(OLD_CLUSTER_VERSION.toString()); + return org.elasticsearch.Version.fromString(OLD_CLUSTER_VERSION); } protected static IndexVersion getOldClusterIndexVersion() { @@ -151,7 +151,11 @@ protected static IndexVersion getOldClusterIndexVersion() { } protected static Version getOldClusterTestVersion() { - return Version.fromString(OLD_CLUSTER_VERSION.toString()); + return Version.fromString(OLD_CLUSTER_VERSION); + } + + protected static boolean isOldClusterVersion(String nodeVersion) { + return OLD_CLUSTER_VERSION.equals(nodeVersion); } protected static boolean isOldCluster() { diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java index 95f4c55314199..4b765849e6ea9 100644 --- a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/SnapshotBasedRecoveryIT.java @@ -100,7 +100,7 @@ public void testSnapshotBasedRecovery() throws Exception { } String primaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); - Version primaryNodeVersion = getNodeVersion(primaryNodeId); + String primaryNodeVersion = getNodeVersion(primaryNodeId); // Sometimes the primary shard ends on the upgraded node (i.e. after a rebalance) // This causes issues when removing and adding replicas, since then we cannot allocate to any of the old nodes. @@ -108,13 +108,13 @@ public void testSnapshotBasedRecovery() throws Exception { // In that case we exclude the upgraded node from the shard allocation and cancel the shard to force moving // the primary to a node in the old version, this allows adding replicas in the first mixed round. logger.info("--> Primary node in first mixed round {} / {}", primaryNodeId, primaryNodeVersion); - if (primaryNodeVersion.after(getOldClusterVersion())) { + if (isOldClusterVersion(primaryNodeVersion) == false) { logger.info("--> cancelling primary shard on node [{}]", primaryNodeId); cancelShard(indexName, 0, primaryNodeId); logger.info("--> done cancelling primary shard on node [{}]", primaryNodeId); String currentPrimaryNodeId = getPrimaryNodeIdOfShard(indexName, 0); - assertThat(getNodeVersion(currentPrimaryNodeId), is(equalTo(getOldClusterVersion()))); + assertTrue(isOldClusterVersion(getNodeVersion(currentPrimaryNodeId))); } } else { logger.info("--> not in first upgrade round, removing exclusions for [{}]", indexName); @@ -148,19 +148,18 @@ private List getUpgradedNodeIds() throws IOException { Map> nodes = extractValue(responseMap, "nodes"); List upgradedNodes = new ArrayList<>(); for (Map.Entry> nodeInfoEntry : nodes.entrySet()) { - Version nodeVersion = Version.fromString(extractValue(nodeInfoEntry.getValue(), "version")); - if (nodeVersion.after(getOldClusterVersion())) { + String nodeVersion = extractValue(nodeInfoEntry.getValue(), "version"); + if (isOldClusterVersion(nodeVersion) == false) { upgradedNodes.add(nodeInfoEntry.getKey()); } } return upgradedNodes; } - private Version getNodeVersion(String primaryNodeId) throws IOException { + private String getNodeVersion(String primaryNodeId) throws IOException { Request request = new Request(HttpGet.METHOD_NAME, "_nodes/" + primaryNodeId); Response response = client().performRequest(request); - String nodeVersion = extractValue(responseAsMap(response), "nodes." + primaryNodeId + ".version"); - return Version.fromString(nodeVersion); + return extractValue(responseAsMap(response), "nodes." + primaryNodeId + ".version"); } private String getPrimaryNodeIdOfShard(String indexName, int shard) throws Exception { diff --git a/x-pack/plugin/eql/qa/mixed-node/build.gradle b/x-pack/plugin/eql/qa/mixed-node/build.gradle index 3e1010e2c0eb9..8b9e082215fc4 100644 --- a/x-pack/plugin/eql/qa/mixed-node/build.gradle +++ b/x-pack/plugin/eql/qa/mixed-node/build.gradle @@ -43,6 +43,8 @@ BuildParams.bwcVersions.withWireCompatible(v -> v.onOrAfter("7.10.0") && nonInputProperties.systemProperty('tests.rest.cluster', cluster.map(c -> c.allHttpSocketURI.join(","))) nonInputProperties.systemProperty('tests.clustername', baseName) } + systemProperty 'tests.bwc_nodes_version', bwcVersion.toString().replace('-SNAPSHOT', '') + systemProperty 'tests.new_nodes_version', project.version.toString().replace('-SNAPSHOT', '') onlyIf("BWC tests disabled") { project.bwc_tests_enabled } } diff --git a/x-pack/plugin/eql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/eql/qa/mixed_node/EqlSearchIT.java b/x-pack/plugin/eql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/eql/qa/mixed_node/EqlSearchIT.java index 5446e9c27a81a..d8b887b98e647 100644 --- a/x-pack/plugin/eql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/eql/qa/mixed_node/EqlSearchIT.java +++ b/x-pack/plugin/eql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/eql/qa/mixed_node/EqlSearchIT.java @@ -49,6 +49,8 @@ */ public class EqlSearchIT extends ESRestTestCase { + private static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + private static final String index = "test_eql_mixed_versions"; private static int numShards; private static int numReplicas = 1; @@ -59,7 +61,7 @@ public class EqlSearchIT extends ESRestTestCase { @Before public void createIndex() throws IOException { - nodes = buildNodeAndVersions(client()); + nodes = buildNodeAndVersions(client(), BWC_NODES_VERSION); numShards = nodes.size(); numDocs = randomIntBetween(numShards, 15); newNodes = new ArrayList<>(nodes.getNewNodes()); diff --git a/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNode.java b/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNode.java index 509e09415ec0d..0a39a25dd8b32 100644 --- a/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNode.java +++ b/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNode.java @@ -9,10 +9,9 @@ import org.apache.http.HttpHost; import org.elasticsearch.TransportVersion; -import org.elasticsearch.Version; import org.elasticsearch.core.Nullable; -public record TestNode(String id, Version version, @Nullable TransportVersion transportVersion, HttpHost publishAddress) { +public record TestNode(String id, String version, @Nullable TransportVersion transportVersion, HttpHost publishAddress) { @Override public String toString() { return "Node{" + "id='" + id + '\'' + ", version=" + version + '}'; diff --git a/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNodes.java b/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNodes.java index af853670a8eb9..25368fa4e15bd 100644 --- a/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNodes.java +++ b/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestNodes.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.ql; import org.elasticsearch.TransportVersion; -import org.elasticsearch.Version; import java.util.Comparator; import java.util.HashMap; @@ -18,25 +17,22 @@ public final class TestNodes extends HashMap { + private final String bwcNodesVersion; + + TestNodes(String bwcNodesVersion) { + this.bwcNodesVersion = bwcNodesVersion; + } + public void add(TestNode node) { put(node.id(), node); } public List getNewNodes() { - Version bwcVersion = getBWCVersion(); - return values().stream().filter(n -> n.version().after(bwcVersion)).collect(Collectors.toList()); + return values().stream().filter(n -> n.version().equals(bwcNodesVersion) == false).collect(Collectors.toList()); } public List getBWCNodes() { - Version bwcVersion = getBWCVersion(); - return values().stream().filter(n -> n.version().equals(bwcVersion)).collect(Collectors.toList()); - } - - public Version getBWCVersion() { - if (isEmpty()) { - throw new IllegalStateException("no nodes available"); - } - return values().stream().map(TestNode::version).min(Comparator.naturalOrder()).get(); + return values().stream().filter(n -> n.version().equals(bwcNodesVersion)).collect(Collectors.toList()); } public TransportVersion getBWCTransportVersion() { diff --git a/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestUtils.java b/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestUtils.java index 8fc0963a8f210..a395ac7766b0a 100644 --- a/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestUtils.java +++ b/x-pack/plugin/ql/test-fixtures/src/main/java/org/elasticsearch/xpack/ql/TestUtils.java @@ -295,22 +295,27 @@ public static Tuple pathAndName(String string) { return new Tuple<>(folder, file); } - public static TestNodes buildNodeAndVersions(RestClient client) throws IOException { + public static TestNodes buildNodeAndVersions(RestClient client, String bwcNodesVersion) throws IOException { Response response = client.performRequest(new Request("GET", "_nodes")); ObjectPath objectPath = ObjectPath.createFromResponse(response); Map nodesAsMap = objectPath.evaluate("nodes"); - TestNodes nodes = new TestNodes(); + TestNodes nodes = new TestNodes(bwcNodesVersion); for (String id : nodesAsMap.keySet()) { - Version nodeVersion = Version.fromString(objectPath.evaluate("nodes." + id + ".version")); + String nodeVersion = objectPath.evaluate("nodes." + id + ".version"); Object tvField; TransportVersion transportVersion = null; - if (nodeVersion.before(Version.V_8_8_0)) { - transportVersion = TransportVersion.fromId(nodeVersion.id); // no transport_version field - } else if ((tvField = objectPath.evaluate("nodes." + id + ".transport_version")) != null) { + if ((tvField = objectPath.evaluate("nodes." + id + ".transport_version")) != null) { // this json might be from a node <8.8.0, but about a node >=8.8.0 // in which case the transport_version field won't exist. Just ignore it for now. transportVersion = TransportVersion.fromString(tvField.toString()); + } else { // no transport_version field + // this json might be from a node <8.8.0, but about a node >=8.8.0 + // In that case the transport_version field won't exist. Just ignore it for now. + Version version = Version.fromString(nodeVersion); + if (version.before(Version.V_8_8_0)) { + transportVersion = TransportVersion.fromId(version.id); + } } nodes.add( diff --git a/x-pack/plugin/sql/qa/mixed-node/build.gradle b/x-pack/plugin/sql/qa/mixed-node/build.gradle index 13bf18e6c080d..412dec62f81f8 100644 --- a/x-pack/plugin/sql/qa/mixed-node/build.gradle +++ b/x-pack/plugin/sql/qa/mixed-node/build.gradle @@ -50,8 +50,11 @@ BuildParams.bwcVersions.withWireCompatible(v -> v.onOrAfter("7.10.3") && nonInputProperties.systemProperty('tests.rest.cluster', baseCluster.map(c->c.allHttpSocketURI.join(","))) nonInputProperties.systemProperty('tests.clustername', baseName) - } - onlyIf("BWC tests disabled") { project.bwc_tests_enabled } + } + systemProperty 'tests.bwc_nodes_version', bwcVersion.toString().replace('-SNAPSHOT', '') + systemProperty 'tests.new_nodes_version', project.version.toString().replace('-SNAPSHOT', '') + + onlyIf("BWC tests disabled") { project.bwc_tests_enabled } } tasks.register(bwcTaskName(bwcVersion)) { diff --git a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlCompatIT.java b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlCompatIT.java index 5bc7e0dd219ca..bb9f707a7f61e 100644 --- a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlCompatIT.java +++ b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlCompatIT.java @@ -38,6 +38,8 @@ public class SqlCompatIT extends BaseRestSqlTestCase { + private static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + private static RestClient newNodesClient; private static RestClient oldNodesClient; private static TransportVersion bwcVersion; @@ -47,7 +49,7 @@ public void initBwcClients() throws IOException { if (newNodesClient == null) { assertNull(oldNodesClient); - TestNodes nodes = buildNodeAndVersions(client()); + TestNodes nodes = buildNodeAndVersions(client(), BWC_NODES_VERSION); bwcVersion = nodes.getBWCTransportVersion(); newNodesClient = buildClient( restClientSettings(), diff --git a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java index acb78a2458c86..41ebd6adffd41 100644 --- a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java +++ b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java @@ -40,7 +40,12 @@ import static org.elasticsearch.xpack.ql.TestUtils.readResource; public class SqlSearchIT extends ESRestTestCase { - private static final Version VERSION_FIELD_QL_INTRODUCTION = Version.V_8_4_0; + + private static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + + // TODO[lor]: replace this with feature-based checks when we have one + private static final boolean SUPPORTS_VERSION_FIELD_QL_INTRODUCTION = Version.fromString(BWC_NODES_VERSION).onOrAfter(Version.V_8_4_0); + private static final String index = "test_sql_mixed_versions"; private static int numShards; private static int numReplicas = 1; @@ -48,16 +53,14 @@ public class SqlSearchIT extends ESRestTestCase { private static TestNodes nodes; private static List newNodes; private static List bwcNodes; - private static Version bwcVersion; @Before public void createIndex() throws IOException { - nodes = buildNodeAndVersions(client()); + nodes = buildNodeAndVersions(client(), BWC_NODES_VERSION); numShards = nodes.size(); numDocs = randomIntBetween(numShards, 15); newNodes = new ArrayList<>(nodes.getNewNodes()); bwcNodes = new ArrayList<>(nodes.getBWCNodes()); - bwcVersion = nodes.getBWCNodes().get(0).version(); String mappings = readResource(SqlSearchIT.class.getResourceAsStream("/all_field_types.json")); createIndex( @@ -153,7 +156,7 @@ private Map prepareTestData( columns.add(columnInfo("scaled_float_field", "scaled_float")); columns.add(columnInfo("boolean_field", "boolean")); columns.add(columnInfo("ip_field", "ip")); - if (bwcVersion.onOrAfter(VERSION_FIELD_QL_INTRODUCTION)) { + if (SUPPORTS_VERSION_FIELD_QL_INTRODUCTION) { columns.add(columnInfo("version_field", "version")); } columns.add(columnInfo("text_field", "text")); @@ -187,7 +190,7 @@ private Map prepareTestData( builder.append("\"scaled_float_field\":" + fieldValues.computeIfAbsent("scaled_float_field", v -> 123.5d) + ","); builder.append("\"boolean_field\":" + fieldValues.computeIfAbsent("boolean_field", v -> randomBoolean()) + ","); builder.append("\"ip_field\":\"" + fieldValues.computeIfAbsent("ip_field", v -> "123.123.123.123") + "\","); - if (bwcVersion.onOrAfter(VERSION_FIELD_QL_INTRODUCTION)) { + if (SUPPORTS_VERSION_FIELD_QL_INTRODUCTION) { builder.append( "\"version_field\":\"" + fieldValues.computeIfAbsent("version_field", v -> randomInt() + "." + randomInt() + "." + randomInt()) diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractUpgradeTestCase.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractUpgradeTestCase.java index 368b8033e0b8d..865ba0c07cfeb 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractUpgradeTestCase.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/AbstractUpgradeTestCase.java @@ -30,10 +30,16 @@ public abstract class AbstractUpgradeTestCase extends ESRestTestCase { new SecureString(SecuritySettingsSourceField.TEST_PASSWORD) ); - protected static final Version UPGRADE_FROM_VERSION = Version.fromString(System.getProperty("tests.upgrade_from_version")); + protected static final String UPGRADE_FROM_VERSION = System.getProperty("tests.upgrade_from_version"); protected static final boolean SKIP_ML_TESTS = Booleans.parseBoolean(System.getProperty("tests.ml.skip", "false")); + // TODO: replace with feature testing + @Deprecated + protected static boolean isOriginalClusterVersionAtLeast(Version supportedVersion) { + return Version.fromString(UPGRADE_FROM_VERSION).onOrAfter(supportedVersion); + } + @Override protected boolean resetFeatureStates() { return false; diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java index ee50f349afddf..850a94f7133e9 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/ApiKeyBackwardsCompatibilityIT.java @@ -54,7 +54,7 @@ public class ApiKeyBackwardsCompatibilityIT extends AbstractUpgradeTestCase { public void testCreatingAndUpdatingApiKeys() throws Exception { assumeTrue( "The remote_indices for API Keys are not supported before version " + API_KEY_SUPPORT_REMOTE_INDICES_VERSION, - UPGRADE_FROM_VERSION.before(API_KEY_SUPPORT_REMOTE_INDICES_VERSION) + isOriginalClusterVersionAtLeast(API_KEY_SUPPORT_REMOTE_INDICES_VERSION) == false ); switch (CLUSTER_TYPE) { case OLD -> { @@ -183,7 +183,7 @@ private Tuple createOrGrantApiKey(RestClient client, String role "role_descriptors": %s }""", name, roles); // Grant API did not exist before 7.7.0 - final boolean grantApiKey = randomBoolean() && UPGRADE_FROM_VERSION.onOrAfter(Version.V_7_7_0); + final boolean grantApiKey = randomBoolean() && isOriginalClusterVersionAtLeast(Version.V_7_7_0); if (grantApiKey) { createApiKeyRequest = new Request("POST", "/_security/api_key/grant"); createApiKeyRequest.setJsonEntity(org.elasticsearch.common.Strings.format(""" @@ -220,16 +220,16 @@ private void updateOrBulkUpdateApiKey(String id, String roles) throws IOExceptio private boolean isUpdateApiSupported(RestClient client) { return switch (CLUSTER_TYPE) { - case OLD -> UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_4_0); // Update API was introduced in 8.4.0. - case MIXED -> UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_4_0) || client == newVersionClient; + case OLD -> isOriginalClusterVersionAtLeast(Version.V_8_4_0); // Update API was introduced in 8.4.0. + case MIXED -> isOriginalClusterVersionAtLeast(Version.V_8_4_0) || client == newVersionClient; case UPGRADED -> true; }; } private boolean isBulkUpdateApiSupported(RestClient client) { return switch (CLUSTER_TYPE) { - case OLD -> UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_5_0); // Bulk update API was introduced in 8.5.0. - case MIXED -> UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_5_0) || client == newVersionClient; + case OLD -> isOriginalClusterVersionAtLeast(Version.V_8_5_0); // Bulk update API was introduced in 8.5.0. + case MIXED -> isOriginalClusterVersionAtLeast(Version.V_8_5_0) || client == newVersionClient; case UPGRADED -> true; }; } diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DataStreamsUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DataStreamsUpgradeIT.java index 4c399d0a01a93..cf2a66bc4fb5b 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DataStreamsUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/DataStreamsUpgradeIT.java @@ -25,7 +25,7 @@ public class DataStreamsUpgradeIT extends AbstractUpgradeTestCase { public void testDataStreams() throws IOException { - assumeTrue("no data streams in versions before " + Version.V_7_9_0, UPGRADE_FROM_VERSION.onOrAfter(Version.V_7_9_0)); + assumeTrue("no data streams in versions before " + Version.V_7_9_0, isOriginalClusterVersionAtLeast(Version.V_7_9_0)); if (CLUSTER_TYPE == ClusterType.OLD) { String requestBody = """ { @@ -110,7 +110,7 @@ public void testDataStreams() throws IOException { } public void testDataStreamValidationDoesNotBreakUpgrade() throws Exception { - assumeTrue("Bug started to occur from version: " + Version.V_7_10_2, UPGRADE_FROM_VERSION.onOrAfter(Version.V_7_10_2)); + assumeTrue("Bug started to occur from version: " + Version.V_7_10_2, isOriginalClusterVersionAtLeast(Version.V_7_10_2)); if (CLUSTER_TYPE == ClusterType.OLD) { String requestBody = """ { diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java index 87d605d29fa86..848dfa6e12ae2 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java @@ -99,7 +99,7 @@ public void removeLogging() throws IOException { @AwaitsFix(bugUrl = "mute to try and reproduce https://github.com/elastic/elasticsearch/issues/100379") public void testTrainedModelDeployment() throws Exception { - assumeTrue("NLP model deployments added in 8.0", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_0_0)); + assumeTrue("NLP model deployments added in 8.0", isOriginalClusterVersionAtLeast(Version.V_8_0_0)); final String modelId = "upgrade-deployment-test"; @@ -135,7 +135,7 @@ public void testTrainedModelDeployment() throws Exception { } public void testTrainedModelDeploymentStopOnMixedCluster() throws Exception { - assumeTrue("NLP model deployments added in 8.0", UPGRADE_FROM_VERSION.onOrAfter(Version.V_8_0_0)); + assumeTrue("NLP model deployments added in 8.0", isOriginalClusterVersionAtLeast(Version.V_8_0_0)); final String modelId = "upgrade-deployment-test-stop-mixed-cluster"; diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java index 8111e9e68df8a..9913c40dac411 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlJobSnapshotUpgradeIT.java @@ -76,7 +76,10 @@ public void testSnapshotUpgrader() throws Exception { switch (CLUSTER_TYPE) { case OLD -> createJobAndSnapshots(); case MIXED -> { - assumeTrue("We should only test if old cluster is before new cluster", UPGRADE_FROM_VERSION.before(Version.CURRENT)); + assumeTrue( + "We should only test if old cluster is before new cluster", + isOriginalClusterVersionAtLeast(Version.CURRENT) == false + ); ensureHealth((request -> { request.addParameter("timeout", "70s"); request.addParameter("wait_for_nodes", "3"); @@ -85,7 +88,10 @@ public void testSnapshotUpgrader() throws Exception { testSnapshotUpgradeFailsOnMixedCluster(); } case UPGRADED -> { - assumeTrue("We should only test if old cluster is before new cluster", UPGRADE_FROM_VERSION.before(Version.CURRENT)); + assumeTrue( + "We should only test if old cluster is before new cluster", + isOriginalClusterVersionAtLeast(Version.CURRENT) == false + ); ensureHealth((request -> { request.addParameter("timeout", "70s"); request.addParameter("wait_for_nodes", "3"); diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlTrainedModelsUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlTrainedModelsUpgradeIT.java index 23ea59811a24d..5427fb0c6cc88 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlTrainedModelsUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlTrainedModelsUpgradeIT.java @@ -57,7 +57,7 @@ protected Collection templatesToWaitFor() { } public void testTrainedModelInference() throws Exception { - assumeTrue("We should only test if old cluster is after trained models we GA", UPGRADE_FROM_VERSION.after(Version.V_7_13_0)); + assumeTrue("We should only test if old cluster is after trained models went GA", isOriginalClusterVersionAtLeast(Version.V_7_13_1)); switch (CLUSTER_TYPE) { case OLD -> { createIndexWithName(INDEX_NAME); diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SearchableSnapshotsRollingUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SearchableSnapshotsRollingUpgradeIT.java index 9225fbfaa6642..3cdeb6dab4d91 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SearchableSnapshotsRollingUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/SearchableSnapshotsRollingUpgradeIT.java @@ -163,29 +163,28 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs final int numberOfNodes = 3; waitForNodes(numberOfNodes); - final Map nodesIdsAndVersions = nodesVersions(); + final Map nodesIdsAndVersions = nodesVersions(); assertThat("Cluster should have 3 nodes", nodesIdsAndVersions.size(), equalTo(numberOfNodes)); - final Version minVersion = nodesIdsAndVersions.values().stream().min(Version::compareTo).get(); - final Version maxVersion = nodesIdsAndVersions.values().stream().max(Version::compareTo).get(); - - final String nodeIdWithMinVersion = randomFrom( - nodesIdsAndVersions.entrySet() - .stream() - .filter(node -> minVersion.equals(node.getValue())) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()) - ); - - final String nodeIdWithMaxVersion = randomValueOtherThan( - nodeIdWithMinVersion, - () -> randomFrom( - nodesIdsAndVersions.entrySet() - .stream() - .filter(node -> maxVersion.equals(node.getValue())) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()) - ) + final var newVersionNodes = nodesIdsAndVersions.entrySet() + .stream() + .filter(node -> UPGRADE_FROM_VERSION.equals(node.getValue()) == false) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + + final var originalVersionNodes = nodesIdsAndVersions.entrySet() + .stream() + .filter(node -> UPGRADE_FROM_VERSION.equals(node.getValue())) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + + final String nodeIdWithOriginalVersion = randomFrom(originalVersionNodes); + + // We may not have upgraded nodes, if we are running these test on the same version (original == current) + final var upgradedVersionNodes = newVersionNodes.isEmpty() ? originalVersionNodes : newVersionNodes; + final String nodeIdWithUpgradedVersion = randomValueOtherThan( + nodeIdWithOriginalVersion, + () -> randomFrom(upgradedVersionNodes) ); // The snapshot is mounted on the node with the min. version in order to force the node to populate the blob store cache index. @@ -197,8 +196,8 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs "mounting snapshot as index [{}] with storage [{}] on node [{}] with min. version [{}]", index, storage, - nodeIdWithMinVersion, - minVersion + nodeIdWithOriginalVersion, + UPGRADE_FROM_VERSION ); mountSnapshot( repository, @@ -208,7 +207,7 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs storage, Settings.builder() // we want a specific node version to create docs in the blob cache index - .put("index.routing.allocation.include._id", nodeIdWithMinVersion) + .put("index.routing.allocation.include._id", nodeIdWithOriginalVersion) // prevent interferences with blob cache when full_copy is used .put("index.store.snapshot.cache.prewarm.enabled", false) .build() @@ -222,8 +221,8 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs "mounting the same snapshot of index [{}] with storage [{}], this time on node [{}] with higher version [{}]", index, storage, - nodeIdWithMaxVersion, - maxVersion + nodeIdWithUpgradedVersion, + nodesIdsAndVersions.get(nodeIdWithUpgradedVersion) ); mountSnapshot( repository, @@ -233,8 +232,8 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs storage, Settings.builder() // we want a specific node version to use the cached blobs created by the nodeIdWithMinVersion - .put("index.routing.allocation.include._id", nodeIdWithMaxVersion) - .put("index.routing.allocation.exclude._id", nodeIdWithMinVersion) + .put("index.routing.allocation.include._id", nodeIdWithUpgradedVersion) + .put("index.routing.allocation.exclude._id", nodeIdWithOriginalVersion) // prevent interferences with blob cache when full_copy is used .put("index.store.snapshot.cache.prewarm.enabled", false) .build() @@ -251,8 +250,8 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs "mounting snapshot as index [{}] with storage [{}] on node [{}] with max. version [{}]", index, storage, - nodeIdWithMaxVersion, - maxVersion + nodeIdWithUpgradedVersion, + nodesIdsAndVersions.get(nodeIdWithUpgradedVersion) ); mountSnapshot( repository, @@ -262,7 +261,7 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs storage, Settings.builder() // we want a specific node version to create docs in the blob cache index - .put("index.routing.allocation.include._id", nodeIdWithMaxVersion) + .put("index.routing.allocation.include._id", nodeIdWithUpgradedVersion) // prevent interferences with blob cache when full_copy is used .put("index.store.snapshot.cache.prewarm.enabled", false) .build() @@ -276,8 +275,8 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs "mounting the same snapshot of index [{}] with storage [{}], this time on node [{}] with lower version [{}]", index, storage, - nodeIdWithMinVersion, - minVersion + nodeIdWithOriginalVersion, + UPGRADE_FROM_VERSION ); mountSnapshot( repository, @@ -287,8 +286,8 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs storage, Settings.builder() // we want a specific node version to use the cached blobs created by the nodeIdWithMinVersion - .put("index.routing.allocation.include._id", nodeIdWithMinVersion) - .put("index.routing.allocation.exclude._id", nodeIdWithMaxVersion) + .put("index.routing.allocation.include._id", nodeIdWithOriginalVersion) + .put("index.routing.allocation.exclude._id", nodeIdWithUpgradedVersion) // prevent interferences with blob cache when full_copy is used .put("index.store.snapshot.cache.prewarm.enabled", false) .build() @@ -297,7 +296,7 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs assertHitCount(index, equalTo(numberOfDocs * 2L)); deleteIndex(index); - if (UPGRADE_FROM_VERSION.onOrAfter(Version.V_7_13_0)) { + if (isOriginalClusterVersionAtLeast(Version.V_7_13_0)) { final Request request = new Request( "GET", "/.snapshot-blob-cache/_settings/index.routing.allocation.include._tier_preference" @@ -330,7 +329,7 @@ private void executeBlobCacheCreationTestCase(Storage storage, long numberOfDocs private static void assumeVersion(Version minSupportedVersion, Storage storageType) { assumeTrue( "Searchable snapshots with storage type [" + storageType + "] is supported since version [" + minSupportedVersion + ']', - UPGRADE_FROM_VERSION.onOrAfter(minSupportedVersion) + isOriginalClusterVersionAtLeast(minSupportedVersion) ); } @@ -364,14 +363,14 @@ private static void waitForNodes(int numberOfNodes) throws IOException { } @SuppressWarnings("unchecked") - private static Map nodesVersions() throws IOException { + private static Map nodesVersions() throws IOException { final Response response = client().performRequest(new Request(HttpGet.METHOD_NAME, "_nodes/_all")); assertThat(response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus())); final Map nodes = (Map) extractValue(responseAsMap(response), "nodes"); assertNotNull("Nodes info is null", nodes); - final Map nodesVersions = Maps.newMapWithExpectedSize(nodes.size()); + final Map nodesVersions = Maps.newMapWithExpectedSize(nodes.size()); for (Map.Entry node : nodes.entrySet()) { - nodesVersions.put(node.getKey(), Version.fromString((String) extractValue((Map) node.getValue(), "version"))); + nodesVersions.put(node.getKey(), (String) extractValue((Map) node.getValue(), "version")); } return nodesVersions; } @@ -391,7 +390,7 @@ private static void mountSnapshot( Settings indexSettings ) throws IOException { final Request request = new Request(HttpPost.METHOD_NAME, "/_snapshot/" + repositoryName + '/' + snapshotName + "/_mount"); - if (UPGRADE_FROM_VERSION.onOrAfter(Version.V_7_12_0)) { + if (isOriginalClusterVersionAtLeast(Version.V_7_12_0)) { request.addParameter("storage", storage.storageName()); } else { assertThat("Parameter 'storage' was introduced in 7.12.0 with " + Storage.SHARED_CACHE, storage, equalTo(Storage.FULL_COPY)); diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransformSurvivesUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransformSurvivesUpgradeIT.java index 3f4eb491d6cf9..c24665d812db6 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransformSurvivesUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransformSurvivesUpgradeIT.java @@ -9,7 +9,7 @@ import org.apache.http.HttpHost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.elasticsearch.Version; +import org.elasticsearch.Build; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; @@ -235,7 +235,7 @@ private void verifyContinuousTransformHandlesData(long expectedLastCheckpoint) t private void verifyUpgradeFailsIfMixedCluster() { // upgrade tests by design are also executed with the same version, this check must be skipped in this case, see gh#39102. - if (UPGRADE_FROM_VERSION.equals(Version.CURRENT)) { + if (UPGRADE_FROM_VERSION.equals(Build.current().version())) { return; } final Request upgradeTransformRequest = new Request("POST", getTransformEndpoint() + "_upgrade"); diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransportVersionClusterStateUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransportVersionClusterStateUpgradeIT.java index 470525a69ea0b..3c073605969af 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransportVersionClusterStateUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TransportVersionClusterStateUpgradeIT.java @@ -19,8 +19,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.oneOf; public class TransportVersionClusterStateUpgradeIT extends AbstractUpgradeTestCase { @@ -66,11 +64,11 @@ private boolean runTransportVersionsTest() throws Exception { switch (CLUSTER_TYPE) { case OLD -> { - if (UPGRADE_FROM_VERSION.before(VERSION_INTRODUCING_TRANSPORT_VERSIONS)) { + if (isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_TRANSPORT_VERSIONS) == false) { // Before 8.8.0 there was only DiscoveryNode#version assertFalse(description, hasTransportVersions); assertFalse(description, hasNodesVersions); - } else if (UPGRADE_FROM_VERSION.before(VERSION_INTRODUCING_NODES_VERSIONS)) { + } else if (isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_NODES_VERSIONS) == false) { // In [8.8.0, 8.11.0) we exposed just transport_versions assertTrue(description, hasTransportVersions); assertFalse(description, hasNodesVersions); @@ -81,10 +79,10 @@ private boolean runTransportVersionsTest() throws Exception { } } case MIXED -> { - if (UPGRADE_FROM_VERSION.before(VERSION_INTRODUCING_TRANSPORT_VERSIONS)) { + if (isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_TRANSPORT_VERSIONS) == false) { // Responding node might be <8.8.0 (so no extra versions) or >=8.11.0 (includes nodes_versions) assertFalse(description, hasTransportVersions); - } else if (UPGRADE_FROM_VERSION.before(VERSION_INTRODUCING_NODES_VERSIONS)) { + } else if (isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_NODES_VERSIONS) == false) { // Responding node might be in [8.8.0, 8.11.0) (transport_versions) or >=8.11.0 (includes nodes_versions) but not both assertTrue(description, hasNodesVersions || hasTransportVersions); } else { @@ -103,8 +101,8 @@ private boolean runTransportVersionsTest() throws Exception { if (hasTransportVersions) { // Upgrading from [8.8.0, 8.11.0) and the responding node is still on the old version - assertThat(description, UPGRADE_FROM_VERSION, lessThan(VERSION_INTRODUCING_NODES_VERSIONS)); - assertThat(description, UPGRADE_FROM_VERSION, greaterThanOrEqualTo(VERSION_INTRODUCING_TRANSPORT_VERSIONS)); + assertFalse(description, isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_NODES_VERSIONS)); + assertTrue(description, isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_TRANSPORT_VERSIONS)); assertNotEquals(description, ClusterType.UPGRADED, CLUSTER_TYPE); // transport_versions includes the correct version for all nodes, no inference is needed @@ -126,7 +124,10 @@ private boolean runTransportVersionsTest() throws Exception { } } else if (hasNodesVersions) { // Either upgrading from ≥8.11.0 (the responding node might be old or new), or from <8.8.0 (the responding node is new) - assertFalse(description, UPGRADE_FROM_VERSION.before(VERSION_INTRODUCING_NODES_VERSIONS) && CLUSTER_TYPE == ClusterType.OLD); + assertFalse( + description, + isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_NODES_VERSIONS) == false && CLUSTER_TYPE == ClusterType.OLD + ); // nodes_versions includes _a_ version for all nodes; it might be correct, or it might be inferred if we're upgrading from // <8.8.0 and the master is still an old node or the TransportVersionsFixupListener hasn't run yet @@ -144,7 +145,7 @@ private boolean runTransportVersionsTest() throws Exception { assertThat( nodeDescription, transportVersion, - UPGRADE_FROM_VERSION.onOrAfter(VERSION_INTRODUCING_TRANSPORT_VERSIONS) + isOriginalClusterVersionAtLeast(VERSION_INTRODUCING_TRANSPORT_VERSIONS) ? equalTo(TransportVersion.current()) : oneOf(TransportVersion.current(), FIRST_TRANSPORT_VERSION) ); diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java index 44eca0dc9a997..59acb7722085f 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java @@ -43,7 +43,7 @@ public class UpgradeClusterClientYamlTestSuiteIT extends ESClientYamlSuiteTestCa public void waitForTemplates() throws Exception { if (AbstractUpgradeTestCase.CLUSTER_TYPE == AbstractUpgradeTestCase.ClusterType.OLD) { try { - boolean clusterUnderstandsComposableTemplates = AbstractUpgradeTestCase.UPGRADE_FROM_VERSION.onOrAfter(Version.V_7_8_0); + boolean clusterUnderstandsComposableTemplates = AbstractUpgradeTestCase.isOriginalClusterVersionAtLeast(Version.V_7_8_0); XPackRestTestHelper.waitForTemplates( client(), XPackRestTestConstants.ML_POST_V7120_TEMPLATES, From db80251fb901b786ed0f0989fb003dab423e899f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Witek?= Date: Wed, 25 Oct 2023 13:15:01 +0200 Subject: [PATCH 097/203] [Transform] Make GetCheckpointAction and GetCheckpointNodeAction time out (#101055) --- docs/changelog/101055.yaml | 5 + .../org/elasticsearch/TransportVersions.java | 2 + .../transform/action/GetCheckpointAction.java | 24 ++- .../action/GetCheckpointNodeAction.java | 24 ++- .../GetCheckpointActionRequestTests.java | 14 +- .../GetCheckpointNodeActionRequestTests.java | 32 ++- .../TransformGetAndGetStatsIT.java | 47 ++++- .../TransformCheckpointServiceNodeTests.java | 2 + .../checkpoint/TransformGetCheckpointIT.java | 45 ++++- .../TransformGetCheckpointTests.java | 37 ++-- .../action/TransportGetCheckpointAction.java | 13 +- .../TransportGetCheckpointNodeAction.java | 25 ++- .../TransportGetTransformStatsAction.java | 6 +- .../checkpoint/CheckpointProvider.java | 3 + .../checkpoint/DefaultCheckpointProvider.java | 37 +++- .../TimeBasedCheckpointProvider.java | 2 +- .../TransformCheckpointService.java | 3 + .../transform/transforms/TransformTask.java | 31 ++- ...TransportGetCheckpointNodeActionTests.java | 187 ++++++++++++++++++ .../MockTimebasedCheckpointProvider.java | 5 +- .../transforms/scheduling/FakeClock.java | 4 +- 21 files changed, 468 insertions(+), 80 deletions(-) create mode 100644 docs/changelog/101055.yaml create mode 100644 x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeActionTests.java diff --git a/docs/changelog/101055.yaml b/docs/changelog/101055.yaml new file mode 100644 index 0000000000000..e4ca4548c2ef6 --- /dev/null +++ b/docs/changelog/101055.yaml @@ -0,0 +1,5 @@ +pr: 101055 +summary: Make tasks that calculate checkpoints time out +area: Transform +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 7c76c8a8c9c9e..60c14740658bb 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -145,6 +145,8 @@ static TransportVersion def(int id) { public static final TransportVersion PLUGIN_DESCRIPTOR_STRING_VERSION = def(8_520_00_0); public static final TransportVersion TOO_MANY_SCROLL_CONTEXTS_EXCEPTION_ADDED = def(8_521_00_0); public static final TransportVersion UNCONTENDED_REGISTER_ANALYSIS_ADDED = def(8_522_00_0); + public static final TransportVersion TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED = def(8_523_00_0); + /* * STOP! READ THIS FIRST! No, really, * ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _ diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java index 1b2378fd63ea3..7ac27d79d3cb8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointAction.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.transform.action; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; @@ -16,6 +17,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.TaskId; @@ -46,16 +48,23 @@ public static class Request extends ActionRequest implements IndicesRequest.Repl private String[] indices; private final IndicesOptions indicesOptions; + private final TimeValue timeout; public Request(StreamInput in) throws IOException { super(in); indices = in.readStringArray(); indicesOptions = IndicesOptions.readIndicesOptions(in); + if (in.getTransportVersion().onOrAfter(TransportVersions.TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED)) { + timeout = in.readOptionalTimeValue(); + } else { + timeout = null; + } } - public Request(String[] indices, IndicesOptions indicesOptions) { + public Request(String[] indices, IndicesOptions indicesOptions, TimeValue timeout) { this.indices = indices != null ? indices : Strings.EMPTY_ARRAY; this.indicesOptions = indicesOptions; + this.timeout = timeout; } @Override @@ -73,6 +82,10 @@ public IndicesOptions indicesOptions() { return indicesOptions; } + public TimeValue getTimeout() { + return timeout; + } + @Override public boolean equals(Object obj) { if (obj == this) { @@ -83,12 +96,14 @@ public boolean equals(Object obj) { } Request that = (Request) obj; - return Arrays.equals(indices, that.indices) && Objects.equals(indicesOptions, that.indicesOptions); + return Arrays.equals(indices, that.indices) + && Objects.equals(indicesOptions, that.indicesOptions) + && Objects.equals(timeout, that.timeout); } @Override public int hashCode() { - return Objects.hash(Arrays.hashCode(indices), indicesOptions); + return Objects.hash(Arrays.hashCode(indices), indicesOptions, timeout); } @Override @@ -96,6 +111,9 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeStringArray(indices); indicesOptions.writeIndicesOptions(out); + if (out.getTransportVersion().onOrAfter(TransportVersions.TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED)) { + out.writeOptionalTimeValue(timeout); + } } @Override diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java index afef902d007f9..8e67dbc6daacd 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeAction.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.transform.action; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; @@ -16,6 +17,7 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.TaskId; @@ -90,16 +92,23 @@ public static class Request extends ActionRequest implements IndicesRequest { private final Set shards; private final OriginalIndices originalIndices; + private final TimeValue timeout; - public Request(Set shards, OriginalIndices originalIndices) { + public Request(Set shards, OriginalIndices originalIndices, TimeValue timeout) { this.shards = shards; this.originalIndices = originalIndices; + this.timeout = timeout; } public Request(StreamInput in) throws IOException { super(in); this.shards = in.readCollectionAsImmutableSet(ShardId::new); this.originalIndices = OriginalIndices.readOriginalIndices(in); + if (in.getTransportVersion().onOrAfter(TransportVersions.TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED)) { + this.timeout = in.readOptionalTimeValue(); + } else { + this.timeout = null; + } } @Override @@ -112,6 +121,9 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeCollection(shards); OriginalIndices.writeOriginalIndices(originalIndices, out); + if (out.getTransportVersion().onOrAfter(TransportVersions.TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED)) { + out.writeOptionalTimeValue(timeout); + } } public Set getShards() { @@ -122,6 +134,10 @@ public OriginalIndices getOriginalIndices() { return originalIndices; } + public TimeValue getTimeout() { + return timeout; + } + @Override public boolean equals(Object obj) { if (obj == this) { @@ -132,12 +148,14 @@ public boolean equals(Object obj) { } Request that = (Request) obj; - return Objects.equals(shards, that.shards) && Objects.equals(originalIndices, that.originalIndices); + return Objects.equals(shards, that.shards) + && Objects.equals(originalIndices, that.originalIndices) + && Objects.equals(timeout, that.timeout); } @Override public int hashCode() { - return Objects.hash(shards, originalIndices); + return Objects.hash(shards, originalIndices, timeout); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java index 1eccab78c8e13..43ec0a0f1b4f5 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointActionRequestTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -41,8 +42,9 @@ protected Reader instanceReader() { protected Request mutateInstance(Request instance) { List indices = instance.indices() != null ? new ArrayList<>(Arrays.asList(instance.indices())) : new ArrayList<>(); IndicesOptions indicesOptions = instance.indicesOptions(); + TimeValue timeout = instance.getTimeout(); - switch (between(0, 1)) { + switch (between(0, 2)) { case 0: indices.add(randomAlphaOfLengthBetween(1, 20)); break; @@ -55,11 +57,14 @@ protected Request mutateInstance(Request instance) { SearchRequest.DEFAULT_INDICES_OPTIONS ); break; + case 2: + timeout = timeout != null ? null : TimeValue.timeValueSeconds(randomIntBetween(1, 300)); + break; default: throw new AssertionError("Illegal randomization branch"); } - return new Request(indices.toArray(new String[0]), indicesOptions); + return new Request(indices.toArray(new String[0]), indicesOptions, timeout); } public void testCreateTask() { @@ -69,7 +74,7 @@ public void testCreateTask() { } public void testCreateTaskWithNullIndices() { - Request request = new Request(null, null); + Request request = new Request(null, null, null); CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); assertThat(task.getDescription(), is(equalTo("get_checkpoint[0]"))); } @@ -83,7 +88,8 @@ private static Request randomRequest(Integer numIndices) { Boolean.toString(randomBoolean()), Boolean.toString(randomBoolean()), SearchRequest.DEFAULT_INDICES_OPTIONS - ) + ), + randomBoolean() ? TimeValue.timeValueSeconds(randomIntBetween(1, 300)) : null ); } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java index aa914122f786f..b2cb2ae68f113 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/action/GetCheckpointNodeActionRequestTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.action.OriginalIndices; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.io.stream.Writeable.Reader; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.tasks.CancellableTask; import org.elasticsearch.tasks.TaskId; @@ -32,13 +33,17 @@ protected Reader instanceReader() { @Override protected Request createTestInstance() { - return new Request(randomShards(randomInt(10)), randomOriginalIndices(randomIntBetween(0, 20))); + return new Request( + randomShards(randomInt(10)), + randomOriginalIndices(randomIntBetween(0, 20)), + randomBoolean() ? randomTimeout() : null + ); } @Override protected Request mutateInstance(Request instance) { - switch (random().nextInt(1)) { + switch (random().nextInt(2)) { case 0 -> { Set shards = new HashSet<>(instance.getShards()); if (randomBoolean() && shards.size() > 0) { @@ -50,36 +55,43 @@ protected Request mutateInstance(Request instance) { } else { shards.add(new ShardId(randomAlphaOfLength(8), randomAlphaOfLength(4), randomInt(5))); } - return new Request(shards, instance.getOriginalIndices()); + return new Request(shards, instance.getOriginalIndices(), instance.getTimeout()); } case 1 -> { OriginalIndices originalIndices = randomOriginalIndices(instance.indices().length + 1); - return new Request(instance.getShards(), originalIndices); + return new Request(instance.getShards(), originalIndices, instance.getTimeout()); + } + case 2 -> { + return new Request( + instance.getShards(), + instance.getOriginalIndices(), + instance.getTimeout() != null ? null : randomTimeout() + ); } default -> throw new IllegalStateException("The test should only allow 1 parameters mutated"); } } public void testCreateTask() { - Request request = new Request(randomShards(7), randomOriginalIndices(19)); + Request request = new Request(randomShards(7), randomOriginalIndices(19), null); CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[19;7]"))); } public void testCreateTaskWithNullShardsAndIndices() { - Request request = new Request(null, OriginalIndices.NONE); + Request request = new Request(null, OriginalIndices.NONE, null); CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[0;0]"))); } public void testCreateTaskWithNullShards() { - Request request = new Request(null, randomOriginalIndices(13)); + Request request = new Request(null, randomOriginalIndices(13), null); CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[13;0]"))); } public void testCreateTaskWithNullIndices() { - Request request = new Request(randomShards(11), OriginalIndices.NONE); + Request request = new Request(randomShards(11), OriginalIndices.NONE, null); CancellableTask task = request.createTask(123, "type", "action", new TaskId("dummy-node:456"), Map.of()); assertThat(task.getDescription(), is(equalTo("get_checkpoint_node[0;11]"))); } @@ -100,4 +112,8 @@ private static OriginalIndices randomOriginalIndices(int numIndices) { IndicesOptions indicesOptions = randomBoolean() ? IndicesOptions.strictExpand() : IndicesOptions.lenientExpandOpen(); return new OriginalIndices(randomIndices, indicesOptions); } + + private static TimeValue randomTimeout() { + return TimeValue.timeValueSeconds(randomIntBetween(1, 300)); + } } diff --git a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformGetAndGetStatsIT.java b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformGetAndGetStatsIT.java index 0f1af34ed5ee2..4321306870bd1 100644 --- a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformGetAndGetStatsIT.java +++ b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformGetAndGetStatsIT.java @@ -89,19 +89,19 @@ public void testGetAndGetStats() throws Exception { String authHeader = randomFrom(BASIC_AUTH_VALUE_TRANSFORM_USER, BASIC_AUTH_VALUE_TRANSFORM_ADMIN); // Check all the different ways to retrieve transform stats - Request getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "_stats", authHeader); + Request getRequest = createRequestWithAuthAndTimeout("GET", getTransformEndpoint() + "_stats", authHeader, randomTimeout()); Map stats = entityAsMap(client().performRequest(getRequest)); assertEquals(3, XContentMapValues.extractValue("count", stats)); - getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "_all/_stats", authHeader); + getRequest = createRequestWithAuthAndTimeout("GET", getTransformEndpoint() + "_all/_stats", authHeader, randomTimeout()); stats = entityAsMap(client().performRequest(getRequest)); assertEquals(3, XContentMapValues.extractValue("count", stats)); - getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "*/_stats", authHeader); + getRequest = createRequestWithAuthAndTimeout("GET", getTransformEndpoint() + "*/_stats", authHeader, randomTimeout()); stats = entityAsMap(client().performRequest(getRequest)); assertEquals(3, XContentMapValues.extractValue("count", stats)); - getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "pivot_1,pivot_2/_stats", authHeader); + getRequest = createRequestWithAuthAndTimeout("GET", getTransformEndpoint() + "pivot_1,pivot_2/_stats", authHeader, randomTimeout()); stats = entityAsMap(client().performRequest(getRequest)); assertEquals(2, XContentMapValues.extractValue("count", stats)); - getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "pivot_*/_stats", authHeader); + getRequest = createRequestWithAuthAndTimeout("GET", getTransformEndpoint() + "pivot_*/_stats", authHeader, randomTimeout()); stats = entityAsMap(client().performRequest(getRequest)); assertEquals(3, XContentMapValues.extractValue("count", stats)); @@ -122,7 +122,7 @@ public void testGetAndGetStats() throws Exception { } // only pivot_1 - getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "pivot_1/_stats", authHeader); + getRequest = createRequestWithAuthAndTimeout("GET", getTransformEndpoint() + "pivot_1/_stats", authHeader, randomTimeout()); stats = entityAsMap(client().performRequest(getRequest)); assertEquals(1, XContentMapValues.extractValue("count", stats)); @@ -133,7 +133,12 @@ public void testGetAndGetStats() throws Exception { assertEquals(1, XContentMapValues.extractValue("checkpointing.last.checkpoint", transformsStats.get(0))); // only continuous - getRequest = createRequestWithAuth("GET", getTransformEndpoint() + "pivot_continuous/_stats", authHeader); + getRequest = createRequestWithAuthAndTimeout( + "GET", + getTransformEndpoint() + "pivot_continuous/_stats", + authHeader, + randomTimeout() + ); stats = entityAsMap(client().performRequest(getRequest)); assertEquals(1, XContentMapValues.extractValue("count", stats)); @@ -300,7 +305,7 @@ private List> verifyGetStatsResponse(String path, int expect // Alternate testing between admin and lowly user, as both should be able to get the configs and stats String authHeader = randomFrom(BASIC_AUTH_VALUE_TRANSFORM_USER, BASIC_AUTH_VALUE_TRANSFORM_ADMIN); - Request request = createRequestWithAuth("GET", path, authHeader); + Request request = createRequestWithAuthAndTimeout("GET", path, authHeader, randomTimeout()); request.setOptions(RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE)); Response response = client().performRequest(request); Map stats = entityAsMap(response); @@ -354,7 +359,12 @@ public void testGetProgressStatsWithPivotQuery() throws Exception { // Alternate testing between admin and lowly user, as both should be able to get the configs and stats String authHeader = randomFrom(BASIC_AUTH_VALUE_TRANSFORM_USER, BASIC_AUTH_VALUE_TRANSFORM_ADMIN); - Request getRequest = createRequestWithAuth("GET", getTransformEndpoint() + transformId + "/_stats", authHeader); + Request getRequest = createRequestWithAuthAndTimeout( + "GET", + getTransformEndpoint() + transformId + "/_stats", + authHeader, + randomTimeout() + ); Map stats = entityAsMap(client().performRequest(getRequest)); assertEquals(1, XContentMapValues.extractValue("count", stats)); List> transformsStats = (List>) XContentMapValues.extractValue("transforms", stats); @@ -420,7 +430,12 @@ public void testGetStatsWithContinuous() throws Exception { assertThat(createTransformResponse.get("acknowledged"), equalTo(Boolean.TRUE)); startAndWaitForContinuousTransform(transformId, transformDest, null); - Request getRequest = createRequestWithAuth("GET", getTransformEndpoint() + transformId + "/_stats", null); + Request getRequest = createRequestWithAuthAndTimeout( + "GET", + getTransformEndpoint() + transformId + "/_stats", + null, + randomTimeout() + ); Map stats = entityAsMap(client().performRequest(getRequest)); List> transformsStats = (List>) XContentMapValues.extractValue("transforms", stats); assertEquals(1, transformsStats.size()); @@ -572,4 +587,16 @@ private static String transformConfig() { } """; } + + private Request createRequestWithAuthAndTimeout(String method, String endpoint, String authHeader, String timeout) { + Request request = createRequestWithAuth(method, endpoint, authHeader); + if (timeout != null) { + request.addParameter("timeout", timeout); + } + return request; + } + + private static String randomTimeout() { + return randomFrom((String) null, "5s", "30s", "1m"); + } } diff --git a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java index 2c01d9d7381e3..9b6b67e76c01c 100644 --- a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java +++ b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointServiceNodeTests.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.Index; import org.elasticsearch.index.cache.query.QueryCacheStats; import org.elasticsearch.index.cache.request.RequestCacheStats; @@ -408,6 +409,7 @@ private static void getCheckpoint( ); transformCheckpointService.getCheckpointingInfo( new ParentTaskAssigningClient(mockClientForCheckpointing, new TaskId("dummy-node:123456")), + TimeValue.timeValueSeconds(5), transformId, lastCheckpointNumber, nextCheckpointPosition, diff --git a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java index 2c69b0b2c8ca5..fde13c9d65324 100644 --- a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java +++ b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java @@ -7,18 +7,29 @@ package org.elasticsearch.xpack.transform.checkpoint; +import org.apache.lucene.util.SetOnce; +import org.elasticsearch.ElasticsearchTimeoutException; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.Strings; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xpack.core.transform.action.GetCheckpointAction; import org.elasticsearch.xpack.transform.TransformSingleNodeTestCase; import java.util.Arrays; import java.util.Comparator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + /** * Test suite for checkpointing using transform getcheckpoint API */ @@ -35,7 +46,8 @@ public void testGetCheckpoint() throws Exception { final GetCheckpointAction.Request request = new GetCheckpointAction.Request( new String[] { indexNamePrefix + "*" }, - IndicesOptions.LENIENT_EXPAND_OPEN + IndicesOptions.LENIENT_EXPAND_OPEN, + TimeValue.timeValueSeconds(5) ); final GetCheckpointAction.Response response = client().execute(GetCheckpointAction.INSTANCE, request).get(); @@ -88,4 +100,35 @@ public void testGetCheckpoint() throws Exception { ); } + public void testGetCheckpointTimeoutExceeded() throws Exception { + final String indexNamePrefix = "test_index-"; + final int indices = 5; + final int shards = 10; + + for (int i = 0; i < indices; ++i) { + indicesAdmin().prepareCreate(indexNamePrefix + i).setSettings(indexSettings(shards, 1)).get(); + } + + final GetCheckpointAction.Request request = new GetCheckpointAction.Request( + new String[] { indexNamePrefix + "*" }, + IndicesOptions.LENIENT_EXPAND_OPEN, + TimeValue.ZERO + ); + + CountDownLatch countDown = new CountDownLatch(1); + SetOnce finalException = new SetOnce<>(); + client().execute(GetCheckpointAction.INSTANCE, request, ActionListener.wrap(r -> countDown.countDown(), e -> { + finalException.set(e); + countDown.countDown(); + })); + countDown.await(10, TimeUnit.SECONDS); + + Exception e = finalException.get(); + assertThat(e, is(notNullValue())); + assertThat(e, is(instanceOf(ElasticsearchTimeoutException.class))); + assertThat( + e.getMessage(), + is(equalTo("Transform checkpointing timed out on node [node_s_0] after [0ms] having processed [0] of [50] shards")) + ); + } } diff --git a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java index bde1777bdb47d..1411576e61d58 100644 --- a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java +++ b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.IndexSettings; @@ -49,6 +50,7 @@ import org.junit.After; import org.junit.Before; +import java.time.Clock; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -97,12 +99,15 @@ public void setUp() throws Exception { @Override protected void onSendRequest(long requestId, String action, TransportRequest request, DiscoveryNode node) { if (action.equals(GetCheckpointNodeAction.NAME)) { - getCheckpointNodeAction.execute(null, (GetCheckpointNodeAction.Request) request, ActionListener.wrap(r -> { - this.handleResponse(requestId, r); - }, e -> { - this.handleError(requestId, new TransportException(e.getMessage(), e)); - - })); + GetCheckpointNodeAction.Request getCheckpointNodeActionRequest = (GetCheckpointNodeAction.Request) request; + Task task = getCheckpointNodeActionRequest.createTask(123, "type", "action", null, Map.of()); + getCheckpointNodeAction.execute( + task, + getCheckpointNodeActionRequest, + ActionListener.wrap(r -> { this.handleResponse(requestId, r); }, e -> { + this.handleError(requestId, new TransportException(e.getMessage(), e)); + }) + ); } } }; @@ -149,19 +154,23 @@ public void tearDown() throws Exception { } public void testEmptyCheckpoint() throws InterruptedException { - GetCheckpointAction.Request request = new GetCheckpointAction.Request(Strings.EMPTY_ARRAY, IndicesOptions.LENIENT_EXPAND_OPEN); + GetCheckpointAction.Request request = new GetCheckpointAction.Request( + Strings.EMPTY_ARRAY, + IndicesOptions.LENIENT_EXPAND_OPEN, + TimeValue.timeValueSeconds(5) + ); assertCheckpointAction(request, response -> { assertNotNull(response.getCheckpoints()); Map checkpoints = response.getCheckpoints(); assertTrue(checkpoints.isEmpty()); - }); } public void testSingleIndexRequest() throws InterruptedException { GetCheckpointAction.Request request = new GetCheckpointAction.Request( new String[] { indexNamePattern + "0" }, - IndicesOptions.LENIENT_EXPAND_OPEN + IndicesOptions.LENIENT_EXPAND_OPEN, + TimeValue.timeValueSeconds(5) ); assertCheckpointAction(request, response -> { @@ -173,12 +182,15 @@ public void testSingleIndexRequest() throws InterruptedException { assertEquals(42 + i, checkpoints.get(indexNamePattern + "0")[i]); } assertEquals(numberOfNodes, getCheckpointNodeAction.getCalls()); - }); } public void testMultiIndexRequest() throws InterruptedException { - GetCheckpointAction.Request request = new GetCheckpointAction.Request(testIndices, IndicesOptions.LENIENT_EXPAND_OPEN); + GetCheckpointAction.Request request = new GetCheckpointAction.Request( + testIndices, + IndicesOptions.LENIENT_EXPAND_OPEN, + TimeValue.timeValueSeconds(5) + ); assertCheckpointAction(request, response -> { assertNotNull(response.getCheckpoints()); Map checkpoints = response.getCheckpoints(); @@ -203,7 +215,6 @@ class TestTransportGetCheckpointAction extends TransportGetCheckpointAction { protected void doExecute(Task task, Request request, ActionListener listener) { resolveIndicesAndGetCheckpoint(task, request, listener, clusterStateWithIndex); } - } class TestTransportGetCheckpointNodeAction extends TransportGetCheckpointNodeAction { @@ -239,7 +250,7 @@ protected void doExecute( ActionListener listener ) { ++calls; - getGlobalCheckpoints(mockIndicesService, task, request.getShards(), listener); + getGlobalCheckpoints(mockIndicesService, task, request.getShards(), request.getTimeout(), Clock.systemUTC(), listener); } public int getCalls() { diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java index 05349d86f012e..5acc2d4541559 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.tasks.CancellableTask; @@ -39,6 +40,7 @@ import org.elasticsearch.xpack.core.transform.action.GetCheckpointAction.Response; import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction; +import java.time.Clock; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -89,7 +91,8 @@ protected void resolveIndicesAndGetCheckpoint(Task task, Request request, Action return; } - new AsyncGetCheckpointsFromNodesAction(state, task, nodesAndShards, new OriginalIndices(request), listener).start(); + new AsyncGetCheckpointsFromNodesAction(state, task, nodesAndShards, new OriginalIndices(request), request.getTimeout(), listener) + .start(); } private static Map> resolveIndicesToPrimaryShards(ClusterState state, String[] concreteIndices) { @@ -127,6 +130,7 @@ protected class AsyncGetCheckpointsFromNodesAction { private final ActionListener listener; private final Map> nodesAndShards; private final OriginalIndices originalIndices; + private final TimeValue timeout; private final DiscoveryNodes nodes; private final String localNodeId; @@ -135,12 +139,14 @@ protected AsyncGetCheckpointsFromNodesAction( Task task, Map> nodesAndShards, OriginalIndices originalIndices, + TimeValue timeout, ActionListener listener ) { this.task = task; this.listener = listener; this.nodesAndShards = nodesAndShards; this.originalIndices = originalIndices; + this.timeout = timeout; this.nodes = clusterState.nodes(); this.localNodeId = clusterService.localNode().getId(); } @@ -163,6 +169,8 @@ public void start() { indicesService, task, oneNodeAndItsShards.getValue(), + timeout, + Clock.systemUTC(), groupedListener ); continue; @@ -170,7 +178,8 @@ public void start() { GetCheckpointNodeAction.Request nodeCheckpointsRequest = new GetCheckpointNodeAction.Request( oneNodeAndItsShards.getValue(), - originalIndices + originalIndices, + timeout ); DiscoveryNode node = nodes.get(oneNodeAndItsShards.getKey()); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java index b0d15d27c0c86..481fe40a764a6 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeAction.java @@ -6,11 +6,13 @@ */ package org.elasticsearch.xpack.transform.action; +import org.elasticsearch.ElasticsearchTimeoutException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.shard.IndexShard; @@ -23,6 +25,8 @@ import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction.Request; import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction.Response; +import java.time.Clock; +import java.time.Instant; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -44,16 +48,19 @@ public TransportGetCheckpointNodeAction( @Override protected void doExecute(Task task, Request request, ActionListener listener) { - getGlobalCheckpoints(indicesService, task, request.getShards(), listener); + getGlobalCheckpoints(indicesService, task, request.getShards(), request.getTimeout(), Clock.systemUTC(), listener); } protected static void getGlobalCheckpoints( IndicesService indicesService, Task task, Set shards, + TimeValue timeout, + Clock clock, ActionListener listener ) { Map checkpointsByIndexOfThisNode = new HashMap<>(); + int numProcessedShards = 0; for (ShardId shardId : shards) { if (task instanceof CancellableTask) { // There is no point continuing this work if the task has been cancelled. @@ -61,6 +68,21 @@ protected static void getGlobalCheckpoints( return; } } + if (timeout != null) { + Instant now = clock.instant(); + if (task.getStartTime() + timeout.millis() < now.toEpochMilli()) { + listener.onFailure( + new ElasticsearchTimeoutException( + "Transform checkpointing timed out on node [{}] after [{}] having processed [{}] of [{}] shards", + indicesService.clusterService().getNodeName(), + timeout.getStringRep(), + numProcessedShards, + shards.size() + ) + ); + return; + } + } final IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); final IndexShard indexShard = indexService.getShard(shardId.id()); @@ -70,6 +92,7 @@ protected static void getGlobalCheckpoints( return seqNumbers; }); checkpointsByIndexOfThisNode.get(shardId.getIndexName())[shardId.getId()] = indexShard.seqNoStats().getGlobalCheckpoint(); + ++numProcessedShards; } listener.onResponse(new Response(checkpointsByIndexOfThisNode)); } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java index 1dffb11975aa1..13abc427460be 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/action/TransportGetTransformStatsAction.java @@ -300,6 +300,7 @@ private void collectStatsForTransformsWithoutTasks( List allStateAndStats = new ArrayList<>(response.getTransformsStats()); addCheckpointingInfoForTransformsWithoutTasks( parentTaskId, + request.getTimeout(), allStateAndStats, statsForTransformsWithoutTasks, transformsWaitingForAssignment, @@ -335,10 +336,12 @@ private void collectStatsForTransformsWithoutTasks( private void populateSingleStoppedTransformStat( TransformStoredDoc transform, TaskId parentTaskId, + TimeValue timeout, ActionListener listener ) { transformCheckpointService.getCheckpointingInfo( new ParentTaskAssigningClient(client, parentTaskId), + timeout, transform.getId(), transform.getTransformState().getCheckpoint(), transform.getTransformState().getPosition(), @@ -352,6 +355,7 @@ private void populateSingleStoppedTransformStat( private void addCheckpointingInfoForTransformsWithoutTasks( TaskId parentTaskId, + TimeValue timeout, List allStateAndStats, List statsForTransformsWithoutTasks, Set transformsWaitingForAssignment, @@ -368,7 +372,7 @@ private void addCheckpointingInfoForTransformsWithoutTasks( AtomicBoolean isExceptionReported = new AtomicBoolean(false); statsForTransformsWithoutTasks.forEach( - stat -> populateSingleStoppedTransformStat(stat, parentTaskId, ActionListener.wrap(checkpointingInfo -> { + stat -> populateSingleStoppedTransformStat(stat, parentTaskId, timeout, ActionListener.wrap(checkpointingInfo -> { synchronized (allStateAndStats) { if (transformsWaitingForAssignment.contains(stat.getId())) { Assignment assignment = TransformNodes.getAssignment(stat.getId(), clusterState); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/CheckpointProvider.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/CheckpointProvider.java index 3aa7116c3aa74..cae0ca07957d0 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/CheckpointProvider.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/CheckpointProvider.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.transform.checkpoint; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.xpack.core.transform.transforms.TransformCheckpoint; import org.elasticsearch.xpack.core.transform.transforms.TransformCheckpointingInfo.TransformCheckpointingInfoBuilder; import org.elasticsearch.xpack.core.transform.transforms.TransformIndexerPosition; @@ -50,6 +51,7 @@ void getCheckpointingInfo( TransformCheckpoint nextCheckpoint, TransformIndexerPosition nextCheckpointPosition, TransformProgress nextCheckpointProgress, + TimeValue timeout, ActionListener listener ); @@ -67,6 +69,7 @@ void getCheckpointingInfo( long lastCheckpointNumber, TransformIndexerPosition nextCheckpointPosition, TransformProgress nextCheckpointProgress, + TimeValue timeout, ActionListener listener ); } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java index f8293787b8943..aa1332b95fe84 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/DefaultCheckpointProvider.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Strings; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.transport.ActionNotFoundTransportException; import org.elasticsearch.transport.RemoteClusterService; import org.elasticsearch.xpack.core.ClientHelper; @@ -55,6 +56,11 @@ class DefaultCheckpointProvider implements CheckpointProvider { // threshold when to audit concrete index names, above this threshold we only report the number of changes private static final int AUDIT_CONCRETED_SOURCE_INDEX_CHANGES = 10; + // Huge timeout for getting index checkpoints internally. + // It might help to release cluster resources earlier if e.g.: someone configures a transform that ends up checkpointing 100000 + // searchable snapshot indices that all have to be retrieved from blob storage. + protected static final TimeValue INTERNAL_GET_INDEX_CHECKPOINTS_TIMEOUT = TimeValue.timeValueHours(12); + private static final Logger logger = LogManager.getLogger(DefaultCheckpointProvider.class); protected final Clock clock; @@ -93,7 +99,7 @@ public void createNextCheckpoint(final TransformCheckpoint lastCheckpoint, final final long timestamp = clock.millis(); final long checkpoint = TransformCheckpoint.isNullOrEmpty(lastCheckpoint) ? 1 : lastCheckpoint.getCheckpoint() + 1; - getIndexCheckpoints(ActionListener.wrap(checkpointsByIndex -> { + getIndexCheckpoints(INTERNAL_GET_INDEX_CHECKPOINTS_TIMEOUT, ActionListener.wrap(checkpointsByIndex -> { reportSourceIndexChanges( TransformCheckpoint.isNullOrEmpty(lastCheckpoint) ? Collections.emptySet() @@ -105,7 +111,7 @@ public void createNextCheckpoint(final TransformCheckpoint lastCheckpoint, final }, listener::onFailure)); } - protected void getIndexCheckpoints(ActionListener> listener) { + protected void getIndexCheckpoints(TimeValue timeout, ActionListener> listener) { try { ResolvedIndices resolvedIndexes = remoteClusterResolver.resolve(transformConfig.getSource().getIndex()); ActionListener> groupedListener = listener; @@ -125,6 +131,7 @@ protected void getIndexCheckpoints(ActionListener> listener) if (resolvedIndexes.getLocalIndices().isEmpty() == false) { getCheckpointsFromOneCluster( client, + timeout, transformConfig.getHeaders(), resolvedIndexes.getLocalIndices().toArray(new String[0]), RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY, @@ -139,6 +146,7 @@ protected void getIndexCheckpoints(ActionListener> listener) ); getCheckpointsFromOneCluster( remoteClient, + timeout, transformConfig.getHeaders(), remoteIndex.getValue().toArray(new String[0]), remoteIndex.getKey(), @@ -152,15 +160,16 @@ protected void getIndexCheckpoints(ActionListener> listener) private void getCheckpointsFromOneCluster( ParentTaskAssigningClient client, + TimeValue timeout, Map headers, String[] indices, String cluster, ActionListener> listener ) { if (fallbackToBWC.contains(cluster)) { - getCheckpointsFromOneClusterBWC(client, headers, indices, cluster, listener); + getCheckpointsFromOneClusterBWC(client, timeout, headers, indices, cluster, listener); } else { - getCheckpointsFromOneClusterV2(client, headers, indices, cluster, ActionListener.wrap(response -> { + getCheckpointsFromOneClusterV2(client, timeout, headers, indices, cluster, ActionListener.wrap(response -> { logger.debug( "[{}] Successfully retrieved checkpoints from cluster [{}] using transform checkpoint API", transformConfig.getId(), @@ -178,7 +187,7 @@ private void getCheckpointsFromOneCluster( ); fallbackToBWC.add(cluster); - getCheckpointsFromOneClusterBWC(client, headers, indices, cluster, listener); + getCheckpointsFromOneClusterBWC(client, timeout, headers, indices, cluster, listener); } else { listener.onFailure(e); } @@ -188,12 +197,17 @@ private void getCheckpointsFromOneCluster( private static void getCheckpointsFromOneClusterV2( ParentTaskAssigningClient client, + TimeValue timeout, Map headers, String[] indices, String cluster, ActionListener> listener ) { - GetCheckpointAction.Request getCheckpointRequest = new GetCheckpointAction.Request(indices, IndicesOptions.LENIENT_EXPAND_OPEN); + GetCheckpointAction.Request getCheckpointRequest = new GetCheckpointAction.Request( + indices, + IndicesOptions.LENIENT_EXPAND_OPEN, + timeout + ); ActionListener checkpointListener; if (RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY.equals(cluster)) { checkpointListener = ActionListener.wrap( @@ -233,6 +247,7 @@ private static void getCheckpointsFromOneClusterV2( */ private static void getCheckpointsFromOneClusterBWC( ParentTaskAssigningClient client, + TimeValue timeout, Map headers, String[] indices, String cluster, @@ -258,7 +273,7 @@ private static void getCheckpointsFromOneClusterBWC( client, ClientHelper.TRANSFORM_ORIGIN, IndicesStatsAction.INSTANCE, - new IndicesStatsRequest().indices(indices).clear().indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN), + new IndicesStatsRequest().indices(indices).timeout(timeout).clear().indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN), ActionListener.wrap(response -> { if (response.getFailedShards() != 0) { for (int i = 0; i < response.getShardFailures().length; ++i) { @@ -349,6 +364,7 @@ public void getCheckpointingInfo( TransformCheckpoint nextCheckpoint, TransformIndexerPosition nextCheckpointPosition, TransformProgress nextCheckpointProgress, + TimeValue timeout, ActionListener listener ) { TransformCheckpointingInfo.TransformCheckpointingInfoBuilder checkpointingInfoBuilder = @@ -361,7 +377,7 @@ public void getCheckpointingInfo( long timestamp = clock.millis(); - getIndexCheckpoints(ActionListener.wrap(checkpointsByIndex -> { + getIndexCheckpoints(timeout, ActionListener.wrap(checkpointsByIndex -> { TransformCheckpoint sourceCheckpoint = new TransformCheckpoint(transformConfig.getId(), timestamp, -1L, checkpointsByIndex, 0L); checkpointingInfoBuilder.setSourceCheckpoint(sourceCheckpoint); checkpointingInfoBuilder.setOperationsBehind(TransformCheckpoint.getBehind(lastCheckpoint, sourceCheckpoint)); @@ -374,6 +390,7 @@ public void getCheckpointingInfo( long lastCheckpointNumber, TransformIndexerPosition nextCheckpointPosition, TransformProgress nextCheckpointProgress, + TimeValue timeout, ActionListener listener ) { @@ -400,7 +417,7 @@ public void getCheckpointingInfo( // <2> got the next checkpoint, get the source checkpoint ActionListener nextCheckpointListener = ActionListener.wrap(nextCheckpointObj -> { checkpointingInfoBuilder.setNextCheckpoint(nextCheckpointObj); - getIndexCheckpoints(checkpointsByIndexListener); + getIndexCheckpoints(timeout, checkpointsByIndexListener); }, e -> { logger.debug( () -> format("[%s] failed to retrieve next checkpoint [%s]", transformConfig.getId(), lastCheckpointNumber + 1), @@ -422,7 +439,7 @@ public void getCheckpointingInfo( if (lastCheckpointNumber != 0) { transformConfigManager.getTransformCheckpoint(transformConfig.getId(), lastCheckpointNumber, lastCheckpointListener); } else { - getIndexCheckpoints(checkpointsByIndexListener); + getIndexCheckpoints(timeout, checkpointsByIndexListener); } } diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java index 28daeab50c640..7b83af1dc1405 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TimeBasedCheckpointProvider.java @@ -95,7 +95,7 @@ public void createNextCheckpoint(final TransformCheckpoint lastCheckpoint, final // for time based synchronization final long timeUpperBound = alignTimestamp.apply(timestamp - timeSyncConfig.getDelay().millis()); - getIndexCheckpoints(ActionListener.wrap(checkpointsByIndex -> { + getIndexCheckpoints(INTERNAL_GET_INDEX_CHECKPOINTS_TIMEOUT, ActionListener.wrap(checkpointsByIndex -> { listener.onResponse( new TransformCheckpoint(transformConfig.getId(), timestamp, checkpoint, checkpointsByIndex, timeUpperBound) ); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java index ac5e9b75c364a..0006a79b6a2b8 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/checkpoint/TransformCheckpointService.java @@ -13,6 +13,7 @@ import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.xpack.core.transform.transforms.TimeSyncConfig; import org.elasticsearch.xpack.core.transform.transforms.TransformCheckpointingInfo.TransformCheckpointingInfoBuilder; import org.elasticsearch.xpack.core.transform.transforms.TransformConfig; @@ -86,6 +87,7 @@ public CheckpointProvider getCheckpointProvider(final ParentTaskAssigningClient */ public void getCheckpointingInfo( final ParentTaskAssigningClient client, + final TimeValue timeout, final String transformId, final long lastCheckpointNumber, final TransformIndexerPosition nextCheckpointPosition, @@ -99,6 +101,7 @@ public void getCheckpointingInfo( lastCheckpointNumber, nextCheckpointPosition, nextCheckpointProgress, + timeout, listener ); }, transformError -> { diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java index 803f68d928a98..753d61410d5a8 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/TransformTask.java @@ -12,9 +12,7 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchStatusException; -import org.elasticsearch.ElasticsearchTimeoutException; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.support.ListenerTimeouts; import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.Strings; @@ -179,29 +177,21 @@ public void getCheckpointingInfo( ActionListener listener, TimeValue timeout ) { - ActionListener checkPointInfoListener = ListenerTimeouts.wrapWithTimeout( - threadPool, - timeout, - threadPool.generic(), - ActionListener.wrap(infoBuilder -> { - if (context.getChangesLastDetectedAt() != null) { - infoBuilder.setChangesLastDetectedAt(context.getChangesLastDetectedAt()); - } - if (context.getLastSearchTime() != null) { - infoBuilder.setLastSearchTime(context.getLastSearchTime()); - } - listener.onResponse(infoBuilder.build()); - }, listener::onFailure), - (ignore) -> listener.onFailure( - new ElasticsearchTimeoutException(format("Timed out retrieving checkpointing info after [%s]", timeout)) - ) - ); + ActionListener checkPointInfoListener = ActionListener.wrap(infoBuilder -> { + if (context.getChangesLastDetectedAt() != null) { + infoBuilder.setChangesLastDetectedAt(context.getChangesLastDetectedAt()); + } + if (context.getLastSearchTime() != null) { + infoBuilder.setLastSearchTime(context.getLastSearchTime()); + } + listener.onResponse(infoBuilder.build()); + }, listener::onFailure); - // TODO: pass `timeout` to the lower layers ClientTransformIndexer transformIndexer = getIndexer(); if (transformIndexer == null) { transformsCheckpointService.getCheckpointingInfo( parentTaskClient, + timeout, transform.getId(), context.getCheckpoint(), initialPosition, @@ -216,6 +206,7 @@ public void getCheckpointingInfo( transformIndexer.getNextCheckpoint(), transformIndexer.getPosition(), transformIndexer.getProgress(), + timeout, checkPointInfoListener ); } diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeActionTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeActionTests.java new file mode 100644 index 0000000000000..25c7f9efa7992 --- /dev/null +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/action/TransportGetCheckpointNodeActionTests.java @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.transform.action; + +import org.apache.lucene.util.SetOnce; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.IndexService; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.index.seqno.SeqNoStats; +import org.elasticsearch.index.shard.IndexShard; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.tasks.CancellableTask; +import org.elasticsearch.tasks.TaskCancelHelper; +import org.elasticsearch.tasks.TaskId; +import org.elasticsearch.tasks.TaskManager; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.core.transform.action.GetCheckpointNodeAction; +import org.elasticsearch.xpack.transform.transforms.scheduling.FakeClock; +import org.junit.Before; + +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.LongStream; + +import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_VERSION_CREATED; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TransportGetCheckpointNodeActionTests extends ESTestCase { + + private static final String NODE_NAME = "dummy-node"; + + private IndicesService indicesService; + private CancellableTask task; + private FakeClock clock; + private Set shards; + + @Before + public void setUp() throws Exception { + super.setUp(); + ClusterService clusterService = new ClusterService( + Settings.builder().put("node.name", NODE_NAME).build(), + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + null, + (TaskManager) null + ); + IndexShard indexShardA0 = mock(IndexShard.class); + when(indexShardA0.seqNoStats()).thenReturn(new SeqNoStats(3_000, 2_000, 3_000)); + IndexShard indexShardA1 = mock(IndexShard.class); + when(indexShardA1.seqNoStats()).thenReturn(new SeqNoStats(3_000, 2_000, 3_001)); + IndexShard indexShardB0 = mock(IndexShard.class); + when(indexShardB0.seqNoStats()).thenReturn(new SeqNoStats(3_000, 2_000, 4_000)); + IndexShard indexShardB1 = mock(IndexShard.class); + when(indexShardB1.seqNoStats()).thenReturn(new SeqNoStats(3_000, 2_000, 4_001)); + Settings commonIndexSettings = Settings.builder() + .put(SETTING_VERSION_CREATED, 1_000_000) + .put(SETTING_NUMBER_OF_SHARDS, 2) + .put(SETTING_NUMBER_OF_REPLICAS, 1) + .build(); + IndexService indexServiceA = mock(IndexService.class); + when(indexServiceA.getIndexSettings()).thenReturn( + new IndexSettings(IndexMetadata.builder("my-index-A").settings(commonIndexSettings).build(), Settings.EMPTY) + ); + when(indexServiceA.getShard(0)).thenReturn(indexShardA0); + when(indexServiceA.getShard(1)).thenReturn(indexShardA1); + IndexService indexServiceB = mock(IndexService.class); + when(indexServiceB.getIndexSettings()).thenReturn( + new IndexSettings(IndexMetadata.builder("my-index-B").settings(commonIndexSettings).build(), Settings.EMPTY) + ); + when(indexServiceB.getShard(0)).thenReturn(indexShardB0); + when(indexServiceB.getShard(1)).thenReturn(indexShardB1); + indicesService = mock(IndicesService.class); + when(indicesService.clusterService()).thenReturn(clusterService); + when(indicesService.indexServiceSafe(new Index("my-index-A", "A"))).thenReturn(indexServiceA); + when(indicesService.indexServiceSafe(new Index("my-index-B", "B"))).thenReturn(indexServiceB); + + task = new CancellableTask(123, "type", "action", "description", new TaskId("dummy-node:456"), Map.of()); + clock = new FakeClock(Instant.now()); + shards = Set.of( + new ShardId(new Index("my-index-A", "A"), 0), + new ShardId(new Index("my-index-A", "A"), 1), + new ShardId(new Index("my-index-B", "B"), 0), + new ShardId(new Index("my-index-B", "B"), 1) + ); + } + + public void testGetGlobalCheckpointsWithNoTimeout() throws InterruptedException { + testGetGlobalCheckpointsSuccess(null); + } + + public void testGetGlobalCheckpointsWithHighTimeout() throws InterruptedException { + testGetGlobalCheckpointsSuccess(TimeValue.timeValueMinutes(1)); + } + + private void testGetGlobalCheckpointsSuccess(TimeValue timeout) throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + SetOnce responseHolder = new SetOnce<>(); + SetOnce exceptionHolder = new SetOnce<>(); + TransportGetCheckpointNodeAction.getGlobalCheckpoints(indicesService, task, shards, timeout, clock, ActionListener.wrap(r -> { + responseHolder.set(r); + latch.countDown(); + }, e -> { + exceptionHolder.set(e); + latch.countDown(); + })); + latch.await(10, TimeUnit.SECONDS); + + Map checkpoints = responseHolder.get().getCheckpoints(); + assertThat(checkpoints.keySet(), containsInAnyOrder("my-index-A", "my-index-B")); + assertThat(LongStream.of(checkpoints.get("my-index-A")).boxed().collect(Collectors.toList()), contains(3000L, 3001L)); + assertThat(LongStream.of(checkpoints.get("my-index-B")).boxed().collect(Collectors.toList()), contains(4000L, 4001L)); + assertThat(exceptionHolder.get(), is(nullValue())); + } + + public void testGetGlobalCheckpointsFailureDueToTaskCancelled() throws InterruptedException { + TaskCancelHelper.cancel(task, "due to apocalypse"); + + CountDownLatch latch = new CountDownLatch(1); + SetOnce responseHolder = new SetOnce<>(); + SetOnce exceptionHolder = new SetOnce<>(); + TransportGetCheckpointNodeAction.getGlobalCheckpoints(indicesService, task, shards, null, clock, ActionListener.wrap(r -> { + responseHolder.set(r); + latch.countDown(); + }, e -> { + exceptionHolder.set(e); + latch.countDown(); + })); + latch.await(10, TimeUnit.SECONDS); + + assertThat("Response was: " + responseHolder.get(), responseHolder.get(), is(nullValue())); + assertThat(exceptionHolder.get().getMessage(), is(equalTo("task cancelled [due to apocalypse]"))); + } + + public void testGetGlobalCheckpointsFailureDueToTimeout() throws InterruptedException { + // Move the current time past the timeout. + clock.advanceTimeBy(Duration.ofSeconds(10)); + + CountDownLatch latch = new CountDownLatch(1); + SetOnce responseHolder = new SetOnce<>(); + SetOnce exceptionHolder = new SetOnce<>(); + TransportGetCheckpointNodeAction.getGlobalCheckpoints( + indicesService, + task, + shards, + TimeValue.timeValueSeconds(5), + clock, + ActionListener.wrap(r -> { + responseHolder.set(r); + latch.countDown(); + }, e -> { + exceptionHolder.set(e); + latch.countDown(); + }) + ); + latch.await(10, TimeUnit.SECONDS); + + assertThat("Response was: " + responseHolder.get(), responseHolder.get(), is(nullValue())); + assertThat( + exceptionHolder.get().getMessage(), + is(equalTo("Transform checkpointing timed out on node [dummy-node] after [5s] having processed [0] of [4] shards")) + ); + } +} diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/MockTimebasedCheckpointProvider.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/MockTimebasedCheckpointProvider.java index b874cbf9140b5..79aa34eebc882 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/MockTimebasedCheckpointProvider.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/checkpoint/MockTimebasedCheckpointProvider.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.transform.checkpoint; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.xpack.core.transform.transforms.TimeSyncConfig; import org.elasticsearch.xpack.core.transform.transforms.TransformCheckpoint; import org.elasticsearch.xpack.core.transform.transforms.TransformCheckpointingInfo.TransformCheckpointingInfoBuilder; @@ -65,6 +66,7 @@ public void getCheckpointingInfo( TransformCheckpoint nextCheckpoint, TransformIndexerPosition nextCheckpointPosition, TransformProgress nextCheckpointProgress, + TimeValue timeout, ActionListener listener ) { TransformCheckpointingInfoBuilder checkpointingInfoBuilder = new TransformCheckpointingInfoBuilder(); @@ -85,6 +87,7 @@ public void getCheckpointingInfo( long lastCheckpointNumber, TransformIndexerPosition nextCheckpointPosition, TransformProgress nextCheckpointProgress, + TimeValue timeout, ActionListener listener ) { long timestamp = System.currentTimeMillis(); @@ -106,7 +109,7 @@ public void getCheckpointingInfo( timestamp - timeSyncConfig.getDelay().millis() ); - getCheckpointingInfo(lastCheckpoint, nextCheckpoint, nextCheckpointPosition, nextCheckpointProgress, listener); + getCheckpointingInfo(lastCheckpoint, nextCheckpoint, nextCheckpointPosition, nextCheckpointProgress, timeout, listener); } } diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/scheduling/FakeClock.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/scheduling/FakeClock.java index 670f88b5195b2..4af9b8cf0ede0 100644 --- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/scheduling/FakeClock.java +++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/scheduling/FakeClock.java @@ -16,11 +16,11 @@ /** * {@link FakeClock} class in a test implementation of {@link Clock} and provides the possibility to set arbitrary current time. */ -class FakeClock extends Clock { +public class FakeClock extends Clock { private Instant currentTime; - FakeClock(Instant time) { + public FakeClock(Instant time) { currentTime = Objects.requireNonNull(time); } From 284f81873ffc84bf2b26ffe0ec2e9d065b5f1503 Mon Sep 17 00:00:00 2001 From: Abdon Pijpelink Date: Wed, 25 Oct 2023 13:19:17 +0200 Subject: [PATCH 098/203] [DOCS] Expand ES|QL DISSECT and GROK documentation (#101225) * Add 'Process data with DISSECT and GROK' page * Expand DISSECT docs * More DISSECT and GROK enhancements * Improve examples * Fix CSV tests * Review feedback * Reword --- docs/reference/esql/esql-language.asciidoc | 3 +- ...ql-process-data-with-dissect-grok.asciidoc | 258 ++++++++++++++++++ .../esql/processing-commands/dissect.asciidoc | 54 +++- .../esql/processing-commands/grok.asciidoc | 62 ++++- .../images/esql/unstructured-data.png | Bin 0 -> 90236 bytes .../ingest/processors/dissect.asciidoc | 22 +- .../src/main/resources/dissect.csv-spec | 4 - .../src/main/resources/docs.csv-spec | 86 ++++++ .../src/main/resources/grok.csv-spec | 7 +- 9 files changed, 466 insertions(+), 30 deletions(-) create mode 100644 docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc create mode 100644 docs/reference/images/esql/unstructured-data.png diff --git a/docs/reference/esql/esql-language.asciidoc b/docs/reference/esql/esql-language.asciidoc index 2becd04cec948..a932b8283f16e 100644 --- a/docs/reference/esql/esql-language.asciidoc +++ b/docs/reference/esql/esql-language.asciidoc @@ -12,6 +12,7 @@ Detailed information about the {esql} language: * <> * <> * <> +* <> * <> include::esql-syntax.asciidoc[] @@ -19,5 +20,5 @@ include::esql-commands.asciidoc[] include::esql-functions-operators.asciidoc[] include::multivalued-fields.asciidoc[] include::metadata-fields.asciidoc[] +include::esql-process-data-with-dissect-grok.asciidoc[] include::esql-enrich-data.asciidoc[] - diff --git a/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc b/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc new file mode 100644 index 0000000000000..b629051d2e53e --- /dev/null +++ b/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc @@ -0,0 +1,258 @@ +[[esql-process-data-with-dissect-and-grok]] +=== Data processing with `DISSECT` and `GROK` + +++++ +Data processing with `DISSECT` and `GROK` +++++ + +Your data may contain unstructured strings that you want to structure. This +makes it easier to analyze the data. For example, log messages may contain IP +addresses that you want to extract so you can find the most active IP addresses. + +image::images/esql/unstructured-data.png[align="center",width=75%] + +{es} can structure your data at index time or query time. At index time, you can +use the <> and <> ingest +processors, or the {ls} {logstash-ref}/plugins-filters-dissect.html[Dissect] and +{logstash-ref}/plugins-filters-grok.html[Grok] filters. At query time, you can +use the {esql} <> and <> commands. + +[[esql-grok-or-dissect]] +==== `DISSECT` or `GROK`? Or both? + +`DISSECT` works by breaking up a string using a delimiter-based pattern. `GROK` +works similarly, but uses regular expressions. This make `GROK` more powerful, +but generally also slower. `DISSECT` works well when data is reliably repeated. +`GROK` is a better choice when you really need the power of regular expressions, +for example when the structure of your text varies from row to row. + +You can use both `DISSECT` and `GROK` for hybrid use cases. For example when a +section of the line is reliably repeated, but the entire line is not. `DISSECT` +can deconstruct the section of the line that is repeated. `GROK` can process the +remaining field values using regular expressions. + +[[esql-process-data-with-dissect]] +==== Process data with `DISSECT` + +The <> processing command matches a string against a +delimiter-based pattern, and extracts the specified keys as columns. + +For example, the following pattern: +[source,txt] +---- +%{clientip} [%{@timestamp}] %{status} +---- + +matches a log line of this format: +[source,txt] +---- +1.2.3.4 [2023-01-23T12:15:00.000Z] Connected +---- + +and results in adding the following columns to the input table: + +[%header.monospaced.styled,format=dsv,separator=|] +|=== +clientip:keyword | @timestamp:keyword | status:keyword +1.2.3.4 | 2023-01-23T12:15:00.000Z | Connected +|=== + +[[esql-dissect-patterns]] +===== Dissect patterns + +include::../ingest/processors/dissect.asciidoc[tag=intro-example-explanation] + +An empty key `%{}` or a <> can be used to +match values, but exclude the value from the output. + +All matched values are output as keyword string data types. Use the +<> to convert to another data type. + +Dissect also supports <> that can +change dissect's default behavior. For example, you can instruct dissect to +ignore certain fields, append fields, skip over padding, etc. + +[[esql-dissect-terminology]] +===== Terminology + +dissect pattern:: +the set of fields and delimiters describing the textual +format. Also known as a dissection. +The dissection is described using a set of `%{}` sections: +`%{a} - %{b} - %{c}` + +field:: +the text from `%{` to `}` inclusive. + +delimiter:: +the text between `}` and the next `%{` characters. +Any set of characters other than `%{`, `'not }'`, or `}` is a delimiter. + +key:: ++ +-- +the text between the `%{` and `}`, exclusive of the `?`, `+`, `&` prefixes +and the ordinal suffix. + +Examples: + +* `%{?aaa}` - the key is `aaa` +* `%{+bbb/3}` - the key is `bbb` +* `%{&ccc}` - the key is `ccc` +-- + +[[esql-dissect-examples]] +===== Examples + +include::processing-commands/dissect.asciidoc[tag=examples] + +[[esql-dissect-key-modifiers]] +===== Dissect key modifiers + +include::../ingest/processors/dissect.asciidoc[tag=dissect-key-modifiers] + +[[esql-dissect-key-modifiers-table]] +.Dissect key modifiers +[options="header",role="styled"] +|====== +| Modifier | Name | Position | Example | Description | Details +| `->` | Skip right padding | (far) right | `%{keyname1->}` | Skips any repeated characters to the right | <> +| `+` | Append | left | `%{+keyname} %{+keyname}` | Appends two or more fields together | <> +| `+` with `/n` | Append with order | left and right | `%{+keyname/2} %{+keyname/1}` | Appends two or more fields together in the order specified | <> +| `?` | Named skip key | left | `%{?ignoreme}` | Skips the matched value in the output. Same behavior as `%{}`| <> +| `*` and `&` | Reference keys | left | `%{*r1} %{&r1}` | Sets the output key as value of `*` and output value of `&` | <> +|====== + +[[esql-dissect-modifier-skip-right-padding]] +====== Right padding modifier (`->`) +include::../ingest/processors/dissect.asciidoc[tag=dissect-modifier-skip-right-padding] + +[[esql-append-modifier]] +====== Append modifier (`+`) +include::../ingest/processors/dissect.asciidoc[tag=append-modifier] + +[[esql-append-order-modifier]] +====== Append with order modifier (`+` and `/n`) +include::../ingest/processors/dissect.asciidoc[tag=append-order-modifier] + +[[esql-named-skip-key]] +====== Named skip key (`?`) +include::../ingest/processors/dissect.asciidoc[tag=named-skip-key] + +[[esql-reference-keys]] +====== Reference keys (`*` and `&`) +include::../ingest/processors/dissect.asciidoc[tag=reference-keys] + +[[esql-process-data-with-grok]] +==== Process data with `GROK` + +The <> processing command matches a string against a pattern based on +regular expressions, and extracts the specified keys as columns. + +For example, the following pattern: +[source,txt] +---- +%{IP:ip} \[%{TIMESTAMP_ISO8601:@timestamp}\] %{GREEDYDATA:status} +---- + +matches a log line of this format: +[source,txt] +---- +1.2.3.4 [2023-01-23T12:15:00.000Z] Connected +---- + +and results in adding the following columns to the input table: + +[%header.monospaced.styled,format=dsv,separator=|] +|=== +@timestamp:keyword | ip:keyword | status:keyword +2023-01-23T12:15:00.000Z | 1.2.3.4 | Connected +|=== + +[[esql-grok-patterns]] +===== Grok patterns + +The syntax for a grok pattern is `%{SYNTAX:SEMANTIC}` + +The `SYNTAX` is the name of the pattern that matches your text. For example, +`3.44` is matched by the `NUMBER` pattern and `55.3.244.1` is matched by the +`IP` pattern. The syntax is how you match. + +The `SEMANTIC` is the identifier you give to the piece of text being matched. +For example, `3.44` could be the duration of an event, so you could call it +simply `duration`. Further, a string `55.3.244.1` might identify the `client` +making a request. + +By default, matched values are output as keyword string data types. To convert a +semantic's data type, suffix it with the target data type. For example +`%{NUMBER:num:int}`, which converts the `num` semantic from a string to an +integer. Currently the only supported conversions are `int` and `float`. For +other types, use the <>. + +For an overview of the available patterns, refer to +{es-repo}/blob/{branch}/libs/grok/src/main/resources/patterns[GitHub]. You can +also retrieve a list of all patterns using a <>. + +[[esql-grok-regex]] +===== Regular expressions + +Grok is based on regular expressions. Any regular expressions are valid in grok +as well. Grok uses the Oniguruma regular expression library. Refer to +https://github.com/kkos/oniguruma/blob/master/doc/RE[the Oniguruma GitHub +repository] for the full supported regexp syntax. + +[NOTE] +==== +Special regex characters like `[` and `]` need to be escaped with a `\`. For +example, in the earlier pattern: +[source,txt] +---- +%{IP:ip} \[%{TIMESTAMP_ISO8601:@timestamp}\] %{GREEDYDATA:status} +---- + +In {esql} queries, the backslash character itself is a special character that +needs to be escaped with another `\`. For this example, the corresponding {esql} +query becomes: +[source.merge.styled,esql] +---- +include::{esql-specs}/docs.csv-spec[tag=grokWithEscape] +---- +==== + +[[esql-custom-patterns]] +===== Custom patterns + +If grok doesn't have a pattern you need, you can use the Oniguruma syntax for +named capture which lets you match a piece of text and save it as a column: +[source,txt] +---- +(?the pattern here) +---- + +For example, postfix logs have a `queue id` that is a 10 or 11-character +hexadecimal value. This can be captured to a column named `queue_id` with: +[source,txt] +---- +(?[0-9A-F]{10,11}) +---- + +[[esql-grok-examples]] +===== Examples + +include::processing-commands/grok.asciidoc[tag=examples] + +[[esql-grok-debugger]] +===== Grok debugger + +To write and debug grok patterns, you can use the +{kibana-ref}/xpack-grokdebugger.html[Grok Debugger]. It provides a UI for +testing patterns against sample data. Under the covers, it uses the same engine +as the `GROK` command. + +[[esql-grok-limitations]] +===== Limitations + +The `GROK` command does not support configuring <>, or <>. The `GROK` command is not +subject to <>. diff --git a/docs/reference/esql/processing-commands/dissect.asciidoc b/docs/reference/esql/processing-commands/dissect.asciidoc index e6206615342f7..eca10c201c968 100644 --- a/docs/reference/esql/processing-commands/dissect.asciidoc +++ b/docs/reference/esql/processing-commands/dissect.asciidoc @@ -2,18 +2,58 @@ [[esql-dissect]] === `DISSECT` -`DISSECT` enables you to extract structured data out of a string. `DISSECT` -matches the string against a delimiter-based pattern, and extracts the specified -keys as columns. +**Syntax** -Refer to the <> for the -syntax of dissect patterns. +[source,txt] +---- +DISSECT input "pattern" [ append_separator=""] +---- + +*Parameters* + +`input`:: +The column that contains the string you want to structure. If the column has +multiple values, `DISSECT` will process each value. + +`pattern`:: +A dissect pattern. + +`append_separator=""`:: +A string used as the separator between appended values, when using the <>. + +*Description* + +`DISSECT` enables you to <>. `DISSECT` matches the string against a +delimiter-based pattern, and extracts the specified keys as columns. + +Refer to <> for the syntax of dissect patterns. + +*Example* + +// tag::examples[] +The following example parses a string that contains a timestamp, some text, and +an IP address: + +[source.merge.styled,esql] +---- +include::{esql-specs}/docs.csv-spec[tag=basicDissect] +---- +[%header.monospaced.styled,format=dsv,separator=|] +|=== +include::{esql-specs}/docs.csv-spec[tag=basicDissect-result] +|=== + +By default, `DISSECT` outputs keyword string columns. To convert to another +type, use <>: [source.merge.styled,esql] ---- -include::{esql-specs}/dissect.csv-spec[tag=dissect] +include::{esql-specs}/docs.csv-spec[tag=dissectWithToDatetime] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/dissect.csv-spec[tag=dissect-result] +include::{esql-specs}/docs.csv-spec[tag=dissectWithToDatetime-result] |=== + +// end::examples[] \ No newline at end of file diff --git a/docs/reference/esql/processing-commands/grok.asciidoc b/docs/reference/esql/processing-commands/grok.asciidoc index 914c13b2320eb..c95fe59f888ce 100644 --- a/docs/reference/esql/processing-commands/grok.asciidoc +++ b/docs/reference/esql/processing-commands/grok.asciidoc @@ -2,20 +2,66 @@ [[esql-grok]] === `GROK` -`GROK` enables you to extract structured data out of a string. `GROK` matches -the string against patterns, based on regular expressions, and extracts the -specified patterns as columns. +**Syntax** -Refer to the <> for the syntax for -of grok patterns. +[source,txt] +---- +GROK input "pattern" +---- + +*Parameters* + +`input`:: +The column that contains the string you want to structure. If the column has +multiple values, `GROK` will process each value. + +`pattern`:: +A grok pattern. + +*Description* + +`GROK` enables you to <>. `GROK` matches the string against patterns, +based on regular expressions, and extracts the specified patterns as columns. + +Refer to <> for the syntax of grok patterns. + +*Examples* + +// tag::examples[] +The following example parses a string that contains a timestamp, an IP address, +an email address, and a number: + +[source.merge.styled,esql] +---- +include::{esql-specs}/docs.csv-spec[tag=basicGrok] +---- +[%header.monospaced.styled,format=dsv,separator=|] +|=== +include::{esql-specs}/docs.csv-spec[tag=basicGrok-result] +|=== + +By default, `GROK` outputs keyword string columns. `int` and `float` types can +be converted by appending `:type` to the semantics in the pattern. For example +`{NUMBER:num:int}`: + +[source.merge.styled,esql] +---- +include::{esql-specs}/docs.csv-spec[tag=grokWithConversionSuffix] +---- +[%header.monospaced.styled,format=dsv,separator=|] +|=== +include::{esql-specs}/docs.csv-spec[tag=grokWithConversionSuffix-result] +|=== -For example: +For other type conversions, use <>: [source.merge.styled,esql] ---- -include::{esql-specs}/grok.csv-spec[tag=grok] +include::{esql-specs}/docs.csv-spec[tag=grokWithToDatetime] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/grok.csv-spec[tag=grok-result] +include::{esql-specs}/docs.csv-spec[tag=grokWithToDatetime-result] |=== +// end::examples[] diff --git a/docs/reference/images/esql/unstructured-data.png b/docs/reference/images/esql/unstructured-data.png new file mode 100644 index 0000000000000000000000000000000000000000..4753a91a71f6789b8a81a36c1a1b67ba7592b14e GIT binary patch literal 90236 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 literal 0 HcmV?d00001 diff --git a/docs/reference/ingest/processors/dissect.asciidoc b/docs/reference/ingest/processors/dissect.asciidoc index 0d3389b4195d7..9d408ea150644 100644 --- a/docs/reference/ingest/processors/dissect.asciidoc +++ b/docs/reference/ingest/processors/dissect.asciidoc @@ -44,11 +44,13 @@ and result in a document with the following fields: -------------------------------------------------- // NOTCONSOLE -A dissect pattern is defined by the parts of the string that will be discarded. In the example above the first part -to be discarded is a single space. Dissect finds this space, then assigns the value of `clientip` is everything up +// tag::intro-example-explanation[] +A dissect pattern is defined by the parts of the string that will be discarded. In the previous example, the first part +to be discarded is a single space. Dissect finds this space, then assigns the value of `clientip` everything up until that space. -Later dissect matches the `[` and then `]` and then assigns `@timestamp` to everything in-between `[` and `]`. -Paying special attention the parts of the string to discard will help build successful dissect patterns. +Next, dissect matches the `[` and then `]` and then assigns `@timestamp` to everything in-between `[` and `]`. +Paying special attention to the parts of the string to discard will help build successful dissect patterns. +// end::intro-example-explanation[] Successful matches require all keys in a pattern to have a value. If any of the `%{keyname}` defined in the pattern do not have a value, then an exception is thrown and may be handled by the <> directive. @@ -85,9 +87,11 @@ include::common-options.asciidoc[] [[dissect-key-modifiers]] ==== Dissect key modifiers +// tag::dissect-key-modifiers[] Key modifiers can change the default behavior for dissection. Key modifiers may be found on the left or right of the `%{keyname}` always inside the `%{` and `}`. For example `%{+keyname ->}` has the append and right padding modifiers. +// end::dissect-key-modifiers[] [[dissect-key-modifiers-table]] .Dissect Key Modifiers @@ -104,6 +108,7 @@ modifiers. [[dissect-modifier-skip-right-padding]] ===== Right padding modifier (`->`) +// tag::dissect-modifier-skip-right-padding[] The algorithm that performs the dissection is very strict in that it requires all characters in the pattern to match the source string. For example, the pattern `%{fookey} %{barkey}` (1 space), will match the string "foo{nbsp}bar" (1 space), but will not match the string "foo{nbsp}{nbsp}bar" (2 spaces) since the pattern has only 1 space and the @@ -137,10 +142,12 @@ Right padding modifier with empty key example * ts = 1998-08-10T17:15:42,466 * level = WARN |====== +// end::dissect-modifier-skip-right-padding[] [[append-modifier]] ===== Append modifier (`+`) [[dissect-modifier-append-key]] +// tag::append-modifier[] Dissect supports appending two or more results together for the output. Values are appended left to right. An append separator can be specified. In this example the append_separator is defined as a space. @@ -152,10 +159,12 @@ Append modifier example | *Result* a| * name = john jacob jingleheimer schmidt |====== +// end::append-modifier[] [[append-order-modifier]] ===== Append with order modifier (`+` and `/n`) [[dissect-modifier-append-key-with-order]] +// tag::append-order-modifier[] Dissect supports appending two or more results together for the output. Values are appended based on the order defined (`/n`). An append separator can be specified. In this example the append_separator is defined as a comma. @@ -167,10 +176,12 @@ Append with order modifier example | *Result* a| * name = schmidt,john,jingleheimer,jacob |====== +// end::append-order-modifier[] [[named-skip-key]] ===== Named skip key (`?`) [[dissect-modifier-named-skip-key]] +// tag::named-skip-key[] Dissect supports ignoring matches in the final result. This can be done with an empty key `%{}`, but for readability it may be desired to give that empty key a name. @@ -182,10 +193,12 @@ Named skip key modifier example * clientip = 1.2.3.4 * @timestamp = 30/Apr/1998:22:00:52 +0000 |====== +// end::named-skip-key[] [[reference-keys]] ===== Reference keys (`*` and `&`) [[dissect-modifier-reference-keys]] +// tag::reference-keys[] Dissect support using parsed values as the key/value pairings for the structured content. Imagine a system that partially logs in key/value pairs. Reference keys allow you to maintain that key/value relationship. @@ -199,3 +212,4 @@ Reference key modifier example * ip = 1.2.3.4 * error = REFUSED |====== +// end::reference-keys[] diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec index 54bc481c54b48..f4eaa44f15408 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec @@ -15,17 +15,13 @@ foo bar | null | null complexPattern -// tag::dissect[] ROW a = "1953-01-23T12:15:00Z - some text - 127.0.0.1;" | DISSECT a "%{Y}-%{M}-%{D}T%{h}:%{m}:%{s}Z - %{msg} - %{ip};" | KEEP Y, M, D, h, m, s, msg, ip -// end::dissect[] ; -// tag::dissect-result[] Y:keyword | M:keyword | D:keyword | h:keyword | m:keyword | s:keyword | msg:keyword | ip:keyword 1953 | 01 | 23 | 12 | 15 | 00 | some text | 127.0.0.1 -// end::dissect-result[] ; diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs.csv-spec index 6a4fce52fc8a7..02d530a2ae835 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs.csv-spec @@ -468,4 +468,90 @@ count:long | languages:integer 19 |2 15 |1 // end::countAll-result[] +; + +basicGrok +// tag::basicGrok[] +ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42" +| GROK a "%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num}" +| KEEP date, ip, email, num +// end::basicGrok[] +; + +// tag::basicGrok-result[] +date:keyword | ip:keyword | email:keyword | num:keyword +2023-01-23T12:15:00.000Z | 127.0.0.1 | some.email@foo.com | 42 +// end::basicGrok-result[] +; + +grokWithConversionSuffix +// tag::grokWithConversionSuffix[] +ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42" +| GROK a "%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}" +| KEEP date, ip, email, num +// end::grokWithConversionSuffix[] +; + +// tag::grokWithConversionSuffix-result[] +date:keyword | ip:keyword | email:keyword | num:integer +2023-01-23T12:15:00.000Z | 127.0.0.1 | some.email@foo.com | 42 +// end::grokWithConversionSuffix-result[] +; + +grokWithToDatetime +// tag::grokWithToDatetime[] +ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42" +| GROK a "%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}" +| KEEP date, ip, email, num +| EVAL date = TO_DATETIME(date) +// end::grokWithToDatetime[] +; + +// tag::grokWithToDatetime-result[] +ip:keyword | email:keyword | num:integer | date:date +127.0.0.1 | some.email@foo.com | 42 | 2023-01-23T12:15:00.000Z +// end::grokWithToDatetime-result[] +; + +grokWithEscape +// tag::grokWithEscape[] +ROW a = "1.2.3.4 [2023-01-23T12:15:00.000Z] Connected" +| GROK a "%{IP:ip} \\[%{TIMESTAMP_ISO8601:@timestamp}\\] %{GREEDYDATA:status}" +// end::grokWithEscape[] +| KEEP @timestamp +; + +// tag::grokWithEscape-result[] +@timestamp:keyword +2023-01-23T12:15:00.000Z +// end::grokWithEscape-result[] +; + +basicDissect +// tag::basicDissect[] +ROW a = "2023-01-23T12:15:00.000Z - some text - 127.0.0.1" +| DISSECT a "%{date} - %{msg} - %{ip}" +| KEEP date, msg, ip +// end::basicDissect[] +; + +// tag::basicDissect-result[] +date:keyword | msg:keyword | ip:keyword +2023-01-23T12:15:00.000Z | some text | 127.0.0.1 +// end::basicDissect-result[] +; + +dissectWithToDatetime +// tag::dissectWithToDatetime[] +ROW a = "2023-01-23T12:15:00.000Z - some text - 127.0.0.1" +| DISSECT a "%{date} - %{msg} - %{ip}" +| KEEP date, msg, ip +| EVAL date = TO_DATETIME(date) +// end::dissectWithToDatetime[] +; + +// tag::dissectWithToDatetime-result[] +msg:keyword | ip:keyword | date:date +some text | 127.0.0.1 | 2023-01-23T12:15:00.000Z +// end::dissectWithToDatetime-result[] ; \ No newline at end of file diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/grok.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/grok.csv-spec index 9dc9444de0155..f71f51d42c45f 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/grok.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/grok.csv-spec @@ -15,17 +15,12 @@ foo bar | null complexPattern -// tag::grok[] ROW a = "1953-01-23T12:15:00Z 127.0.0.1 some.email@foo.com 42" | GROK a "%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}" -| KEEP date, ip, email, num -// end::grok[] -; +| KEEP date, ip, email, num; -// tag::grok-result[] date:keyword | ip:keyword | email:keyword | num:integer 1953-01-23T12:15:00Z | 127.0.0.1 | some.email@foo.com | 42 -// end::grok-result[] ; From b19ae8d5ee9b5041270fd8599c95dbca5803871f Mon Sep 17 00:00:00 2001 From: AlexB Date: Wed, 25 Oct 2023 05:00:48 -0700 Subject: [PATCH 099/203] esql docs: structure, examples, minor improvements (#101292) Small improvements in ESQL docs and addition of examples --------- Co-authored-by: Alexandros Batsakis Co-authored-by: Abdon Pijpelink --- docs/reference/esql/esql-enrich-data.asciidoc | 39 +++++--- docs/reference/esql/esql-examples.asciidoc | 99 +++++++++++++++++++ docs/reference/esql/esql-get-started.asciidoc | 28 +++++- docs/reference/esql/esql-language.asciidoc | 8 +- docs/reference/esql/esql-limitations.asciidoc | 12 ++- ...ql-process-data-with-dissect-grok.asciidoc | 4 +- docs/reference/esql/index.asciidoc | 42 +++----- docs/reference/esql/metadata-fields.asciidoc | 2 +- 8 files changed, 178 insertions(+), 56 deletions(-) create mode 100644 docs/reference/esql/esql-examples.asciidoc diff --git a/docs/reference/esql/esql-enrich-data.asciidoc b/docs/reference/esql/esql-enrich-data.asciidoc index 9708728e6b305..f1ebe7bc218a8 100644 --- a/docs/reference/esql/esql-enrich-data.asciidoc +++ b/docs/reference/esql/esql-enrich-data.asciidoc @@ -1,12 +1,13 @@ [[esql-enrich-data]] -=== Enrich data +== Data enrichment ++++ -Enrich data +Data enrichment ++++ -You can use {esql}'s <> processing command to enrich a table with -data from indices in {es}. +The {esql} <> processing command combines, at query-time, data from +one or more source indexes with field-value combinations found in {es} enrich +indexes. For example, you can use `ENRICH` to: @@ -14,14 +15,16 @@ For example, you can use `ENRICH` to: * Add product information to retail orders based on product IDs * Supplement contact information based on an email address +[discrete] [[esql-how-enrich-works]] -==== How the `ENRICH` command works +=== How the `ENRICH` command works The `ENRICH` command adds new columns to a table, with data from {es} indices. It requires a few special components: image::images/esql/esql-enrich.png[align="center"] + [[esql-enrich-policy]] Enrich policy:: + @@ -60,8 +63,9 @@ enrich index. include::../ingest/enrich.asciidoc[tag=enrich-index] -- +[discrete] [[esql-set-up-enrich-policy]] -==== Set up an enrich policy +=== Set up an enrich policy To start using `ENRICH`, follow these steps: @@ -75,29 +79,35 @@ Once you have enrich policies set up, you can <> and <>. +[discrete] [IMPORTANT] ==== The `ENRICH` command performs several operations and may impact the speed of your query. +[discrete] ==== +[discrete] [[esql-enrich-prereqs]] -==== Prerequisites +=== Prerequisites include::{es-repo-dir}/ingest/apis/enrich/put-enrich-policy.asciidoc[tag=enrich-policy-api-prereqs] +[discrete] [[esql-create-enrich-source-index]] -==== Add enrich data +=== Add enrich data include::../ingest/enrich.asciidoc[tag=create-enrich-source-index] +[discrete] [[esql-create-enrich-policy]] -==== Create an enrich policy +=== Create an enrich policy include::../ingest/enrich.asciidoc[tag=create-enrich-policy] +[discrete] [[esql-execute-enrich-policy]] -==== Execute the enrich policy +=== Execute the enrich policy include::../ingest/enrich.asciidoc[tag=execute-enrich-policy1] @@ -105,8 +115,9 @@ image::images/esql/esql-enrich-policy.png[align="center"] include::../ingest/enrich.asciidoc[tag=execute-enrich-policy2] +[discrete] [[esql-use-enrich]] -==== Use the enrich policy +=== Use the enrich policy After the policy has been executed, you can use the <> to enrich your data. @@ -115,12 +126,14 @@ image::images/esql/esql-enrich-command.png[align="center",width=50%] include::processing-commands/enrich.asciidoc[tag=examples] +[discrete] [[esql-update-enrich-data]] -==== Update an enrich index +=== Update an enrich index include::{es-repo-dir}/ingest/apis/enrich/execute-enrich-policy.asciidoc[tag=update-enrich-index] +[discrete] [[esql-update-enrich-policies]] -==== Update an enrich policy +=== Update an enrich policy include::../ingest/enrich.asciidoc[tag=update-enrich-policy] diff --git a/docs/reference/esql/esql-examples.asciidoc b/docs/reference/esql/esql-examples.asciidoc new file mode 100644 index 0000000000000..da13608175910 --- /dev/null +++ b/docs/reference/esql/esql-examples.asciidoc @@ -0,0 +1,99 @@ +[[esql-examples]] +== Examples + +++++ +Examples +++++ + + +[discrete] +=== Aggregating and enriching windows event logs + +[source,esql] +---- +FROM logs-* +| WHERE event.code IS NOT NULL +| STATS event_code_count = count(event.code) by event.code,host.name +| ENRICH win_events on event.code with event_description +| WHERE event_description IS NOT NULL and host.name IS NOT NULL +| RENAME event_description as event.description +| SORT event_code_count desc +| KEEP event_code_count,event.code,host.name,event.description +---- + +* It starts by querying logs from indices that match the pattern "logs-*". +* Filters events where the "event.code" field is not null. +* Aggregates the count of events by "event.code" and "host.name." +* Enriches the events with additional information using the "EVENT_DESCRIPTION" field. +* Filters out events where "EVENT_DESCRIPTION" or "host.name" is null. +* Renames "EVENT_DESCRIPTION" as "event.description." +* Sorts the result by "event_code_count" in descending order. +* Keeps only selected fields: "event_code_count," "event.code," "host.name," and "event.description." + + +[discrete] +=== Summing outbound traffic from a process `curl.exe` + +[source,esql] +---- +FROM logs-endpoint +| WHERE process.name == "curl.exe" +| STATS bytes = SUM(destination.bytes) BY destination.address +| EVAL kb = bytes/1024 +| SORT kb desc +| LIMIT 10 +| KEEP kb,destination.address +---- + +* Queries logs from the "logs-endpoint" source. +* Filters events where the "process.name" field is "curl.exe." +* Calculates the sum of bytes sent to destination addresses and converts it to kilobytes (KB). +* Sorts the results by "kb" (kilobytes) in descending order. +* Limits the output to the top 10 results. +* Keeps only the "kb" and "destination.address" fields. + + +[discrete] +=== Manipulating DNS logs to find a high number of unique dns queries per registered domain + +[source,esql] +---- +FROM logs-* +| GROK dns.question.name "%{DATA}\\.%{GREEDYDATA:dns.question.registered_domain:string}" +| STATS unique_queries = count_distinct(dns.question.name) by dns.question.registered_domain, process.name +| WHERE unique_queries > 10 +| SORT unique_queries DESC +| RENAME unique_queries AS `Unique Queries`, dns.question.registered_domain AS `Registered Domain`, process.name AS `Process` +---- + +* Queries logs from indices matching "logs-*." +* Uses the "grok" pattern to extract the registered domain from the "dns.question.name" field. +* Calculates the count of unique DNS queries per registered domain and process name. +* Filters results where "unique_queries" are greater than 10. +* Sorts the results by "unique_queries" in descending order. +* Renames fields for clarity: "unique_queries" to "Unique Queries," "dns.question.registered_domain" to "Registered Domain," and "process.name" to "Process." + + +[discrete] +=== Identifying high-numbers of outbound user connections + +[source,esql] +---- +FROM logs-* +| WHERE NOT CIDR_MATCH(destination.ip, "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16") +| STATS destcount = COUNT(destination.ip) BY user.name, host.name +| ENRICH ldap_lookup_new ON user.name +| WHERE group.name IS NOT NULL +| EVAL follow_up = CASE(destcount >= 100, "true","false") +| SORT destcount desc +| KEEP destcount, host.name, user.name, group.name, follow_up +---- + +* Queries logs from indices matching "logs-*." +* Filters out events where the destination IP address falls within private IP address ranges (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). +* Calculates the count of unique destination IPs by "user.name" and "host.name." +* Enriches the "user.name" field with LDAP group information. +* Filters out results where "group.name" is not null. +* Uses a "CASE" statement to create a "follow_up" field, setting it to "true" when "destcount" is greater than or equal to 100 and "false" otherwise. +* Sorts the results by "destcount" in descending order. +* Keeps selected fields: "destcount," "host.name," "user.name," "group.name," and "follow_up." diff --git a/docs/reference/esql/esql-get-started.asciidoc b/docs/reference/esql/esql-get-started.asciidoc index 1f3cdf85c173e..676ad0ca0bf10 100644 --- a/docs/reference/esql/esql-get-started.asciidoc +++ b/docs/reference/esql/esql-get-started.asciidoc @@ -5,4 +5,30 @@ Getting started ++++ -coming::[8.11] \ No newline at end of file +A simple example of an {esql} query is shown below: +[source,esql] +---- +FROM employees +| EVAL age = DATE_DIFF(NOW(), birth_date, 'Y') +| STATS AVG(age) BY department +| SORT age DESC +---- + +Each {esql} query starts with a <>. A source command produces +a table, typically with data from {es}. + +image::images/esql/source-command.svg[A source command producing a table from {es},align="center"] + +A source command can be followed by one or more +<>. Processing commands change an +input table by adding, removing, or changing rows and columns. +Processing commands can perform filtering, projection, aggregation, and more. + +image::images/esql/processing-command.svg[A processing command changing an input table,align="center"] + +You can chain processing commands, separated by a pipe character: `|`. Each +processing command works on the output table of the previous command. + +image::images/esql/chaining-processing-commands.svg[Processing commands can be chained,align="center"] + +The result of a query is the table produced by the final processing command. diff --git a/docs/reference/esql/esql-language.asciidoc b/docs/reference/esql/esql-language.asciidoc index a932b8283f16e..6f8e54ebd86cb 100644 --- a/docs/reference/esql/esql-language.asciidoc +++ b/docs/reference/esql/esql-language.asciidoc @@ -1,8 +1,8 @@ [[esql-language]] -== Working with the {esql} language +== Learning {esql} ++++ -Working with the {esql} language +Learning {esql} ++++ Detailed information about the {esql} language: @@ -11,14 +11,10 @@ Detailed information about the {esql} language: * <> * <> * <> -* <> * <> -* <> include::esql-syntax.asciidoc[] include::esql-commands.asciidoc[] include::esql-functions-operators.asciidoc[] include::multivalued-fields.asciidoc[] -include::metadata-fields.asciidoc[] include::esql-process-data-with-dissect-grok.asciidoc[] -include::esql-enrich-data.asciidoc[] diff --git a/docs/reference/esql/esql-limitations.asciidoc b/docs/reference/esql/esql-limitations.asciidoc index f39ff73744276..2fa0b628352b1 100644 --- a/docs/reference/esql/esql-limitations.asciidoc +++ b/docs/reference/esql/esql-limitations.asciidoc @@ -5,11 +5,13 @@ Limitations ++++ +This is work in progress. + [discrete] [[esql-supported-types]] === Supported types -* {esql} currently supports the following <>: +{esql} currently supports the following <>: ** `alias` ** `boolean` @@ -24,9 +26,15 @@ ** `unsigned_long` ** `version` +[discrete] +[[esql-tsdb]] +=== {esql} and time series data + +{esql} does not support time series data (TSDS). + [discrete] [[esql-max-rows]] === 10,000 row maximum A single query will not return more than 10,000 rows, regardless of the -`LIMIT` command's value. \ No newline at end of file +`LIMIT` command's value. diff --git a/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc b/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc index b629051d2e53e..43b4a2a15f92f 100644 --- a/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc +++ b/docs/reference/esql/esql-process-data-with-dissect-grok.asciidoc @@ -1,8 +1,8 @@ [[esql-process-data-with-dissect-and-grok]] -=== Data processing with `DISSECT` and `GROK` +=== Data processing with DISSECT and GROK ++++ -Data processing with `DISSECT` and `GROK` +Data processing with DISSECT and GROK ++++ Your data may contain unstructured strings that you want to structure. This diff --git a/docs/reference/esql/index.asciidoc b/docs/reference/esql/index.asciidoc index 09b74740a5b67..c86dc088ff6b8 100644 --- a/docs/reference/esql/index.asciidoc +++ b/docs/reference/esql/index.asciidoc @@ -8,47 +8,21 @@ preview::[] -The {es} Query Language ({esql}) provides a powerful way to filter, transform, and analyze data stored in {es}. +The {es} Query Language ({esql}) provides a powerful way to filter, transform, and analyze data stored in {es}, and in the future in other runtimes. +It is designed to be easy to learn and use, by end users, SRE teams, application developers, and administrators. + Users can author {esql} queries to find specific events, perform statistical analysis, and generate visualizations. It supports a wide range of commands and functions that enable users to perform various data operations, such as filtering, aggregation, time-series analysis, and more. -The {es} Query Language ({esql}) makes use of "pipes" to manipulate and transform data in a step-by-step fashion. +The {es} Query Language ({esql}) makes use of "pipes" (|) to manipulate and transform data in a step-by-step fashion. This approach allows users to compose a series of operations, where the output of one operation becomes the input for the next, enabling complex data transformations and analysis. -A simple example of an {esql} query is shown below: -[source,esql] ----- -FROM employees -| EVAL age = DATE_DIFF(NOW(), birth_date, 'Y') -| STATS AVG(age) BY department -| SORT age DESC ----- - -Each {esql} query starts with a <>. A source command produces -a table, typically with data from {es}. - -image::images/esql/source-command.svg[A source command producing a table from {es},align="center"] - -A source command can be followed by one or more -<>. Processing commands change an -input table by adding, removing, or changing rows and columns. -Processing commands can perform filtering, projection, aggregation, and more. - -image::images/esql/processing-command.svg[A processing command changing an input table,align="center"] - -You can chain processing commands, separated by a pipe character: `|`. Each -processing command works on the output table of the previous command. - -image::images/esql/chaining-processing-commands.svg[Processing commands can be chained,align="center"] - -The result of a query is the table produced by the final processing command. - [discrete] === The {esql} Compute Engine -{esql} is more than a language. It represents a significant investment in new compute capabilities within {es}. +{esql} is more than a language: it represents a significant investment in new compute capabilities within {es}. To achieve both the functional and performance requirements for {esql}, it was necessary to build an entirely new compute architecture. {esql} search, aggregation, and transformation functions are directly executed within Elasticsearch itself. Query expressions are not transpiled to Query DSL for execution. This approach allows {esql} to be extremely performant and versatile. @@ -61,11 +35,17 @@ include::esql-language.asciidoc[] include::esql-rest.asciidoc[] +include::metadata-fields.asciidoc[] + include::esql-kibana.asciidoc[] include::task-management.asciidoc[] +include::esql-enrich-data.asciidoc[] + include::esql-limitations.asciidoc[] +include::esql-examples.asciidoc[] + :esql-tests!: :esql-specs!: diff --git a/docs/reference/esql/metadata-fields.asciidoc b/docs/reference/esql/metadata-fields.asciidoc index c034d4d0dd2b3..69c9c0c04dd7b 100644 --- a/docs/reference/esql/metadata-fields.asciidoc +++ b/docs/reference/esql/metadata-fields.asciidoc @@ -1,5 +1,5 @@ [[esql-metadata-fields]] -=== {esql} metadata fields +== {esql} metadata fields ++++ Metadata fields From 27956c05c0bd2aae0037c2752b81db4b7dbf72a4 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 25 Oct 2023 14:30:01 +0100 Subject: [PATCH 100/203] [ML] Remove Version from MlConfigVersion completely (#100131) Removed usage of Version from MlConfigVersion. DiscoveryNode will have to read this integer off the wire when serialized from these old nodes, so even if Version is removed from DiscoveryNode it will be able to provide the required value. --- .../cluster/node/DiscoveryNode.java | 11 +++++++ .../xpack/core/ml/MlConfigVersion.java | 32 ++++++++----------- .../xpack/core/ml/MlConfigVersionTests.java | 23 ++----------- .../InferenceProcessorFactoryTests.java | 18 +++++++++-- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java index 60aab68ec65dc..43f117acbd9fe 100644 --- a/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java +++ b/server/src/main/java/org/elasticsearch/cluster/node/DiscoveryNode.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -488,6 +489,16 @@ public Version getVersion() { return this.versionInfo.nodeVersion(); } + public OptionalInt getPre811VersionId() { + // Even if Version is removed from this class completely it will need to read the version ID + // off the wire for old node versions, so the value of this variable can be obtained from that + int versionId = versionInfo.nodeVersion().id; + if (versionId >= Version.V_8_11_0.id) { + return OptionalInt.empty(); + } + return OptionalInt.of(versionId); + } + public IndexVersion getMinIndexVersion() { return versionInfo.minIndexVersion(); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java index e631e3efe5cb6..7ab8e41cd2453 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java @@ -268,17 +268,6 @@ public static MlConfigVersion max(MlConfigVersion version1, MlConfigVersion vers return version1.id > version2.id ? version1 : version2; } - // Visible only for testing - static MlConfigVersion fromVersion(Version version) { - if (version.equals(Version.V_8_10_0)) { - return V_10; - } - if (version.after(Version.V_8_10_0)) { - throw new IllegalArgumentException("Cannot convert " + version + ". Incompatible version"); - } - return fromId(version.id); - } - public static MlConfigVersion getMinMlConfigVersion(DiscoveryNodes nodes) { return getMinMaxMlConfigVersion(nodes).v1(); } @@ -308,10 +297,10 @@ public static Tuple getMinMaxMlConfigVersion(D public static MlConfigVersion getMlConfigVersionForNode(DiscoveryNode node) { String mlConfigVerStr = node.getAttributes().get(ML_CONFIG_VERSION_NODE_ATTR); - if (mlConfigVerStr == null) { - return fromVersion(node.getVersion()); + if (mlConfigVerStr != null) { + return fromString(mlConfigVerStr); } - return fromString(mlConfigVerStr); + return fromId(node.getPre811VersionId().orElseThrow(() -> new IllegalStateException("getting legacy version id not possible"))); } // Parse an MlConfigVersion from a string. @@ -329,12 +318,17 @@ public static MlConfigVersion fromString(String str) { if (str.startsWith("8.10.") || str.equals("8.11.0")) { return V_10; } - Matcher matcher = Pattern.compile("^(\\d+)\\.0\\.0$").matcher(str); - int versionNum; - if (matcher.matches() == false || (versionNum = Integer.parseInt(matcher.group(1))) < 10) { - return fromVersion(Version.fromString(str)); + Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(?:-\\w+)?$").matcher(str); + if (matcher.matches() == false) { + throw new IllegalArgumentException("ML config version [" + str + "] not valid"); + } + int first = Integer.parseInt(matcher.group(1)); + int second = Integer.parseInt(matcher.group(2)); + int third = Integer.parseInt(matcher.group(3)); + if (first >= 10 && (second > 0 || third > 0)) { + throw new IllegalArgumentException("ML config version [" + str + "] not valid"); } - return fromId(1000000 * versionNum + 99); + return fromId(1000000 * first + 10000 * second + 100 * third + 99); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java index 75eee620c031c..f97d9e1f21d07 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/MlConfigVersionTests.java @@ -167,7 +167,7 @@ public void testGetMlConfigVersionForNode() { .version(VersionInformation.inferVersions(Version.fromString("8.7.0"))) .build(); MlConfigVersion mlConfigVersion1 = MlConfigVersion.getMlConfigVersionForNode(node1); - assertEquals(MlConfigVersion.fromVersion(Version.V_8_5_0), mlConfigVersion1); + assertEquals(MlConfigVersion.V_8_5_0, mlConfigVersion1); } public void testDefinedConstants() throws IllegalAccessException { @@ -232,19 +232,6 @@ public void testMax() { ); } - public void testFromVersion() { - Version version_V_7_7_0 = Version.V_7_0_0; - MlConfigVersion mlConfigVersion_V_7_7_0 = MlConfigVersion.fromVersion(version_V_7_7_0); - assertEquals(version_V_7_7_0.id, mlConfigVersion_V_7_7_0.id()); - - // Version 8.10.0 is treated as if it is MlConfigVersion V_10. - assertEquals(MlConfigVersion.V_10.id(), MlConfigVersion.fromVersion(Version.V_8_10_0).id()); - - // There's no mapping between Version and MlConfigVersion values after Version.V_8_10_0. - Exception e = expectThrows(IllegalArgumentException.class, () -> MlConfigVersion.fromVersion(Version.fromId(8_11_00_99))); - assertEquals("Cannot convert " + Version.fromId(8_11_00_99) + ". Incompatible version", e.getMessage()); - } - public void testVersionConstantPresent() { Set ignore = Set.of(MlConfigVersion.ZERO, MlConfigVersion.CURRENT, MlConfigVersion.FIRST_ML_VERSION); assertThat(MlConfigVersion.CURRENT, sameInstance(MlConfigVersion.fromId(MlConfigVersion.CURRENT.id()))); @@ -298,13 +285,9 @@ public void testFromString() { assertEquals(false, KnownMlConfigVersions.ALL_VERSIONS.contains(unknownVersion)); assertEquals(MlConfigVersion.CURRENT.id() + 1, unknownVersion.id()); - for (String version : new String[] { "10.2", "7.17.2.99" }) { + for (String version : new String[] { "10.2", "7.17.2.99", "9" }) { Exception e = expectThrows(IllegalArgumentException.class, () -> MlConfigVersion.fromString(version)); - assertEquals("the version needs to contain major, minor, and revision, and optionally the build: " + version, e.getMessage()); + assertEquals("ML config version [" + version + "] not valid", e.getMessage()); } - - String version = "9"; - Exception e = expectThrows(IllegalArgumentException.class, () -> MlConfigVersion.fromString(version)); - assertEquals("the version needs to contain major, minor, and revision, and optionally the build: " + version, e.getMessage()); } } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java index a8d3af2efe7cd..5c98ac53c7228 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/inference/ingest/InferenceProcessorFactoryTests.java @@ -922,8 +922,22 @@ private static ClusterState builderClusterStateWithModelReferences(MlConfigVersi Set.of(DiscoveryNodeRole.MASTER_ROLE, DiscoveryNodeRole.ML_ROLE, DiscoveryNodeRole.DATA_ROLE) ) ) - .add(DiscoveryNodeUtils.create("current_node", new TransportAddress(InetAddress.getLoopbackAddress(), 9302))) - .add(DiscoveryNodeUtils.create("_node_id", new TransportAddress(InetAddress.getLoopbackAddress(), 9304))) + .add( + DiscoveryNodeUtils.create( + "current_node", + new TransportAddress(InetAddress.getLoopbackAddress(), 9302), + Map.of(MachineLearning.ML_CONFIG_VERSION_NODE_ATTR, MlConfigVersion.CURRENT.toString()), + Set.of(DiscoveryNodeRole.MASTER_ROLE, DiscoveryNodeRole.ML_ROLE, DiscoveryNodeRole.DATA_ROLE) + ) + ) + .add( + DiscoveryNodeUtils.create( + "_node_id", + new TransportAddress(InetAddress.getLoopbackAddress(), 9304), + Map.of(MachineLearning.ML_CONFIG_VERSION_NODE_ATTR, MlConfigVersion.CURRENT.toString()), + Set.of(DiscoveryNodeRole.MASTER_ROLE, DiscoveryNodeRole.ML_ROLE, DiscoveryNodeRole.DATA_ROLE) + ) + ) .localNodeId("_node_id") .masterNodeId("_node_id") ) From 9e135ebfd13cc27b58f67e49a3407265a990172e Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 25 Oct 2023 15:00:20 +0100 Subject: [PATCH 101/203] Reorder/extract methods in S3 CompareAndExchangeOperation (#101299) Shorten `run()` and put the methods it calls into a more logical order. --- .../repositories/s3/S3BlobContainer.java | 226 +++++++++--------- 1 file changed, 114 insertions(+), 112 deletions(-) diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java index fa5b6a2aee5d3..296dd81b14ce0 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java @@ -580,111 +580,20 @@ private class CompareAndExchangeOperation { this.threadPool = threadPool; } - private List listMultipartUploads() { - final var listRequest = new ListMultipartUploadsRequest(bucket); - listRequest.setPrefix(blobKey); - listRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.LIST_OBJECTS, purpose)); - try { - return SocketAccess.doPrivileged(() -> client.listMultipartUploads(listRequest)).getMultipartUploads(); - } catch (AmazonS3Exception e) { - if (e.getStatusCode() == 404) { - return List.of(); - } - throw e; - } - } - - private int getUploadIndex(String targetUploadId, List multipartUploads) { - var uploadIndex = 0; - var found = false; - for (MultipartUpload multipartUpload : multipartUploads) { - final var observedUploadId = multipartUpload.getUploadId(); - if (observedUploadId.equals(targetUploadId)) { - final var currentTimeMillis = blobStore.getThreadPool().absoluteTimeInMillis(); - final var ageMillis = currentTimeMillis - multipartUpload.getInitiated().toInstant().toEpochMilli(); - final var expectedAgeRangeMillis = blobStore.getCompareAndExchangeTimeToLive().millis(); - if (ageMillis < -expectedAgeRangeMillis || ageMillis > expectedAgeRangeMillis) { - logger.warn( - """ - compare-and-exchange of blob [{}:{}] was initiated at [{}={}] \ - which deviates from local node epoch time [{}] by more than the warn threshold of [{}ms]""", - bucket, - blobKey, - multipartUpload.getInitiated(), - multipartUpload.getInitiated().toInstant().toEpochMilli(), - currentTimeMillis, - expectedAgeRangeMillis - ); - } - found = true; - } else if (observedUploadId.compareTo(targetUploadId) < 0) { - uploadIndex += 1; - } - } - - return found ? uploadIndex : -1; - } - - /** - * @return {@code true} if there are already ongoing uploads, so we should not proceed with the operation - */ - private boolean hasPreexistingUploads() { - final var uploads = listMultipartUploads(); - if (uploads.isEmpty()) { - return false; - } - - final var expiryDate = Date.from( - Instant.ofEpochMilli( - blobStore.getThreadPool().absoluteTimeInMillis() - blobStore.getCompareAndExchangeTimeToLive().millis() - ) - ); - if (uploads.stream().anyMatch(upload -> upload.getInitiated().after(expiryDate))) { - return true; - } - - // there are uploads, but they are all older than the TTL, so clean them up before carrying on (should be rare) - for (final var upload : uploads) { - logger.warn( - "cleaning up stale compare-and-swap upload [{}] initiated at [{}]", - upload.getUploadId(), - upload.getInitiated() - ); - safeAbortMultipartUpload(upload.getUploadId()); - } - - return false; - } - void run(BytesReference expected, BytesReference updated, ActionListener listener) throws Exception { BlobContainerUtils.ensureValidRegisterContent(updated); if (hasPreexistingUploads()) { - // This is a small optimization to improve the liveness properties of this algorithm. // // We can safely proceed even if there are other uploads in progress, but that would add to the potential for collisions and // delays. Thus in this case we prefer avoid disturbing the ongoing attempts and just fail up front. - listener.onResponse(OptionalBytesReference.MISSING); return; } - final var initiateRequest = new InitiateMultipartUploadRequest(bucket, blobKey); - initiateRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.PUT_MULTIPART_OBJECT, purpose)); - final var uploadId = SocketAccess.doPrivileged(() -> client.initiateMultipartUpload(initiateRequest)).getUploadId(); - - final var uploadPartRequest = new UploadPartRequest(); - uploadPartRequest.setBucketName(bucket); - uploadPartRequest.setKey(blobKey); - uploadPartRequest.setUploadId(uploadId); - uploadPartRequest.setPartNumber(1); - uploadPartRequest.setLastPart(true); - uploadPartRequest.setInputStream(updated.streamInput()); - uploadPartRequest.setPartSize(updated.length()); - uploadPartRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.PUT_MULTIPART_OBJECT, purpose)); - final var partETag = SocketAccess.doPrivileged(() -> client.uploadPart(uploadPartRequest)).getPartETag(); - + final var uploadId = initiateMultipartUpload(); + final var partETag = uploadPart(updated, uploadId); final var currentUploads = listMultipartUploads(); final var uploadIndex = getUploadIndex(uploadId, currentUploads); @@ -710,16 +619,7 @@ void run(BytesReference expected, BytesReference updated, ActionListener ActionListener.completeWith(delegate2, () -> { if (currentValue.isPresent() && currentValue.bytesReference().equals(expected)) { - final var completeMultipartUploadRequest = new CompleteMultipartUploadRequest( - bucket, - blobKey, - uploadId, - List.of(partETag) - ); - completeMultipartUploadRequest.setRequestMetricCollector( - blobStore.getMetricCollector(Operation.PUT_MULTIPART_OBJECT, purpose) - ); - SocketAccess.doPrivilegedVoid(() -> client.completeMultipartUpload(completeMultipartUploadRequest)); + completeMultipartUpload(uploadId, partETag); isComplete.set(true); } return currentValue; @@ -740,15 +640,7 @@ void run(BytesReference expected, BytesReference updated, ActionListener { try { - for (MultipartUpload currentUpload : currentUploads) { - final var currentUploadId = currentUpload.getUploadId(); - if (uploadId.equals(currentUploadId) == false) { - blobStore.getSnapshotExecutor() - .execute( - ActionRunnable.run(listeners.acquire(), () -> abortMultipartUploadIfExists(currentUploadId)) - ); - } - } + cancelOtherUploads(uploadId, currentUploads, listeners); } finally { delayListener.onResponse(null); } @@ -769,6 +661,111 @@ void run(BytesReference expected, BytesReference updated, ActionListener upload.getInitiated().after(expiryDate))) { + return true; + } + + // there are uploads, but they are all older than the TTL, so clean them up before carrying on (should be rare) + for (final var upload : uploads) { + logger.warn( + "cleaning up stale compare-and-swap upload [{}] initiated at [{}]", + upload.getUploadId(), + upload.getInitiated() + ); + safeAbortMultipartUpload(upload.getUploadId()); + } + + return false; + } + + private List listMultipartUploads() { + final var listRequest = new ListMultipartUploadsRequest(bucket); + listRequest.setPrefix(blobKey); + listRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.LIST_OBJECTS, purpose)); + try { + return SocketAccess.doPrivileged(() -> client.listMultipartUploads(listRequest)).getMultipartUploads(); + } catch (AmazonS3Exception e) { + if (e.getStatusCode() == 404) { + return List.of(); + } + throw e; + } + } + + private String initiateMultipartUpload() { + final var initiateRequest = new InitiateMultipartUploadRequest(bucket, blobKey); + initiateRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.PUT_MULTIPART_OBJECT, purpose)); + return SocketAccess.doPrivileged(() -> client.initiateMultipartUpload(initiateRequest)).getUploadId(); + } + + private PartETag uploadPart(BytesReference updated, String uploadId) throws IOException { + final var uploadPartRequest = new UploadPartRequest(); + uploadPartRequest.setBucketName(bucket); + uploadPartRequest.setKey(blobKey); + uploadPartRequest.setUploadId(uploadId); + uploadPartRequest.setPartNumber(1); + uploadPartRequest.setLastPart(true); + uploadPartRequest.setInputStream(updated.streamInput()); + uploadPartRequest.setPartSize(updated.length()); + uploadPartRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.PUT_MULTIPART_OBJECT, purpose)); + return SocketAccess.doPrivileged(() -> client.uploadPart(uploadPartRequest)).getPartETag(); + } + + private int getUploadIndex(String targetUploadId, List multipartUploads) { + var uploadIndex = 0; + var found = false; + for (MultipartUpload multipartUpload : multipartUploads) { + final var observedUploadId = multipartUpload.getUploadId(); + if (observedUploadId.equals(targetUploadId)) { + final var currentTimeMillis = blobStore.getThreadPool().absoluteTimeInMillis(); + final var ageMillis = currentTimeMillis - multipartUpload.getInitiated().toInstant().toEpochMilli(); + final var expectedAgeRangeMillis = blobStore.getCompareAndExchangeTimeToLive().millis(); + if (ageMillis < -expectedAgeRangeMillis || ageMillis > expectedAgeRangeMillis) { + logger.warn( + """ + compare-and-exchange of blob [{}:{}] was initiated at [{}={}] \ + which deviates from local node epoch time [{}] by more than the warn threshold of [{}ms]""", + bucket, + blobKey, + multipartUpload.getInitiated(), + multipartUpload.getInitiated().toInstant().toEpochMilli(), + currentTimeMillis, + expectedAgeRangeMillis + ); + } + found = true; + } else if (observedUploadId.compareTo(targetUploadId) < 0) { + uploadIndex += 1; + } + } + + return found ? uploadIndex : -1; + } + + private void cancelOtherUploads(String uploadId, List currentUploads, RefCountingListener listeners) { + for (final var currentUpload : currentUploads) { + final var currentUploadId = currentUpload.getUploadId(); + if (uploadId.equals(currentUploadId) == false) { + blobStore.getSnapshotExecutor() + .execute(ActionRunnable.run(listeners.acquire(), () -> abortMultipartUploadIfExists(currentUploadId))); + } + } + } + private void safeAbortMultipartUpload(String uploadId) { try { abortMultipartUploadIfExists(uploadId); @@ -791,6 +788,11 @@ private void abortMultipartUploadIfExists(String uploadId) { } } + private void completeMultipartUpload(String uploadId, PartETag partETag) { + final var completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucket, blobKey, uploadId, List.of(partETag)); + completeMultipartUploadRequest.setRequestMetricCollector(blobStore.getMetricCollector(Operation.PUT_MULTIPART_OBJECT, purpose)); + SocketAccess.doPrivilegedVoid(() -> client.completeMultipartUpload(completeMultipartUploadRequest)); + } } @Override From 5b2c25f80bd1bbb43c06dec30414f0817ee33a5a Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Wed, 25 Oct 2023 09:30:05 -0500 Subject: [PATCH 102/203] Metrics Tests - Recording Otel Meter (#101281) Adds an implementation of Otel's Meter that records all instrument calls made through the open telemetry interface. This allows the registry to avoid mocking out otel in testing. Updates the GaugeAdapterTests to use the recording meter. --- .../telemetry/apm/RecordingOtelMeter.java | 648 ++++++++++++++++++ .../internal/metrics/GaugeAdapterTests.java | 113 +-- .../telemetry/InstrumentType.java | 2 +- .../telemetry/MetricRecorder.java | 16 +- 4 files changed, 685 insertions(+), 94 deletions(-) create mode 100644 modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java new file mode 100644 index 0000000000000..a067d5e0f8c60 --- /dev/null +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java @@ -0,0 +1,648 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.apm; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleCounter; +import io.opentelemetry.api.metrics.DoubleCounterBuilder; +import io.opentelemetry.api.metrics.DoubleGaugeBuilder; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.DoubleHistogramBuilder; +import io.opentelemetry.api.metrics.DoubleUpDownCounter; +import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongCounterBuilder; +import io.opentelemetry.api.metrics.LongGaugeBuilder; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.LongHistogramBuilder; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.LongUpDownCounterBuilder; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleCounter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import io.opentelemetry.api.metrics.ObservableDoubleMeasurement; +import io.opentelemetry.api.metrics.ObservableDoubleUpDownCounter; +import io.opentelemetry.api.metrics.ObservableLongCounter; +import io.opentelemetry.api.metrics.ObservableLongGauge; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; +import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.context.Context; + +import org.elasticsearch.telemetry.InstrumentType; +import org.elasticsearch.telemetry.MetricRecorder; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Consumer; + +public class RecordingOtelMeter implements Meter { + + Queue callbacks = new ConcurrentLinkedQueue<>(); + + public void collectMetrics() { + callbacks.forEach(Callback::doCall); + } + + public MetricRecorder getRecorder() { + return recorder; + } + + private final MetricRecorder recorder = new MetricRecorder<>(); + + @Override + public LongCounterBuilder counterBuilder(String name) { + return new RecordingLongCounterBuilder(name); + } + + @Override + public LongUpDownCounterBuilder upDownCounterBuilder(String name) { + return new RecordingLongUpDownBuilder(name); + } + + @Override + public DoubleHistogramBuilder histogramBuilder(String name) { + return new RecordingDoubleHistogramBuilder(name); + } + + @Override + public DoubleGaugeBuilder gaugeBuilder(String name) { + return new RecordingDoubleGaugeBuilder(name); + } + + // Counter + private class RecordingLongCounterBuilder extends AbstractBuilder implements LongCounterBuilder { + RecordingLongCounterBuilder(String name) { + super(name); + } + + @Override + public LongCounterBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public LongCounterBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public DoubleCounterBuilder ofDoubles() { + return new RecordingDoubleCounterBuilder(this); + } + + @Override + public LongCounter build() { + LongRecorder counter = new LongRecorder(name); + recorder.register(counter, counter.getInstrument(), name, description, unit); + return counter; + } + + @Override + public ObservableLongCounter buildWithCallback(Consumer callback) { + unimplemented(); + return null; + } + + @Override + public ObservableLongMeasurement buildObserver() { + unimplemented(); + return null; + } + } + + private class LongRecorder extends LongUpDownRecorder implements LongCounter, OtelInstrument { + LongRecorder(String name) { + super(name, InstrumentType.LONG_COUNTER); + } + + @Override + public void add(long value) { + assert value >= 0; + super.add(value); + } + + @Override + public void add(long value, Attributes attributes) { + assert value >= 0; + super.add(value, attributes); + } + + @Override + public void add(long value, Attributes attributes, Context context) { + assert value >= 0; + super.add(value, attributes, context); + } + } + + private class RecordingDoubleCounterBuilder extends AbstractBuilder implements DoubleCounterBuilder { + + RecordingDoubleCounterBuilder(AbstractBuilder other) { + super(other); + } + + @Override + public DoubleCounterBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public DoubleCounterBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public DoubleCounter build() { + DoubleRecorder counter = new DoubleRecorder(name); + recorder.register(counter, counter.getInstrument(), name, description, unit); + return counter; + } + + @Override + public ObservableDoubleCounter buildWithCallback(Consumer callback) { + unimplemented(); + return null; + } + + @Override + public ObservableDoubleMeasurement buildObserver() { + unimplemented(); + return null; + } + } + + private class DoubleRecorder extends DoubleUpDownRecorder implements DoubleCounter, OtelInstrument { + DoubleRecorder(String name) { + super(name, InstrumentType.DOUBLE_COUNTER); + } + + @Override + public void add(double value) { + assert value >= 0; + super.add(value); + } + + @Override + public void add(double value, Attributes attributes) { + assert value >= 0; + super.add(value, attributes); + } + + @Override + public void add(double value, Attributes attributes, Context context) { + assert value >= 0; + super.add(value, attributes, context); + } + } + + private class RecordingLongUpDownBuilder extends AbstractBuilder implements LongUpDownCounterBuilder { + RecordingLongUpDownBuilder(String name) { + super(name); + } + + @Override + public LongUpDownCounterBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public LongUpDownCounterBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public DoubleUpDownCounterBuilder ofDoubles() { + return new RecordingDoubleUpDownBuilder(this); + } + + @Override + public LongUpDownCounter build() { + LongUpDownRecorder counter = new LongUpDownRecorder(name); + recorder.register(counter, counter.getInstrument(), name, description, unit); + return counter; + } + + @Override + public ObservableLongUpDownCounter buildWithCallback(Consumer callback) { + unimplemented(); + return null; + } + + @Override + public ObservableLongMeasurement buildObserver() { + unimplemented(); + return null; + } + } + + private class LongUpDownRecorder extends AbstractInstrument implements LongUpDownCounter, OtelInstrument { + LongUpDownRecorder(String name) { + super(name, InstrumentType.LONG_UP_DOWN_COUNTER); + } + + protected LongUpDownRecorder(String name, InstrumentType instrument) { + // used by LongRecorder + super(name, instrument); + } + + @Override + public void add(long value) { + recorder.call(instrument, name, value, null); + } + + @Override + public void add(long value, Attributes attributes) { + recorder.call(instrument, name, value, toMap(attributes)); + } + + @Override + public void add(long value, Attributes attributes, Context context) { + unimplemented(); + } + } + + private class RecordingDoubleUpDownBuilder extends AbstractBuilder implements DoubleUpDownCounterBuilder { + + RecordingDoubleUpDownBuilder(AbstractBuilder other) { + super(other); + } + + @Override + public DoubleUpDownCounterBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public DoubleUpDownCounterBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public DoubleUpDownCounter build() { + DoubleUpDownRecorder counter = new DoubleUpDownRecorder(name); + recorder.register(counter, counter.getInstrument(), name, description, unit); + return counter; + } + + @Override + public ObservableDoubleUpDownCounter buildWithCallback(Consumer callback) { + unimplemented(); + return null; + } + + @Override + public ObservableDoubleMeasurement buildObserver() { + unimplemented(); + return null; + } + } + + private class DoubleUpDownRecorder extends AbstractInstrument implements DoubleUpDownCounter, OtelInstrument { + DoubleUpDownRecorder(String name) { + super(name, InstrumentType.LONG_UP_DOWN_COUNTER); + } + + protected DoubleUpDownRecorder(String name, InstrumentType instrument) { + // used by DoubleRecorder + super(name, instrument); + } + + @Override + public void add(double value) { + recorder.call(instrument, name, value, null); + } + + @Override + public void add(double value, Attributes attributes) { + recorder.call(instrument, name, value, toMap(attributes)); + } + + @Override + public void add(double value, Attributes attributes, Context context) { + unimplemented(); + } + } + + interface Callback { + void doCall(); + } + + abstract static class AbstractInstrument { + protected final String name; + protected final InstrumentType instrument; + + AbstractInstrument(String name, InstrumentType instrument) { + this.name = name; + this.instrument = instrument; + } + + public InstrumentType getInstrument() { + return instrument; + } + + protected void unimplemented() { + throw new UnsupportedOperationException("unimplemented"); + } + + Map toMap(Attributes attributes) { + if (attributes == null) { + return null; + } + if (attributes.isEmpty()) { + return Collections.emptyMap(); + } + Map map = new HashMap<>(attributes.size()); + attributes.forEach((k, v) -> map.put(k.getKey(), v)); + return map; + } + } + + abstract static class AbstractBuilder { + protected final String name; + protected String description; + protected String unit; + + AbstractBuilder(String name) { + this.name = name; + } + + AbstractBuilder(AbstractBuilder other) { + this.name = other.name; + this.description = other.description; + this.unit = other.unit; + } + + void innerSetDescription(String description) { + this.description = description; + } + + void innerSetUnit(String unit) { + this.unit = unit; + } + + protected void unimplemented() { + throw new UnsupportedOperationException("unimplemented"); + } + } + + interface OtelInstrument {} + + // Gauges + private class RecordingDoubleGaugeBuilder extends AbstractBuilder implements DoubleGaugeBuilder { + RecordingDoubleGaugeBuilder(String name) { + super(name); + } + + @Override + public DoubleGaugeBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public DoubleGaugeBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public LongGaugeBuilder ofLongs() { + return new RecordingLongGaugeBuilder(this); + } + + @Override + public ObservableDoubleGauge buildWithCallback(Consumer callback) { + DoubleGaugeRecorder gauge = new DoubleGaugeRecorder(name, callback); + recorder.register(gauge, gauge.getInstrument(), name, description, unit); + callbacks.add(gauge); + return gauge; + } + + @Override + public ObservableDoubleMeasurement buildObserver() { + DoubleMeasurementRecorder measurement = new DoubleMeasurementRecorder(name); + recorder.register(measurement, measurement.getInstrument(), name, description, unit); + return measurement; + } + } + + private class DoubleGaugeRecorder extends AbstractInstrument implements ObservableDoubleGauge, Callback, OtelInstrument { + final Consumer callback; + + DoubleGaugeRecorder(String name, Consumer callback) { + super(name, InstrumentType.DOUBLE_GAUGE_OBSERVER); + this.callback = callback; + } + + @Override + public void close() { + callbacks.remove(this); + } + + public void doCall() { + callback.accept(new DoubleMeasurementRecorder(name, instrument)); + } + } + + private class DoubleMeasurementRecorder extends AbstractInstrument implements ObservableDoubleMeasurement, OtelInstrument { + DoubleMeasurementRecorder(String name, InstrumentType instrument) { + super(name, instrument); + } + + DoubleMeasurementRecorder(String name) { + super(name, InstrumentType.DOUBLE_GAUGE); + } + + @Override + public void record(double value) { + recorder.call(instrument, name, value, null); + } + + @Override + public void record(double value, Attributes attributes) { + recorder.call(instrument, name, value, toMap(attributes)); + } + } + + private class RecordingLongGaugeBuilder extends AbstractBuilder implements LongGaugeBuilder { + RecordingLongGaugeBuilder(AbstractBuilder other) { + super(other); + } + + @Override + public LongGaugeBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public LongGaugeBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public ObservableLongGauge buildWithCallback(Consumer callback) { + LongGaugeRecorder gauge = new LongGaugeRecorder(name, callback); + recorder.register(gauge, gauge.getInstrument(), name, description, unit); + callbacks.add(gauge); + return gauge; + } + + @Override + public ObservableLongMeasurement buildObserver() { + LongMeasurementRecorder measurement = new LongMeasurementRecorder(name); + recorder.register(measurement, measurement.getInstrument(), name, description, unit); + return measurement; + } + } + + private class LongGaugeRecorder extends AbstractInstrument implements ObservableLongGauge, Callback, OtelInstrument { + final Consumer callback; + + LongGaugeRecorder(String name, Consumer callback) { + super(name, InstrumentType.LONG_GAUGE_OBSERVER); + this.callback = callback; + } + + @Override + public void close() { + callbacks.remove(this); + } + + public void doCall() { + callback.accept(new LongMeasurementRecorder(name, instrument)); + } + } + + private class LongMeasurementRecorder extends AbstractInstrument implements ObservableLongMeasurement, OtelInstrument { + LongMeasurementRecorder(String name, InstrumentType instrument) { + super(name, instrument); + } + + LongMeasurementRecorder(String name) { + super(name, InstrumentType.LONG_GAUGE); + } + + @Override + public void record(long value) { + recorder.call(instrument, name, value, null); + } + + @Override + public void record(long value, Attributes attributes) { + recorder.call(instrument, name, value, toMap(attributes)); + } + } + + // Histograms + private class RecordingDoubleHistogramBuilder extends AbstractBuilder implements DoubleHistogramBuilder { + RecordingDoubleHistogramBuilder(String name) { + super(name); + } + + @Override + public DoubleHistogramBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public DoubleHistogramBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public LongHistogramBuilder ofLongs() { + return new RecordingLongHistogramBuilder(this); + } + + @Override + public DoubleHistogram build() { + return new DoubleHistogramRecorder(name); + } + } + + private class DoubleHistogramRecorder extends AbstractInstrument implements DoubleHistogram, OtelInstrument { + DoubleHistogramRecorder(String name) { + super(name, InstrumentType.DOUBLE_HISTOGRAM); + } + + @Override + public void record(double value) { + recorder.call(getInstrument(), name, value, null); + } + + @Override + public void record(double value, Attributes attributes) { + recorder.call(getInstrument(), name, value, toMap(attributes)); + } + + @Override + public void record(double value, Attributes attributes, Context context) { + unimplemented(); + } + } + + private class RecordingLongHistogramBuilder extends AbstractBuilder implements LongHistogramBuilder { + + RecordingLongHistogramBuilder(AbstractBuilder other) { + super(other); + } + + @Override + public LongHistogramBuilder setDescription(String description) { + innerSetDescription(description); + return this; + } + + @Override + public LongHistogramBuilder setUnit(String unit) { + innerSetUnit(unit); + return this; + } + + @Override + public LongHistogram build() { + return new LongHistogramRecorder(name); + } + } + + private class LongHistogramRecorder extends AbstractInstrument implements LongHistogram, OtelInstrument { + LongHistogramRecorder(String name) { + super(name, InstrumentType.LONG_HISTOGRAM); + } + + @Override + public void record(long value) { + recorder.call(getInstrument(), name, value, null); + } + + @Override + public void record(long value, Attributes attributes) { + recorder.call(getInstrument(), name, value, toMap(attributes)); + } + + @Override + public void record(long value, Attributes attributes, Context context) { + unimplemented(); + } + } +} diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java index 1e230eefe32dc..6e349842f7673 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java @@ -8,116 +8,59 @@ package org.elasticsearch.telemetry.apm.internal.metrics; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.metrics.DoubleGaugeBuilder; -import io.opentelemetry.api.metrics.LongGaugeBuilder; -import io.opentelemetry.api.metrics.Meter; -import io.opentelemetry.api.metrics.ObservableDoubleMeasurement; -import io.opentelemetry.api.metrics.ObservableLongMeasurement; - +import org.elasticsearch.telemetry.Measurement; +import org.elasticsearch.telemetry.apm.APMMeterRegistry; +import org.elasticsearch.telemetry.apm.RecordingOtelMeter; +import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.LongGauge; import org.elasticsearch.test.ESTestCase; -import org.hamcrest.Matchers; import org.junit.Before; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; +import java.util.List; import java.util.Map; -import java.util.function.Consumer; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; public class GaugeAdapterTests extends ESTestCase { - Meter testMeter = Mockito.mock(Meter.class); - LongGaugeBuilder longGaugeBuilder = Mockito.mock(LongGaugeBuilder.class); - DoubleGaugeBuilder mockDoubleGaugeBuilder = Mockito.mock(DoubleGaugeBuilder.class); + RecordingOtelMeter otelMeter; + APMMeterRegistry registry; @Before public void init() { - when(longGaugeBuilder.setDescription(Mockito.anyString())).thenReturn(longGaugeBuilder); - when(longGaugeBuilder.setUnit(Mockito.anyString())).thenReturn(longGaugeBuilder); - - - when(mockDoubleGaugeBuilder.ofLongs()).thenReturn(longGaugeBuilder); - when(mockDoubleGaugeBuilder.setUnit(Mockito.anyString())).thenReturn(mockDoubleGaugeBuilder); - when(mockDoubleGaugeBuilder.setDescription(Mockito.anyString())).thenReturn(mockDoubleGaugeBuilder); - when(testMeter.gaugeBuilder(anyString())).thenReturn(mockDoubleGaugeBuilder); + otelMeter = new RecordingOtelMeter(); + registry = new APMMeterRegistry(otelMeter); } // testing that a value reported is then used in a callback @SuppressWarnings("unchecked") public void testLongGaugeRecord() { - LongGaugeAdapter longGaugeAdapter = new LongGaugeAdapter(testMeter, "name", "desc", "unit"); + LongGauge longGauge = registry.registerLongGauge("name", "desc", "unit"); // recording a value - longGaugeAdapter.record(1L, Map.of("k", 1L)); + Map attributes = Map.of("k", 1L); + longGauge.record(1L, attributes); - // upon metric export, the consumer will be called - ArgumentCaptor> captor = ArgumentCaptor.forClass(Consumer.class); - verify(longGaugeBuilder).buildWithCallback(captor.capture()); + otelMeter.collectMetrics(); - Consumer value = captor.getValue(); - // making sure that a consumer will fetch the value passed down upon recording of a value - TestLongMeasurement testLongMeasurement = new TestLongMeasurement(); - value.accept(testLongMeasurement); - - assertThat(testLongMeasurement.value, Matchers.equalTo(1L)); - assertThat(testLongMeasurement.attributes, Matchers.equalTo(Attributes.builder().put("k", 1).build())); + List metrics = otelMeter.getRecorder().getMeasurements(longGauge); + assertThat(metrics, hasSize(1)); + assertThat(metrics.get(0).attributes(), equalTo(attributes)); + assertThat(metrics.get(0).getLong(), equalTo(1L)); } // testing that a value reported is then used in a callback @SuppressWarnings("unchecked") public void testDoubleGaugeRecord() { - DoubleGaugeAdapter doubleGaugeAdapter = new DoubleGaugeAdapter(testMeter, "name", "desc", "unit"); - - // recording a value - doubleGaugeAdapter.record(1.0, Map.of("k", 1.0)); - - // upon metric export, the consumer will be called - ArgumentCaptor> captor = ArgumentCaptor.forClass(Consumer.class); - verify(mockDoubleGaugeBuilder).buildWithCallback(captor.capture()); - - Consumer value = captor.getValue(); - // making sure that a consumer will fetch the value passed down upon recording of a value - TestDoubleMeasurement testLongMeasurement = new TestDoubleMeasurement(); - value.accept(testLongMeasurement); - - assertThat(testLongMeasurement.value, Matchers.equalTo(1.0)); - assertThat(testLongMeasurement.attributes, Matchers.equalTo(Attributes.builder().put("k", 1.0).build())); - } - - private static class TestDoubleMeasurement implements ObservableDoubleMeasurement { - double value; - Attributes attributes; - - @Override - public void record(double value) { - this.value = value; - } - - @Override - public void record(double value, Attributes attributes) { - this.value = value; - this.attributes = attributes; - - } - } - - private static class TestLongMeasurement implements ObservableLongMeasurement { - long value; - Attributes attributes; - - @Override - public void record(long value) { - this.value = value; - } + DoubleGauge doubleGauge = registry.registerDoubleGauge("name", "desc", "unit"); + Map attributes = Map.of("k", 1L); + doubleGauge.record(1.0, attributes); - @Override - public void record(long value, Attributes attributes) { - this.value = value; - this.attributes = attributes; + otelMeter.collectMetrics(); - } + List metrics = otelMeter.getRecorder().getMeasurements(doubleGauge); + assertThat(metrics, hasSize(1)); + assertThat(metrics.get(0).attributes(), equalTo(attributes)); + assertThat(metrics.get(0).getDouble(), equalTo(1.0)); } } diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java b/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java index 405e12d277307..ed99d79fd37ab 100644 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java @@ -24,7 +24,7 @@ * Enum with the different types for use as keys. This enum acts a bridge between the Otel and Elasticsearch versions of each * of the instruments. */ -enum InstrumentType { +public enum InstrumentType { DOUBLE_COUNTER(true), LONG_COUNTER(false), DOUBLE_UP_DOWN_COUNTER(true), diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java b/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java index b94ca0919c89a..faf6b0b31c837 100644 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java @@ -23,7 +23,7 @@ * Records invocations of the Instruments as {@link Measurement}s. * @param The supertype of the registered instrument. */ -class MetricRecorder { +public class MetricRecorder { /** * Container for Instrument of a given type, such as DoubleGauge, LongHistogram, etc. @@ -54,7 +54,7 @@ void call(String name, Measurement call) { */ private final Map> metrics; - MetricRecorder() { + public MetricRecorder() { metrics = new HashMap<>(InstrumentType.values().length); for (var instrument : InstrumentType.values()) { metrics.put(instrument, new RegisteredMetric<>(new HashMap<>(), new HashMap<>(), new HashMap<>())); @@ -64,21 +64,21 @@ void call(String name, Measurement call) { /** * Register an instrument. Instruments must be registered before they are used. */ - void register(I instrument, InstrumentType instrumentType, String name, String description, String unit) { + public void register(I instrument, InstrumentType instrumentType, String name, String description, String unit) { metrics.get(instrumentType).register(name, description, unit, instrument); } /** * Record a call made to a registered Elasticsearch {@link Instrument}. */ - void call(Instrument instrument, Number value, Map attributes) { + public void call(Instrument instrument, Number value, Map attributes) { call(InstrumentType.fromInstrument(instrument), instrument.getName(), value, attributes); } /** * Record a call made to the registered instrument represented by the {@link InstrumentType} enum. */ - void call(InstrumentType instrumentType, String name, Number value, Map attributes) { + public void call(InstrumentType instrumentType, String name, Number value, Map attributes) { metrics.get(instrumentType).call(name, new Measurement(value, attributes, instrumentType.isDouble)); } @@ -89,21 +89,21 @@ public List getMeasurements(Instrument instrument) { return getMeasurements(InstrumentType.fromInstrument(instrument), instrument.getName()); } - List getMeasurements(InstrumentType instrumentType, String name) { + public List getMeasurements(InstrumentType instrumentType, String name) { return metrics.get(instrumentType).called.getOrDefault(Objects.requireNonNull(name), Collections.emptyList()); } /** * Get the {@link Registration} for a given elasticsearch {@link Instrument}. */ - Registration getRegistration(Instrument instrument) { + public Registration getRegistration(Instrument instrument) { return metrics.get(InstrumentType.fromInstrument(instrument)).registered().get(instrument.getName()); } /** * Fetch the instrument instance given the type and registered name. */ - I getInstrument(InstrumentType instrumentType, String name) { + public I getInstrument(InstrumentType instrumentType, String name) { return metrics.get(instrumentType).instruments.get(name); } } From c73b3b6403e6e88d161d169149e6ecafe2e2bcac Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 25 Oct 2023 10:31:12 -0400 Subject: [PATCH 103/203] ESQL DOCS: speed note on stats (#101318) Grouping by no fields is much much faster than grouping by one field. Grouping by one field is like 5x faster than grouping on two fields. --- docs/reference/esql/processing-commands/stats.asciidoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/reference/esql/processing-commands/stats.asciidoc b/docs/reference/esql/processing-commands/stats.asciidoc index 71f4470e3dfb0..e0a9bbb52b03e 100644 --- a/docs/reference/esql/processing-commands/stats.asciidoc +++ b/docs/reference/esql/processing-commands/stats.asciidoc @@ -43,3 +43,11 @@ include::{esql-specs}/docs.csv-spec[tag=statsGroupByMultipleValues] The following aggregation functions are supported: include::../functions/aggregation-functions.asciidoc[tag=agg_list] + +NOTE: `STATS` without any groups is much much faster than adding group. + +NOTE: Grouping on a single field is currently much more optimized than grouping + on many fields. In some tests we've seen grouping on a single `keyword` + field to be five times faster than grouping on two `keyword` fields. Don't + try to work around this combining the two fields together with something + like <> and then grouping - that's not going to be faster. From a6ed18c1447fc38def8135325723ed6c98652fa3 Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Wed, 25 Oct 2023 17:17:24 +0200 Subject: [PATCH 104/203] [DOCS] [Enterprise Search] Migrate ingest pipelines/ML docs (#101156) * WIP, port docs - Update link syntax - Update ids - Fix n^n build failures :/ - * Fix id for doclink * Let's try this on for size * Idem * Update attributes, Test image rendering * Update image name * Fix typo * Update filename * Add images, cleanup, standardize naming * Tweak heading * Cleanup, rewordings - Modified introduction in `search-inference-processing.asciidoc`. - Changed "Search connector" to "Elastic connector". - Adjusted heading levels in `search-inference-processing.asciidoc`. - Simplified ingest pipelines intro in `search-ingest-pipelines.asciidoc`. - Edited ingest pipelines section for the *Content* UI. - Reordered file inclusions in `search-ingest-pipelines.asciidoc`. - Formatted inference pipeline creation into steps in `search-nlp-tutorial.asciidoc`. * Lingering erroneousness * Delete FAQ --- ...ment-enrichment-add-inference-pipeline.png | Bin 0 -> 28200 bytes .../ingest/document-enrichment-diagram.png | Bin 0 -> 133927 bytes .../ingest/ingest-pipeline-ent-search-ui.png | Bin 0 -> 102078 bytes docs/reference/ingest.asciidoc | 1 + .../search-inference-processing.asciidoc | 187 ++++++++++++ .../ingest/search-ingest-pipelines.asciidoc | 280 ++++++++++++++++++ .../ingest/search-nlp-tutorial.asciidoc | 259 ++++++++++++++++ docs/reference/redirects.asciidoc | 22 +- 8 files changed, 728 insertions(+), 21 deletions(-) create mode 100644 docs/reference/images/ingest/document-enrichment-add-inference-pipeline.png create mode 100644 docs/reference/images/ingest/document-enrichment-diagram.png create mode 100644 docs/reference/images/ingest/ingest-pipeline-ent-search-ui.png create mode 100644 docs/reference/ingest/search-inference-processing.asciidoc create mode 100644 docs/reference/ingest/search-ingest-pipelines.asciidoc create mode 100644 docs/reference/ingest/search-nlp-tutorial.asciidoc diff --git a/docs/reference/images/ingest/document-enrichment-add-inference-pipeline.png b/docs/reference/images/ingest/document-enrichment-add-inference-pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..ddcf42e24ab83ec8c17b63ef8b0176c4ddf218a4 GIT binary patch literal 28200 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

?G#_(HVg4)?f7F@jVx*EG36wS}>LXExe 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⋘2|g`+3%opeuA+iNK&5g4o!@58j+M)8`#KwN36P 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

@t=|uo#j_VqKR!WRN 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

=JLQ=7QT5g5Hqli-yGYG)sA03 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�Ez9^ySq#xrX$e4qZe4juH#;*(& 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/ingest/ingest-pipeline-ent-search-ui.png b/docs/reference/images/ingest/ingest-pipeline-ent-search-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..e0fc7d1ba8ffc9489cfaf6558ed9995bd0fd0e7c GIT binary patch literal 102078 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 PVL)XDDo- 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 literal 0 HcmV?d00001 diff --git a/docs/reference/ingest.asciidoc b/docs/reference/ingest.asciidoc index 64f327b09f182..e2b4cf6fa10db 100644 --- a/docs/reference/ingest.asciidoc +++ b/docs/reference/ingest.asciidoc @@ -990,3 +990,4 @@ GET _nodes/stats/ingest?filter_path=nodes.*.ingest include::ingest/common-log-format-example.asciidoc[] include::ingest/enrich.asciidoc[] include::ingest/processors.asciidoc[] +include::ingest/search-ingest-pipelines.asciidoc[] diff --git a/docs/reference/ingest/search-inference-processing.asciidoc b/docs/reference/ingest/search-inference-processing.asciidoc new file mode 100644 index 0000000000000..fad11b28858b7 --- /dev/null +++ b/docs/reference/ingest/search-inference-processing.asciidoc @@ -0,0 +1,187 @@ +[[ingest-pipeline-search-inference]] +=== Inference processing + +When you create an index through the **Content** UI, a set of default ingest pipelines are also created, including a ML inference pipeline. +The <> uses inference processors to analyze fields and enrich documents with the output. +Inference processors use ML trained models, so you need to use a built-in model or {ml-docs}/ml-nlp-deploy-models.html[deploy a trained model in your cluster^] to use this feature. + +This guide focuses on the ML inference pipeline, its use, and how to manage it. + +[IMPORTANT] +==== +This feature is not available at all Elastic subscription levels. +Refer to the Elastic subscriptions pages for https://www.elastic.co/subscriptions/cloud[Elastic Cloud^] and https://www.elastic.co/subscriptions[self-managed] deployments. +==== + +[discrete#ingest-pipeline-search-inference-nlp-use-cases] +==== NLP use cases + +{ml-docs}/ml-nlp-overview.html[Natural Language Processing (NLP)^] allows developers to create rich search experiences that go beyond the standards of lexical search. +A few examples of ways to improve search experiences through the use of NLP models: + +[discrete#ingest-pipeline-search-inference-elser] +==== ELSER text expansion + +Using Elastic's {ml-docs}/ml-nlp-elser.html[ELSER machine learning model^] you can easily incorporate text expansion for your queries. +This works by using ELSER to provide semantic enrichments to your documents upon ingestion, combined with the power of <> to provide automated text expansion at query time. + +[discrete#ingest-pipeline-search-inference-ner] +==== Named entity recognition (NER) + +Most commonly used to detect entities such as People, Places, and Organization information from text, {ml-docs}/ml-nlp-extract-info.html#ml-nlp-ner[NER^] can be used to extract key information from text and group results based on that information. +A sports news media site could use NER to automatically extract names of professional athletes, stadiums, and sports teams in their articles and link to season stats or schedules. + +[discrete#ingest-pipeline-search-inference-text-classification] +==== Text classification + +{ml-docs}/ml-nlp-classify-text.html#ml-nlp-text-classification[Text classification^] is commonly used for sentiment analysis and can be used for similar tasks, such as labeling content as containing hate speech in public forums, or triaging and labeling support tickets so they reach the correct level of escalation automatically. + +[discrete#ingest-pipeline-search-inference-text-embedding] +==== Text embedding + +Analyzing a text field using a {ml-docs}/ml-nlp-search-compare.html#ml-nlp-text-embedding[Text embedding^] model will generate a <> representation of the text. +This array of numeric values encodes the semantic _meaning_ of the text. +Using the same model with a user's search query will produce a vector that can then be used to search, ranking results based on vector similarity - semantic similarity - as opposed to traditional word or text similarity. + +A common use case is a user searching FAQs, or a support agent searching a knowledge base, where semantically similar content may be indexed with little similarity in phrasing. + +[discrete#ingest-pipeline-search-inference-nlp-in-enterprise-search] +==== NLP in Content UI + +[discrete#ingest-pipeline-search-inference-overview] +===== Overview of ML inference pipeline + +The diagram below shows how documents are processed during ingestion. + +// Original diagram: https://whimsical.com/ml-in-enterprise-search-ErCetPqrcCPu2QYHvAwrgP@2bsEvpTYSt1Hiuq6UBf68tUWvFiXdzLt6ao +image::../images/ingest/document-enrichment-diagram.png["ML inference pipeline diagram"] + +* Documents are processed by the `my-index-0001` pipeline, which happens automatically when indexing through a an Elastic connector or crawler. +* The `_run_ml_inference` field is set to `true` to ensure the ML inference pipeline (`my-index-0001@ml-inference`) is executed. + This field is removed during the ingestion process. +* The inference processor analyzes the `message` field on the document using the `my-positivity-model-id` trained model. + The inference output is stored in the `ml.inference.positivity_prediction` field. +* The resulting enriched document is then indexed into the `my-index-0001` index. +* The `ml.inference.positivity_prediction` field can now be used at query time to search for documents above or below a certain threshold. + +[discrete#ingest-pipeline-search-inference-find-deploy-manage-trained-models] +==== Find, deploy, and manage trained models + +This feature is intended to make it easier to use your ML trained models. +First, you need to figure out which model works best for your data. +Make sure to use a {ml-docs}/ml-nlp-model-ref.html[compatible third party NLP model^]. +Since these are publicly available, it is not possible to fine-tune models before {ml-docs}/ml-nlp-deploy-models.html[deploying them^]. + +Trained models must be available in the current {kibana-ref}/xpack-spaces.html[Kibana Space^] and running in order to use them. +By default, models should be available in all Kibana Spaces that have the *Analytics* > *Machine Learning* feature enabled. +To manage your trained models, use the Kibana UI and navigate to *Stack Management -> Machine Learning -> Trained Models*. +Spaces can be controlled in the **spaces** column. +To stop or start a model, go to the *Machine Learning* tab in the *Analytics* menu of Kibana and click *Trained Models* in the *Model Management* section. + +[NOTE] +========================= +The `monitor_ml` <> is required to manage ML models and ML inference pipelines which use those models. +========================= + +[discrete#ingest-pipeline-search-inference-add-inference-processors] +===== Add inference processors to your ML inference pipeline + +To create the index-specific ML inference pipeline, go to *Search -> Content -> Indices -> -> Pipelines* in the Kibana UI. + +If you only see the `ent-search-generic-ingestion` pipeline, you will need to click *Copy and customize* to create index-specific pipelines. +This will create the `{index_name}@ml-inference` pipeline. + +Once your index-specific ML inference pipeline is ready, you can add inference processors that use your ML trained models. +To add an inference processor to the ML inference pipeline, click the *Add Inference Pipeline* button in the *Machine Learning Inference Pipelines* card. + +[role="screenshot"] +image::../images/ingest/document-enrichment-add-inference-pipeline.png["Add Inference Pipeline"] + +Here, you'll be able to: + +1. Choose a name for your pipeline. + - This name will need to be unique across the whole deployment. + If you want this pipeline to be index-specific, we recommend including the name of your index in the pipeline name. +2. Select the ML trained model you want to use. +3. Select one or more source fields as input for the inference processor. + - If there are no source fields available, your index will need a <>. +4. (Optional) Choose a name for your target field. +This is where the output of the inference model will be stored. Changing the default name is only possible if you have a single source field selected. +5. Add the source-target field mapping to the configuration by clicking the *Add* button. +6. Repeat steps 3-5 for each field mapping you want to add. +7. (Optional) Test the pipeline with a sample document. +8. (Optional) Review the pipeline definition before creating it with the *Create pipeline* button. + +[discrete#ingest-pipeline-search-inference-manage-inference-processors] +===== Manage and delete inference processors from your ML inference pipeline + +Inference processors added to your index-specific ML inference pipelines are normal Elasticsearch pipelines. +Once created, each processor will have options to *View in Stack Management* and *Delete Pipeline*. +Deleting an inference processor from within the *Content* UI deletes the pipeline and also removes its reference from your index-specific ML inference pipeline. + +These pipelines can also be viewed, edited, and deleted in Kibana via *Stack Management -> Ingest Pipelines*, just like all other Elasticsearch ingest pipelines. +You may also use the <>. +If you delete any of these pipelines outside of the *Content* UI in Kibana, make sure to edit the ML inference pipelines that reference them. + +[discrete#ingest-pipeline-search-inference-update-mapping] +==== Update mappings to use ML inference pipelines + +After setting up an ML inference pipeline or attaching an existing one, it may be necessary to manually create the field mappings in order to support the referenced trained ML model's output. +This needs to happen before the pipeline is first used to index some documents, otherwise the model output fields could be inferred with the wrong type. + +[NOTE] +==== +This doesn't apply when you're creating a pipeline with the ELSER model, for which the index mappings are automatically updated in the process. +==== + +The required field name and type depends on the configuration of the pipeline and the trained model it uses. +For example, if you configure a `text_embedding` model, select `summary` as a source field, and `ml.inference.summary` as the target field, the inference output will be stored in `ml.inference..predicted_value` as a <> type. +In order to support semantic search on this field, it must be added to the mapping: + +[source,console] +---- +PUT my-index-0001/_mapping +{ + "properties": { + "ml.inference.summary.predicted_value": { <1> + "type": "dense_vector", <2> + "dims": 768, <3> + "index": true, + "similarity": "dot_product" + } + } +} +---- +// NOTCONSOLE +// TEST[skip:TODO] + +<1> The output of the ML model is stored in the configured target field suffixed with `predicted_value`. +<2> Choose a field type that is compatible with the inference output and supports your search use cases. +<3> Set additional properties as necessary. + +[TIP] +==== +You can check the shape of the generated output before indexing any documents while creating the ML inference pipeline under the *Test* tab. +Simply provide a sample document, click *Simulate*, and look for the `ml.inference` object in the results. +==== + +[discrete#ingest-pipeline-search-inference-test-inference-pipeline] +==== Test your ML inference pipeline + +To ensure the ML inference pipeline will be run when ingesting documents, you must make sure the documents you are ingesting have a field named `_run_ml_inference` that is set to `true` and you must set the pipeline to `{index_name}`. +For connector and crawler indices, this will happen automatically if you've configured the settings appropriately for the pipeline name `{index_name}`. +To manage these settings: + + 1. Go to *Search > Content > Indices > > Pipelines*. + 2. Click on the *Settings* link in the *Ingest Pipelines* card for the `{index_name}` pipeline. + 3. Ensure *ML inference pipelines* is selected. + If it is not, select it and save the changes. + +[discrete#ingest-pipeline-search-inference-learn-more] +==== Learn More + +* See <> for information on the various pipelines that are created. +* Learn about {ml-docs}/ml-nlp-elser.html[ELSER], Elastic's proprietary retrieval model for semantic search with sparse vectors. +* https://huggingface.co/models?library=pytorch&pipeline_tag=token-classification&sort=downloads[NER HuggingFace Models^] +* https://huggingface.co/models?library=pytorch&pipeline_tag=text-classification&sort=downloads[Text Classification HuggingFace Models^] +* https://huggingface.co/models?library=pytorch&pipeline_tag=sentence-similarity&sort=downloads[Text Embedding HuggingFace Models^] diff --git a/docs/reference/ingest/search-ingest-pipelines.asciidoc b/docs/reference/ingest/search-ingest-pipelines.asciidoc new file mode 100644 index 0000000000000..049a74670581d --- /dev/null +++ b/docs/reference/ingest/search-ingest-pipelines.asciidoc @@ -0,0 +1,280 @@ +[[ingest-pipeline-search]] +== Ingest pipelines in Search + +You can manage ingest pipelines through Elasticsearch APIs or Kibana UIs. + +The *Content* UI under *Search* has a set of tools for creating and managing indices optimized for search use cases (non time series data). +You can also manage your ingest pipelines in this UI. + +[discrete] +[[ingest-pipeline-search-where]] +=== Find pipelines in Content UI + +To work with ingest pipelines using these UI tools, you'll be using the *Pipelines* tab on your search-optimized Elasticsearch index. + +To find this tab in the Kibana UI: + +1. Go to *Search > Content > Elasticsearch indices*. +2. Select the index you want to work with. For example, `search-my-index`. +3. On the index's overview page, open the *Pipelines* tab. +4. From here, you can follow the instructions to create custom pipelines, and set up ML inference pipelines. + +The tab is highlighted in this screenshot: + +[.screenshot] +image::../images/ingest/ingest-pipeline-ent-search-ui.png[align="center"] + +[discrete#ingest-pipeline-search-in-enterprise-search] +=== Overview + +These tools can be particularly helpful by providing a layer of customization and post-processing of documents. +For example: + +* providing consistent extraction of text from binary data types +* ensuring consistent formatting +* providing consistent sanitization steps (removing PII like phone numbers or SSN's) + +It can be a lot of work to set up and manage production-ready pipelines from scratch. +Considerations such as error handling, conditional execution, sequencing, versioning, and modularization must all be taken into account. + +To this end, when you create indices for search use cases, (including {enterprise-search-ref}/crawler.html[Elastic web crawler], {enterprise-search-ref}/connectors.html[Elastic connector], and API indices), each index already has a pipeline set up with several processors that optimize your content for search. + +This pipeline is called `ent-search-generic-ingestion`. +While it is a "managed" pipeline (meaning it should not be tampered with), you can view its details via the Kibana UI or the Elasticsearch API. +You can also <>. + +You can control whether you run some of these processors. +While all features are enabled by default, they are eligible for opt-out. +For {enterprise-search-ref}/crawler.html[Elastic crawler] and {enterprise-search-ref}/connectors.html[Elastic connectors], you can opt out (or back in) per index, and your choices are saved. +For API indices, you can opt out (or back in) by including specific fields in your documents. +<>. + +At the deployment level, you can change the default settings for all new indices. +This will not effect existing indices. + +Each index also provides the capability to easily create index-specific ingest pipelines with customizable processing. +If you need that extra flexibility, you can create a custom pipeline by going to your pipeline settings and choosing to "copy and customize". +This will replace the index's use of `ent-search-generic-ingestion` with 3 newly generated pipelines: + +1. `` +2. `@custom` +3. `@ml-inference` + +Like `ent-search-generic-ingestion`, the first of these is "managed", but the other two can and should be modified to fit your needs. +You can view these pipelines using the platform tools (Kibana UI, Elasticsearch API), and can also +<>. + +[discrete#ingest-pipeline-search-pipeline-settings] +=== Pipeline Settings + +Aside from the pipeline itself, you have a few configuration options which control individual features of the pipelines. + +* **Extract Binary Content** - This controls whether or not binary documents should be processed and any textual content should be extracted. +* **Reduce Whitespace** - This controls whether or not consecutive, leading, and trailing whitespaces should be removed. + This can help to display more content in some search experiences. +* **Run ML Inference** - Only available on index-specific pipelines. + This controls whether or not the optional `@ml-inference` pipeline will be run. + Enabled by default. + +For Elastic web crawler and connectors, you can opt in or out per index. +These settings are stored in Elasticsearch in the `.elastic-connectors` index, in the document that corresponds to the specific index. +These settings can be changed there directly, or through the Kibana UI at *Search > Content > Indices > > Pipelines > Settings*. + +You can also change the deployment wide defaults. +These settings are stored in the Elasticsearch mapping for `.elastic-connectors` in the `_meta` section. +These settings can be changed there directly, or from the Kibana UI at *Search > Content > Settings* tab. +Changing the deployment wide defaults will not impact any existing indices, but will only impact any newly created indices defaults. +Those defaults will still be able to be overriden by the index-specific settings. + +[discrete#ingest-pipeline-search-pipeline-settings-using-the-api] +==== Using the API + +These settings are not persisted for indices that "Use the API". +Instead, changing these settings will, in real time, change the example cURL request displayed. +Notice that the example document in the cURL request contains three underscore-prefixed fields: + +[source,js] +---- +{ + ... + "_extract_binary_content": true, + "_reduce_whitespace": true, + "_run_ml_inference": true +} +---- +// NOTCONSOLE + +Omitting one of these special fields is the same as specifying it with the value `false`. + +[NOTE] +========================= +You must also specify the pipeline in your indexing request. +This is also shown in the example cURL request. +========================= + +[WARNING] +========================= +If the pipeline is not specified, the underscore-prefixed fields will actually be indexed, and will not impact any processing behaviors. +========================= + +[discrete#ingest-pipeline-search-details] +=== Details + +[discrete#ingest-pipeline-search-details-generic-reference] +==== `ent-search-generic-ingestion` Reference + +You can access this pipeline with the <> or via Kibana's < Ingest Pipelines>> UI. + +[WARNING] +========================= +This pipeline is a "managed" pipeline. +That means that it is not intended to be edited. +Editing/updating this pipeline manually could result in unintended behaviors, or difficulty in upgrading in the future. +If you want to make customizations, we recommend you utilize index-specific pipelines (see below), specifically <@custom` pipeline>>. +========================= + +[discrete#ingest-pipeline-search-details-generic-reference-processors] +===== Processors + +1. `attachment` - this uses the <> processor to convert any binary data stored in a document's `_attachment` field to a nested object of plain text and metadata. +2. `set_body` - this uses the <> processor to copy any plain text extracted from the previous step and persist it on the document in the `body` field. +3. `remove_replacement_chars` - this uses the <> processor to remove characters like "�" from the `body` field. +4. `remove_extra_whitespace` - this uses the <> processor to replace consecutive whitespace characters with single spaces in the `body` field. + While not perfect for every use case (see below for how to disable), this can ensure that search experiences display more content and highlighting and less empty space for your search results. +5. `trim` - this uses the <> processor to remove any remaining leading or trailing whitespace from the `body` field. +6. `remove_meta_fields` - this final step of the pipeline uses the <> processor to remove special fields that may have been used elsewhere in the pipeline, whether as temporary storage or as control flow parameters. + +[discrete#ingest-pipeline-search-details-generic-reference-params] +===== Control flow parameters + +The `ent-search-generic-ingestion` pipeline does not always run all processors. +It utilizes a feature of ingest pipelines to <> based on the contents of each individual document. + +* `_extract_binary_content` - if this field is present and has a value of `true` on a source document, the pipeline will attempt to run the `attachment`, `set_body`, and `remove_replacement_chars` processors. + Note that the document will also need an `_attachment` field populated with base64-encoded binary data in order for the `attachment` processor to have any output. + If the `_extract_binary_content` field is missing or `false` on a source document, these processors will be skipped. +* `_reduce_whitespace` - if this field is present and has a value of `true` on a source document, the pipeline will attempt to run the `remove_extra_whitespace` and `trim` processors. + These processors only apply to the `body` field. + If the `_reduce_whitespace` field is missing or `false` on a source document, these processors will be skipped. + +Crawler, Native Connectors, and Connector Clients will automatically add these control flow parameters based on the settings in the index's Pipeline tab. +To control what settings any new indices will have upon creation, see the deployment wide content settings. +See <>. + +[discrete#ingest-pipeline-search-details-specific] +==== Index-specific ingest pipelines + +In the Kibana UI for your index, by clicking on the Pipelines tab, then *Settings > Copy and customize*, you can quickly generate 3 pipelines which are specific to your index. +These 3 pipelines replace `ent-search-generic-ingestion` for the index. +There is nothing lost in this action, as the `` pipeline is a superset of functionality over the `ent-search-generic-ingestion` pipeline. + +[IMPORTANT] +==== +The "copy and customize" button is not available at all Elastic subscription levels. +Refer to the Elastic subscriptions pages for https://www.elastic.co/subscriptions/cloud[Elastic Cloud^] and https://www.elastic.co/subscriptions[self-managed] deployments. +==== + +[discrete#ingest-pipeline-search-details-specific-reference] +===== `` Reference + +This pipeline looks and behaves a lot like the <>, but with <>. + +[WARNING] +========================= +You should not rename this pipeline. +========================= + +[WARNING] +========================= +This pipeline is a "managed" pipeline. +That means that it is not intended to be edited. +Editing/updating this pipeline manually could result in unintended behaviors, or difficulty in upgrading in the future. +If you want to make customizations, we recommend you utilize <@custom` pipeline>>. +========================= + +[discrete#ingest-pipeline-search-details-specific-reference-processors] +====== Processors + +In addition to the processors inherited from the <>, the index-specific pipeline also defines: + +* `index_ml_inference_pipeline` - this uses the <> processor to run the `@ml-inference` pipeline. + This processor will only be run if the source document includes a `_run_ml_inference` field with the value `true`. +* `index_custom_pipeline` - this uses the <> processor to run the `@custom` pipeline. + +[discrete#ingest-pipeline-search-details-specific-reference-params] +====== Control flow parameters + +Like the `ent-search-generic-ingestion` pipeline, the `` pipeline does not always run all processors. +In addition to the `_extract_binary_content` and `_reduce_whitespace` control flow parameters, the `` pipeline also supports: + +* `_run_ml_inference` - if this field is present and has a value of `true` on a source document, the pipeline will attempt to run the `index_ml_inference_pipeline` processor. + If the `_run_ml_inference` field is missing or `false` on a source document, this processor will be skipped. + +Crawler, Native Connectors, and Connector Clients will automatically add these control flow parameters based on the settings in the index's Pipeline tab. +To control what settings any new indices will have upon creation, see the deployment wide content settings. +See <>. + +[discrete#ingest-pipeline-search-details-specific-ml-reference] +===== `@ml-inference` Reference + +This pipeline is empty to start (no processors), but can be added to via the Kibana UI either through the Pipelines tab of your index, or from the *Stack Management > Ingest Pipelines* page. +Unlike the `ent-search-generic-ingestion` pipeline and the `` pipeline, this pipeline is NOT "managed". + +It's possible to add one or more ML inference pipelines to an index in the *Content* UI. +This pipeline will serve as a container for all of the ML inference pipelines configured for the index. +Each ML inference pipeline added to the index is referenced within `@ml-inference` using a `pipeline` processor. + +[WARNING] +========================= +You should not rename this pipeline. +========================= + +[NOTE] +========================= +The `monitor_ml` Elasticsearch cluster permission is required in order to manage ML models and ML inference pipelines which use those models. +========================= + +[discrete#ingest-pipeline-search-details-specific-custom-reference] +===== `@custom` Reference + +This pipeline is empty to start (no processors), but can be added to via the Kibana UI either through the Pipelines +tab of your index, or from the *Stack Management > Ingest Pipelines* page. +Unlike the `ent-search-generic-ingestion` pipeline and the `` pipeline, this pipeline is NOT "managed". + +You are encouraged to make additions and edits to this pipeline, provided its name remains the same. +This provides a convenient hook from which to add custom processing and transformations for your data. +Be sure to read the <> to see what options are available. + +[WARNING] +========================= +You should not rename this pipeline. +========================= + +[discrete#ingest-pipeline-search-upgrading-notes] +=== Upgrading notes + +.Expand to see upgrading notes +[%collapsible%closed] +============= + +* `app_search_crawler` - Since 8.3, {app-search-crawler} has utilized this pipeline to power its binary content +extraction. + You can read more about this pipeline and its usage in the {app-search-ref}/web-crawler-reference.html#web-crawler-reference-binary-content-extraction[App Search Guide]. + When upgrading from 8.3 to 8.5+, be sure to note any changes that you made to the `app_search_crawler` pipeline. + These changes should be re-applied to each index's `@custom` pipeline in order to ensure a consistent data processing experience. + In 8.5+, the <> is required *in addition* to the configurations mentioned in the {app-search-ref}/web-crawler-reference.html#web-crawler-reference-binary-content-extraction[App Search Guide]. + +* `ent_search_crawler` - Since 8.4, the Elastic web crawler has utilized this pipeline to power its binary content extraction. + You can read more about this pipeline and its usage in the {enterprise-search-ref}/crawler-managing.html#crawler-managing-binary-content[Elastic web crawler Guide]. + When upgrading from 8.4 to 8.5+, be sure to note any changes that you made to the `ent_search_crawler` pipeline. +These changes should be re-applied to each index's `@custom` pipeline in order to ensure a consistent data processing experience. + In 8.5+, the <> is required *in addition* to the configurations mentioned in the {enterprise-search-ref}/crawler-managing.html#crawler-managing-binary-content[Elastic web crawler Guide]. + +* `ent-search-generic-ingestion` - Since 8.5, Native Connectors, Connector Clients, and new (>8.4) Elastic web crawler indices will all make use of this pipeline by default. + You can <> above. + As this pipeline is "managed", any modifications that were made to `app_search_crawler` and/or `ent_search_crawler` should NOT be made to `ent-search-generic-ingestion`. + Instead, if such customizations are desired, you should utilize <>, placing all modifications in the `@custom` pipeline(s). +============= + +include::search-inference-processing.asciidoc[] +include::search-nlp-tutorial.asciidoc[] diff --git a/docs/reference/ingest/search-nlp-tutorial.asciidoc b/docs/reference/ingest/search-nlp-tutorial.asciidoc new file mode 100644 index 0000000000000..d5eacb6951023 --- /dev/null +++ b/docs/reference/ingest/search-nlp-tutorial.asciidoc @@ -0,0 +1,259 @@ +[[nlp-example]] +=== Tutorial: Natural language processing (NLP) +++++ +NLP tutorial +++++ + +This guide focuses on a concrete task: getting a machine learning trained model loaded into Elasticsearch and set up to enrich your documents. + +Elasticsearch supports many different ways to use machine learning models. +In this guide, we will use a trained model to enrich documents at ingest time using ingest pipelines configured within Kibana's *Content* UI. + +In this guide, we'll accomplish the above using the following steps: + +- *Set up a Cloud deployment*: We will use Elastic Cloud to host our deployment, as it makes it easy to scale machine learning nodes. +- *Load a model with Eland*: We will use the Eland Elasticsearch client to import our chosen model into Elasticsearch. +Once we've verified that the model is loaded, we will be able to use it in an ingest pipeline. +- *Setup an ML inference pipeline*: We will create an Elasticsearch index with a predefined mapping and add an inference pipeline. +- *Show enriched results*: We will ingest some data into our index and observe that the pipeline enriches our documents. + +Follow the instructions to load a text classification model and set it up to enrich some photo comment data. +Once you're comfortable with the steps involved, use this guide as a blueprint for working with other machine learning trained models. + +*Table of contents*: + +* <> +* <> +* <> +* <> +* <> +* <> +* <> + +[discrete#nlp-example-cloud-deployment] +==== Create an {ecloud} deployment + +Your deployment will need a machine learning instance to upload and deploy trained models. + +If your team already has an Elastic Cloud deployment, make sure it has at least one machine learning instance. +If it does not, *Edit* your deployment to add capacity. +For this tutorial, we'll need at least 2GB of RAM on a single machine learning instance. + +If your team does not have an Elastic Cloud deployment, start by signing up for a https://cloud.elastic.co/registration[free Elastic Cloud trial^]. +After creating an account, you'll have an active subscription and you'll be prompted to create your first deployment. + +Follow the steps to *Create* a new deployment. +Make sure to add capacity to the *Machine Learning instances* under the *Advanced settings* before creating the deployment. +To simplify scaling, turn on the *Autoscale this deployment* feature. +If you use autoscaling, you should increase the minimum RAM for the machine learning instance. +For this tutorial, we'll need at least 2GB of RAM. +For more details, refer to {cloud}/ec-create-deployment.html[Create a deployment^] in the Elastic Cloud documentation. + +Enriching documents using machine learning was introduced in Enterprise Search *8.5.0*, so be sure to use version *8.5.0 or later*. + +[discrete#nlp-example-clone-eland] +==== Clone Eland + +Elastic's https://github.com/elastic/eland[Eland^] tool makes it easy to upload trained models to your deployment via Docker. + +Eland is a specialized Elasticsearch client for exploring and manipulating data, which we can use to upload trained models into Elasticsearch. + +To clone and build Eland using Docker, run the following commands: + +[source,sh] +---- +git clone git@github.com:elastic/eland.git +cd eland +docker build -t elastic/eland . +---- + +[discrete#nlp-example-deploy-model] +==== Deploy the trained model + +Now that you have a deployment and a way to upload models, you will need to choose a trained model that fits your data. +https://huggingface.co/[Hugging Face^] has a large repository of publicly available trained models. +The model you choose will depend on your data and what you would like to do with it. + +For the purposes of this guide, let's say we have a data set of photo comments. +In order to promote a positive atmosphere on our platform, we'd like the first few comments on each photo to be positive comments. +For this task, the https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english?text=I+like+you.+I+love+you[`distilbert-base-uncased-finetuned-sst-2-english`^] model is a good fit. + +To upload this model to your deployment, you need a few pieces of data: + +- The deployment URL. + You can get this via the *Copy endpoint* link next to *Elasticsearch* on the deployment management screen. + It will look like `https://ml-test.es.us-west1.gcp.cloud.es.io:443`. + Make sure to append the port if it isn't present, as Eland requires the URL to have a scheme, host, and port. + 443 is the default port for HTTPS. +- The deployment username and password for your deployment. + This is displayed one time when the deployment is created. + It will look like `elastic` and `xUjaFNTyycG34tQx5Iq9JIIA`. +- The trained model id. + This comes from Hugging Face. + It will look like `distilbert-base-uncased-finetuned-sst-2-english`. +- The trained model task type. + This is the kind of machine learning task the model is designed to achieve. + It will be one of: `fill_mask`, `ner`, `text_classification`, `text_embedding`, and `zero_shot_classification`. + For our use case, we will use `text_classification`. + +We can now upload our chosen model to Elasticsearch by providing these options to Eland. + +[source,sh] +---- +docker run -it --rm --network host \ + elastic/eland \ + eland_import_hub_model \ + --url https://ml-test.es.us-west1.gcp.cloud.es.io:443 \ + -u elastic -p \ + --hub-model-id distilbert-base-uncased-finetuned-sst-2-english \ + --task-type text_classification \ + --start +---- + +This script should take roughly 2-3 minutes to run. +Once your model has been successfully deployed to your Elastic deployment, navigate to Kibana's *Trained Models* page to verify it is ready. +You can find this page under *Machine Learning > Analytics* menu and then *Trained Models > Model Management*. +If you do not see your model in the list, you may need to click *Synchronize your jobs and trained models*. +Your model is now ready to be used. + +[discrete#nlp-example-create-index-and-define-ml-inference-pipeline] +==== Create an index and define an ML inference pipeline + +We are now ready to use Kibana's *Content* UI to enrich our documents with inference data. +Before we ingest photo comments into Elasticsearch, we will first create an ML inference pipeline. +The pipeline will enrich the incoming photo comments with inference data indicating if the comments are positive. + +Let's say our photo comments look like this when they are uploaded as a document into Elasticsearch: + +[source,js] +---- +{ + "photo_id": "78sdv71-8vdkjaj-knew629-vc8459p", + "body": "your dog is so cute!", + ... +} +---- +// NOTCONSOLE + +We want to run our documents through an inference processor that uses the trained model we uploaded to determine if the comments are positive. +To do this, we first need to set up an Elasticsearch index. + +* From the Kibana home page, start by clicking the Search card. +* Click the button to *Create an Elasticsearch index*. +* Choose to *Use the API* and give your index a name. +It will automatically be prefixed with `search-`. +For this demo, we will name the index `search-photo-comments`. +* After clicking *Create Index*, you will be redirected to the overview page for your new index. + +To configure the ML inference pipeline, we need the index to have an existing field mapping so we can choose which field to analyze. +This can be done via the <> in the Kibana Dev Tools or simply through a cURL command: + +[source,js] +---- +PUT search-photo-comments/_mapping +{ + "properties": { + "photo_id": { "type": "keyword" }, + "body": { "type": "text" } + } +} +---- +// NOTCONSOLE + +Now it's time to create an inference pipeline. + +1. From the overview page for your `search-photo-comments` index in "Search", click the *Pipelines* tab. +By default, Elasticsearch does not create any index-specific ingest pipelines. +2. Because we want to customize these pipelines, we need to *Copy and customize* the `ent-search-generic-ingestion` ingest pipeline. +Find this option above the settings for the `ent-search-generic-ingestion` ingest pipeline. +This will create two new index-specific ingest pipelines. + +Next, we'll add an inference pipeline. + +1. Locate the section *Machine Learning Inference Pipelines*, then select *Add inference pipeline*. +2. Give your inference pipeline a name, select the trained model we uploaded, and select the `body` field to be analyzed. +3. Optionally, choose a field name to store the output. +We'll call it `positivity_result`. + +You can also run example documents through a simulator and review the pipeline before creating it. + +[discrete#nlp-example-index-documents] +==== Index documents + +At this point, everything is ready to enrich documents at index time. + +From the Kibana Dev Console, or simply using a cURL command, we can index a document. +We'll use a `_run_ml_inference` flag to tell the `search-photo-comments` pipeline to run the index-specific ML inference pipeline that we created. +This field will not be indexed in the document. + +[source,js] +---- +POST search-photo-comments/_doc/my-new-doc?pipeline=search-photo-comments +{ + "photo_id": "78sdv71-8vdkjaj-knew629-vc8459p", + "body": "your dog is so cute!", + "_run_ml_inference": true +} +---- +// NOTCONSOLE + +Once the document is indexed, use the API to retrieve it and view the enriched data. + +[source,js] +---- +GET search-photo-comments/_doc/my-new-doc +---- +// NOTCONSOLE + +[source,js] +---- +{ + "_index": "search-photo-comments", + "_id": "_MQggoQBKYghsSwHbDvG", + ... + "_source": { + ... + "photo_id": "78sdv71-8vdkjaj-knew629-vc8459p", + "body": "your dog is so cute!", + "ml": { + "inference": { + "positivity_result": { + "predicted_value": "POSITIVE", + "prediction_probability": 0.9998022925461774, + "model_id": "distilbert-base-uncased-finetuned-sst-2-english" + } + } + } + } +} +---- +// NOTCONSOLE + +The document has new fields with the enriched data. +The `ml.inference.positivity_result` field is an object with the analysis from the machine learning model. +The model we used predicted with 99.98% confidence that the analyzed text is positive. + +From here, we can write search queries to boost on `ml.inference.positivity_result.predicted_value`. +This field will also be stored in a top-level `positivity_result` field if the model was confident enough. + +[discrete#nlp-example-summary] +==== Summary + +In this guide, we covered how to: + +- Set up a deployment on Elastic Cloud with a machine learning instance. +- Deploy a machine learning trained model using the Eland Elasticsearch client. +- Configure an inference pipeline to use the trained model with Elasticsearch. +- Enrich documents with inference results from the trained model at ingest time. +- Query your search engine and sort by `positivity_result`. + +[discrete#nlp-example-learn-more] +==== Learn more + +* {ml-docs}/ml-nlp-model-ref.html[Compatible third party models^] +* {ml-docs}/ml-nlp-overview.html[NLP Overview^] +* https://github.com/elastic/eland#docker[Docker section of Eland readme^] +* {ml-docs}/ml-nlp-deploy-models.html[Deploying a model ML guide^] +* {ml-docs}/ml-nlp-import-model.html#ml-nlp-authentication[Eland Authentication methods^] +* <> +// * <> diff --git a/docs/reference/redirects.asciidoc b/docs/reference/redirects.asciidoc index a79ceace09233..f065c2deeae72 100644 --- a/docs/reference/redirects.asciidoc +++ b/docs/reference/redirects.asciidoc @@ -1931,24 +1931,4 @@ Refer to <>. [role="exclude",id="remote-clusters-privileges"] === Configure roles and users for remote clusters -Refer to <>. - -[role="exclude",id="ingest-pipeline-search"] -=== Ingest pipelines for Search indices - -coming::[8.11.0] - -[role="exclude",id="ingest-pipeline-search-inference"] -=== Inference processing for Search indices - -coming::[8.11.0] - -[id="ingest-pipeline-search-inference-update-mapping"] -==== Update mapping - -coming::[8.11.0] - -[role="exclude",id="nlp-example"] -=== Tutorial: Natural language processing (NLP) - -coming::[8.11.0] +Refer to <>. \ No newline at end of file From c34e0c0746cd45fe0842a05d507e3e839887a5e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Wed, 25 Oct 2023 17:18:05 +0200 Subject: [PATCH 105/203] [DOCS] Clarifies that inference input must be single string (#101301) --- docs/reference/ingest/processors/inference.asciidoc | 7 +++++-- .../ml/trained-models/apis/infer-trained-model.asciidoc | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/reference/ingest/processors/inference.asciidoc b/docs/reference/ingest/processors/inference.asciidoc index 5f0fedfd7902c..0995e3f643813 100644 --- a/docs/reference/ingest/processors/inference.asciidoc +++ b/docs/reference/ingest/processors/inference.asciidoc @@ -25,10 +25,13 @@ ingested in the pipeline. include::common-options.asciidoc[] |====== -IMPORTANT: You cannot use the `input_output` field with the `target_field` and +[IMPORTANT] +================================================== +* You cannot use the `input_output` field with the `target_field` and `field_map` fields. For NLP models, use the `input_output` option. For {dfanalytics} models, use the `target_field` and `field_map` option. - +* Each {infer} input field must be single strings, not arrays of strings. +================================================== [discrete] [[inference-input-output-example]] diff --git a/docs/reference/ml/trained-models/apis/infer-trained-model.asciidoc b/docs/reference/ml/trained-models/apis/infer-trained-model.asciidoc index 85de1fb7d4105..d7201fcf42c0a 100644 --- a/docs/reference/ml/trained-models/apis/infer-trained-model.asciidoc +++ b/docs/reference/ml/trained-models/apis/infer-trained-model.asciidoc @@ -57,7 +57,8 @@ Controls the amount of time to wait for {infer} results. Defaults to 10 seconds. (Required, array) An array of objects to pass to the model for inference. The objects should contain the fields matching your configured trained model input. Typically for -NLP models, the field name is `text_field`. +NLP models, the field name is `text_field`. Each {infer} input field specified +in this property must be single strings not arrays of strings. //Begin inference_config `inference_config`:: From dbace39f3eca3eb21e3f12f507e0a8c171366ec9 Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Wed, 25 Oct 2023 10:29:40 -0500 Subject: [PATCH 106/203] NodeShutdownIT.testShardsMoveOffRemovingNode re-enable testShardsMoveOffRemovingNode has been muted for a few years. Re-enabling to see if it's still failing as it doesn't locally reproduce. Refs #77488 --- .../java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java b/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java index b9b324b8e0b63..dca0b33e17a59 100644 --- a/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java +++ b/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java @@ -269,7 +269,6 @@ public void testAllocationPreventedForRemoval() throws Exception { * 2) Ensures the status properly comes to rest at COMPLETE after the shards have moved. */ @SuppressWarnings("unchecked") - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/77488") public void testShardsMoveOffRemovingNode() throws Exception { String nodeIdToShutdown = getRandomNodeId(); From 860ff94191b474a62e9f004fef2f76b081b989df Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 25 Oct 2023 17:47:38 +0100 Subject: [PATCH 107/203] [Transform] Remove last use of Version from TransformConfigVersion (#101325) Uses the same approach as #100131 to make TransformConfigVersion completely independent of the Version class. --- .../transform/TransformConfigVersion.java | 32 ++++++++----------- .../TransformConfigVersionTests.java | 23 ++----------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java index 3d6a1aef8477a..ef65f4bca1c35 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/transform/TransformConfigVersion.java @@ -302,17 +302,6 @@ public static TransformConfigVersion max(TransformConfigVersion version1, Transf return version1.id > version2.id ? version1 : version2; } - // Visible only for testing - static TransformConfigVersion fromVersion(Version version) { - if (version.equals(Version.V_8_10_0)) { - return V_10; - } - if (version.after(Version.V_8_10_0)) { - throw new IllegalArgumentException("Cannot convert " + version + ". Incompatible version"); - } - return fromId(version.id); - } - public static TransformConfigVersion getMinTransformConfigVersion(DiscoveryNodes nodes) { return getMinMaxTransformConfigVersion(nodes).v1(); } @@ -342,10 +331,10 @@ public static Tuple getMinMaxTra public static TransformConfigVersion getTransformConfigVersionForNode(DiscoveryNode node) { String transformConfigVerStr = node.getAttributes().get(TRANSFORM_CONFIG_VERSION_NODE_ATTR); - if (transformConfigVerStr == null) { - return fromVersion(node.getVersion()); + if (transformConfigVerStr != null) { + return fromString(transformConfigVerStr); } - return fromString(transformConfigVerStr); + return fromId(node.getPre811VersionId().orElseThrow(() -> new IllegalStateException("getting legacy version id not possible"))); } // Parse an TransformConfigVersion from a string. @@ -358,12 +347,17 @@ public static TransformConfigVersion fromString(String str) { if (str.equals("8.10.0")) { return V_10; } - Matcher matcher = Pattern.compile("^(\\d+)\\.0\\.0$").matcher(str); - int versionNum; - if (matcher.matches() == false || (versionNum = Integer.parseInt(matcher.group(1))) < 10) { - return fromVersion(Version.fromString(str)); + Matcher matcher = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(?:-\\w+)?$").matcher(str); + if (matcher.matches() == false) { + throw new IllegalArgumentException("Transform config version [" + str + "] not valid"); + } + int first = Integer.parseInt(matcher.group(1)); + int second = Integer.parseInt(matcher.group(2)); + int third = Integer.parseInt(matcher.group(3)); + if (first >= 10 && (second > 0 || third > 0)) { + throw new IllegalArgumentException("Transform config version [" + str + "] not valid"); } - return fromId(1000000 * versionNum + 99); + return fromId(1000000 * first + 10000 * second + 100 * third + 99); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java index 8b83b9dfd3bff..b42056372b1ab 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/transform/TransformConfigVersionTests.java @@ -172,7 +172,7 @@ public void testGetTransformConfigVersionForNode() { .version(VersionInformation.inferVersions(Version.fromString("8.7.0"))) .build(); TransformConfigVersion TransformConfigVersion1 = TransformConfigVersion.getTransformConfigVersionForNode(node1); - assertEquals(TransformConfigVersion.fromVersion(Version.V_8_5_0), TransformConfigVersion1); + assertEquals(TransformConfigVersion.V_8_5_0, TransformConfigVersion1); } public void testDefinedConstants() throws IllegalAccessException { @@ -246,19 +246,6 @@ public void testMax() { ); } - public void testFromVersion() { - Version version_V_7_7_0 = Version.V_7_0_0; - TransformConfigVersion TransformConfigVersion_V_7_7_0 = TransformConfigVersion.fromVersion(version_V_7_7_0); - assertEquals(version_V_7_7_0.id, TransformConfigVersion_V_7_7_0.id()); - - // Version 8.10.0 is treated as if it is TransformConfigVersion V_10. - assertEquals(TransformConfigVersion.V_10.id(), TransformConfigVersion.fromVersion(Version.V_8_10_0).id()); - - // There's no mapping between Version and TransformConfigVersion values after Version.V_8_10_0. - Exception e = expectThrows(IllegalArgumentException.class, () -> TransformConfigVersion.fromVersion(Version.fromId(8_11_00_99))); - assertEquals("Cannot convert " + Version.fromId(8_11_00_99) + ". Incompatible version", e.getMessage()); - } - public void testVersionConstantPresent() { Set ignore = Set.of( TransformConfigVersion.ZERO, @@ -316,13 +303,9 @@ public void testFromString() { assertEquals(false, KnownTransformConfigVersions.ALL_VERSIONS.contains(unknownVersion)); assertEquals(TransformConfigVersion.CURRENT.id() + 1, unknownVersion.id()); - for (String version : new String[] { "10.2", "7.17.2.99" }) { + for (String version : new String[] { "10.2", "7.17.2.99", "9" }) { Exception e = expectThrows(IllegalArgumentException.class, () -> TransformConfigVersion.fromString(version)); - assertEquals("the version needs to contain major, minor, and revision, and optionally the build: " + version, e.getMessage()); + assertEquals("Transform config version [" + version + "] not valid", e.getMessage()); } - - String version = "9"; - Exception e = expectThrows(IllegalArgumentException.class, () -> TransformConfigVersion.fromString(version)); - assertEquals("the version needs to contain major, minor, and revision, and optionally the build: " + version, e.getMessage()); } } From a20a4f5f8905a88f32ab6aa52eff98ce384b90ed Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 25 Oct 2023 10:15:55 -0700 Subject: [PATCH 108/203] Fix exchange layout of intermediate aggs (#101296) Today, we assume that an intermediate state of a non-grouped aggregation has two blocks. An avg(float) or avg(double) requires three blocks: sum, delta, and seen. I believe we should introduce a Skip operator that skips querying the source, thus bypassing the execution pipeline, instead of manually manipulating the layout. This pull request, however, is a quick fix for version 8.11. --- .../xpack/esql/action/EsqlActionIT.java | 33 ++++++++++++++++++ .../esql/optimizer/LogicalPlanOptimizer.java | 34 +++++++++++++------ 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java index adf6e1841bab2..6ebd1b24c13bd 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java @@ -1257,6 +1257,39 @@ public void testStatsNestFields() { } } + public void testStatsMissingFields() { + String node1 = internalCluster().startDataOnlyNode(); + String node2 = internalCluster().startDataOnlyNode(); + assertAcked( + client().admin() + .indices() + .prepareCreate("foo-index") + .setSettings(Settings.builder().put("index.routing.allocation.require._name", node1)) + .setMapping("foo_int", "type=integer", "foo_long", "type=long", "foo_float", "type=float", "foo_double", "type=double") + ); + assertAcked( + client().admin() + .indices() + .prepareCreate("bar-index") + .setSettings(Settings.builder().put("index.routing.allocation.require._name", node2)) + .setMapping("bar_int", "type=integer", "bar_long", "type=long", "bar_float", "type=float", "bar_double", "type=double") + ); + + var fields = List.of("foo_int", "foo_long", "foo_float", "foo_double"); + var functions = List.of("sum", "count", "avg", "count_distinct"); + for (String field : fields) { + for (String function : functions) { + String stat = String.format(Locale.ROOT, "stats s = %s(%s)", function, field); + String command = String.format(Locale.ROOT, "from foo-index,bar-index | where %s is not null | %s", field, stat); + try (var resp = run(command)) { + var valuesList = getValuesList(resp); + assertEquals(1, resp.columns().size()); + assertEquals(1, valuesList.size()); + } + } + } + } + private void createNestedMappingIndex(String indexName) throws IOException { XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java index 7557a36d4fe30..e7409543ca68e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java @@ -25,6 +25,7 @@ import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; import org.elasticsearch.xpack.esql.plan.logical.local.LocalRelation; import org.elasticsearch.xpack.esql.plan.logical.local.LocalSupplier; +import org.elasticsearch.xpack.esql.planner.AbstractPhysicalOperationProviders; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Alias; @@ -62,6 +63,7 @@ import org.elasticsearch.xpack.ql.rule.ParameterizedRuleExecutor; import org.elasticsearch.xpack.ql.rule.Rule; import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.util.CollectionUtils; import org.elasticsearch.xpack.ql.util.Holder; @@ -598,7 +600,8 @@ protected LogicalPlan rule(UnaryPlan plan) { if (plan.child() instanceof LocalRelation local && local.supplier() == LocalSupplier.EMPTY) { // only care about non-grouped aggs might return something (count) if (plan instanceof Aggregate agg && agg.groupings().isEmpty()) { - p = skipPlan(plan, aggsFromEmpty(agg.aggregates())); + List emptyBlocks = aggsFromEmpty(agg.aggregates()); + p = skipPlan(plan, LocalSupplier.of(emptyBlocks.toArray(Block[]::new))); } else { p = skipPlan(plan); } @@ -606,25 +609,34 @@ protected LogicalPlan rule(UnaryPlan plan) { return p; } - private static LocalSupplier aggsFromEmpty(List aggs) { - Block[] blocks = new Block[aggs.size()]; + private static List aggsFromEmpty(List aggs) { + // TODO: Should we introduce skip operator that just never queries the source + List blocks = new ArrayList<>(); var blockFactory = BlockFactory.getNonBreakingInstance(); int i = 0; for (var agg : aggs) { // there needs to be an alias if (agg instanceof Alias a && a.child() instanceof AggregateFunction aggFunc) { - // look for count(literal) with literal != null - Object value = aggFunc instanceof Count count && (count.foldable() == false || count.fold() != null) ? 0L : null; - var wrapper = BlockUtils.wrapperFor(blockFactory, LocalExecutionPlanner.toElementType(aggFunc.dataType()), 1); - wrapper.accept(value); - blocks[i++] = wrapper.builder().build(); - BlockUtils.constantBlock(blockFactory, value, 1); + List output = AbstractPhysicalOperationProviders.intermediateAttributes(List.of(agg), List.of()); + for (Attribute o : output) { + DataType dataType = o.dataType(); + // fill the boolean block later in LocalExecutionPlanner + if (dataType != DataTypes.BOOLEAN) { + // look for count(literal) with literal != null + var wrapper = BlockUtils.wrapperFor(blockFactory, LocalExecutionPlanner.toElementType(dataType), 1); + if (aggFunc instanceof Count count && (count.foldable() == false || count.fold() != null)) { + wrapper.accept(0L); + } else { + wrapper.accept(null); + } + blocks.add(wrapper.builder().build()); + } + } } else { throw new EsqlIllegalArgumentException("Did not expect a non-aliased aggregation {}", agg); } } - - return LocalSupplier.of(blocks); + return blocks; } } From 9796de4503596a2af5b8abcc061f58a326aaa557 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 25 Oct 2023 13:56:59 -0400 Subject: [PATCH 109/203] ESQL: Generate evaluator factories (#100516) This generates the evaluator factories which we'd been implementing inline with `->`. The advantage of generating them is that they have a useful `toString` and class name. These are useful for debugging and let us remove a kind of errant use `ThrowingDriverContext` just to generate a factory description for logging. Now we can just use the generated `toString`. --- .../org/elasticsearch/compute/ann/Fixed.java | 14 ++ .../gen/ConvertEvaluatorImplementer.java | 51 ++++++ .../compute/gen/EvaluatorImplementer.java | 152 +++++++++++++++++- .../compute/gen/MvEvaluatorImplementer.java | 56 +++++++ .../org/elasticsearch/compute/gen/Types.java | 1 + .../compute/operator/EvalOperator.java | 17 +- .../compute/operator/MultivalueDedupe.java | 90 ++++++----- .../compute/operator/EvalOperatorTests.java | 12 +- .../comparison/EqualsBoolsEvaluator.java | 22 +++ .../comparison/EqualsDoublesEvaluator.java | 22 +++ .../comparison/EqualsIntsEvaluator.java | 22 +++ .../comparison/EqualsKeywordsEvaluator.java | 22 +++ .../comparison/EqualsLongsEvaluator.java | 22 +++ .../GreaterThanDoublesEvaluator.java | 22 +++ .../comparison/GreaterThanIntsEvaluator.java | 22 +++ .../GreaterThanKeywordsEvaluator.java | 22 +++ .../comparison/GreaterThanLongsEvaluator.java | 22 +++ .../GreaterThanOrEqualDoublesEvaluator.java | 22 +++ .../GreaterThanOrEqualIntsEvaluator.java | 22 +++ .../GreaterThanOrEqualKeywordsEvaluator.java | 22 +++ .../GreaterThanOrEqualLongsEvaluator.java | 22 +++ .../comparison/LessThanDoublesEvaluator.java | 22 +++ .../comparison/LessThanIntsEvaluator.java | 22 +++ .../comparison/LessThanKeywordsEvaluator.java | 22 +++ .../comparison/LessThanLongsEvaluator.java | 22 +++ .../LessThanOrEqualDoublesEvaluator.java | 22 +++ .../LessThanOrEqualIntsEvaluator.java | 22 +++ .../LessThanOrEqualKeywordsEvaluator.java | 22 +++ .../LessThanOrEqualLongsEvaluator.java | 22 +++ .../comparison/NotEqualsBoolsEvaluator.java | 22 +++ .../comparison/NotEqualsDoublesEvaluator.java | 22 +++ .../comparison/NotEqualsIntsEvaluator.java | 22 +++ .../NotEqualsKeywordsEvaluator.java | 22 +++ .../comparison/NotEqualsLongsEvaluator.java | 22 +++ .../operator/logical/NotEvaluator.java | 18 +++ .../operator/regex/RegexMatchEvaluator.java | 21 +++ .../conditional/GreatestBooleanEvaluator.java | 19 +++ .../GreatestBytesRefEvaluator.java | 19 +++ .../conditional/GreatestDoubleEvaluator.java | 19 +++ .../conditional/GreatestIntEvaluator.java | 19 +++ .../conditional/GreatestLongEvaluator.java | 19 +++ .../conditional/LeastBooleanEvaluator.java | 19 +++ .../conditional/LeastBytesRefEvaluator.java | 19 +++ .../conditional/LeastDoubleEvaluator.java | 19 +++ .../scalar/conditional/LeastIntEvaluator.java | 19 +++ .../conditional/LeastLongEvaluator.java | 19 +++ .../convert/ToBooleanFromDoubleEvaluator.java | 21 +++ .../convert/ToBooleanFromIntEvaluator.java | 21 +++ .../convert/ToBooleanFromLongEvaluator.java | 21 +++ .../convert/ToBooleanFromStringEvaluator.java | 21 +++ .../ToBooleanFromUnsignedLongEvaluator.java | 21 +++ .../ToDatetimeFromStringEvaluator.java | 21 +++ .../scalar/convert/ToDegreesEvaluator.java | 21 +++ .../convert/ToDoubleFromBooleanEvaluator.java | 21 +++ .../convert/ToDoubleFromIntEvaluator.java | 21 +++ .../convert/ToDoubleFromLongEvaluator.java | 21 +++ .../convert/ToDoubleFromStringEvaluator.java | 21 +++ .../ToDoubleFromUnsignedLongEvaluator.java | 21 +++ .../convert/ToIPFromStringEvaluator.java | 21 +++ .../ToIntegerFromBooleanEvaluator.java | 21 +++ .../convert/ToIntegerFromDoubleEvaluator.java | 21 +++ .../convert/ToIntegerFromLongEvaluator.java | 21 +++ .../convert/ToIntegerFromStringEvaluator.java | 21 +++ .../ToIntegerFromUnsignedLongEvaluator.java | 21 +++ .../convert/ToLongFromBooleanEvaluator.java | 21 +++ .../convert/ToLongFromDoubleEvaluator.java | 21 +++ .../convert/ToLongFromIntEvaluator.java | 21 +++ .../convert/ToLongFromStringEvaluator.java | 21 +++ .../ToLongFromUnsignedLongEvaluator.java | 21 +++ .../scalar/convert/ToRadiansEvaluator.java | 21 +++ .../convert/ToStringFromBooleanEvaluator.java | 21 +++ .../ToStringFromDatetimeEvaluator.java | 21 +++ .../convert/ToStringFromDoubleEvaluator.java | 21 +++ .../convert/ToStringFromIPEvaluator.java | 21 +++ .../convert/ToStringFromIntEvaluator.java | 21 +++ .../convert/ToStringFromLongEvaluator.java | 21 +++ .../ToStringFromUnsignedLongEvaluator.java | 21 +++ .../convert/ToStringFromVersionEvaluator.java | 21 +++ .../ToUnsignedLongFromBooleanEvaluator.java | 21 +++ .../ToUnsignedLongFromDoubleEvaluator.java | 21 +++ .../ToUnsignedLongFromIntEvaluator.java | 21 +++ .../ToUnsignedLongFromLongEvaluator.java | 21 +++ .../ToUnsignedLongFromStringEvaluator.java | 21 +++ .../convert/ToVersionFromStringEvaluator.java | 21 +++ .../date/DateExtractConstantEvaluator.java | 25 +++ .../scalar/date/DateExtractEvaluator.java | 28 ++++ .../date/DateFormatConstantEvaluator.java | 21 +++ .../scalar/date/DateFormatEvaluator.java | 25 +++ .../date/DateParseConstantEvaluator.java | 25 +++ .../scalar/date/DateParseEvaluator.java | 28 ++++ .../scalar/date/DateTruncEvaluator.java | 21 +++ .../function/scalar/date/NowEvaluator.java | 18 +++ .../scalar/ip/CIDRMatchEvaluator.java | 23 +++ .../scalar/math/AbsDoubleEvaluator.java | 18 +++ .../function/scalar/math/AbsIntEvaluator.java | 18 +++ .../scalar/math/AbsLongEvaluator.java | 18 +++ .../function/scalar/math/AcosEvaluator.java | 21 +++ .../function/scalar/math/AsinEvaluator.java | 21 +++ .../function/scalar/math/Atan2Evaluator.java | 22 +++ .../function/scalar/math/AtanEvaluator.java | 18 +++ .../scalar/math/CastIntToDoubleEvaluator.java | 18 +++ .../scalar/math/CastIntToLongEvaluator.java | 18 +++ .../math/CastIntToUnsignedLongEvaluator.java | 18 +++ .../math/CastLongToDoubleEvaluator.java | 18 +++ .../math/CastLongToUnsignedLongEvaluator.java | 18 +++ .../CastUnsignedLongToDoubleEvaluator.java | 18 +++ .../scalar/math/CeilDoubleEvaluator.java | 18 +++ .../function/scalar/math/CosEvaluator.java | 18 +++ .../function/scalar/math/CoshEvaluator.java | 21 +++ .../scalar/math/FloorDoubleEvaluator.java | 18 +++ .../scalar/math/IsFiniteEvaluator.java | 18 +++ .../scalar/math/IsInfiniteEvaluator.java | 18 +++ .../function/scalar/math/IsNaNEvaluator.java | 18 +++ .../scalar/math/Log10DoubleEvaluator.java | 21 +++ .../scalar/math/Log10IntEvaluator.java | 21 +++ .../scalar/math/Log10LongEvaluator.java | 21 +++ .../math/Log10UnsignedLongEvaluator.java | 21 +++ .../scalar/math/PowDoubleEvaluator.java | 25 +++ .../function/scalar/math/PowIntEvaluator.java | 25 +++ .../scalar/math/PowLongEvaluator.java | 25 +++ .../scalar/math/RoundDoubleEvaluator.java | 22 +++ .../math/RoundDoubleNoDecimalsEvaluator.java | 18 +++ .../scalar/math/RoundIntEvaluator.java | 22 +++ .../scalar/math/RoundLongEvaluator.java | 22 +++ .../math/RoundUnsignedLongEvaluator.java | 22 +++ .../function/scalar/math/SinEvaluator.java | 18 +++ .../function/scalar/math/SinhEvaluator.java | 21 +++ .../scalar/math/SqrtDoubleEvaluator.java | 21 +++ .../scalar/math/SqrtIntEvaluator.java | 21 +++ .../scalar/math/SqrtLongEvaluator.java | 21 +++ .../math/SqrtUnsignedLongEvaluator.java | 18 +++ .../function/scalar/math/TanEvaluator.java | 18 +++ .../function/scalar/math/TanhEvaluator.java | 18 +++ .../multivalue/MvAvgDoubleEvaluator.java | 18 +++ .../scalar/multivalue/MvAvgIntEvaluator.java | 18 +++ .../scalar/multivalue/MvAvgLongEvaluator.java | 18 +++ .../MvAvgUnsignedLongEvaluator.java | 18 +++ .../multivalue/MvMaxBooleanEvaluator.java | 18 +++ .../multivalue/MvMaxBytesRefEvaluator.java | 18 +++ .../multivalue/MvMaxDoubleEvaluator.java | 18 +++ .../scalar/multivalue/MvMaxIntEvaluator.java | 18 +++ .../scalar/multivalue/MvMaxLongEvaluator.java | 18 +++ .../multivalue/MvMedianDoubleEvaluator.java | 18 +++ .../multivalue/MvMedianIntEvaluator.java | 18 +++ .../multivalue/MvMedianLongEvaluator.java | 18 +++ .../MvMedianUnsignedLongEvaluator.java | 18 +++ .../multivalue/MvMinBooleanEvaluator.java | 18 +++ .../multivalue/MvMinBytesRefEvaluator.java | 18 +++ .../multivalue/MvMinDoubleEvaluator.java | 18 +++ .../scalar/multivalue/MvMinIntEvaluator.java | 18 +++ .../scalar/multivalue/MvMinLongEvaluator.java | 18 +++ .../multivalue/MvSumDoubleEvaluator.java | 18 +++ .../scalar/multivalue/MvSumIntEvaluator.java | 21 +++ .../scalar/multivalue/MvSumLongEvaluator.java | 21 +++ .../MvSumUnsignedLongEvaluator.java | 21 +++ .../scalar/string/ConcatEvaluator.java | 24 +++ .../scalar/string/EndsWithEvaluator.java | 22 +++ .../scalar/string/LTrimEvaluator.java | 18 +++ .../function/scalar/string/LeftEvaluator.java | 31 ++++ .../scalar/string/LengthEvaluator.java | 18 +++ .../scalar/string/RTrimEvaluator.java | 18 +++ .../string/ReplaceConstantEvaluator.java | 28 ++++ .../scalar/string/ReplaceEvaluator.java | 29 ++++ .../scalar/string/RightEvaluator.java | 31 ++++ .../string/SplitSingleByteEvaluator.java | 26 +++ .../scalar/string/SplitVariableEvaluator.java | 26 +++ .../scalar/string/StartsWithEvaluator.java | 22 +++ .../scalar/string/SubstringEvaluator.java | 26 +++ .../string/SubstringNoLengthEvaluator.java | 22 +++ .../function/scalar/string/TrimEvaluator.java | 18 +++ .../arithmetic/AddDatetimesEvaluator.java | 25 +++ .../arithmetic/AddDoublesEvaluator.java | 22 +++ .../operator/arithmetic/AddIntsEvaluator.java | 25 +++ .../arithmetic/AddLongsEvaluator.java | 25 +++ .../arithmetic/AddUnsignedLongsEvaluator.java | 25 +++ .../arithmetic/DivDoublesEvaluator.java | 22 +++ .../operator/arithmetic/DivIntsEvaluator.java | 25 +++ .../arithmetic/DivLongsEvaluator.java | 25 +++ .../arithmetic/DivUnsignedLongsEvaluator.java | 25 +++ .../arithmetic/ModDoublesEvaluator.java | 22 +++ .../operator/arithmetic/ModIntsEvaluator.java | 25 +++ .../arithmetic/ModLongsEvaluator.java | 25 +++ .../arithmetic/ModUnsignedLongsEvaluator.java | 25 +++ .../arithmetic/MulDoublesEvaluator.java | 22 +++ .../operator/arithmetic/MulIntsEvaluator.java | 25 +++ .../arithmetic/MulLongsEvaluator.java | 25 +++ .../arithmetic/MulUnsignedLongsEvaluator.java | 25 +++ .../arithmetic/NegDoublesEvaluator.java | 18 +++ .../operator/arithmetic/NegIntsEvaluator.java | 21 +++ .../arithmetic/NegLongsEvaluator.java | 21 +++ .../arithmetic/SubDatetimesEvaluator.java | 25 +++ .../arithmetic/SubDoublesEvaluator.java | 22 +++ .../operator/arithmetic/SubIntsEvaluator.java | 25 +++ .../arithmetic/SubLongsEvaluator.java | 25 +++ .../arithmetic/SubUnsignedLongsEvaluator.java | 25 +++ .../xpack/esql/evaluator/EvalMapper.java | 64 ++++++-- .../operator/comparison/ComparisonMapper.java | 113 ++++++------- .../function/scalar/conditional/Case.java | 39 +++-- .../function/scalar/conditional/Greatest.java | 51 ++---- .../function/scalar/conditional/Least.java | 51 ++---- .../convert/AbstractConvertFunction.java | 18 ++- .../function/scalar/convert/ToBoolean.java | 31 ++-- .../function/scalar/convert/ToDatetime.java | 31 ++-- .../function/scalar/convert/ToDegrees.java | 38 ++--- .../function/scalar/convert/ToDouble.java | 34 ++-- .../function/scalar/convert/ToIP.java | 19 +-- .../function/scalar/convert/ToInteger.java | 34 ++-- .../function/scalar/convert/ToLong.java | 34 ++-- .../function/scalar/convert/ToRadians.java | 38 ++--- .../function/scalar/convert/ToString.java | 43 ++--- .../scalar/convert/ToUnsignedLong.java | 34 ++-- .../function/scalar/convert/ToVersion.java | 19 +-- .../function/scalar/date/DateExtract.java | 10 +- .../function/scalar/date/DateParse.java | 6 +- .../function/scalar/date/DateTrunc.java | 2 +- .../expression/function/scalar/math/Abs.java | 6 +- .../math/AbstractTrigonometricFunction.java | 6 +- .../expression/function/scalar/math/Acos.java | 5 +- .../expression/function/scalar/math/Asin.java | 5 +- .../expression/function/scalar/math/Atan.java | 5 +- .../function/scalar/math/Atan2.java | 2 +- .../expression/function/scalar/math/Cast.java | 14 +- .../expression/function/scalar/math/Ceil.java | 2 +- .../expression/function/scalar/math/Cos.java | 5 +- .../expression/function/scalar/math/Cosh.java | 5 +- .../function/scalar/math/Floor.java | 3 +- .../function/scalar/math/IsFinite.java | 2 +- .../function/scalar/math/IsInfinite.java | 3 +- .../function/scalar/math/IsNaN.java | 3 +- .../function/scalar/math/Log10.java | 8 +- .../expression/function/scalar/math/Pow.java | 21 ++- .../function/scalar/math/Round.java | 22 +-- .../expression/function/scalar/math/Sin.java | 5 +- .../expression/function/scalar/math/Sinh.java | 5 +- .../expression/function/scalar/math/Sqrt.java | 8 +- .../expression/function/scalar/math/Tan.java | 5 +- .../expression/function/scalar/math/Tanh.java | 5 +- .../function/scalar/multivalue/MvAvg.java | 10 +- .../function/scalar/multivalue/MvConcat.java | 27 +++- .../function/scalar/multivalue/MvCount.java | 14 +- .../function/scalar/multivalue/MvMax.java | 12 +- .../function/scalar/multivalue/MvMedian.java | 8 +- .../function/scalar/multivalue/MvMin.java | 12 +- .../function/scalar/multivalue/MvSum.java | 8 +- .../function/scalar/nulls/Coalesce.java | 21 ++- .../function/scalar/string/Concat.java | 11 +- .../function/scalar/string/EndsWith.java | 4 +- .../function/scalar/string/LTrim.java | 3 +- .../function/scalar/string/Left.java | 17 +- .../function/scalar/string/Length.java | 3 +- .../function/scalar/string/RTrim.java | 3 +- .../function/scalar/string/Replace.java | 4 +- .../function/scalar/string/Right.java | 17 +- .../function/scalar/string/Split.java | 14 +- .../function/scalar/string/StartsWith.java | 4 +- .../function/scalar/string/Substring.java | 10 +- .../function/scalar/string/Trim.java | 2 +- .../predicate/operator/arithmetic/Add.java | 10 +- .../DateTimeArithmeticOperation.java | 15 +- .../predicate/operator/arithmetic/Div.java | 8 +- .../arithmetic/EsqlArithmeticOperation.java | 14 +- .../predicate/operator/arithmetic/Mod.java | 8 +- .../predicate/operator/arithmetic/Mul.java | 8 +- .../predicate/operator/arithmetic/Neg.java | 12 +- .../predicate/operator/arithmetic/Sub.java | 10 +- .../function/AbstractFunctionTestCase.java | 11 +- 266 files changed, 4797 insertions(+), 716 deletions(-) diff --git a/x-pack/plugin/esql/compute/ann/src/main/java/org/elasticsearch/compute/ann/Fixed.java b/x-pack/plugin/esql/compute/ann/src/main/java/org/elasticsearch/compute/ann/Fixed.java index 286c36ab2314d..62703fa400ff7 100644 --- a/x-pack/plugin/esql/compute/ann/src/main/java/org/elasticsearch/compute/ann/Fixed.java +++ b/x-pack/plugin/esql/compute/ann/src/main/java/org/elasticsearch/compute/ann/Fixed.java @@ -11,6 +11,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.function.Function; /** * Used on parameters on methods annotated with {@link Evaluator} to indicate @@ -20,5 +21,18 @@ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.SOURCE) public @interface Fixed { + /** + * Should this attribute be in the Evaluator's {@code toString}? + */ boolean includeInToString() default true; + + /** + * Should the Evaluator's factory build this per evaluator with a + * {@code Function} or just take fixed implementation? + * This is typically set to {@code true} to use the {@link Function} + * to make "scratch" objects which have to be isolated in a single thread. + * This is typically set to {@code false} when the parameter is simply + * immutable and can be shared. + */ + boolean build() default false; } diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java index e04158971334a..86ed41c57cb59 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/ConvertEvaluatorImplementer.java @@ -13,6 +13,10 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; @@ -25,6 +29,7 @@ import static org.elasticsearch.compute.gen.Types.BYTES_REF; import static org.elasticsearch.compute.gen.Types.DRIVER_CONTEXT; import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR; +import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR_FACTORY; import static org.elasticsearch.compute.gen.Types.SOURCE; import static org.elasticsearch.compute.gen.Types.VECTOR; import static org.elasticsearch.compute.gen.Types.blockType; @@ -78,6 +83,7 @@ private TypeSpec type() { builder.addMethod(evalValue(true)); builder.addMethod(evalBlock()); builder.addMethod(evalValue(false)); + builder.addType(factory()); return builder.build(); } @@ -259,4 +265,49 @@ private MethodSpec evalValue(boolean forVector) { return builder.build(); } + + private TypeSpec factory() { + TypeSpec.Builder builder = TypeSpec.classBuilder("Factory"); + builder.addSuperinterface(EXPRESSION_EVALUATOR_FACTORY); + builder.addModifiers(Modifier.PUBLIC, Modifier.STATIC); + + builder.addField(SOURCE, "source", Modifier.PRIVATE, Modifier.FINAL); + builder.addField(EXPRESSION_EVALUATOR_FACTORY, "field", Modifier.PRIVATE, Modifier.FINAL); + + builder.addMethod(factoryCtor()); + builder.addMethod(factoryGet()); + builder.addMethod(factoryToString()); + return builder.build(); + } + + private MethodSpec factoryCtor() { + MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC); + builder.addParameter(EXPRESSION_EVALUATOR_FACTORY, "field"); + builder.addParameter(SOURCE, "source"); + builder.addStatement("this.field = field"); + builder.addStatement("this.source = source"); + return builder.build(); + } + + private MethodSpec factoryGet() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("get").addAnnotation(Override.class); + builder.addModifiers(Modifier.PUBLIC); + builder.addParameter(DRIVER_CONTEXT, "context"); + builder.returns(implementation); + + List args = new ArrayList<>(); + args.add("field.get(context)"); + args.add("source"); + args.add("context"); + builder.addStatement("return new $T($L)", implementation, args.stream().collect(Collectors.joining(", "))); + return builder.build(); + } + + private MethodSpec factoryToString() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("toString").addAnnotation(Override.class); + builder.addModifiers(Modifier.PUBLIC); + builder.returns(String.class); + builder.addStatement("return $S + field + $S", declarationType.getSimpleName() + extraName + "Evaluator[field=", "]"); + return builder.build(); + } } diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java index 8474993b99583..dac45aa11143b 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java @@ -11,6 +11,7 @@ import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; @@ -19,6 +20,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; import javax.lang.model.element.ExecutableElement; @@ -36,6 +38,7 @@ import static org.elasticsearch.compute.gen.Types.BYTES_REF; import static org.elasticsearch.compute.gen.Types.DRIVER_CONTEXT; import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR; +import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR_FACTORY; import static org.elasticsearch.compute.gen.Types.PAGE; import static org.elasticsearch.compute.gen.Types.RELEASABLE; import static org.elasticsearch.compute.gen.Types.RELEASABLES; @@ -82,6 +85,8 @@ private TypeSpec type() { builder.addModifiers(Modifier.PUBLIC, Modifier.FINAL); builder.addSuperinterface(EXPRESSION_EVALUATOR); + builder.addType(factory()); + if (processFunction.warnExceptions.isEmpty() == false) { builder.addField(WARNINGS, "warnings", Modifier.PRIVATE, Modifier.FINAL); } @@ -106,6 +111,7 @@ private MethodSpec ctor() { builder.addStatement("this.warnings = new Warnings(source)"); } processFunction.args.stream().forEach(a -> a.implementCtor(builder)); + builder.addParameter(DRIVER_CONTEXT, "driverContext"); builder.addStatement("this.driverContext = driverContext"); return builder.build(); @@ -247,6 +253,55 @@ private MethodSpec close() { return builder.build(); } + private TypeSpec factory() { + TypeSpec.Builder builder = TypeSpec.classBuilder("Factory"); + builder.addSuperinterface(EXPRESSION_EVALUATOR_FACTORY); + builder.addModifiers(Modifier.STATIC); + + if (processFunction.warnExceptions.isEmpty() == false) { + builder.addField(SOURCE, "source", Modifier.PRIVATE, Modifier.FINAL); + } + processFunction.args.stream().forEach(a -> a.declareFactoryField(builder)); + + builder.addMethod(factoryCtor()); + builder.addMethod(factoryGet()); + builder.addMethod(toStringMethod()); + + return builder.build(); + } + + private MethodSpec factoryCtor() { + MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC); + if (processFunction.warnExceptions.isEmpty() == false) { + builder.addParameter(SOURCE, "source"); + builder.addStatement("this.source = source"); + } + processFunction.args.stream().forEach(a -> a.implementFactoryCtor(builder)); + + return builder.build(); + } + + private MethodSpec factoryGet() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("get").addAnnotation(Override.class); + builder.addModifiers(Modifier.PUBLIC); + builder.addParameter(DRIVER_CONTEXT, "context"); + builder.returns(implementation); + + List args = new ArrayList<>(); + if (processFunction.warnExceptions.isEmpty() == false) { + args.add("source"); + } + for (ProcessFunctionArg arg : processFunction.args) { + String invocation = arg.factoryInvocation(builder); + if (invocation != null) { + args.add(invocation); + } + } + args.add("context"); + builder.addStatement("return new $T($L)", implementation, args.stream().collect(Collectors.joining(", "))); + return builder.build(); + } + private interface ProcessFunctionArg { /** * Type containing the actual data for a page of values for this field. Usually a @@ -260,16 +315,34 @@ private interface ProcessFunctionArg { String paramName(boolean blockStyle); /** - * Declare any required fields on the type for this parameter. + * Declare any required fields for the evaluator to implement this type of parameter. */ void declareField(TypeSpec.Builder builder); + /** + * Declare any required fields for the evaluator factory to implement this type of parameter. + */ + void declareFactoryField(TypeSpec.Builder builder); + /** * Implement the ctor for this parameter. Will declare parameters * and assign values to declared fields. */ void implementCtor(MethodSpec.Builder builder); + /** + * Implement the ctor for the evaluator factory for this parameter. + * Will declare parameters and assign values to declared fields. + */ + void implementFactoryCtor(MethodSpec.Builder builder); + + /** + * Invocation called in the ExpressionEvaluator.Factory#get method to + * convert from whatever the factory holds to what the evaluator needs, + * or {@code null} this parameter isn't passed to the evaluator's ctor. + */ + String factoryInvocation(MethodSpec.Builder factoryMethodBuilder); + /** * Emits code to evaluate this parameter to a Block.Ref or array of Block.Refs * and begins a {@code try} block for those refs. Noop if the parameter is {@link Fixed}. @@ -340,12 +413,28 @@ public void declareField(TypeSpec.Builder builder) { builder.addField(EXPRESSION_EVALUATOR, name, Modifier.PRIVATE, Modifier.FINAL); } + @Override + public void declareFactoryField(TypeSpec.Builder builder) { + builder.addField(EXPRESSION_EVALUATOR_FACTORY, name, Modifier.PRIVATE, Modifier.FINAL); + } + @Override public void implementCtor(MethodSpec.Builder builder) { builder.addParameter(EXPRESSION_EVALUATOR, name); builder.addStatement("this.$L = $L", name, name); } + @Override + public void implementFactoryCtor(MethodSpec.Builder builder) { + builder.addParameter(EXPRESSION_EVALUATOR_FACTORY, name); + builder.addStatement("this.$L = $L", name, name); + } + + @Override + public String factoryInvocation(MethodSpec.Builder factoryMethodBuilder) { + return name + ".get(context)"; + } + @Override public void evalToBlock(MethodSpec.Builder builder) { TypeName blockType = blockType(type); @@ -443,12 +532,35 @@ public void declareField(TypeSpec.Builder builder) { builder.addField(ArrayTypeName.of(EXPRESSION_EVALUATOR), name, Modifier.PRIVATE, Modifier.FINAL); } + @Override + public void declareFactoryField(TypeSpec.Builder builder) { + builder.addField(ArrayTypeName.of(EXPRESSION_EVALUATOR_FACTORY), name, Modifier.PRIVATE, Modifier.FINAL); + } + @Override public void implementCtor(MethodSpec.Builder builder) { builder.addParameter(ArrayTypeName.of(EXPRESSION_EVALUATOR), name); builder.addStatement("this.$L = $L", name, name); } + @Override + public void implementFactoryCtor(MethodSpec.Builder builder) { + builder.addParameter(ArrayTypeName.of(EXPRESSION_EVALUATOR_FACTORY), name); + builder.addStatement("this.$L = $L", name, name); + } + + @Override + public String factoryInvocation(MethodSpec.Builder factoryMethodBuilder) { + factoryMethodBuilder.addStatement( + "$T[] $L = Arrays.stream(this.$L).map(a -> a.get(context)).toArray($T[]::new)", + EXPRESSION_EVALUATOR, + name, + name, + EXPRESSION_EVALUATOR + ); + return name; + } + @Override public void evalToBlock(MethodSpec.Builder builder) { TypeName blockType = blockType(componentType); @@ -541,7 +653,7 @@ public String closeInvocation() { } } - private record FixedProcessFunctionArg(TypeName type, String name, boolean includeInToString, boolean releasable) + private record FixedProcessFunctionArg(TypeName type, String name, boolean includeInToString, boolean build, boolean releasable) implements ProcessFunctionArg { @Override @@ -560,12 +672,32 @@ public void declareField(TypeSpec.Builder builder) { builder.addField(type, name, Modifier.PRIVATE, Modifier.FINAL); } + @Override + public void declareFactoryField(TypeSpec.Builder builder) { + builder.addField(factoryFieldType(), name, Modifier.PRIVATE, Modifier.FINAL); + } + @Override public void implementCtor(MethodSpec.Builder builder) { builder.addParameter(type, name); builder.addStatement("this.$L = $L", name, name); } + @Override + public void implementFactoryCtor(MethodSpec.Builder builder) { + builder.addParameter(factoryFieldType(), name); + builder.addStatement("this.$L = $L", name, name); + } + + private TypeName factoryFieldType() { + return build ? ParameterizedTypeName.get(ClassName.get(Function.class), DRIVER_CONTEXT, type.box()) : type; + } + + @Override + public String factoryInvocation(MethodSpec.Builder factoryMethodBuilder) { + return build ? name + ".apply(context)" : name; + } + @Override public void evalToBlock(MethodSpec.Builder builder) { // nothing to do @@ -634,11 +766,26 @@ public void declareField(TypeSpec.Builder builder) { // Nothing to declare } + @Override + public void declareFactoryField(TypeSpec.Builder builder) { + // Nothing to declare + } + @Override public void implementCtor(MethodSpec.Builder builder) { // Nothing to do } + @Override + public void implementFactoryCtor(MethodSpec.Builder builder) { + // Nothing to do + } + + @Override + public String factoryInvocation(MethodSpec.Builder factoryMethodBuilder) { + return null; // Not used in the factory + } + @Override public void evalToBlock(MethodSpec.Builder builder) { // nothing to do @@ -711,6 +858,7 @@ private ProcessFunction( type, name, fixed.includeInToString(), + fixed.build(), Types.extendsSuper(types, v.asType(), "org.elasticsearch.core.Releasable") ) ); diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java index c86f3d82c0015..3a97b44634bd3 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/MvEvaluatorImplementer.java @@ -35,6 +35,7 @@ import static org.elasticsearch.compute.gen.Types.BYTES_REF; import static org.elasticsearch.compute.gen.Types.DRIVER_CONTEXT; import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR; +import static org.elasticsearch.compute.gen.Types.EXPRESSION_EVALUATOR_FACTORY; import static org.elasticsearch.compute.gen.Types.SOURCE; import static org.elasticsearch.compute.gen.Types.WARNINGS; import static org.elasticsearch.compute.gen.Types.blockType; @@ -145,6 +146,8 @@ private TypeSpec type() { builder.addMethod(evalAscending("evalAscendingNullable", true)); builder.addMethod(evalAscending("evalAscendingNotNullable", false)); } + + builder.addType(factory()); return builder.build(); } @@ -349,6 +352,59 @@ private void writeResult(MethodSpec.Builder builder) { } } + private TypeSpec factory() { + TypeSpec.Builder builder = TypeSpec.classBuilder("Factory"); + builder.addSuperinterface(EXPRESSION_EVALUATOR_FACTORY); + builder.addModifiers(Modifier.PUBLIC, Modifier.STATIC); + + if (warnExceptions.isEmpty() == false) { + builder.addField(SOURCE, "source", Modifier.PRIVATE, Modifier.FINAL); + } + builder.addField(EXPRESSION_EVALUATOR_FACTORY, "field", Modifier.PRIVATE, Modifier.FINAL); + + builder.addMethod(factoryCtor()); + builder.addMethod(factoryGet()); + builder.addMethod(factoryToString()); + return builder.build(); + } + + private MethodSpec factoryCtor() { + MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC); + if (warnExceptions.isEmpty() == false) { + builder.addParameter(SOURCE, "source"); + } + builder.addParameter(EXPRESSION_EVALUATOR_FACTORY, "field"); + if (warnExceptions.isEmpty() == false) { + builder.addStatement("this.source = source"); + } + builder.addStatement("this.field = field"); + return builder.build(); + } + + private MethodSpec factoryGet() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("get").addAnnotation(Override.class); + builder.addModifiers(Modifier.PUBLIC); + builder.addParameter(DRIVER_CONTEXT, "context"); + builder.returns(implementation); + + List args = new ArrayList<>(); + if (warnExceptions.isEmpty() == false) { + args.add("source"); + } + args.add("field.get(context)"); + args.add("context"); + builder.addStatement("return new $T($L)", implementation, args.stream().collect(Collectors.joining(", "))); + return builder.build(); + } + + private MethodSpec factoryToString() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("toString").addAnnotation(Override.class); + builder.addModifiers(Modifier.PUBLIC); + builder.returns(String.class); + builder.addStatement("return $S + field + $S", declarationType.getSimpleName() + "[field=", "]"); + return builder.build(); + } + /** * Function "finishing" the computation on a multivalued field. It converts {@link #workType} into {@link #resultType}. */ diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java index 46fb6af22e79b..c1802b671f2a6 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Types.java @@ -96,6 +96,7 @@ public class Types { static final ClassName DRIVER_CONTEXT = ClassName.get(OPERATOR_PACKAGE, "DriverContext"); static final ClassName EXPRESSION_EVALUATOR = ClassName.get(OPERATOR_PACKAGE, "EvalOperator", "ExpressionEvaluator"); + static final ClassName EXPRESSION_EVALUATOR_FACTORY = ClassName.get(OPERATOR_PACKAGE, "EvalOperator", "ExpressionEvaluator", "Factory"); static final ClassName ABSTRACT_MULTIVALUE_FUNCTION_EVALUATOR = ClassName.get( "org.elasticsearch.xpack.esql.expression.function.scalar.multivalue", "AbstractMultivalueFunction", diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java index ffeb7aac1f98c..65efdc4266b28 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/EvalOperator.java @@ -29,8 +29,7 @@ public Operator get(DriverContext driverContext) { @Override public String describe() { - // TODO ThrowingDriverContext blows up when combined with Concat - return "EvalOperator[evaluator=" + evaluator.get(new ThrowingDriverContext()) + "]"; + return "EvalOperator[evaluator=" + evaluator + "]"; } } @@ -65,7 +64,7 @@ public void close() { public interface ExpressionEvaluator extends Releasable { /** A Factory for creating ExpressionEvaluators. */ interface Factory { - ExpressionEvaluator get(DriverContext driverContext); + ExpressionEvaluator get(DriverContext context); } /** @@ -74,6 +73,18 @@ interface Factory { Block.Ref eval(Page page); } + public static final ExpressionEvaluator.Factory CONSTANT_NULL_FACTORY = new ExpressionEvaluator.Factory() { + @Override + public ExpressionEvaluator get(DriverContext driverContext) { + return CONSTANT_NULL; + } + + @Override + public String toString() { + return CONSTANT_NULL.toString(); + } + }; + public static final ExpressionEvaluator CONSTANT_NULL = new ExpressionEvaluator() { @Override public Block.Ref eval(Page page) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java index 517743357a440..ea4f9dc1e05a6 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/MultivalueDedupe.java @@ -18,6 +18,8 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import java.util.function.BiFunction; + /** * Utilities to remove duplicates from multivalued fields. */ @@ -77,44 +79,29 @@ public static Block.Ref dedupeToBlockUsingCopyAndSort(Block.Ref ref, BlockFactor * Build and {@link EvalOperator.ExpressionEvaluator} that deduplicates values * using an adaptive algorithm based on the size of the input list. */ - public static ExpressionEvaluator.Factory evaluator(ElementType elementType, ExpressionEvaluator.Factory nextSupplier) { + public static ExpressionEvaluator.Factory evaluator(ElementType elementType, ExpressionEvaluator.Factory field) { return switch (elementType) { - case BOOLEAN -> dvrCtx -> new MvDedupeEvaluator(nextSupplier.get(dvrCtx)) { - @Override - public Block.Ref eval(Page page) { - return new MultivalueDedupeBoolean(field.eval(page)).dedupeToBlock(dvrCtx.blockFactory()); - } - }; - case BYTES_REF -> dvrCtx -> new MvDedupeEvaluator(nextSupplier.get(dvrCtx)) { - @Override - public Block.Ref eval(Page page) { - return new MultivalueDedupeBytesRef(field.eval(page)).dedupeToBlockAdaptive(dvrCtx.blockFactory()); - } - }; - case INT -> dvrCtx -> new MvDedupeEvaluator(nextSupplier.get(dvrCtx)) { - @Override - public Block.Ref eval(Page page) { - return new MultivalueDedupeInt(field.eval(page)).dedupeToBlockAdaptive(dvrCtx.blockFactory()); - } - }; - case LONG -> dvrCtx -> new MvDedupeEvaluator(nextSupplier.get(dvrCtx)) { - @Override - public Block.Ref eval(Page page) { - return new MultivalueDedupeLong(field.eval(page)).dedupeToBlockAdaptive(dvrCtx.blockFactory()); - } - }; - case DOUBLE -> dvrCtx -> new MvDedupeEvaluator(nextSupplier.get(dvrCtx)) { - @Override - public Block.Ref eval(Page page) { - return new MultivalueDedupeDouble(field.eval(page)).dedupeToBlockAdaptive(dvrCtx.blockFactory()); - } - }; - case NULL -> dvrCtx -> new MvDedupeEvaluator(nextSupplier.get(dvrCtx)) { - @Override - public Block.Ref eval(Page page) { - return field.eval(page); // The page is all nulls and when you dedupe that it's still all nulls - } - }; + case BOOLEAN -> new EvaluatorFactory( + field, + (blockFactory, ref) -> new MultivalueDedupeBoolean(ref).dedupeToBlock(blockFactory) + ); + case BYTES_REF -> new EvaluatorFactory( + field, + (blockFactory, ref) -> new MultivalueDedupeBytesRef(ref).dedupeToBlockAdaptive(blockFactory) + ); + case INT -> new EvaluatorFactory( + field, + (blockFactory, ref) -> new MultivalueDedupeInt(ref).dedupeToBlockAdaptive(blockFactory) + ); + case LONG -> new EvaluatorFactory( + field, + (blockFactory, ref) -> new MultivalueDedupeLong(ref).dedupeToBlockAdaptive(blockFactory) + ); + case DOUBLE -> new EvaluatorFactory( + field, + (blockFactory, ref) -> new MultivalueDedupeDouble(ref).dedupeToBlockAdaptive(blockFactory) + ); + case NULL -> field; // The page is all nulls and when you dedupe that it's still all nulls default -> throw new IllegalArgumentException("unsupported type [" + elementType + "]"); }; } @@ -156,11 +143,34 @@ public static BatchEncoder batchEncoder(Block.Ref ref, int batchSize, boolean al } } - private abstract static class MvDedupeEvaluator implements EvalOperator.ExpressionEvaluator { - protected final EvalOperator.ExpressionEvaluator field; + private record EvaluatorFactory(ExpressionEvaluator.Factory field, BiFunction dedupe) + implements + ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new Evaluator(context.blockFactory(), field.get(context), dedupe); + } - private MvDedupeEvaluator(EvalOperator.ExpressionEvaluator field) { + @Override + public String toString() { + return "MvDedupe[field=" + field + "]"; + } + } + + private static class Evaluator implements ExpressionEvaluator { + private final BlockFactory blockFactory; + private final ExpressionEvaluator field; + private final BiFunction dedupe; + + protected Evaluator(BlockFactory blockFactory, ExpressionEvaluator field, BiFunction dedupe) { + this.blockFactory = blockFactory; this.field = field; + this.dedupe = dedupe; + } + + @Override + public Block.Ref eval(Page page) { + return dedupe.apply(blockFactory, field.eval(page)); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java index b017535676b87..95a5647717851 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java @@ -66,7 +66,17 @@ public void close() {} @Override protected Operator.OperatorFactory simple(BigArrays bigArrays) { - return new EvalOperator.EvalOperatorFactory(dvrCtx -> new Addition(dvrCtx, 0, 1)); + return new EvalOperator.EvalOperatorFactory(new EvalOperator.ExpressionEvaluator.Factory() { + @Override + public EvalOperator.ExpressionEvaluator get(DriverContext context) { + return new Addition(context, 0, 1); + } + + @Override + public String toString() { + return "Addition[lhs=0, rhs=1]"; + } + }); } @Override diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java index 57ef850c8b224..623e9baa79d94 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public EqualsBoolsEvaluator get(DriverContext context) { + return new EqualsBoolsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "EqualsBoolsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java index 323068466c185..2531ac7451507 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public EqualsDoublesEvaluator get(DriverContext context) { + return new EqualsDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "EqualsDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java index 8d08b1a1cc8a9..77f6f7f20d0da 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public EqualsIntsEvaluator get(DriverContext context) { + return new EqualsIntsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "EqualsIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java index 8b898ebde641b..786041453b967 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java @@ -99,4 +99,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public EqualsKeywordsEvaluator get(DriverContext context) { + return new EqualsKeywordsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "EqualsKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java index 5a48f6e7b4efd..5ecd0fe796afa 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public EqualsLongsEvaluator get(DriverContext context) { + return new EqualsLongsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "EqualsLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java index da69eb3363912..29c883fcf8d14 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanDoublesEvaluator get(DriverContext context) { + return new GreaterThanDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java index e1d45cbd10b81..cf6936af15646 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanIntsEvaluator get(DriverContext context) { + return new GreaterThanIntsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java index 855b7aa8ef594..ed08299b14fce 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java @@ -99,4 +99,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanKeywordsEvaluator get(DriverContext context) { + return new GreaterThanKeywordsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java index 32c8f661a0088..966460cf09dd2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanLongsEvaluator get(DriverContext context) { + return new GreaterThanLongsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java index c46d148395d11..55596509977a4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanOrEqualDoublesEvaluator get(DriverContext context) { + return new GreaterThanOrEqualDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanOrEqualDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java index 684fd59912ee4..e10bb4c959d2e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanOrEqualIntsEvaluator get(DriverContext context) { + return new GreaterThanOrEqualIntsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanOrEqualIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java index 79a4d1aa14870..2258853b3c9ca 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java @@ -99,4 +99,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanOrEqualKeywordsEvaluator get(DriverContext context) { + return new GreaterThanOrEqualKeywordsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanOrEqualKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java index 8adb0e553e800..0018f225a6d42 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public GreaterThanOrEqualLongsEvaluator get(DriverContext context) { + return new GreaterThanOrEqualLongsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "GreaterThanOrEqualLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java index 7d2ef9d99aa07..f56c037143925 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanDoublesEvaluator get(DriverContext context) { + return new LessThanDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java index cd6b6e25d0adf..765c283d3e0e4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanIntsEvaluator get(DriverContext context) { + return new LessThanIntsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java index cbd94fdff111a..ceaedec1afe49 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java @@ -99,4 +99,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanKeywordsEvaluator get(DriverContext context) { + return new LessThanKeywordsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java index 15c79206d8a45..ee468abc02729 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanLongsEvaluator get(DriverContext context) { + return new LessThanLongsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java index 2a0b34b7e9739..924d66a789e4c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanOrEqualDoublesEvaluator get(DriverContext context) { + return new LessThanOrEqualDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanOrEqualDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java index f139a38429aa9..7fbc1c5c866fc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanOrEqualIntsEvaluator get(DriverContext context) { + return new LessThanOrEqualIntsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanOrEqualIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java index 5610ce7055688..8b06dd0695caf 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java @@ -99,4 +99,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanOrEqualKeywordsEvaluator get(DriverContext context) { + return new LessThanOrEqualKeywordsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanOrEqualKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java index f2caf7477ab73..2d24260f3e734 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public LessThanOrEqualLongsEvaluator get(DriverContext context) { + return new LessThanOrEqualLongsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "LessThanOrEqualLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java index 57e91a6cf6962..8319b0d18e96e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public NotEqualsBoolsEvaluator get(DriverContext context) { + return new NotEqualsBoolsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "NotEqualsBoolsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java index 1304deb2b2e3a..dac4f67f25641 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public NotEqualsDoublesEvaluator get(DriverContext context) { + return new NotEqualsDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "NotEqualsDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java index 80d34e7312753..4f7c404e9fcea 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public NotEqualsIntsEvaluator get(DriverContext context) { + return new NotEqualsIntsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "NotEqualsIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java index 8b5481e86a7d0..161151cd67d03 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java @@ -99,4 +99,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public NotEqualsKeywordsEvaluator get(DriverContext context) { + return new NotEqualsKeywordsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "NotEqualsKeywordsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java index 77073f3e16140..f70fa2b7bf1b2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public NotEqualsLongsEvaluator get(DriverContext context) { + return new NotEqualsLongsEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "NotEqualsLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java index 74ca67b12c792..0f0474ff4f810 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public NotEvaluator get(DriverContext context) { + return new NotEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "NotEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java index 652eb00996ba5..5850a6ebfdcc4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java @@ -84,4 +84,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(input); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory input; + + private final CharacterRunAutomaton pattern; + + public Factory(EvalOperator.ExpressionEvaluator.Factory input, CharacterRunAutomaton pattern) { + this.input = input; + this.pattern = pattern; + } + + @Override + public RegexMatchEvaluator get(DriverContext context) { + return new RegexMatchEvaluator(input.get(context), pattern, context); + } + + @Override + public String toString() { + return "RegexMatchEvaluator[" + "input=" + input + ", pattern=" + pattern + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java index ded2ae6b176f7..97a776f2a98be 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public GreatestBooleanEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new GreatestBooleanEvaluator(values, context); + } + + @Override + public String toString() { + return "GreatestBooleanEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java index 74b5dbef66b6d..926eef7d351df 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java @@ -108,4 +108,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public GreatestBytesRefEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new GreatestBytesRefEvaluator(values, context); + } + + @Override + public String toString() { + return "GreatestBytesRefEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java index 454b72246fa5a..8453f7352473c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public GreatestDoubleEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new GreatestDoubleEvaluator(values, context); + } + + @Override + public String toString() { + return "GreatestDoubleEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java index 605a6592f9e8d..af29574388fcc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public GreatestIntEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new GreatestIntEvaluator(values, context); + } + + @Override + public String toString() { + return "GreatestIntEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java index e03d4cf1101d9..116a6dbdaa987 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public GreatestLongEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new GreatestLongEvaluator(values, context); + } + + @Override + public String toString() { + return "GreatestLongEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java index 08649061d107e..c839ac1bb8169 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public LeastBooleanEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new LeastBooleanEvaluator(values, context); + } + + @Override + public String toString() { + return "LeastBooleanEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java index 862dc06a76d3f..b2a5d9a629f1e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java @@ -108,4 +108,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public LeastBytesRefEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new LeastBytesRefEvaluator(values, context); + } + + @Override + public String toString() { + return "LeastBytesRefEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java index e82da59ab2d3e..39fdda5c489c9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public LeastDoubleEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new LeastDoubleEvaluator(values, context); + } + + @Override + public String toString() { + return "LeastDoubleEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java index 14db9f8d36866..4add29c1e545d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java @@ -98,4 +98,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public LeastIntEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new LeastIntEvaluator(values, context); + } + + @Override + public String toString() { + return "LeastIntEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java index 062ea464d4182..8eced2f7a246b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java @@ -99,4 +99,23 @@ public String toString() { public void close() { Releasables.closeExpectNoException(() -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(EvalOperator.ExpressionEvaluator.Factory[] values) { + this.values = values; + } + + @Override + public LeastLongEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new LeastLongEvaluator(values, context); + } + + @Override + public String toString() { + return "LeastLongEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java index 4649b9788d141..962ebc5aa5c3f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromDoubleEvaluator.java @@ -97,4 +97,25 @@ private static boolean evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToBoolean.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToBooleanFromDoubleEvaluator get(DriverContext context) { + return new ToBooleanFromDoubleEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToBooleanFromDoubleEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java index 0ae15fb252dcf..620e4117b7c17 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromIntEvaluator.java @@ -97,4 +97,25 @@ private static boolean evalValue(IntBlock container, int index) { int value = container.getInt(index); return ToBoolean.fromInt(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToBooleanFromIntEvaluator get(DriverContext context) { + return new ToBooleanFromIntEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToBooleanFromIntEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java index 7afa5006c47c7..b267399fe1f29 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromLongEvaluator.java @@ -97,4 +97,25 @@ private static boolean evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToBoolean.fromLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToBooleanFromLongEvaluator get(DriverContext context) { + return new ToBooleanFromLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToBooleanFromLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java index 389429da469f2..abd51282e4f3e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromStringEvaluator.java @@ -100,4 +100,25 @@ private static boolean evalValue(BytesRefBlock container, int index, BytesRef sc BytesRef value = container.getBytesRef(index, scratchPad); return ToBoolean.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToBooleanFromStringEvaluator get(DriverContext context) { + return new ToBooleanFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToBooleanFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java index a1a35051afd6f..130ce5e5a7517 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBooleanFromUnsignedLongEvaluator.java @@ -97,4 +97,25 @@ private static boolean evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToBoolean.fromUnsignedLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToBooleanFromUnsignedLongEvaluator get(DriverContext context) { + return new ToBooleanFromUnsignedLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToBooleanFromUnsignedLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java index 1971eca9a9013..8cc49cb6de969 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeFromStringEvaluator.java @@ -100,4 +100,25 @@ private static long evalValue(BytesRefBlock container, int index, BytesRef scrat BytesRef value = container.getBytesRef(index, scratchPad); return ToDatetime.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDatetimeFromStringEvaluator get(DriverContext context) { + return new ToDatetimeFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDatetimeFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java index ddf175b4f8cf7..770c179e90363 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesEvaluator.java @@ -96,4 +96,25 @@ private static double evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToDegrees.process(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDegreesEvaluator get(DriverContext context) { + return new ToDegreesEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDegreesEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java index d0e4ee9eabfd6..b2b75e0dc74e3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromBooleanEvaluator.java @@ -97,4 +97,25 @@ private static double evalValue(BooleanBlock container, int index) { boolean value = container.getBoolean(index); return ToDouble.fromBoolean(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDoubleFromBooleanEvaluator get(DriverContext context) { + return new ToDoubleFromBooleanEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDoubleFromBooleanEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java index 99f86c44fb7c7..35894afb2ed70 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromIntEvaluator.java @@ -97,4 +97,25 @@ private static double evalValue(IntBlock container, int index) { int value = container.getInt(index); return ToDouble.fromInt(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDoubleFromIntEvaluator get(DriverContext context) { + return new ToDoubleFromIntEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDoubleFromIntEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java index 9cd28577964a0..2e8fb850a3d89 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromLongEvaluator.java @@ -97,4 +97,25 @@ private static double evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToDouble.fromLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDoubleFromLongEvaluator get(DriverContext context) { + return new ToDoubleFromLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDoubleFromLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java index ca3860edbb5dc..1109318c9246d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromStringEvaluator.java @@ -100,4 +100,25 @@ private static double evalValue(BytesRefBlock container, int index, BytesRef scr BytesRef value = container.getBytesRef(index, scratchPad); return ToDouble.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDoubleFromStringEvaluator get(DriverContext context) { + return new ToDoubleFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDoubleFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java index 34aabdbd345d1..759568428441a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleFromUnsignedLongEvaluator.java @@ -97,4 +97,25 @@ private static double evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToDouble.fromUnsignedLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToDoubleFromUnsignedLongEvaluator get(DriverContext context) { + return new ToDoubleFromUnsignedLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToDoubleFromUnsignedLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java index eeeff15cce7c0..bbbe89cd31879 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIPFromStringEvaluator.java @@ -99,4 +99,25 @@ private static BytesRef evalValue(BytesRefBlock container, int index, BytesRef s BytesRef value = container.getBytesRef(index, scratchPad); return ToIP.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToIPFromStringEvaluator get(DriverContext context) { + return new ToIPFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToIPFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java index 85ae5de0d5a4c..e3fbc2750e35f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromBooleanEvaluator.java @@ -97,4 +97,25 @@ private static int evalValue(BooleanBlock container, int index) { boolean value = container.getBoolean(index); return ToInteger.fromBoolean(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToIntegerFromBooleanEvaluator get(DriverContext context) { + return new ToIntegerFromBooleanEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToIntegerFromBooleanEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java index 6b4d5b1f2c18c..e39ef77725d89 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromDoubleEvaluator.java @@ -97,4 +97,25 @@ private static int evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToInteger.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToIntegerFromDoubleEvaluator get(DriverContext context) { + return new ToIntegerFromDoubleEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToIntegerFromDoubleEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java index 31d7daf08e31e..db8e818cb2031 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromLongEvaluator.java @@ -97,4 +97,25 @@ private static int evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToInteger.fromLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToIntegerFromLongEvaluator get(DriverContext context) { + return new ToIntegerFromLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToIntegerFromLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java index 7000d691df9fe..5dda74375ec0d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromStringEvaluator.java @@ -100,4 +100,25 @@ private static int evalValue(BytesRefBlock container, int index, BytesRef scratc BytesRef value = container.getBytesRef(index, scratchPad); return ToInteger.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToIntegerFromStringEvaluator get(DriverContext context) { + return new ToIntegerFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToIntegerFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java index bcbdc37c2d491..64be1ad6a44dd 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerFromUnsignedLongEvaluator.java @@ -97,4 +97,25 @@ private static int evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToInteger.fromUnsignedLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToIntegerFromUnsignedLongEvaluator get(DriverContext context) { + return new ToIntegerFromUnsignedLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToIntegerFromUnsignedLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java index a3a3ae3ba9988..5337dbdb32d38 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromBooleanEvaluator.java @@ -97,4 +97,25 @@ private static long evalValue(BooleanBlock container, int index) { boolean value = container.getBoolean(index); return ToLong.fromBoolean(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToLongFromBooleanEvaluator get(DriverContext context) { + return new ToLongFromBooleanEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToLongFromBooleanEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java index 92a7092e1fdf6..853882774550b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromDoubleEvaluator.java @@ -97,4 +97,25 @@ private static long evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToLong.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToLongFromDoubleEvaluator get(DriverContext context) { + return new ToLongFromDoubleEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToLongFromDoubleEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java index 8c78fab528baf..3717a2b2da2a8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromIntEvaluator.java @@ -97,4 +97,25 @@ private static long evalValue(IntBlock container, int index) { int value = container.getInt(index); return ToLong.fromInt(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToLongFromIntEvaluator get(DriverContext context) { + return new ToLongFromIntEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToLongFromIntEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java index 4c8de77a31dd4..49c82c93ccf92 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromStringEvaluator.java @@ -100,4 +100,25 @@ private static long evalValue(BytesRefBlock container, int index, BytesRef scrat BytesRef value = container.getBytesRef(index, scratchPad); return ToLong.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToLongFromStringEvaluator get(DriverContext context) { + return new ToLongFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToLongFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java index 3e56089b571dc..7f098b16edc7a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongFromUnsignedLongEvaluator.java @@ -96,4 +96,25 @@ private static long evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToLong.fromUnsignedLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToLongFromUnsignedLongEvaluator get(DriverContext context) { + return new ToLongFromUnsignedLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToLongFromUnsignedLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java index 820eeff44e37d..b470f96434f34 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansEvaluator.java @@ -96,4 +96,25 @@ private static double evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToRadians.process(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToRadiansEvaluator get(DriverContext context) { + return new ToRadiansEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToRadiansEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java index 99ee841b67f9e..a11cccfe5d5be 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromBooleanEvaluator.java @@ -98,4 +98,25 @@ private static BytesRef evalValue(BooleanBlock container, int index) { boolean value = container.getBoolean(index); return ToString.fromBoolean(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromBooleanEvaluator get(DriverContext context) { + return new ToStringFromBooleanEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromBooleanEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java index c934a2207272c..94f7357bc5917 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDatetimeEvaluator.java @@ -98,4 +98,25 @@ private static BytesRef evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToString.fromDatetime(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromDatetimeEvaluator get(DriverContext context) { + return new ToStringFromDatetimeEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromDatetimeEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java index 63a92b239d744..d219f607d44f7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromDoubleEvaluator.java @@ -98,4 +98,25 @@ private static BytesRef evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToString.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromDoubleEvaluator get(DriverContext context) { + return new ToStringFromDoubleEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromDoubleEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java index 411b55ce74548..709e06844327c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIPEvaluator.java @@ -99,4 +99,25 @@ private static BytesRef evalValue(BytesRefBlock container, int index, BytesRef s BytesRef value = container.getBytesRef(index, scratchPad); return ToString.fromIP(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromIPEvaluator get(DriverContext context) { + return new ToStringFromIPEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromIPEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java index 177d16eb105b6..659c92203f575 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromIntEvaluator.java @@ -98,4 +98,25 @@ private static BytesRef evalValue(IntBlock container, int index) { int value = container.getInt(index); return ToString.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromIntEvaluator get(DriverContext context) { + return new ToStringFromIntEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromIntEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java index 8bb24f09bdc87..f54494cff5704 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromLongEvaluator.java @@ -98,4 +98,25 @@ private static BytesRef evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToString.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromLongEvaluator get(DriverContext context) { + return new ToStringFromLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java index 4212bd183bc43..76803d0684682 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromUnsignedLongEvaluator.java @@ -98,4 +98,25 @@ private static BytesRef evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToString.fromUnsignedLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromUnsignedLongEvaluator get(DriverContext context) { + return new ToStringFromUnsignedLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromUnsignedLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java index c33de8bc1a6b0..4866f68dae6d4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromVersionEvaluator.java @@ -99,4 +99,25 @@ private static BytesRef evalValue(BytesRefBlock container, int index, BytesRef s BytesRef value = container.getBytesRef(index, scratchPad); return ToString.fromVersion(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToStringFromVersionEvaluator get(DriverContext context) { + return new ToStringFromVersionEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToStringFromVersionEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java index 9f7b2edaf3b81..dff1819ac4f66 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromBooleanEvaluator.java @@ -97,4 +97,25 @@ private static long evalValue(BooleanBlock container, int index) { boolean value = container.getBoolean(index); return ToUnsignedLong.fromBoolean(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToUnsignedLongFromBooleanEvaluator get(DriverContext context) { + return new ToUnsignedLongFromBooleanEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToUnsignedLongFromBooleanEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java index c4ac4be9fad76..38bf437a5f720 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromDoubleEvaluator.java @@ -97,4 +97,25 @@ private static long evalValue(DoubleBlock container, int index) { double value = container.getDouble(index); return ToUnsignedLong.fromDouble(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToUnsignedLongFromDoubleEvaluator get(DriverContext context) { + return new ToUnsignedLongFromDoubleEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToUnsignedLongFromDoubleEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java index 4c4db207c8214..d395e37410b7a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromIntEvaluator.java @@ -97,4 +97,25 @@ private static long evalValue(IntBlock container, int index) { int value = container.getInt(index); return ToUnsignedLong.fromInt(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToUnsignedLongFromIntEvaluator get(DriverContext context) { + return new ToUnsignedLongFromIntEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToUnsignedLongFromIntEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java index a8f8261043d28..398142981cbef 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromLongEvaluator.java @@ -96,4 +96,25 @@ private static long evalValue(LongBlock container, int index) { long value = container.getLong(index); return ToUnsignedLong.fromLong(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToUnsignedLongFromLongEvaluator get(DriverContext context) { + return new ToUnsignedLongFromLongEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToUnsignedLongFromLongEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java index c60f4e1aea2d6..07e2ec42da7b8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongFromStringEvaluator.java @@ -100,4 +100,25 @@ private static long evalValue(BytesRefBlock container, int index, BytesRef scrat BytesRef value = container.getBytesRef(index, scratchPad); return ToUnsignedLong.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToUnsignedLongFromStringEvaluator get(DriverContext context) { + return new ToUnsignedLongFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToUnsignedLongFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java index e64e7fd575e8b..00f88ba76b3e7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersionFromStringEvaluator.java @@ -99,4 +99,25 @@ private static BytesRef evalValue(BytesRefBlock container, int index, BytesRef s BytesRef value = container.getBytesRef(index, scratchPad); return ToVersion.fromKeyword(value); } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field, Source source) { + this.field = field; + this.source = source; + } + + @Override + public ToVersionFromStringEvaluator get(DriverContext context) { + return new ToVersionFromStringEvaluator(field.get(context), source, context); + } + + @Override + public String toString() { + return "ToVersionFromStringEvaluator[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java index 22feef4b574db..f261dd0b93028 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java @@ -83,4 +83,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(value); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory value; + + private final ChronoField chronoField; + + private final ZoneId zone; + + public Factory(EvalOperator.ExpressionEvaluator.Factory value, ChronoField chronoField, + ZoneId zone) { + this.value = value; + this.chronoField = chronoField; + this.zone = zone; + } + + @Override + public DateExtractConstantEvaluator get(DriverContext context) { + return new DateExtractConstantEvaluator(value.get(context), chronoField, zone, context); + } + + @Override + public String toString() { + return "DateExtractConstantEvaluator[" + "value=" + value + ", chronoField=" + chronoField + ", zone=" + zone + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java index a9a2bd8a8b15f..7160b2910c10f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java @@ -118,4 +118,32 @@ public String toString() { public void close() { Releasables.closeExpectNoException(value, chronoField); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory value; + + private final EvalOperator.ExpressionEvaluator.Factory chronoField; + + private final ZoneId zone; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory value, + EvalOperator.ExpressionEvaluator.Factory chronoField, ZoneId zone) { + this.source = source; + this.value = value; + this.chronoField = chronoField; + this.zone = zone; + } + + @Override + public DateExtractEvaluator get(DriverContext context) { + return new DateExtractEvaluator(source, value.get(context), chronoField.get(context), zone, context); + } + + @Override + public String toString() { + return "DateExtractEvaluator[" + "value=" + value + ", chronoField=" + chronoField + ", zone=" + zone + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java index 873332f3de8fd..6a00964b65ac1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java @@ -81,4 +81,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final DateFormatter formatter; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val, DateFormatter formatter) { + this.val = val; + this.formatter = formatter; + } + + @Override + public DateFormatConstantEvaluator get(DriverContext context) { + return new DateFormatConstantEvaluator(val.get(context), formatter, context); + } + + @Override + public String toString() { + return "DateFormatConstantEvaluator[" + "val=" + val + ", formatter=" + formatter + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java index ba1308ccb7dd6..02680217f4b30 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java @@ -102,4 +102,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val, formatter); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final EvalOperator.ExpressionEvaluator.Factory formatter; + + private final Locale locale; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val, + EvalOperator.ExpressionEvaluator.Factory formatter, Locale locale) { + this.val = val; + this.formatter = formatter; + this.locale = locale; + } + + @Override + public DateFormatEvaluator get(DriverContext context) { + return new DateFormatEvaluator(val.get(context), formatter.get(context), locale, context); + } + + @Override + public String toString() { + return "DateFormatEvaluator[" + "val=" + val + ", formatter=" + formatter + ", locale=" + locale + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java index 55de843011250..d8cd727237724 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java @@ -99,4 +99,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final DateFormatter formatter; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val, + DateFormatter formatter) { + this.source = source; + this.val = val; + this.formatter = formatter; + } + + @Override + public DateParseConstantEvaluator get(DriverContext context) { + return new DateParseConstantEvaluator(source, val.get(context), formatter, context); + } + + @Override + public String toString() { + return "DateParseConstantEvaluator[" + "val=" + val + ", formatter=" + formatter + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java index 536ebfbf16a5d..47875d579ac4d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java @@ -119,4 +119,32 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val, formatter); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final EvalOperator.ExpressionEvaluator.Factory formatter; + + private final ZoneId zoneId; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val, + EvalOperator.ExpressionEvaluator.Factory formatter, ZoneId zoneId) { + this.source = source; + this.val = val; + this.formatter = formatter; + this.zoneId = zoneId; + } + + @Override + public DateParseEvaluator get(DriverContext context) { + return new DateParseEvaluator(source, val.get(context), formatter.get(context), zoneId, context); + } + + @Override + public String toString() { + return "DateParseEvaluator[" + "val=" + val + ", formatter=" + formatter + ", zoneId=" + zoneId + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java index f828fdf94b311..81e67a3ac4fae 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java @@ -79,4 +79,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(fieldVal); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory fieldVal; + + private final Rounding.Prepared rounding; + + public Factory(EvalOperator.ExpressionEvaluator.Factory fieldVal, Rounding.Prepared rounding) { + this.fieldVal = fieldVal; + this.rounding = rounding; + } + + @Override + public DateTruncEvaluator get(DriverContext context) { + return new DateTruncEvaluator(fieldVal.get(context), rounding, context); + } + + @Override + public String toString() { + return "DateTruncEvaluator[" + "fieldVal=" + fieldVal + ", rounding=" + rounding + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java index ba6038fdf44fa..c68a04e5fad30 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowEvaluator.java @@ -48,4 +48,22 @@ public String toString() { @Override public void close() { } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final long now; + + public Factory(long now) { + this.now = now; + } + + @Override + public NowEvaluator get(DriverContext context) { + return new NowEvaluator(now, context); + } + + @Override + public String toString() { + return "NowEvaluator[" + "now=" + now + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java index 5a9a6efdbfc35..1d46b98a87785 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java @@ -130,4 +130,27 @@ public String toString() { public void close() { Releasables.closeExpectNoException(ip, () -> Releasables.close(cidrs)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory ip; + + private final EvalOperator.ExpressionEvaluator.Factory[] cidrs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory ip, + EvalOperator.ExpressionEvaluator.Factory[] cidrs) { + this.ip = ip; + this.cidrs = cidrs; + } + + @Override + public CIDRMatchEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] cidrs = Arrays.stream(this.cidrs).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new CIDRMatchEvaluator(ip.get(context), cidrs, context); + } + + @Override + public String toString() { + return "CIDRMatchEvaluator[" + "ip=" + ip + ", cidrs=" + Arrays.toString(cidrs) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java index f4c5d6afc802d..3fcd3c321a442 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java @@ -75,4 +75,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(fieldVal); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory fieldVal; + + public Factory(EvalOperator.ExpressionEvaluator.Factory fieldVal) { + this.fieldVal = fieldVal; + } + + @Override + public AbsDoubleEvaluator get(DriverContext context) { + return new AbsDoubleEvaluator(fieldVal.get(context), context); + } + + @Override + public String toString() { + return "AbsDoubleEvaluator[" + "fieldVal=" + fieldVal + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java index 5b736c21bdaa6..84ba198252bf3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(fieldVal); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory fieldVal; + + public Factory(EvalOperator.ExpressionEvaluator.Factory fieldVal) { + this.fieldVal = fieldVal; + } + + @Override + public AbsIntEvaluator get(DriverContext context) { + return new AbsIntEvaluator(fieldVal.get(context), context); + } + + @Override + public String toString() { + return "AbsIntEvaluator[" + "fieldVal=" + fieldVal + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java index 25a26377e2799..1657ce63149e4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(fieldVal); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory fieldVal; + + public Factory(EvalOperator.ExpressionEvaluator.Factory fieldVal) { + this.fieldVal = fieldVal; + } + + @Override + public AbsLongEvaluator get(DriverContext context) { + return new AbsLongEvaluator(fieldVal.get(context), context); + } + + @Override + public String toString() { + return "AbsLongEvaluator[" + "fieldVal=" + fieldVal + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java index 6f858f17d9245..8a57d6a6e5210 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public AcosEvaluator get(DriverContext context) { + return new AcosEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "AcosEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java index 449f06bf6db43..695f51c01f9df 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public AsinEvaluator get(DriverContext context) { + return new AsinEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "AsinEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java index dea34d613c807..38cdbf3490dca 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(y, x); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory y; + + private final EvalOperator.ExpressionEvaluator.Factory x; + + public Factory(EvalOperator.ExpressionEvaluator.Factory y, + EvalOperator.ExpressionEvaluator.Factory x) { + this.y = y; + this.x = x; + } + + @Override + public Atan2Evaluator get(DriverContext context) { + return new Atan2Evaluator(y.get(context), x.get(context), context); + } + + @Override + public String toString() { + return "Atan2Evaluator[" + "y=" + y + ", x=" + x + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java index ae1202630f262..ec96778cabedd 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public AtanEvaluator get(DriverContext context) { + return new AtanEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "AtanEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java index 50e82d58a9ab6..ce5b21cdacfb0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java @@ -76,4 +76,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public CastIntToDoubleEvaluator get(DriverContext context) { + return new CastIntToDoubleEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "CastIntToDoubleEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java index 6498e3c456d41..e9d6ae8a19b4f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java @@ -76,4 +76,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public CastIntToLongEvaluator get(DriverContext context) { + return new CastIntToLongEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "CastIntToLongEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java index a55ed80c60b26..bc74c15cffb19 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public CastIntToUnsignedLongEvaluator get(DriverContext context) { + return new CastIntToUnsignedLongEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "CastIntToUnsignedLongEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java index b5aad5ccbe1a3..a0321cb889e8b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public CastLongToDoubleEvaluator get(DriverContext context) { + return new CastLongToDoubleEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "CastLongToDoubleEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java index 777912eb318e4..a03da9a18f9e0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java @@ -75,4 +75,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public CastLongToUnsignedLongEvaluator get(DriverContext context) { + return new CastLongToUnsignedLongEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "CastLongToUnsignedLongEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java index 446c78556297a..e655630ed9af5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public CastUnsignedLongToDoubleEvaluator get(DriverContext context) { + return new CastUnsignedLongToDoubleEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "CastUnsignedLongToDoubleEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java index e2dca44c7e367..d9c54514f3713 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public CeilDoubleEvaluator get(DriverContext context) { + return new CeilDoubleEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "CeilDoubleEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java index 441e468f06967..8da902073ec6b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public CosEvaluator get(DriverContext context) { + return new CosEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "CosEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java index 9f5ea0e7829f3..fe5e8cd09aba6 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public CoshEvaluator get(DriverContext context) { + return new CoshEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "CoshEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java index d7de9b12aa201..3e0b2683a8022 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public FloorDoubleEvaluator get(DriverContext context) { + return new FloorDoubleEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "FloorDoubleEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java index 2535e078691c9..0ef188805bddf 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java @@ -76,4 +76,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public IsFiniteEvaluator get(DriverContext context) { + return new IsFiniteEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "IsFiniteEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java index 1d12ffe2f50f9..730cd3b8c5312 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java @@ -76,4 +76,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public IsInfiniteEvaluator get(DriverContext context) { + return new IsInfiniteEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "IsInfiniteEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java index af809390d0991..a9f88911c5ec2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java @@ -76,4 +76,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public IsNaNEvaluator get(DriverContext context) { + return new IsNaNEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "IsNaNEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java index 665d441de8e04..f2c3fe7de297f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public Log10DoubleEvaluator get(DriverContext context) { + return new Log10DoubleEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "Log10DoubleEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java index 4ae2b4b4ec944..1bb8af1eb53cb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java @@ -92,4 +92,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public Log10IntEvaluator get(DriverContext context) { + return new Log10IntEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "Log10IntEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java index bac27b792f0ef..a1348ce3874a1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java @@ -92,4 +92,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public Log10LongEvaluator get(DriverContext context) { + return new Log10LongEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "Log10LongEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java index f7c77e64313fd..7f281103e1bbe 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java @@ -92,4 +92,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public Log10UnsignedLongEvaluator get(DriverContext context) { + return new Log10UnsignedLongEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "Log10UnsignedLongEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java index ed12852d25ca3..2dfbe1e174d71 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(base, exponent); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory base; + + private final EvalOperator.ExpressionEvaluator.Factory exponent; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory base, + EvalOperator.ExpressionEvaluator.Factory exponent) { + this.source = source; + this.base = base; + this.exponent = exponent; + } + + @Override + public PowDoubleEvaluator get(DriverContext context) { + return new PowDoubleEvaluator(source, base.get(context), exponent.get(context), context); + } + + @Override + public String toString() { + return "PowDoubleEvaluator[" + "base=" + base + ", exponent=" + exponent + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java index 28218b016707a..4800264ecf9b0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java @@ -109,4 +109,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(base, exponent); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory base; + + private final EvalOperator.ExpressionEvaluator.Factory exponent; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory base, + EvalOperator.ExpressionEvaluator.Factory exponent) { + this.source = source; + this.base = base; + this.exponent = exponent; + } + + @Override + public PowIntEvaluator get(DriverContext context) { + return new PowIntEvaluator(source, base.get(context), exponent.get(context), context); + } + + @Override + public String toString() { + return "PowIntEvaluator[" + "base=" + base + ", exponent=" + exponent + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java index 8f15fbb359ffb..241299faad478 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java @@ -109,4 +109,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(base, exponent); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory base; + + private final EvalOperator.ExpressionEvaluator.Factory exponent; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory base, + EvalOperator.ExpressionEvaluator.Factory exponent) { + this.source = source; + this.base = base; + this.exponent = exponent; + } + + @Override + public PowLongEvaluator get(DriverContext context) { + return new PowLongEvaluator(source, base.get(context), exponent.get(context), context); + } + + @Override + public String toString() { + return "PowLongEvaluator[" + "base=" + base + ", exponent=" + exponent + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java index 92c5077cee1e9..91ee8f68e74ea 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val, decimals); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final EvalOperator.ExpressionEvaluator.Factory decimals; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val, + EvalOperator.ExpressionEvaluator.Factory decimals) { + this.val = val; + this.decimals = decimals; + } + + @Override + public RoundDoubleEvaluator get(DriverContext context) { + return new RoundDoubleEvaluator(val.get(context), decimals.get(context), context); + } + + @Override + public String toString() { + return "RoundDoubleEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java index 3d51037b55235..c2cd4ac9d2a54 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java @@ -75,4 +75,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public RoundDoubleNoDecimalsEvaluator get(DriverContext context) { + return new RoundDoubleNoDecimalsEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "RoundDoubleNoDecimalsEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java index d9723b429e7a2..431b7717b397e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java @@ -94,4 +94,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val, decimals); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final EvalOperator.ExpressionEvaluator.Factory decimals; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val, + EvalOperator.ExpressionEvaluator.Factory decimals) { + this.val = val; + this.decimals = decimals; + } + + @Override + public RoundIntEvaluator get(DriverContext context) { + return new RoundIntEvaluator(val.get(context), decimals.get(context), context); + } + + @Override + public String toString() { + return "RoundIntEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java index 43521218e7c80..1a5bc14eabc7a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val, decimals); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final EvalOperator.ExpressionEvaluator.Factory decimals; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val, + EvalOperator.ExpressionEvaluator.Factory decimals) { + this.val = val; + this.decimals = decimals; + } + + @Override + public RoundLongEvaluator get(DriverContext context) { + return new RoundLongEvaluator(val.get(context), decimals.get(context), context); + } + + @Override + public String toString() { + return "RoundLongEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java index d0d339719211b..beb7ec29fd955 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val, decimals); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + private final EvalOperator.ExpressionEvaluator.Factory decimals; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val, + EvalOperator.ExpressionEvaluator.Factory decimals) { + this.val = val; + this.decimals = decimals; + } + + @Override + public RoundUnsignedLongEvaluator get(DriverContext context) { + return new RoundUnsignedLongEvaluator(val.get(context), decimals.get(context), context); + } + + @Override + public String toString() { + return "RoundUnsignedLongEvaluator[" + "val=" + val + ", decimals=" + decimals + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java index 57dc5e200469a..c6613c8f1c94f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public SinEvaluator get(DriverContext context) { + return new SinEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "SinEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java index 05082475132d4..1b77cde7b87d3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public SinhEvaluator get(DriverContext context) { + return new SinhEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "SinhEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java index 264a7e6c70dde..de925d7a36d54 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public SqrtDoubleEvaluator get(DriverContext context) { + return new SqrtDoubleEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "SqrtDoubleEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java index 9ea795b7917ba..6bd0dae184a96 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java @@ -92,4 +92,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public SqrtIntEvaluator get(DriverContext context) { + return new SqrtIntEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "SqrtIntEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java index b64044ef1fb98..d6239d01d0f56 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java @@ -92,4 +92,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) { + this.source = source; + this.val = val; + } + + @Override + public SqrtLongEvaluator get(DriverContext context) { + return new SqrtLongEvaluator(source, val.get(context), context); + } + + @Override + public String toString() { + return "SqrtLongEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java index d0a64c44128bd..876a751fc6bf9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public SqrtUnsignedLongEvaluator get(DriverContext context) { + return new SqrtUnsignedLongEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "SqrtUnsignedLongEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java index 51f0b4efa5fd1..ea08945abb54f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public TanEvaluator get(DriverContext context) { + return new TanEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "TanEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java index be09dbf20ba46..6d024c6a4f02a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public TanhEvaluator get(DriverContext context) { + return new TanhEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "TanhEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java index a72ed66bcab0a..58081a25c1e69 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgDoubleEvaluator.java @@ -85,4 +85,22 @@ public Block.Ref evalNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvAvgDoubleEvaluator get(DriverContext context) { + return new MvAvgDoubleEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvAvg[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java index c8c433766c157..eb807984feb7a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgIntEvaluator.java @@ -148,4 +148,22 @@ public Block.Ref evalSingleValuedNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvAvgIntEvaluator get(DriverContext context) { + return new MvAvgIntEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvAvg[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java index d2907cfc3bdf1..a81ec98c8d3e4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgLongEvaluator.java @@ -148,4 +148,22 @@ public Block.Ref evalSingleValuedNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvAvgLongEvaluator get(DriverContext context) { + return new MvAvgLongEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvAvg[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java index cf8a6f00743e4..3700872c98e42 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvgUnsignedLongEvaluator.java @@ -149,4 +149,22 @@ public Block.Ref evalSingleValuedNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvAvgUnsignedLongEvaluator get(DriverContext context) { + return new MvAvgUnsignedLongEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvAvg[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java index 2b1efbff7abf8..c3e7c600f259f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBooleanEvaluator.java @@ -135,4 +135,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMaxBooleanEvaluator get(DriverContext context) { + return new MvMaxBooleanEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMax[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java index 4f8a77708126c..0a125eaa9a579 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxBytesRefEvaluator.java @@ -144,4 +144,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMaxBytesRefEvaluator get(DriverContext context) { + return new MvMaxBytesRefEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMax[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java index dd176dfc1a64b..7912cb7f5ce5c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxDoubleEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMaxDoubleEvaluator get(DriverContext context) { + return new MvMaxDoubleEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMax[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java index f7492da9d463b..b42ec323fe7f8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxIntEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMaxIntEvaluator get(DriverContext context) { + return new MvMaxIntEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMax[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java index b9fd4cb54d1a0..baaf5e49a3fa0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxLongEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMaxLongEvaluator get(DriverContext context) { + return new MvMaxLongEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMax[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java index a63d52f6eab43..b8f122e99db0f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianDoubleEvaluator.java @@ -85,4 +85,22 @@ public Block.Ref evalNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMedianDoubleEvaluator get(DriverContext context) { + return new MvMedianDoubleEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMedian[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java index 94431da85fdb0..08e351c6c0122 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianIntEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMedianIntEvaluator get(DriverContext context) { + return new MvMedianIntEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMedian[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java index 66926f1dfb038..76bdea1ce761e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianLongEvaluator.java @@ -135,4 +135,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMedianLongEvaluator get(DriverContext context) { + return new MvMedianLongEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMedian[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java index b427fb25c3d84..23191d6a64ca1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianUnsignedLongEvaluator.java @@ -135,4 +135,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMedianUnsignedLongEvaluator get(DriverContext context) { + return new MvMedianUnsignedLongEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMedian[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java index 8d66b27e2c8da..0935a2f28569c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBooleanEvaluator.java @@ -135,4 +135,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMinBooleanEvaluator get(DriverContext context) { + return new MvMinBooleanEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMin[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java index 6b64cde3be6f2..cdc54a1f73990 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinBytesRefEvaluator.java @@ -144,4 +144,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMinBytesRefEvaluator get(DriverContext context) { + return new MvMinBytesRefEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMin[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java index bc4a942d3b348..ec3129cfd11be 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinDoubleEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMinDoubleEvaluator get(DriverContext context) { + return new MvMinDoubleEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMin[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java index f4ef6729bf598..58579ae9b0f91 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinIntEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMinIntEvaluator get(DriverContext context) { + return new MvMinIntEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMin[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java index 8720cbd82108e..89ecd6f5fcafc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinLongEvaluator.java @@ -134,4 +134,22 @@ private Block.Ref evalAscendingNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvMinLongEvaluator get(DriverContext context) { + return new MvMinLongEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvMin[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java index eddc8b7d0a69e..53e07d01b37dc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumDoubleEvaluator.java @@ -85,4 +85,22 @@ public Block.Ref evalNotNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(EvalOperator.ExpressionEvaluator.Factory field) { + this.field = field; + } + + @Override + public MvSumDoubleEvaluator get(DriverContext context) { + return new MvSumDoubleEvaluator(field.get(context), context); + } + + @Override + public String toString() { + return "MvSum[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java index d9dcdd7239648..97dabfe927507 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumIntEvaluator.java @@ -69,4 +69,25 @@ public Block.Ref evalNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field) { + this.source = source; + this.field = field; + } + + @Override + public MvSumIntEvaluator get(DriverContext context) { + return new MvSumIntEvaluator(source, field.get(context), context); + } + + @Override + public String toString() { + return "MvSum[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java index 15dc035d3314e..dbdeca3d5d3e3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumLongEvaluator.java @@ -69,4 +69,25 @@ public Block.Ref evalNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field) { + this.source = source; + this.field = field; + } + + @Override + public MvSumLongEvaluator get(DriverContext context) { + return new MvSumLongEvaluator(source, field.get(context), context); + } + + @Override + public String toString() { + return "MvSum[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java index fb79d99209bcc..d92e61744a89d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSumUnsignedLongEvaluator.java @@ -69,4 +69,25 @@ public Block.Ref evalNullable(Block.Ref ref) { } } } + + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field) { + this.source = source; + this.field = field; + } + + @Override + public MvSumUnsignedLongEvaluator get(DriverContext context) { + return new MvSumUnsignedLongEvaluator(source, field.get(context), context); + } + + @Override + public String toString() { + return "MvSum[field=" + field + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java index 0436bb6ada170..b3fd79aec7d30 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java @@ -7,6 +7,7 @@ import java.lang.Override; import java.lang.String; import java.util.Arrays; +import java.util.function.Function; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; @@ -112,4 +113,27 @@ public String toString() { public void close() { Releasables.closeExpectNoException(scratch, () -> Releasables.close(values)); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Function scratch; + + private final EvalOperator.ExpressionEvaluator.Factory[] values; + + public Factory(Function scratch, + EvalOperator.ExpressionEvaluator.Factory[] values) { + this.scratch = scratch; + this.values = values; + } + + @Override + public ConcatEvaluator get(DriverContext context) { + EvalOperator.ExpressionEvaluator[] values = Arrays.stream(this.values).map(a -> a.get(context)).toArray(EvalOperator.ExpressionEvaluator[]::new); + return new ConcatEvaluator(scratch.apply(context), values, context); + } + + @Override + public String toString() { + return "ConcatEvaluator[" + "values=" + Arrays.toString(values) + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java index 361a4f791f925..9cc2bdc4c6739 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java @@ -100,4 +100,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, suffix); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory suffix; + + public Factory(EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory suffix) { + this.str = str; + this.suffix = suffix; + } + + @Override + public EndsWithEvaluator get(DriverContext context) { + return new EndsWithEvaluator(str.get(context), suffix.get(context), context); + } + + @Override + public String toString() { + return "EndsWithEvaluator[" + "str=" + str + ", suffix=" + suffix + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java index 7abfb07ee64d4..ff661b3a388be 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public LTrimEvaluator get(DriverContext context) { + return new LTrimEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "LTrimEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java index 818b702e60b34..7d972b7cd74bc 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java @@ -6,6 +6,7 @@ import java.lang.Override; import java.lang.String; +import java.util.function.Function; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.UnicodeUtil; import org.elasticsearch.compute.data.Block; @@ -105,4 +106,34 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, length); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Function out; + + private final Function cp; + + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory length; + + public Factory(Function out, + Function cp, + EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory length) { + this.out = out; + this.cp = cp; + this.str = str; + this.length = length; + } + + @Override + public LeftEvaluator get(DriverContext context) { + return new LeftEvaluator(out.apply(context), cp.apply(context), str.get(context), length.get(context), context); + } + + @Override + public String toString() { + return "LeftEvaluator[" + "str=" + str + ", length=" + length + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java index 9345551875ad4..9e63d5f4e80a5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java @@ -79,4 +79,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public LengthEvaluator get(DriverContext context) { + return new LengthEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "LengthEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java index b9504b4305431..9ea3b811c4d0d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public RTrimEvaluator get(DriverContext context) { + return new RTrimEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "RTrimEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java index c37abac4ff689..111b15249edaf 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java @@ -118,4 +118,32 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, newStr); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final Pattern regex; + + private final EvalOperator.ExpressionEvaluator.Factory newStr; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory str, Pattern regex, + EvalOperator.ExpressionEvaluator.Factory newStr) { + this.source = source; + this.str = str; + this.regex = regex; + this.newStr = newStr; + } + + @Override + public ReplaceConstantEvaluator get(DriverContext context) { + return new ReplaceConstantEvaluator(source, str.get(context), regex, newStr.get(context), context); + } + + @Override + public String toString() { + return "ReplaceConstantEvaluator[" + "str=" + str + ", regex=" + regex + ", newStr=" + newStr + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java index f8e3ca32e8731..a26963ddea64b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java @@ -135,4 +135,33 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, regex, newStr); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory regex; + + private final EvalOperator.ExpressionEvaluator.Factory newStr; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory regex, + EvalOperator.ExpressionEvaluator.Factory newStr) { + this.source = source; + this.str = str; + this.regex = regex; + this.newStr = newStr; + } + + @Override + public ReplaceEvaluator get(DriverContext context) { + return new ReplaceEvaluator(source, str.get(context), regex.get(context), newStr.get(context), context); + } + + @Override + public String toString() { + return "ReplaceEvaluator[" + "str=" + str + ", regex=" + regex + ", newStr=" + newStr + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java index ad8bee9d41e1d..460ae54f0d39b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java @@ -6,6 +6,7 @@ import java.lang.Override; import java.lang.String; +import java.util.function.Function; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.UnicodeUtil; import org.elasticsearch.compute.data.Block; @@ -105,4 +106,34 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, length); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Function out; + + private final Function cp; + + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory length; + + public Factory(Function out, + Function cp, + EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory length) { + this.out = out; + this.cp = cp; + this.str = str; + this.length = length; + } + + @Override + public RightEvaluator get(DriverContext context) { + return new RightEvaluator(out.apply(context), cp.apply(context), str.get(context), length.get(context), context); + } + + @Override + public String toString() { + return "RightEvaluator[" + "str=" + str + ", length=" + length + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java index 7b8e2a34bafdd..1bc04d7b0b68a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java @@ -6,6 +6,7 @@ import java.lang.Override; import java.lang.String; +import java.util.function.Function; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; @@ -84,4 +85,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final byte delim; + + private final Function scratch; + + public Factory(EvalOperator.ExpressionEvaluator.Factory str, byte delim, + Function scratch) { + this.str = str; + this.delim = delim; + this.scratch = scratch; + } + + @Override + public SplitSingleByteEvaluator get(DriverContext context) { + return new SplitSingleByteEvaluator(str.get(context), delim, scratch.apply(context), context); + } + + @Override + public String toString() { + return "SplitSingleByteEvaluator[" + "str=" + str + ", delim=" + delim + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java index f83393c7e8293..64323851dab09 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java @@ -6,6 +6,7 @@ import java.lang.Override; import java.lang.String; +import java.util.function.Function; import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; @@ -101,4 +102,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, delim); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory delim; + + private final Function scratch; + + public Factory(EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory delim, Function scratch) { + this.str = str; + this.delim = delim; + this.scratch = scratch; + } + + @Override + public SplitVariableEvaluator get(DriverContext context) { + return new SplitVariableEvaluator(str.get(context), delim.get(context), scratch.apply(context), context); + } + + @Override + public String toString() { + return "SplitVariableEvaluator[" + "str=" + str + ", delim=" + delim + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java index 25e9d7b2732d1..4e9bd21ca7a97 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java @@ -100,4 +100,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, prefix); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory prefix; + + public Factory(EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory prefix) { + this.str = str; + this.prefix = prefix; + } + + @Override + public StartsWithEvaluator get(DriverContext context) { + return new StartsWithEvaluator(str.get(context), prefix.get(context), context); + } + + @Override + public String toString() { + return "StartsWithEvaluator[" + "str=" + str + ", prefix=" + prefix + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java index 411d1fd864333..a95d09d65fb4e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java @@ -117,4 +117,30 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, start, length); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory start; + + private final EvalOperator.ExpressionEvaluator.Factory length; + + public Factory(EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory start, + EvalOperator.ExpressionEvaluator.Factory length) { + this.str = str; + this.start = start; + this.length = length; + } + + @Override + public SubstringEvaluator get(DriverContext context) { + return new SubstringEvaluator(str.get(context), start.get(context), length.get(context), context); + } + + @Override + public String toString() { + return "SubstringEvaluator[" + "str=" + str + ", start=" + start + ", length=" + length + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java index d393f2b6c5409..9667d1b2fbb3f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java @@ -97,4 +97,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(str, start); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory str; + + private final EvalOperator.ExpressionEvaluator.Factory start; + + public Factory(EvalOperator.ExpressionEvaluator.Factory str, + EvalOperator.ExpressionEvaluator.Factory start) { + this.str = str; + this.start = start; + } + + @Override + public SubstringNoLengthEvaluator get(DriverContext context) { + return new SubstringNoLengthEvaluator(str.get(context), start.get(context), context); + } + + @Override + public String toString() { + return "SubstringNoLengthEvaluator[" + "str=" + str + ", start=" + start + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java index 8ee20059ba000..bb28283cc0d99 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java @@ -77,4 +77,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(val); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory val; + + public Factory(EvalOperator.ExpressionEvaluator.Factory val) { + this.val = val; + } + + @Override + public TrimEvaluator get(DriverContext context) { + return new TrimEvaluator(val.get(context), context); + } + + @Override + public String toString() { + return "TrimEvaluator[" + "val=" + val + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java index ca6088fad8652..ee40b5ff778c9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java @@ -96,4 +96,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(datetime); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory datetime; + + private final TemporalAmount temporalAmount; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory datetime, + TemporalAmount temporalAmount) { + this.source = source; + this.datetime = datetime; + this.temporalAmount = temporalAmount; + } + + @Override + public AddDatetimesEvaluator get(DriverContext context) { + return new AddDatetimesEvaluator(source, datetime.get(context), temporalAmount, context); + } + + @Override + public String toString() { + return "AddDatetimesEvaluator[" + "datetime=" + datetime + ", temporalAmount=" + temporalAmount + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java index 016c6c501dd88..9b2101b0134c7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public AddDoublesEvaluator get(DriverContext context) { + return new AddDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "AddDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java index 00c7ffa4164c7..f57e6cf4f841f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public AddIntsEvaluator get(DriverContext context) { + return new AddIntsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "AddIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java index 09b90f9357341..cb0144ee07b13 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public AddLongsEvaluator get(DriverContext context) { + return new AddLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "AddLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java index 5dfa2b51cb000..4aecd8dbce899 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public AddUnsignedLongsEvaluator get(DriverContext context) { + return new AddUnsignedLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "AddUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java index 50ba6515e5004..7abda14d7e8a3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public DivDoublesEvaluator get(DriverContext context) { + return new DivDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "DivDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java index b3c6b9ea2b5e6..5f31c9a485dfa 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public DivIntsEvaluator get(DriverContext context) { + return new DivIntsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "DivIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java index 6cd600c1089be..dd933110f2393 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public DivLongsEvaluator get(DriverContext context) { + return new DivLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "DivLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java index c565a7b7ee55b..0152e6b2178f2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public DivUnsignedLongsEvaluator get(DriverContext context) { + return new DivUnsignedLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "DivUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java index 5b2588a761cfd..62d7bb54e6244 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public ModDoublesEvaluator get(DriverContext context) { + return new ModDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "ModDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java index 1e98bf4cd124f..61bfebaf15491 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public ModIntsEvaluator get(DriverContext context) { + return new ModIntsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "ModIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java index 4a9f1c6829994..c0671d0532512 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public ModLongsEvaluator get(DriverContext context) { + return new ModLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "ModLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java index dc86ebd8da292..e40a5e2bb52b6 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public ModUnsignedLongsEvaluator get(DriverContext context) { + return new ModUnsignedLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "ModUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java index 3c941a8189b92..f7e5e797251a5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public MulDoublesEvaluator get(DriverContext context) { + return new MulDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "MulDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java index 180c7d9301efe..16304eaddc596 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public MulIntsEvaluator get(DriverContext context) { + return new MulIntsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "MulIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java index ed59fb7600944..45bd89539e093 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public MulLongsEvaluator get(DriverContext context) { + return new MulLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "MulLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java index 841da4f6cd251..bf5d1a73556ba 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public MulUnsignedLongsEvaluator get(DriverContext context) { + return new MulUnsignedLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "MulUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java index a15407b0102f8..943aca14ed97b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java @@ -74,4 +74,22 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(EvalOperator.ExpressionEvaluator.Factory v) { + this.v = v; + } + + @Override + public NegDoublesEvaluator get(DriverContext context) { + return new NegDoublesEvaluator(v.get(context), context); + } + + @Override + public String toString() { + return "NegDoublesEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java index c71ec463e84ed..dde3d44582ee3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory v) { + this.source = source; + this.v = v; + } + + @Override + public NegIntsEvaluator get(DriverContext context) { + return new NegIntsEvaluator(source, v.get(context), context); + } + + @Override + public String toString() { + return "NegIntsEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java index 70f465d715977..0490080520c85 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java @@ -91,4 +91,25 @@ public String toString() { public void close() { Releasables.closeExpectNoException(v); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory v; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory v) { + this.source = source; + this.v = v; + } + + @Override + public NegLongsEvaluator get(DriverContext context) { + return new NegLongsEvaluator(source, v.get(context), context); + } + + @Override + public String toString() { + return "NegLongsEvaluator[" + "v=" + v + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java index 25240f9b0ac7f..574c0d48f8f89 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java @@ -96,4 +96,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(datetime); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory datetime; + + private final TemporalAmount temporalAmount; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory datetime, + TemporalAmount temporalAmount) { + this.source = source; + this.datetime = datetime; + this.temporalAmount = temporalAmount; + } + + @Override + public SubDatetimesEvaluator get(DriverContext context) { + return new SubDatetimesEvaluator(source, datetime.get(context), temporalAmount, context); + } + + @Override + public String toString() { + return "SubDatetimesEvaluator[" + "datetime=" + datetime + ", temporalAmount=" + temporalAmount + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java index 9e873eaef85b1..dc766ab59a1d4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java @@ -92,4 +92,26 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public SubDoublesEvaluator get(DriverContext context) { + return new SubDoublesEvaluator(lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "SubDoublesEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java index 90ac7b908648f..14e6f00bc51d4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public SubIntsEvaluator get(DriverContext context) { + return new SubIntsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "SubIntsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java index 8cf7a07cc1761..40447fe38f4f6 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public SubLongsEvaluator get(DriverContext context) { + return new SubLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "SubLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java index aa0e1bf842971..8415baf744587 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java @@ -108,4 +108,29 @@ public String toString() { public void close() { Releasables.closeExpectNoException(lhs, rhs); } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory lhs; + + private final EvalOperator.ExpressionEvaluator.Factory rhs; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory lhs, + EvalOperator.ExpressionEvaluator.Factory rhs) { + this.source = source; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public SubUnsignedLongsEvaluator get(DriverContext context) { + return new SubUnsignedLongsEvaluator(source, lhs.get(context), rhs.get(context), context); + } + + @Override + public String toString() { + return "SubUnsignedLongsEvaluator[" + "lhs=" + lhs + ", rhs=" + rhs + "]"; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java index 53a915046b45f..845cccdff78bf 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java @@ -172,8 +172,18 @@ public Block.Ref eval(Page page) { @Override public void close() {} } - int channel = layout.get(attr.id()).channel(); - return driverContext -> new Attribute(channel); + record AttributeFactory(int channel) implements ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext driverContext) { + return new Attribute(channel); + } + + @Override + public String toString() { + return "Attribute[channel=" + channel + "]"; + } + } + return new AttributeFactory(layout.get(attr.id()).channel()); } } @@ -195,7 +205,18 @@ public String toString() { @Override public void close() {} } - return context -> new LiteralsEvaluator(context, lit); + record LiteralsEvaluatorFactory(Literal lit) implements ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext driverContext) { + return new LiteralsEvaluator(driverContext, lit); + } + + @Override + public String toString() { + return "LiteralsEvaluator[lit=" + lit + "]"; + } + } + return new LiteralsEvaluatorFactory(lit); } private static Block block(Literal lit, BlockFactory blockFactory, int positions) { @@ -221,12 +242,22 @@ static class IsNulls extends ExpressionMapper { @Override public ExpressionEvaluator.Factory map(IsNull isNull, Layout layout) { var field = toEvaluator(isNull.field(), layout); - return driverContext -> new IsNullEvaluator(driverContext, field.get(driverContext)); + return new IsNullEvaluatorFactory(field); } - record IsNullEvaluator(DriverContext driverContext, EvalOperator.ExpressionEvaluator field) - implements - EvalOperator.ExpressionEvaluator { + record IsNullEvaluatorFactory(EvalOperator.ExpressionEvaluator.Factory field) implements ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new IsNullEvaluator(context, field.get(context)); + } + + @Override + public String toString() { + return "IsNullEvaluator[field=" + field + ']'; + } + } + + record IsNullEvaluator(DriverContext driverContext, EvalOperator.ExpressionEvaluator field) implements ExpressionEvaluator { @Override public Block.Ref eval(Page page) { try (Block.Ref fieldBlock = field.eval(page)) { @@ -256,7 +287,7 @@ public void close() { @Override public String toString() { - return "IsNullEvaluator[" + "field=" + field + ']'; + return "IsNullEvaluator[field=" + field + ']'; } } } @@ -265,8 +296,19 @@ static class IsNotNulls extends ExpressionMapper { @Override public ExpressionEvaluator.Factory map(IsNotNull isNotNull, Layout layout) { - var field = toEvaluator(isNotNull.field(), layout); - return driverContext -> new IsNotNullEvaluator(driverContext, field.get(driverContext)); + return new IsNotNullEvaluatorFactory(toEvaluator(isNotNull.field(), layout)); + } + + record IsNotNullEvaluatorFactory(EvalOperator.ExpressionEvaluator.Factory field) implements ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new IsNotNullEvaluator(context, field.get(context)); + } + + @Override + public String toString() { + return "IsNotNullEvaluator[field=" + field + ']'; + } } record IsNotNullEvaluator(DriverContext driverContext, EvalOperator.ExpressionEvaluator field) @@ -301,7 +343,7 @@ public void close() { @Override public String toString() { - return "IsNotNullEvaluator[" + "field=" + field + ']'; + return "IsNotNullEvaluator[field=" + field + ']'; } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/ComparisonMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/ComparisonMapper.java index cb10499ae6d0b..f609bb5491569 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/ComparisonMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/ComparisonMapper.java @@ -7,9 +7,6 @@ package org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison; -import org.elasticsearch.common.TriFunction; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.ExpressionMapper; @@ -18,75 +15,76 @@ import org.elasticsearch.xpack.esql.type.EsqlDataTypeRegistry; import org.elasticsearch.xpack.ql.expression.predicate.BinaryOperator; import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.BinaryComparison; -import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; +import java.util.function.BiFunction; + import static org.elasticsearch.xpack.esql.evaluator.EvalMapper.toEvaluator; public abstract class ComparisonMapper extends ExpressionMapper { public static final ExpressionMapper EQUALS = new ComparisonMapper( - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsIntsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsLongsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsDoublesEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsKeywordsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsBoolsEvaluator::new + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsIntsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsLongsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsDoublesEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsKeywordsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.EqualsBoolsEvaluator.Factory::new ) { }; public static final ExpressionMapper NOT_EQUALS = new ComparisonMapper( - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsIntsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsLongsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsDoublesEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsKeywordsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsBoolsEvaluator::new + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsIntsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsLongsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsDoublesEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsKeywordsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.NotEqualsBoolsEvaluator.Factory::new ) { }; public static final ExpressionMapper GREATER_THAN = new ComparisonMapper( - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanIntsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanLongsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanDoublesEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanKeywordsEvaluator::new + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanIntsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanLongsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanDoublesEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanKeywordsEvaluator.Factory::new ) { }; public static final ExpressionMapper GREATER_THAN_OR_EQUAL = new ComparisonMapper( - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualIntsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualLongsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualDoublesEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualKeywordsEvaluator::new + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualIntsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualLongsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualDoublesEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.GreaterThanOrEqualKeywordsEvaluator.Factory::new ) { }; public static final ExpressionMapper LESS_THAN = new ComparisonMapper( - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanIntsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanLongsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanDoublesEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanKeywordsEvaluator::new + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanIntsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanLongsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanDoublesEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanKeywordsEvaluator.Factory::new ) { }; public static final ExpressionMapper LESS_THAN_OR_EQUAL = new ComparisonMapper( - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualIntsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualLongsEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualDoublesEvaluator::new, - org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualKeywordsEvaluator::new + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualIntsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualLongsEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualDoublesEvaluator.Factory::new, + org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqualKeywordsEvaluator.Factory::new ) { }; - private final TriFunction ints; - private final TriFunction longs; - private final TriFunction doubles; - private final TriFunction keywords; - private final TriFunction bools; + private final BiFunction ints; + private final BiFunction longs; + private final BiFunction doubles; + private final BiFunction keywords; + private final BiFunction bools; private ComparisonMapper( - TriFunction ints, - TriFunction longs, - TriFunction doubles, - TriFunction keywords, - TriFunction bools + BiFunction ints, + BiFunction longs, + BiFunction doubles, + BiFunction keywords, + BiFunction bools ) { this.ints = ints; this.longs = longs; @@ -96,16 +94,16 @@ private ComparisonMapper( } ComparisonMapper( - TriFunction ints, - TriFunction longs, - TriFunction doubles, - TriFunction keywords + BiFunction ints, + BiFunction longs, + BiFunction doubles, + BiFunction keywords ) { this.ints = ints; this.longs = longs; this.doubles = doubles; this.keywords = keywords; - this.bools = (lhs, rhs, dvrCtx) -> { throw EsqlIllegalArgumentException.illegalDataType(DataTypes.BOOLEAN); }; + this.bools = (lhs, rhs) -> { throw EsqlIllegalArgumentException.illegalDataType(DataTypes.BOOLEAN); }; } @Override @@ -130,13 +128,13 @@ public final ExpressionEvaluator.Factory map(BinaryComparison bc, Layout layout) var leftEval = toEvaluator(bc.left(), layout); var rightEval = toEvaluator(bc.right(), layout); if (leftType == DataTypes.KEYWORD || leftType == DataTypes.TEXT || leftType == DataTypes.IP || leftType == DataTypes.VERSION) { - return dvrCtx -> keywords.apply(leftEval.get(dvrCtx), rightEval.get(dvrCtx), dvrCtx); + return keywords.apply(leftEval, rightEval); } if (leftType == DataTypes.BOOLEAN) { - return dvrCtx -> bools.apply(leftEval.get(dvrCtx), rightEval.get(dvrCtx), dvrCtx); + return bools.apply(leftEval, rightEval); } if (leftType == DataTypes.DATETIME) { - return dvrCtx -> longs.apply(leftEval.get(dvrCtx), rightEval.get(dvrCtx), dvrCtx); + return longs.apply(leftEval, rightEval); } throw new EsqlIllegalArgumentException("resolved type for [" + bc + "] but didn't implement mapping"); } @@ -145,25 +143,10 @@ public static ExpressionEvaluator.Factory castToEvaluator( BinaryOperator op, Layout layout, DataType required, - TriFunction buildEvaluator - ) { - var lhs = Cast.cast(op.left().dataType(), required, toEvaluator(op.left(), layout)); - var rhs = Cast.cast(op.right().dataType(), required, toEvaluator(op.right(), layout)); - return dvrCtx -> buildEvaluator.apply(lhs.get(dvrCtx), rhs.get(dvrCtx), dvrCtx); - } - - public static ExpressionEvaluator.Factory castToEvaluatorWithSource( - BinaryOperator op, - Layout layout, - DataType required, - TriFunction< - Source, - EvalOperator.ExpressionEvaluator, - EvalOperator.ExpressionEvaluator, - EvalOperator.ExpressionEvaluator> buildEvaluator + BiFunction factory ) { var lhs = Cast.cast(op.left().dataType(), required, toEvaluator(op.left(), layout)); var rhs = Cast.cast(op.right().dataType(), required, toEvaluator(op.right(), layout)); - return dvrCtx -> buildEvaluator.apply(op.source(), lhs.get(dvrCtx), rhs.get(dvrCtx)); + return factory.apply(lhs, rhs); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java index 744f9f3815e4e..5db081c0dc529 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java @@ -156,17 +156,33 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - - List conditionsEval = conditions.stream() + ElementType resultType = LocalExecutionPlanner.toElementType(dataType()); + List conditionsFactories = conditions.stream() .map(c -> new ConditionEvaluatorSupplier(toEvaluator.apply(c.condition), toEvaluator.apply(c.value))) .toList(); - var elseValueEval = toEvaluator.apply(elseValue); - return dvrCtx -> new CaseEvaluator( - dvrCtx, - LocalExecutionPlanner.toElementType(dataType()), - conditionsEval.stream().map(x -> x.apply(dvrCtx)).toList(), - elseValueEval.get(dvrCtx) - ); + ExpressionEvaluator.Factory elseValueFactory = toEvaluator.apply(elseValue); + return new ExpressionEvaluator.Factory() { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new CaseEvaluator( + context, + resultType, + conditionsFactories.stream().map(x -> x.apply(context)).toList(), + elseValueFactory.get(context) + ); + } + + @Override + public String toString() { + return "CaseEvaluator[resultType=" + + resultType + + ", conditions=" + + conditionsFactories + + ", elseVal=" + + elseValueFactory + + ']'; + } + }; } record ConditionEvaluatorSupplier(ExpressionEvaluator.Factory condition, ExpressionEvaluator.Factory value) @@ -176,6 +192,11 @@ record ConditionEvaluatorSupplier(ExpressionEvaluator.Factory condition, Express public ConditionEvaluator apply(DriverContext driverContext) { return new ConditionEvaluator(condition.get(driverContext), value.get(driverContext)); } + + @Override + public String toString() { + return "ConditionEvaluator[" + "condition=" + condition + ", value=" + value + ']'; + } } record ConditionEvaluator(EvalOperator.ExpressionEvaluator condition, EvalOperator.ExpressionEvaluator value) implements Releasable { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Greatest.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Greatest.java index ae2a28f5d0907..948e44f946920 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Greatest.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Greatest.java @@ -9,16 +9,11 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.Param; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMaxBooleanEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMaxBytesRefEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMaxDoubleEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMaxIntEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMaxLongEvaluator; +import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMax; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.Expressions; import org.elasticsearch.xpack.ql.expression.TypeResolutions; @@ -111,42 +106,20 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var suppliers = children().stream().map(toEvaluator).toList(); + ExpressionEvaluator.Factory[] factories = children().stream() + .map(e -> toEvaluator.apply(new MvMax(e.source(), e))) + .toArray(ExpressionEvaluator.Factory[]::new); if (dataType == DataTypes.BOOLEAN) { - return dvrCtx -> new GreatestBooleanEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMaxBooleanEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new GreatestBooleanEvaluator.Factory(factories); } if (dataType == DataTypes.DOUBLE) { - return dvrCtx -> new GreatestDoubleEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMaxDoubleEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new GreatestDoubleEvaluator.Factory(factories); } if (dataType == DataTypes.INTEGER) { - return dvrCtx -> new GreatestIntEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMaxIntEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new GreatestIntEvaluator.Factory(factories); } if (dataType == DataTypes.LONG) { - return dvrCtx -> new GreatestLongEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMaxLongEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new GreatestLongEvaluator.Factory(factories); } if (dataType == DataTypes.KEYWORD || dataType == DataTypes.TEXT @@ -154,13 +127,7 @@ public ExpressionEvaluator.Factory toEvaluator(Function new GreatestBytesRefEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMaxBytesRefEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new GreatestBytesRefEvaluator.Factory(factories); } throw EsqlIllegalArgumentException.illegalDataType(dataType); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Least.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Least.java index 1219ae83b318c..f3b15a9f1f7eb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Least.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Least.java @@ -9,16 +9,11 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.Param; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMinBooleanEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMinBytesRefEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMinDoubleEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMinIntEvaluator; -import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMinLongEvaluator; +import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMin; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.Expressions; import org.elasticsearch.xpack.ql.expression.TypeResolutions; @@ -111,42 +106,20 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var suppliers = children().stream().map(toEvaluator).toList(); + ExpressionEvaluator.Factory[] factories = children().stream() + .map(e -> toEvaluator.apply(new MvMin(e.source(), e))) + .toArray(ExpressionEvaluator.Factory[]::new); if (dataType == DataTypes.BOOLEAN) { - return dvrCtx -> new LeastBooleanEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMinBooleanEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new LeastBooleanEvaluator.Factory(factories); } if (dataType == DataTypes.DOUBLE) { - return dvrCtx -> new LeastDoubleEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMinDoubleEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new LeastDoubleEvaluator.Factory(factories); } if (dataType == DataTypes.INTEGER) { - return dvrCtx -> new LeastIntEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMinIntEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new LeastIntEvaluator.Factory(factories); } if (dataType == DataTypes.LONG) { - return dvrCtx -> new LeastLongEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMinLongEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new LeastLongEvaluator.Factory(factories); } if (dataType == DataTypes.KEYWORD || dataType == DataTypes.TEXT @@ -154,13 +127,7 @@ public ExpressionEvaluator.Factory toEvaluator(Function new LeastBytesRefEvaluator( - suppliers.stream() - .map(es -> es.get(dvrCtx)) - .map(ev -> new MvMinBytesRefEvaluator(ev, dvrCtx)) - .toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + return new LeastBytesRefEvaluator.Factory(factories); } throw EsqlIllegalArgumentException.illegalDataType(dataType); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java index 291506ce5afb3..b8913c6d8a85d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java @@ -9,7 +9,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.data.Vector; @@ -45,11 +44,11 @@ protected AbstractConvertFunction(Source source, Expression field) { */ protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { DataType sourceType = field().dataType(); - var evaluator = evaluators().get(sourceType); - if (evaluator == null) { + var factory = factories().get(sourceType); + if (factory == null) { throw EsqlIllegalArgumentException.illegalDataType(sourceType); } - return dvrCtx -> evaluator.apply(fieldEval.get(dvrCtx), source(), dvrCtx); + return factory.build(fieldEval, source()); } @Override @@ -59,14 +58,19 @@ protected final TypeResolution resolveType() { } return isType( field(), - evaluators()::containsKey, + factories()::containsKey, sourceText(), null, - evaluators().keySet().stream().map(dt -> dt.name().toLowerCase(Locale.ROOT)).sorted().toArray(String[]::new) + factories().keySet().stream().map(dt -> dt.name().toLowerCase(Locale.ROOT)).sorted().toArray(String[]::new) ); } - protected abstract Map> evaluators(); + @FunctionalInterface + interface BuildFactory { + ExpressionEvaluator.Factory build(ExpressionEvaluator.Factory field, Source source); + } + + protected abstract Map factories(); @Override public final Object fold() { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java index 701b3fa67732c..442c106042fa0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToBoolean.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; @@ -31,31 +28,21 @@ public class ToBoolean extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - BOOLEAN, - (fieldEval, source, driverContext) -> fieldEval, - KEYWORD, - ToBooleanFromStringEvaluator::new, - DOUBLE, - ToBooleanFromDoubleEvaluator::new, - LONG, - ToBooleanFromLongEvaluator::new, - UNSIGNED_LONG, - ToBooleanFromUnsignedLongEvaluator::new, - INTEGER, - ToBooleanFromIntEvaluator::new - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(BOOLEAN, (field, source) -> field), + Map.entry(KEYWORD, ToBooleanFromStringEvaluator.Factory::new), + Map.entry(DOUBLE, ToBooleanFromDoubleEvaluator.Factory::new), + Map.entry(LONG, ToBooleanFromLongEvaluator.Factory::new), + Map.entry(UNSIGNED_LONG, ToBooleanFromUnsignedLongEvaluator.Factory::new), + Map.entry(INTEGER, ToBooleanFromIntEvaluator.Factory::new) + ); public ToBoolean(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java index eb23e460b88ff..d73cb59308be7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetime.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.scalar.date.DateParse; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -30,31 +27,21 @@ public class ToDatetime extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - DATETIME, - (fieldEval, source, driverContext) -> fieldEval, - LONG, - (fieldEval, source, driverContext) -> fieldEval, - KEYWORD, - ToDatetimeFromStringEvaluator::new, - DOUBLE, - ToLongFromDoubleEvaluator::new, - UNSIGNED_LONG, - ToLongFromUnsignedLongEvaluator::new, - INTEGER, - ToLongFromIntEvaluator::new // CastIntToLongEvaluator would be a candidate, but not MV'd - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(DATETIME, (field, source) -> field), + Map.entry(LONG, (field, source) -> field), + Map.entry(KEYWORD, ToDatetimeFromStringEvaluator.Factory::new), + Map.entry(DOUBLE, ToLongFromDoubleEvaluator.Factory::new), + Map.entry(UNSIGNED_LONG, ToLongFromUnsignedLongEvaluator.Factory::new), + Map.entry(INTEGER, ToLongFromIntEvaluator.Factory::new) // CastIntToLongEvaluator would be a candidate, but not MV'd + ); public ToDatetime(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java index 299e8cfe8643e..6b0d638e875a0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegrees.java @@ -7,10 +7,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -30,39 +27,22 @@ * to degrees. */ public class ToDegrees extends AbstractConvertFunction implements EvaluatorMapper { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - DOUBLE, - ToDegreesEvaluator::new, - INTEGER, - (field, source, driverContext) -> new ToDegreesEvaluator( - new ToDoubleFromIntEvaluator(field, source, driverContext), - source, - driverContext - ), - LONG, - (field, source, driverContext) -> new ToDegreesEvaluator( - new ToDoubleFromLongEvaluator(field, source, driverContext), - source, - driverContext - ), + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(DOUBLE, ToDegreesEvaluator.Factory::new), + Map.entry(INTEGER, (field, source) -> new ToDegreesEvaluator.Factory(new ToDoubleFromIntEvaluator.Factory(field, source), source)), + Map.entry(LONG, (field, source) -> new ToDegreesEvaluator.Factory(new ToDoubleFromLongEvaluator.Factory(field, source), source)), + Map.entry( UNSIGNED_LONG, - (field, source, driverContext) -> new ToDegreesEvaluator( - new ToDoubleFromUnsignedLongEvaluator(field, source, driverContext), - source, - driverContext - ) - ); + (field, source) -> new ToDegreesEvaluator.Factory(new ToDoubleFromUnsignedLongEvaluator.Factory(field, source), source) + ) + ); public ToDegrees(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java index 690f7a66cbece..9972ae1d3dd81 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDouble.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; @@ -31,33 +28,22 @@ public class ToDouble extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - DOUBLE, - (fieldEval, source, driverContext) -> fieldEval, - BOOLEAN, - ToDoubleFromBooleanEvaluator::new, - DATETIME, - ToDoubleFromLongEvaluator::new, // CastLongToDoubleEvaluator would be a candidate, but not MV'd - KEYWORD, - ToDoubleFromStringEvaluator::new, - UNSIGNED_LONG, - ToDoubleFromUnsignedLongEvaluator::new, - LONG, - ToDoubleFromLongEvaluator::new, // CastLongToDoubleEvaluator would be a candidate, but not MV'd - INTEGER, - ToDoubleFromIntEvaluator::new // CastIntToDoubleEvaluator would be a candidate, but not MV'd - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(DOUBLE, (fieldEval, source) -> fieldEval), + Map.entry(BOOLEAN, ToDoubleFromBooleanEvaluator.Factory::new), + Map.entry(DATETIME, ToDoubleFromLongEvaluator.Factory::new), // CastLongToDoubleEvaluator would be a candidate, but not MV'd + Map.entry(KEYWORD, ToDoubleFromStringEvaluator.Factory::new), + Map.entry(UNSIGNED_LONG, ToDoubleFromUnsignedLongEvaluator.Factory::new), + Map.entry(LONG, ToDoubleFromLongEvaluator.Factory::new), // CastLongToDoubleEvaluator would be a candidate, but not MV'd + Map.entry(INTEGER, ToDoubleFromIntEvaluator.Factory::new) // CastIntToDoubleEvaluator would be a candidate, but not MV'd + ); public ToDouble(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java index d55b9d23975e1..07c0bfedb98c9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIP.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; @@ -26,23 +23,17 @@ public class ToIP extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - IP, - (fieldEval, source, driverContext) -> fieldEval, - KEYWORD, - ToIPFromStringEvaluator::new - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(IP, (field, source) -> field), + Map.entry(KEYWORD, ToIPFromStringEvaluator.Factory::new) + ); public ToIP(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java index 0fcf62ed3864a..3f3b492095949 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToInteger.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; @@ -32,33 +29,22 @@ public class ToInteger extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - INTEGER, - (fieldEval, source, driverContext) -> fieldEval, - BOOLEAN, - ToIntegerFromBooleanEvaluator::new, - DATETIME, - ToIntegerFromLongEvaluator::new, - KEYWORD, - ToIntegerFromStringEvaluator::new, - DOUBLE, - ToIntegerFromDoubleEvaluator::new, - UNSIGNED_LONG, - ToIntegerFromUnsignedLongEvaluator::new, - LONG, - ToIntegerFromLongEvaluator::new - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(INTEGER, (fieldEval, source) -> fieldEval), + Map.entry(BOOLEAN, ToIntegerFromBooleanEvaluator.Factory::new), + Map.entry(DATETIME, ToIntegerFromLongEvaluator.Factory::new), + Map.entry(KEYWORD, ToIntegerFromStringEvaluator.Factory::new), + Map.entry(DOUBLE, ToIntegerFromDoubleEvaluator.Factory::new), + Map.entry(UNSIGNED_LONG, ToIntegerFromUnsignedLongEvaluator.Factory::new), + Map.entry(LONG, ToIntegerFromLongEvaluator.Factory::new) + ); public ToInteger(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java index 8e50dd8540ffd..e7f60abc6c3d4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLong.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; @@ -33,33 +30,22 @@ public class ToLong extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - LONG, - (fieldEval, source, driverContext) -> fieldEval, - DATETIME, - (fieldEval, source, driverContext) -> fieldEval, - BOOLEAN, - ToLongFromBooleanEvaluator::new, - KEYWORD, - ToLongFromStringEvaluator::new, - DOUBLE, - ToLongFromDoubleEvaluator::new, - UNSIGNED_LONG, - ToLongFromUnsignedLongEvaluator::new, - INTEGER, - ToLongFromIntEvaluator::new // CastIntToLongEvaluator would be a candidate, but not MV'd - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(LONG, (fieldEval, source) -> fieldEval), + Map.entry(DATETIME, (fieldEval, source) -> fieldEval), + Map.entry(BOOLEAN, ToLongFromBooleanEvaluator.Factory::new), + Map.entry(KEYWORD, ToLongFromStringEvaluator.Factory::new), + Map.entry(DOUBLE, ToLongFromDoubleEvaluator.Factory::new), + Map.entry(UNSIGNED_LONG, ToLongFromUnsignedLongEvaluator.Factory::new), + Map.entry(INTEGER, ToLongFromIntEvaluator.Factory::new) // CastIntToLongEvaluator would be a candidate, but not MV'd + ); public ToLong(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java index 8bb5180e09752..9f39015a8e063 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadians.java @@ -7,10 +7,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -30,39 +27,22 @@ * to radians. */ public class ToRadians extends AbstractConvertFunction implements EvaluatorMapper { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - DOUBLE, - ToRadiansEvaluator::new, - INTEGER, - (field, source, driverContext) -> new ToRadiansEvaluator( - new ToDoubleFromIntEvaluator(field, source, driverContext), - source, - driverContext - ), - LONG, - (field, source, driverContext) -> new ToRadiansEvaluator( - new ToDoubleFromLongEvaluator(field, source, driverContext), - source, - driverContext - ), + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(DOUBLE, ToRadiansEvaluator.Factory::new), + Map.entry(INTEGER, (field, source) -> new ToRadiansEvaluator.Factory(new ToDoubleFromIntEvaluator.Factory(field, source), source)), + Map.entry(LONG, (field, source) -> new ToRadiansEvaluator.Factory(new ToDoubleFromLongEvaluator.Factory(field, source), source)), + Map.entry( UNSIGNED_LONG, - (field, source, driverContext) -> new ToRadiansEvaluator( - new ToDoubleFromUnsignedLongEvaluator(field, source, driverContext), - source, - driverContext - ) - ); + (field, source) -> new ToRadiansEvaluator.Factory(new ToDoubleFromUnsignedLongEvaluator.Factory(field, source), source) + ) + ); public ToRadians(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java index 89d1b43ace0dd..98118162e742d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToString.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.Param; @@ -39,30 +36,18 @@ public class ToString extends AbstractConvertFunction implements EvaluatorMapper { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - KEYWORD, - (fieldEval, source, driverContext) -> fieldEval, - BOOLEAN, - ToStringFromBooleanEvaluator::new, - DATETIME, - ToStringFromDatetimeEvaluator::new, - IP, - ToStringFromIPEvaluator::new, - DOUBLE, - ToStringFromDoubleEvaluator::new, - LONG, - ToStringFromLongEvaluator::new, - INTEGER, - ToStringFromIntEvaluator::new, - TEXT, - (fieldEval, source, driverContext) -> fieldEval, - VERSION, - ToStringFromVersionEvaluator::new, - UNSIGNED_LONG, - ToStringFromUnsignedLongEvaluator::new - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(KEYWORD, (fieldEval, source) -> fieldEval), + Map.entry(BOOLEAN, ToStringFromBooleanEvaluator.Factory::new), + Map.entry(DATETIME, ToStringFromDatetimeEvaluator.Factory::new), + Map.entry(IP, ToStringFromIPEvaluator.Factory::new), + Map.entry(DOUBLE, ToStringFromDoubleEvaluator.Factory::new), + Map.entry(LONG, ToStringFromLongEvaluator.Factory::new), + Map.entry(INTEGER, ToStringFromIntEvaluator.Factory::new), + Map.entry(TEXT, (fieldEval, source) -> fieldEval), + Map.entry(VERSION, ToStringFromVersionEvaluator.Factory::new), + Map.entry(UNSIGNED_LONG, ToStringFromUnsignedLongEvaluator.Factory::new) + ); public ToString( Source source, @@ -75,9 +60,7 @@ public ToString( } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java index 396aa03f39dc6..be96fdb7139d1 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLong.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; @@ -34,33 +31,22 @@ public class ToUnsignedLong extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.of( - UNSIGNED_LONG, - (fieldEval, source, driverContext) -> fieldEval, - DATETIME, - ToUnsignedLongFromLongEvaluator::new, - BOOLEAN, - ToUnsignedLongFromBooleanEvaluator::new, - KEYWORD, - ToUnsignedLongFromStringEvaluator::new, - DOUBLE, - ToUnsignedLongFromDoubleEvaluator::new, - LONG, - ToUnsignedLongFromLongEvaluator::new, - INTEGER, - ToUnsignedLongFromIntEvaluator::new - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(UNSIGNED_LONG, (fieldEval, source) -> fieldEval), + Map.entry(DATETIME, ToUnsignedLongFromLongEvaluator.Factory::new), + Map.entry(BOOLEAN, ToUnsignedLongFromBooleanEvaluator.Factory::new), + Map.entry(KEYWORD, ToUnsignedLongFromStringEvaluator.Factory::new), + Map.entry(DOUBLE, ToUnsignedLongFromDoubleEvaluator.Factory::new), + Map.entry(LONG, ToUnsignedLongFromLongEvaluator.Factory::new), + Map.entry(INTEGER, ToUnsignedLongFromIntEvaluator.Factory::new) + ); public ToUnsignedLong(Source source, Expression field) { super(source, field); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java index d652792ea9819..ad7712f33d947 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToVersion.java @@ -8,10 +8,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.convert; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.ConvertEvaluator; -import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -28,22 +25,18 @@ public class ToVersion extends AbstractConvertFunction { - private static final Map< - DataType, - TriFunction> EVALUATORS = Map.ofEntries( - Map.entry(VERSION, (fieldEval, source, driverContext) -> fieldEval), - Map.entry(KEYWORD, ToVersionFromStringEvaluator::new), - Map.entry(TEXT, ToVersionFromStringEvaluator::new) - ); + private static final Map EVALUATORS = Map.ofEntries( + Map.entry(VERSION, (fieldEval, source) -> fieldEval), + Map.entry(KEYWORD, ToVersionFromStringEvaluator.Factory::new), + Map.entry(TEXT, ToVersionFromStringEvaluator.Factory::new) + ); public ToVersion(Source source, @Param(name = "v", type = { "keyword", "text", "version" }) Expression v) { super(source, v); } @Override - protected - Map> - evaluators() { + protected Map factories() { return EVALUATORS; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java index 1b33d5829e472..af1a536787398 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java @@ -52,16 +52,10 @@ public ExpressionEvaluator.Factory toEvaluator(Function new DateExtractConstantEvaluator(fieldEvaluator.get(dvrCtx), chrono, configuration().zoneId(), dvrCtx); + return new DateExtractConstantEvaluator.Factory(fieldEvaluator, chrono, configuration().zoneId()); } var chronoEvaluator = toEvaluator.apply(children().get(0)); - return dvrCtx -> new DateExtractEvaluator( - source(), - fieldEvaluator.get(dvrCtx), - chronoEvaluator.get(dvrCtx), - configuration().zoneId(), - dvrCtx - ); + return new DateExtractEvaluator.Factory(source(), fieldEvaluator, chronoEvaluator, configuration().zoneId()); } private ChronoField chronoField() { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParse.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParse.java index 60985b80986c4..4e014690288f6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParse.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParse.java @@ -104,7 +104,7 @@ public ExpressionEvaluator.Factory toEvaluator(Function new DateParseConstantEvaluator(source(), fieldEvaluator.get(dvrCtx), DEFAULT_FORMATTER, dvrCtx); + return new DateParseConstantEvaluator.Factory(source(), fieldEvaluator, DEFAULT_FORMATTER); } if (format.dataType() != DataTypes.KEYWORD) { throw new IllegalArgumentException("unsupported data type for date_parse [" + format.dataType() + "]"); @@ -112,13 +112,13 @@ public ExpressionEvaluator.Factory toEvaluator(Function new DateParseConstantEvaluator(source(), fieldEvaluator.get(dvrCtx), formatter, dvrCtx); + return new DateParseConstantEvaluator.Factory(source(), fieldEvaluator, formatter); } catch (IllegalArgumentException e) { throw new EsqlIllegalArgumentException(e, "invalid date pattern for [{}]: {}", sourceText(), e.getMessage()); } } ExpressionEvaluator.Factory formatEvaluator = toEvaluator.apply(format); - return dvrCtx -> new DateParseEvaluator(source(), fieldEvaluator.get(dvrCtx), formatEvaluator.get(dvrCtx), zone, dvrCtx); + return new DateParseEvaluator.Factory(source(), fieldEvaluator, formatEvaluator, zone); } private static DateFormatter toFormatter(Object format, ZoneId zone) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java index 55885bf514fe2..4ef2504bd7fc8 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java @@ -166,6 +166,6 @@ public ExpressionEvaluator.Factory toEvaluator(Function new DateTruncEvaluator(fieldEvaluator.get(dvrCtx), rounding, dvrCtx); + return new DateTruncEvaluator.Factory(fieldEvaluator, rounding); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java index ea4fd3820319b..f686630838313 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java @@ -50,16 +50,16 @@ static int process(int fieldVal) { public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { var field = toEvaluator.apply(field()); if (dataType() == DataTypes.DOUBLE) { - return dvrCtx -> new AbsDoubleEvaluator(field.get(dvrCtx), dvrCtx); + return new AbsDoubleEvaluator.Factory(field); } if (dataType() == DataTypes.UNSIGNED_LONG) { return field; } if (dataType() == DataTypes.LONG) { - return dvrCtx -> new AbsLongEvaluator(field.get(dvrCtx), dvrCtx); + return new AbsLongEvaluator.Factory(field); } if (dataType() == DataTypes.INTEGER) { - return dvrCtx -> new AbsIntEvaluator(field.get(dvrCtx), dvrCtx); + return new AbsIntEvaluator.Factory(field); } throw EsqlIllegalArgumentException.illegalDataType(dataType()); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbstractTrigonometricFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbstractTrigonometricFunction.java index 400118a1f7edf..08a842e8b9fd7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbstractTrigonometricFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbstractTrigonometricFunction.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; @@ -30,12 +29,11 @@ abstract class AbstractTrigonometricFunction extends UnaryScalarFunction impleme super(source, field); } - protected abstract EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrDtx); + protected abstract EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field); @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var fieldEval = Cast.cast(field().dataType(), DataTypes.DOUBLE, toEvaluator.apply(field())); - return dvrCtx -> doubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); + return doubleEvaluator(Cast.cast(field().dataType(), DataTypes.DOUBLE, toEvaluator.apply(field()))); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java index 8484af1ff738e..05c3414aee188 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Acos(Source source, @Param(name = "n", type = { "integer", "long", "doubl } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new AcosEvaluator(source(), field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new AcosEvaluator.Factory(source(), field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java index 7a87bca7d942c..66e6f2654742f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Asin(Source source, @Param(name = "n", type = { "integer", "long", "doubl } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new AsinEvaluator(source(), field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new AsinEvaluator.Factory(source(), field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java index 02631e3f39f31..99f8e35974d2d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Atan(Source source, @Param(name = "n", type = { "integer", "long", "doubl } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new AtanEvaluator(field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new AtanEvaluator.Factory(field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java index c861bb30ef3f6..204bdbcdfa183 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java @@ -85,7 +85,7 @@ public boolean foldable() { public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { var yEval = Cast.cast(y.dataType(), DataTypes.DOUBLE, toEvaluator.apply(y)); var xEval = Cast.cast(x.dataType(), DataTypes.DOUBLE, toEvaluator.apply(x)); - return dvrCtx -> new Atan2Evaluator(yEval.get(dvrCtx), xEval.get(dvrCtx), dvrCtx); + return new Atan2Evaluator.Factory(yEval, xEval); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java index 4179b7d0f750d..91e4bbf5eae94 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cast.java @@ -26,31 +26,31 @@ public static ExpressionEvaluator.Factory cast(DataType current, DataType requir return in; } if (current == DataTypes.NULL || required == DataTypes.NULL) { - return dvrCtx -> EvalOperator.CONSTANT_NULL; + return EvalOperator.CONSTANT_NULL_FACTORY; } if (required == DataTypes.DOUBLE) { if (current == DataTypes.LONG) { - return dvrCtx -> new CastLongToDoubleEvaluator(in.get(dvrCtx), dvrCtx); + return new CastLongToDoubleEvaluator.Factory(in); } if (current == DataTypes.INTEGER) { - return dvrCtx -> new CastIntToDoubleEvaluator(in.get(dvrCtx), dvrCtx); + return new CastIntToDoubleEvaluator.Factory(in); } if (current == DataTypes.UNSIGNED_LONG) { - return dvrCtx -> new CastUnsignedLongToDoubleEvaluator(in.get(dvrCtx), dvrCtx); + return new CastUnsignedLongToDoubleEvaluator.Factory(in); } throw cantCast(current, required); } if (required == DataTypes.UNSIGNED_LONG) { if (current == DataTypes.LONG) { - return dvrCtx -> new CastLongToUnsignedLongEvaluator(in.get(dvrCtx), dvrCtx); + return new CastLongToUnsignedLongEvaluator.Factory(in); } if (current == DataTypes.INTEGER) { - return dvrCtx -> new CastIntToUnsignedLongEvaluator(in.get(dvrCtx), dvrCtx); + return new CastIntToUnsignedLongEvaluator.Factory(in); } } if (required == DataTypes.LONG) { if (current == DataTypes.INTEGER) { - return dvrCtx -> new CastIntToLongEvaluator(in.get(dvrCtx), dvrCtx); + return new CastIntToLongEvaluator.Factory(in); } throw cantCast(current, required); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Ceil.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Ceil.java index 2569e4fae6035..d9b9089795103 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Ceil.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Ceil.java @@ -40,7 +40,7 @@ public ExpressionEvaluator.Factory toEvaluator(Function new CeilDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); + return new CeilDoubleEvaluator.Factory(fieldEval); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java index 327f99fbebd13..7b193752dc97a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Cos(Source source, @Param(name = "n", type = { "integer", "long", "double } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new CosEvaluator(field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new CosEvaluator.Factory(field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java index bbf1a77182c54..b6df7e1260624 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Cosh(Source source, @Param(name = "n", type = { "integer", "long", "doubl } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new CoshEvaluator(source(), field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new CoshEvaluator.Factory(source(), field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Floor.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Floor.java index f30bf79a74fbb..98e72b7dedb61 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Floor.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Floor.java @@ -39,8 +39,7 @@ public ExpressionEvaluator.Factory toEvaluator(Function new FloorDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); + return new FloorDoubleEvaluator.Factory(toEvaluator.apply(field())); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFinite.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFinite.java index 4d73516f2399c..19d080647b374 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFinite.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFinite.java @@ -24,7 +24,7 @@ public IsFinite(Source source, Expression field) { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { var field = toEvaluator.apply(field()); - return dvrCtx -> new IsFiniteEvaluator(field.get(dvrCtx), dvrCtx); + return new IsFiniteEvaluator.Factory(field); } @Evaluator diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfinite.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfinite.java index 0fb65d14eee04..70e8137d8871e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfinite.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfinite.java @@ -23,8 +23,7 @@ public IsInfinite(Source source, Expression field) { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var field = toEvaluator.apply(field()); - return dvrCtx -> new IsInfiniteEvaluator(field.get(dvrCtx), dvrCtx); + return new IsInfiniteEvaluator.Factory(toEvaluator.apply(field())); } @Evaluator diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaN.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaN.java index 1b44158d9e8fc..4db5534631fc9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaN.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaN.java @@ -23,8 +23,7 @@ public IsNaN(Source source, Expression field) { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var field = toEvaluator.apply(field()); - return dvrCtx -> new IsNaNEvaluator(field.get(dvrCtx), dvrCtx); + return new IsNaNEvaluator.Factory(toEvaluator.apply(field())); } @Evaluator diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10.java index 89bcc40d59ff4..84bc9d19b409e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10.java @@ -37,16 +37,16 @@ public ExpressionEvaluator.Factory toEvaluator(Function new Log10DoubleEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new Log10DoubleEvaluator.Factory(source(), field); } if (fieldType == DataTypes.INTEGER) { - return dvrCtx -> new Log10IntEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new Log10IntEvaluator.Factory(source(), field); } if (fieldType == DataTypes.LONG) { - return dvrCtx -> new Log10LongEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new Log10LongEvaluator.Factory(source(), field); } if (fieldType == DataTypes.UNSIGNED_LONG) { - return dvrCtx -> new Log10UnsignedLongEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new Log10UnsignedLongEvaluator.Factory(source(), field); } throw EsqlIllegalArgumentException.illegalDataType(fieldType); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Pow.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Pow.java index 0049d02a74b2c..48db81fefbc98 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Pow.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Pow.java @@ -172,25 +172,22 @@ public ExpressionEvaluator.Factory toEvaluator(Function new PowDoubleEvaluator( + return new PowDoubleEvaluator.Factory( source(), - cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator).get(dvrCtx), - cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator).get(dvrCtx), - dvrCtx + cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator), + cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator) ); } else if (dataType == DataTypes.LONG) { - return dvrCtx -> new PowLongEvaluator( + return new PowLongEvaluator.Factory( source(), - cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator).get(dvrCtx), - cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator).get(dvrCtx), - dvrCtx + cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator), + cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator) ); } else { - return dvrCtx -> new PowIntEvaluator( + return new PowIntEvaluator.Factory( source(), - cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator).get(dvrCtx), - cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator).get(dvrCtx), - dvrCtx + cast(base.dataType(), DataTypes.DOUBLE, baseEvaluator), + cast(exponent.dataType(), DataTypes.DOUBLE, exponentEvaluator) ); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Round.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Round.java index 7736148ea8f9b..4e1d12606a34f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Round.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Round.java @@ -7,9 +7,7 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; -import org.elasticsearch.common.TriFunction; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; @@ -136,35 +134,31 @@ public ScriptTemplate asScript() { public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { DataType fieldType = dataType(); if (fieldType == DataTypes.DOUBLE) { - return toEvaluator(toEvaluator, RoundDoubleNoDecimalsEvaluator::new, RoundDoubleEvaluator::new); + return toEvaluator(toEvaluator, RoundDoubleNoDecimalsEvaluator.Factory::new, RoundDoubleEvaluator.Factory::new); } if (fieldType == DataTypes.INTEGER) { - return toEvaluator(toEvaluator, identity(), RoundIntEvaluator::new); + return toEvaluator(toEvaluator, Function.identity(), RoundIntEvaluator.Factory::new); } if (fieldType == DataTypes.LONG) { - return toEvaluator(toEvaluator, identity(), RoundLongEvaluator::new); + return toEvaluator(toEvaluator, Function.identity(), RoundLongEvaluator.Factory::new); } if (fieldType == DataTypes.UNSIGNED_LONG) { - return toEvaluator(toEvaluator, identity(), RoundUnsignedLongEvaluator::new); + return toEvaluator(toEvaluator, Function.identity(), RoundUnsignedLongEvaluator.Factory::new); } throw EsqlIllegalArgumentException.illegalDataType(fieldType); } - private static BiFunction identity() { - return (t, u) -> t; - } - private ExpressionEvaluator.Factory toEvaluator( Function toEvaluator, - BiFunction noDecimals, - TriFunction withDecimals + Function noDecimals, + BiFunction withDecimals ) { var fieldEvaluator = toEvaluator.apply(field()); if (decimals == null) { - return dvrCtx -> noDecimals.apply(fieldEvaluator.get(dvrCtx), dvrCtx); + return noDecimals.apply(fieldEvaluator); } var decimalsEvaluator = Cast.cast(decimals().dataType(), DataTypes.LONG, toEvaluator.apply(decimals())); - return dvrCtx -> withDecimals.apply(fieldEvaluator.get(dvrCtx), decimalsEvaluator.get(dvrCtx), dvrCtx); + return withDecimals.apply(fieldEvaluator, decimalsEvaluator); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sin.java index 4be75f853b91f..eaf632ee8c40e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sin.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; @@ -32,8 +31,8 @@ public Sin( } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new SinEvaluator(field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new SinEvaluator.Factory(field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java index d6f62a52704b6..e55e607f9a09a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Sinh(Source source, @Param(name = "n", type = { "integer", "long", "doubl } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new SinhEvaluator(source(), field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new SinhEvaluator.Factory(source(), field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sqrt.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sqrt.java index 29888db439dcf..dc9e3bc2b3fde 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sqrt.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sqrt.java @@ -37,16 +37,16 @@ public ExpressionEvaluator.Factory toEvaluator(Function new SqrtDoubleEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new SqrtDoubleEvaluator.Factory(source(), field); } if (fieldType == DataTypes.INTEGER) { - return dvrCtx -> new SqrtIntEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new SqrtIntEvaluator.Factory(source(), field); } if (fieldType == DataTypes.LONG) { - return dvrCtx -> new SqrtLongEvaluator(source(), field.get(dvrCtx), dvrCtx); + return new SqrtLongEvaluator.Factory(source(), field); } if (fieldType == DataTypes.UNSIGNED_LONG) { - return dvrCtx -> new SqrtUnsignedLongEvaluator(field.get(dvrCtx), dvrCtx); + return new SqrtUnsignedLongEvaluator.Factory(field); } throw EsqlIllegalArgumentException.illegalDataType(fieldType); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java index bffdaf440e40e..2fee1f3e41850 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Tan(Source source, @Param(name = "n", type = { "integer", "long", "double } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new TanEvaluator(field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new TanEvaluator.Factory(field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java index 1079ca5e7f914..af48285192ccf 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.math; import org.elasticsearch.compute.ann.Evaluator; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; @@ -26,8 +25,8 @@ public Tanh(Source source, @Param(name = "n", type = { "integer", "long", "doubl } @Override - protected EvalOperator.ExpressionEvaluator doubleEvaluator(EvalOperator.ExpressionEvaluator field, DriverContext dvrCtx) { - return new TanhEvaluator(field, dvrCtx); + protected EvalOperator.ExpressionEvaluator.Factory doubleEvaluator(EvalOperator.ExpressionEvaluator.Factory field) { + return new TanhEvaluator.Factory(field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvg.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvg.java index 7930af6b25d8c..0a6a5d50ee552 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvg.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAvg.java @@ -46,12 +46,12 @@ public DataType dataType() { @Override protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { return switch (LocalExecutionPlanner.toElementType(field().dataType())) { - case DOUBLE -> dvrCtx -> new MvAvgDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case INT -> dvrCtx -> new MvAvgIntEvaluator(fieldEval.get(dvrCtx), dvrCtx); + case DOUBLE -> new MvAvgDoubleEvaluator.Factory(fieldEval); + case INT -> new MvAvgIntEvaluator.Factory(fieldEval); case LONG -> field().dataType() == DataTypes.UNSIGNED_LONG - ? dvrCtx -> new MvAvgUnsignedLongEvaluator(fieldEval.get(dvrCtx), dvrCtx) - : dvrCtx -> new MvAvgLongEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case NULL -> dvrCtx -> EvalOperator.CONSTANT_NULL; + ? new MvAvgUnsignedLongEvaluator.Factory(fieldEval) + : new MvAvgLongEvaluator.Factory(fieldEval); + case NULL -> EvalOperator.CONSTANT_NULL_FACTORY; default -> throw EsqlIllegalArgumentException.illegalDataType(field.dataType()); }; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java index 8ba7db4c6b551..0cb97e28b0053 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java @@ -13,7 +13,6 @@ import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; @@ -67,9 +66,7 @@ public DataType dataType() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var fieldEval = toEvaluator.apply(left()); - var delimEval = toEvaluator.apply(right()); - return dvrCtx -> new MvConcatEvaluator(dvrCtx, fieldEval.get(dvrCtx), delimEval.get(dvrCtx)); + return new EvaluatorFactory(toEvaluator.apply(left()), toEvaluator.apply(right())); } @Override @@ -87,6 +84,20 @@ protected NodeInfo info() { return NodeInfo.create(this, MvConcat::new, left(), right()); } + private record EvaluatorFactory(ExpressionEvaluator.Factory field, ExpressionEvaluator.Factory delim) + implements + ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new Evaluator(context, field.get(context), delim.get(context)); + } + + @Override + public String toString() { + return "MvConcat[field=" + field + ", delim=" + delim + "]"; + } + } + /** * Evaluator for {@link MvConcat}. Not generated and doesn't extend from * {@link AbstractMultivalueFunction.AbstractEvaluator} because it's just @@ -97,12 +108,12 @@ protected NodeInfo info() { *

  • The actual joining process needs init step per row - {@link BytesRefBuilder#clear()}
  • * */ - private class MvConcatEvaluator implements EvalOperator.ExpressionEvaluator { + private static class Evaluator implements ExpressionEvaluator { private final DriverContext context; - private final EvalOperator.ExpressionEvaluator field; - private final EvalOperator.ExpressionEvaluator delim; + private final ExpressionEvaluator field; + private final ExpressionEvaluator delim; - MvConcatEvaluator(DriverContext context, EvalOperator.ExpressionEvaluator field, EvalOperator.ExpressionEvaluator delim) { + Evaluator(DriverContext context, ExpressionEvaluator field, ExpressionEvaluator delim) { this.context = context; this.field = field; this.delim = delim; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java index 0fdf1d8d80b77..528c0b6d5f0cb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java @@ -56,7 +56,7 @@ public DataType dataType() { @Override protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { - return dvrCtx -> new Evaluator(dvrCtx, fieldEval.get(dvrCtx)); + return new EvaluatorFactory(fieldEval); } @Override @@ -69,6 +69,18 @@ protected NodeInfo info() { return NodeInfo.create(this, MvCount::new, field()); } + private record EvaluatorFactory(ExpressionEvaluator.Factory field) implements ExpressionEvaluator.Factory { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new Evaluator(context, field.get(context)); + } + + @Override + public String toString() { + return "MvCount[field=" + field + ']'; + } + } + private static class Evaluator extends AbstractEvaluator { private final DriverContext driverContext; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java index 3ed9be9667ab9..cee1d533b4332 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java @@ -47,12 +47,12 @@ protected TypeResolution resolveFieldType() { @Override protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { return switch (LocalExecutionPlanner.toElementType(field().dataType())) { - case BOOLEAN -> dvrCtx -> new MvMaxBooleanEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case BYTES_REF -> dvrCtx -> new MvMaxBytesRefEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case DOUBLE -> dvrCtx -> new MvMaxDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case INT -> dvrCtx -> new MvMaxIntEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case LONG -> dvrCtx -> new MvMaxLongEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case NULL -> dvrCtx -> EvalOperator.CONSTANT_NULL; + case BOOLEAN -> new MvMaxBooleanEvaluator.Factory(fieldEval); + case BYTES_REF -> new MvMaxBytesRefEvaluator.Factory(fieldEval); + case DOUBLE -> new MvMaxDoubleEvaluator.Factory(fieldEval); + case INT -> new MvMaxIntEvaluator.Factory(fieldEval); + case LONG -> new MvMaxLongEvaluator.Factory(fieldEval); + case NULL -> EvalOperator.CONSTANT_NULL_FACTORY; default -> throw EsqlIllegalArgumentException.illegalDataType(field.dataType()); }; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedian.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedian.java index edd68b1a45a37..e10cbdd86a072 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedian.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedian.java @@ -45,11 +45,11 @@ protected TypeResolution resolveFieldType() { @Override protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { return switch (LocalExecutionPlanner.toElementType(field().dataType())) { - case DOUBLE -> dvrCtx -> new MvMedianDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case INT -> dvrCtx -> new MvMedianIntEvaluator(fieldEval.get(dvrCtx), dvrCtx); + case DOUBLE -> new MvMedianDoubleEvaluator.Factory(fieldEval); + case INT -> new MvMedianIntEvaluator.Factory(fieldEval); case LONG -> field().dataType() == DataTypes.UNSIGNED_LONG - ? dvrCtx -> new MvMedianUnsignedLongEvaluator(fieldEval.get(dvrCtx), dvrCtx) - : dvrCtx -> new MvMedianLongEvaluator(fieldEval.get(dvrCtx), dvrCtx); + ? new MvMedianUnsignedLongEvaluator.Factory(fieldEval) + : new MvMedianLongEvaluator.Factory(fieldEval); default -> throw EsqlIllegalArgumentException.illegalDataType(field.dataType()); }; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java index 3678bf532271e..18b452f9c7040 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java @@ -47,12 +47,12 @@ protected TypeResolution resolveFieldType() { @Override protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { return switch (LocalExecutionPlanner.toElementType(field().dataType())) { - case BOOLEAN -> dvrCtx -> new MvMinBooleanEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case BYTES_REF -> dvrCtx -> new MvMinBytesRefEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case DOUBLE -> dvrCtx -> new MvMinDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case INT -> dvrCtx -> new MvMinIntEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case LONG -> dvrCtx -> new MvMinLongEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case NULL -> dvrCtx -> EvalOperator.CONSTANT_NULL; + case BOOLEAN -> new MvMinBooleanEvaluator.Factory(fieldEval); + case BYTES_REF -> new MvMinBytesRefEvaluator.Factory(fieldEval); + case DOUBLE -> new MvMinDoubleEvaluator.Factory(fieldEval); + case INT -> new MvMinIntEvaluator.Factory(fieldEval); + case LONG -> new MvMinLongEvaluator.Factory(fieldEval); + case NULL -> EvalOperator.CONSTANT_NULL_FACTORY; default -> throw EsqlIllegalArgumentException.illegalDataType(field.dataType()); }; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSum.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSum.java index 858842cd78721..f543a8ec3878b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSum.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSum.java @@ -40,11 +40,11 @@ protected TypeResolution resolveFieldType() { @Override protected ExpressionEvaluator.Factory evaluator(ExpressionEvaluator.Factory fieldEval) { return switch (LocalExecutionPlanner.toElementType(field().dataType())) { - case DOUBLE -> dvrCtx -> new MvSumDoubleEvaluator(fieldEval.get(dvrCtx), dvrCtx); - case INT -> dvrCtx -> new MvSumIntEvaluator(source(), fieldEval.get(dvrCtx), dvrCtx); + case DOUBLE -> new MvSumDoubleEvaluator.Factory(fieldEval); + case INT -> new MvSumIntEvaluator.Factory(source(), fieldEval); case LONG -> field().dataType() == DataTypes.UNSIGNED_LONG - ? dvrCtx -> new MvSumUnsignedLongEvaluator(source(), fieldEval.get(dvrCtx), dvrCtx) - : dvrCtx -> new MvSumLongEvaluator(source(), fieldEval.get(dvrCtx), dvrCtx); + ? new MvSumUnsignedLongEvaluator.Factory(source(), fieldEval) + : new MvSumLongEvaluator.Factory(source(), fieldEval); case NULL -> dvrCtx -> EvalOperator.CONSTANT_NULL; default -> throw EsqlIllegalArgumentException.illegalDataType(field.dataType()); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java index 93086317be45c..d0fe387d680db 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java @@ -121,11 +121,22 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - return dvrCxt -> new CoalesceEvaluator( - dvrCxt, - LocalExecutionPlanner.toElementType(dataType()), - children().stream().map(toEvaluator).map(x -> x.get(dvrCxt)).toList() - ); + List childEvaluators = children().stream().map(toEvaluator).toList(); + return new ExpressionEvaluator.Factory() { + @Override + public ExpressionEvaluator get(DriverContext context) { + return new CoalesceEvaluator( + context, + LocalExecutionPlanner.toElementType(dataType()), + childEvaluators.stream().map(x -> x.get(context)).toList() + ); + } + + @Override + public String toString() { + return "CoalesceEvaluator[values=" + childEvaluators + ']'; + } + }; } private record CoalesceEvaluator(DriverContext driverContext, ElementType resultType, List evaluators) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java index c987513d5919e..1e84bf60b0dde 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Concat.java @@ -11,7 +11,6 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.ann.Fixed; import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; -import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xpack.esql.EsqlClientException; @@ -79,16 +78,12 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var values = children().stream().map(toEvaluator).toList(); - return dvrCtx -> new ConcatEvaluator( - new BreakingBytesRefBuilder(dvrCtx.breaker(), "concat"), - values.stream().map(fac -> fac.get(dvrCtx)).toArray(EvalOperator.ExpressionEvaluator[]::new), - dvrCtx - ); + var values = children().stream().map(toEvaluator).toArray(ExpressionEvaluator.Factory[]::new); + return new ConcatEvaluator.Factory(context -> new BreakingBytesRefBuilder(context.breaker(), "concat"), values); } @Evaluator - static BytesRef process(@Fixed(includeInToString = false) BreakingBytesRefBuilder scratch, BytesRef[] values) { + static BytesRef process(@Fixed(includeInToString = false, build = true) BreakingBytesRefBuilder scratch, BytesRef[] values) { scratch.grow(checkedTotalLength(values)); scratch.clear(); for (int i = 0; i < values.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWith.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWith.java index 367d5323f28b8..1140bfcf1f5d9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWith.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWith.java @@ -98,8 +98,6 @@ public ScriptTemplate asScript() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var strEval = toEvaluator.apply(str); - var suffixEval = toEvaluator.apply(suffix); - return dvrCtx -> new EndsWithEvaluator(strEval.get(dvrCtx), suffixEval.get(dvrCtx), dvrCtx); + return new EndsWithEvaluator.Factory(toEvaluator.apply(str), toEvaluator.apply(suffix)); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrim.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrim.java index ffb5a3543f3f2..952c3314af80a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrim.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrim.java @@ -47,8 +47,7 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var field = toEvaluator.apply(field()); - return dvrCtx -> new LTrimEvaluator(field.get(dvrCtx), dvrCtx); + return new LTrimEvaluator.Factory(toEvaluator.apply(field())); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Left.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Left.java index 73024bd74e624..14cb03943f520 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Left.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Left.java @@ -55,8 +55,8 @@ public Left( @Evaluator static BytesRef process( - @Fixed(includeInToString = false) BytesRef out, - @Fixed(includeInToString = false) UnicodeUtil.UTF8CodePoint cp, + @Fixed(includeInToString = false, build = true) BytesRef out, + @Fixed(includeInToString = false, build = true) UnicodeUtil.UTF8CodePoint cp, BytesRef str, int length ) { @@ -73,13 +73,12 @@ static BytesRef process( @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var strSupplier = toEvaluator.apply(str); - var lengthSupplier = toEvaluator.apply(length); - return dvrCtx -> { - BytesRef out = new BytesRef(); - UnicodeUtil.UTF8CodePoint cp = new UnicodeUtil.UTF8CodePoint(); - return new LeftEvaluator(out, cp, strSupplier.get(dvrCtx), lengthSupplier.get(dvrCtx), dvrCtx); - }; + return new LeftEvaluator.Factory( + context -> new BytesRef(), + context -> new UnicodeUtil.UTF8CodePoint(), + toEvaluator.apply(str), + toEvaluator.apply(length) + ); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Length.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Length.java index e0a1a8ed297a6..47ee8f20e7f32 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Length.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Length.java @@ -72,7 +72,6 @@ protected NodeInfo info() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var field = toEvaluator.apply(field()); - return dvrCtx -> new LengthEvaluator(field.get(dvrCtx), dvrCtx); + return new LengthEvaluator.Factory(toEvaluator.apply(field())); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrim.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrim.java index 46c8d43f0a5a7..273a032a90ed3 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrim.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrim.java @@ -47,8 +47,7 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var field = toEvaluator.apply(field()); - return dvrCtx -> new RTrimEvaluator(field.get(dvrCtx), dvrCtx); + return new RTrimEvaluator.Factory(toEvaluator.apply(field())); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Replace.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Replace.java index 99d44b534ac26..0ed4bd0fe7d02 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Replace.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Replace.java @@ -128,10 +128,10 @@ public ExpressionEvaluator.Factory toEvaluator(Function new ReplaceConstantEvaluator(source(), strEval.get(drvCtx), regexPattern, newStrEval.get(drvCtx), drvCtx); + return new ReplaceConstantEvaluator.Factory(source(), strEval, regexPattern, newStrEval); } var regexEval = toEvaluator.apply(regex); - return (drvCtx) -> new ReplaceEvaluator(source(), strEval.get(drvCtx), regexEval.get(drvCtx), newStrEval.get(drvCtx), drvCtx); + return new ReplaceEvaluator.Factory(source(), strEval, regexEval, newStrEval); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Right.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Right.java index 3b79b6683f16f..f77c703e7cb0c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Right.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Right.java @@ -55,8 +55,8 @@ public Right( @Evaluator static BytesRef process( - @Fixed(includeInToString = false) BytesRef out, - @Fixed(includeInToString = false) UnicodeUtil.UTF8CodePoint cp, + @Fixed(includeInToString = false, build = true) BytesRef out, + @Fixed(includeInToString = false, build = true) UnicodeUtil.UTF8CodePoint cp, BytesRef str, int length ) { @@ -77,13 +77,12 @@ static BytesRef process( @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var strSupplier = toEvaluator.apply(str); - var lengthSupplier = toEvaluator.apply(length); - return dvrCtx -> { - BytesRef out = new BytesRef(); - UnicodeUtil.UTF8CodePoint cp = new UnicodeUtil.UTF8CodePoint(); - return new RightEvaluator(out, cp, strSupplier.get(dvrCtx), lengthSupplier.get(dvrCtx), dvrCtx); - }; + return new RightEvaluator.Factory( + context -> new BytesRef(), + context -> new UnicodeUtil.UTF8CodePoint(), + toEvaluator.apply(str), + toEvaluator.apply(length) + ); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Split.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Split.java index 75d2ada1ca97b..7f18be0e7b18e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Split.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Split.java @@ -70,7 +70,7 @@ static void process( BytesRefBlock.Builder builder, BytesRef str, @Fixed byte delim, - @Fixed(includeInToString = false) BytesRef scratch + @Fixed(includeInToString = false, build = true) BytesRef scratch ) { scratch.bytes = str.bytes; scratch.offset = str.offset; @@ -96,7 +96,12 @@ static void process( } @Evaluator(extraName = "Variable") - static void process(BytesRefBlock.Builder builder, BytesRef str, BytesRef delim, @Fixed(includeInToString = false) BytesRef scratch) { + static void process( + BytesRefBlock.Builder builder, + BytesRef str, + BytesRef delim, + @Fixed(includeInToString = false, build = true) BytesRef scratch + ) { if (delim.length != 1) { throw new QlIllegalArgumentException("delimiter must be single byte for now"); } @@ -117,13 +122,12 @@ protected NodeInfo info() { public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { var str = toEvaluator.apply(left()); if (right().foldable() == false) { - var delim = toEvaluator.apply(right()); - return dvrCtx -> new SplitVariableEvaluator(str.get(dvrCtx), delim.get(dvrCtx), new BytesRef(), dvrCtx); + return new SplitVariableEvaluator.Factory(str, toEvaluator.apply(right()), context -> new BytesRef()); } BytesRef delim = (BytesRef) right().fold(); if (delim.length != 1) { throw new QlIllegalArgumentException("for now delimiter must be a single byte"); } - return dvrCtx -> new SplitSingleByteEvaluator(str.get(dvrCtx), delim.bytes[delim.offset], new BytesRef(), dvrCtx); + return new SplitSingleByteEvaluator.Factory(str, delim.bytes[delim.offset], context -> new BytesRef()); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWith.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWith.java index 8d147f3cf9caf..3497d9360b187 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWith.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWith.java @@ -91,8 +91,6 @@ public ScriptTemplate asScript() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var strEval = toEvaluator.apply(str); - var prefixEval = toEvaluator.apply(prefix); - return dvrCtx -> new StartsWithEvaluator(strEval.get(dvrCtx), prefixEval.get(dvrCtx), dvrCtx); + return new StartsWithEvaluator.Factory(toEvaluator.apply(str), toEvaluator.apply(prefix)); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Substring.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Substring.java index 9b8a6c6aa1720..261b7aeb19da2 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Substring.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Substring.java @@ -132,12 +132,12 @@ public ScriptTemplate asScript() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { - var strSupplier = toEvaluator.apply(str); - var startSupplier = toEvaluator.apply(start); + var strFactory = toEvaluator.apply(str); + var startFactory = toEvaluator.apply(start); if (length == null) { - return dvrCtx -> new SubstringNoLengthEvaluator(strSupplier.get(dvrCtx), startSupplier.get(dvrCtx), dvrCtx); + return new SubstringNoLengthEvaluator.Factory(strFactory, startFactory); } - var lengthSupplier = toEvaluator.apply(length); - return dvrCtx -> new SubstringEvaluator(strSupplier.get(dvrCtx), startSupplier.get(dvrCtx), lengthSupplier.get(dvrCtx), dvrCtx); + var lengthFactory = toEvaluator.apply(length); + return new SubstringEvaluator.Factory(strFactory, startFactory, lengthFactory); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Trim.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Trim.java index f9d5febd5fc02..b865199c1c2ae 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Trim.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Trim.java @@ -49,7 +49,7 @@ public Object fold() { @Override public ExpressionEvaluator.Factory toEvaluator(Function toEvaluator) { var field = toEvaluator.apply(field()); - return dvrCtx -> new TrimEvaluator(field.get(dvrCtx), dvrCtx); + return new TrimEvaluator.Factory(field); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Add.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Add.java index f59211ab42882..1e1da2634fadf 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Add.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Add.java @@ -32,11 +32,11 @@ public Add(Source source, Expression left, Expression right) { left, right, ADD, - AddIntsEvaluator::new, - AddLongsEvaluator::new, - AddUnsignedLongsEvaluator::new, - (s, l, r, dvrCtx) -> new AddDoublesEvaluator(l, r, dvrCtx), - AddDatetimesEvaluator::new + AddIntsEvaluator.Factory::new, + AddLongsEvaluator.Factory::new, + AddUnsignedLongsEvaluator.Factory::new, + (s, lhs, rhs) -> new AddDoublesEvaluator.Factory(lhs, rhs), + AddDatetimesEvaluator.Factory::new ); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java index af827522b136e..0132301cb79b5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.ExceptionUtils; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; @@ -31,12 +30,7 @@ abstract class DateTimeArithmeticOperation extends EsqlArithmeticOperation { /** Arithmetic (quad) function. */ interface DatetimeArithmeticEvaluator { - ExpressionEvaluator apply( - Source source, - ExpressionEvaluator expressionEvaluator, - TemporalAmount temporalAmount, - DriverContext driverContext - ); + ExpressionEvaluator.Factory apply(Source source, ExpressionEvaluator.Factory expressionEvaluator, TemporalAmount temporalAmount); } private final DatetimeArithmeticEvaluator datetimes; @@ -149,12 +143,7 @@ public ExpressionEvaluator.Factory toEvaluator(Function datetimes.apply( - source(), - toEvaluator.apply(datetimeArgument).get(dvrCtx), - (TemporalAmount) temporalAmountArgument.fold(), - dvrCtx - ); + return datetimes.apply(source(), toEvaluator.apply(datetimeArgument), (TemporalAmount) temporalAmountArgument.fold()); } else { return super.toEvaluator(toEvaluator); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Div.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Div.java index 5a89e24eb6007..0bcbe21c60a63 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Div.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Div.java @@ -31,10 +31,10 @@ public Div(Source source, Expression left, Expression right, DataType type) { left, right, DIV, - DivIntsEvaluator::new, - DivLongsEvaluator::new, - DivUnsignedLongsEvaluator::new, - (s, l, r, dvrCtx) -> new DivDoublesEvaluator(l, r, dvrCtx) + DivIntsEvaluator.Factory::new, + DivLongsEvaluator.Factory::new, + DivUnsignedLongsEvaluator.Factory::new, + (s, lhs, rhs) -> new DivDoublesEvaluator.Factory(lhs, rhs) ); this.type = type; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/EsqlArithmeticOperation.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/EsqlArithmeticOperation.java index d09ae25d91746..dc5be3373198b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/EsqlArithmeticOperation.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/EsqlArithmeticOperation.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; @@ -71,12 +70,7 @@ public String symbol() { /** Arithmetic (quad) function. */ interface ArithmeticEvaluator { - ExpressionEvaluator apply( - Source source, - ExpressionEvaluator expressionEvaluator1, - ExpressionEvaluator expressionEvaluator2, - DriverContext driverContext - ); + ExpressionEvaluator.Factory apply(Source source, ExpressionEvaluator.Factory lhs, ExpressionEvaluator.Factory rhs); } private final ArithmeticEvaluator ints; @@ -121,8 +115,8 @@ public ExpressionEvaluator.Factory toEvaluator(Function eval.apply(source(), l.get(dvrCtx), r.get(dvrCtx), dvrCtx); + return eval.apply(source(), lhs, rhs); } throw new EsqlIllegalArgumentException("Unsupported type " + leftType); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mod.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mod.java index f1aaeb1adaf14..85510a2f63c33 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mod.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mod.java @@ -23,10 +23,10 @@ public Mod(Source source, Expression left, Expression right) { left, right, MOD, - ModIntsEvaluator::new, - ModLongsEvaluator::new, - ModUnsignedLongsEvaluator::new, - (s, l, r, dvrCtx) -> new ModDoublesEvaluator(l, r, dvrCtx) + ModIntsEvaluator.Factory::new, + ModLongsEvaluator.Factory::new, + ModUnsignedLongsEvaluator.Factory::new, + (s, lhs, rhs) -> new ModDoublesEvaluator.Factory(lhs, rhs) ); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mul.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mul.java index 9b42cfce182b9..963f09486a361 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mul.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Mul.java @@ -27,10 +27,10 @@ public Mul(Source source, Expression left, Expression right) { left, right, MUL, - MulIntsEvaluator::new, - MulLongsEvaluator::new, - MulUnsignedLongsEvaluator::new, - (s, l, r, dvrCtx) -> new MulDoublesEvaluator(l, r, dvrCtx) + MulIntsEvaluator.Factory::new, + MulLongsEvaluator.Factory::new, + MulUnsignedLongsEvaluator.Factory::new, + (s, lhs, rhs) -> new MulDoublesEvaluator.Factory(lhs, rhs) ); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java index 97a0323829d59..2ad5c5b9de5b5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Neg.java @@ -46,20 +46,20 @@ public ExpressionEvaluator.Factory toEvaluator(Function new NegIntsEvaluator(source(), f.get(dvrCtx), dvrCtx); + factory = new NegIntsEvaluator.Factory(source(), f); } // Unsigned longs are unsupported by choice; negating them would require implicitly converting to long. else if (type == DataTypes.LONG) { - supplier = dvrCtx -> new NegLongsEvaluator(source(), f.get(dvrCtx), dvrCtx); + factory = new NegLongsEvaluator.Factory(source(), f); } else if (type == DataTypes.DOUBLE) { - supplier = dvrCtx -> new NegDoublesEvaluator(f.get(dvrCtx), dvrCtx); + factory = new NegDoublesEvaluator.Factory(f); } - if (supplier != null) { - return supplier; + if (factory != null) { + return factory; } } else if (isTemporalAmount(type)) { return toEvaluator.apply(field()); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Sub.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Sub.java index ba071c05a15a8..d7999c87c4398 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Sub.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/Sub.java @@ -35,11 +35,11 @@ public Sub(Source source, Expression left, Expression right) { left, right, SUB, - SubIntsEvaluator::new, - SubLongsEvaluator::new, - SubUnsignedLongsEvaluator::new, - (s, l, r, dvrCtx) -> new SubDoublesEvaluator(l, r, dvrCtx), - SubDatetimesEvaluator::new + SubIntsEvaluator.Factory::new, + SubLongsEvaluator.Factory::new, + SubUnsignedLongsEvaluator.Factory::new, + (s, lhs, rhs) -> new SubDoublesEvaluator.Factory(lhs, rhs), + SubDatetimesEvaluator.Factory::new ); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 3adb40152d51c..8079fb5bb70f7 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -458,12 +458,19 @@ public final void testEvaluateInManyThreads() throws ExecutionException, Interru public final void testEvaluatorToString() { assumeTrue("nothing to do if a type error", testCase.getExpectedTypeError() == null); assumeTrue("All test data types must be representable in order to build fields", testCase.allTypesAreRepresentable()); - var supplier = evaluator(buildFieldExpression(testCase)); - try (ExpressionEvaluator ev = supplier.get(driverContext())) { + var factory = evaluator(buildFieldExpression(testCase)); + try (ExpressionEvaluator ev = factory.get(driverContext())) { assertThat(ev.toString(), equalTo(testCase.evaluatorToString)); } } + public final void testFactoryToString() { + assumeTrue("nothing to do if a type error", testCase.getExpectedTypeError() == null); + assumeTrue("All test data types must be representable in order to build fields", testCase.allTypesAreRepresentable()); + var factory = evaluator(buildFieldExpression(testCase)); + assertThat(factory.toString(), equalTo(testCase.evaluatorToString)); + } + public final void testFold() { Expression expression = buildLiteralExpression(testCase); if (testCase.getExpectedTypeError() != null) { From 4ca793ec1abeb986f8d1302884b8d64fd8dcdcbb Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 25 Oct 2023 14:05:09 -0400 Subject: [PATCH 110/203] ESQL: Load values a different way (#101235) This changes how we load values in ESQL, delegating to the `MappedFieldType` like we do with doc values and synthetic source. This allows a much more OO way of getting the loads working which makes that path much easier to read. And! It means those code paths look like doc values. So there's symmetry. It's like it rhymes. There are a few side effects here: 1. It's fairly simple to load from ordinals efficiently. I wrote some block-at-a-time code for resolving ordinals and it's about twice as fast. With more work it should be possible to make custom ordinal-shaped blocks move through the system to save space and speed things up. 2. Most fields can now be loaded from `_source`. Everything that can be loaded from `_source` in scripts will load from `_source` in ESQL. 3. We get a *lot* more tests for loading fields in different configurations by piggybacking on the synthetic source testing framework. 4. Loading from `_source` no longer sorts the fields. Same for stored fields. Now we keep them in whatever they were stored in. This is a pretty marginal time save because loading from `_source` is so much more time consuming than the sort. But it's something. --- .../operator/ValuesSourceReaderBenchmark.java | 95 +- docs/changelog/101235.yaml | 5 + .../extras/MatchOnlyTextFieldMapper.java | 11 + .../mapper/extras/ScaledFloatFieldMapper.java | 16 + .../extras/MatchOnlyTextFieldMapperTests.java | 7 + .../extras/ScaledFloatFieldMapperTests.java | 24 +- .../fielddata/IndexFieldDataService.java | 6 +- .../index/mapper/BlockDocValuesReader.java | 857 ++++++++++++++++++ .../index/mapper/BlockLoader.java | 191 ++++ .../index/mapper/BlockSourceReader.java | 198 ++++ .../index/mapper/BlockStoredFieldsReader.java | 152 ++++ .../index/mapper/BooleanFieldMapper.java | 8 + .../BooleanScriptBlockDocValuesReader.java | 77 ++ .../index/mapper/BooleanScriptFieldType.java | 5 + .../index/mapper/DateFieldMapper.java | 8 + .../DateScriptBlockDocValuesReader.java | 72 ++ .../index/mapper/DateScriptFieldType.java | 5 + .../DoubleScriptBlockDocValuesReader.java | 72 ++ .../index/mapper/DoubleScriptFieldType.java | 5 + .../index/mapper/IndexFieldMapper.java | 40 + .../index/mapper/IpFieldMapper.java | 8 + .../mapper/IpScriptBlockDocValuesReader.java | 74 ++ .../index/mapper/IpScriptFieldType.java | 5 + .../index/mapper/KeywordFieldMapper.java | 18 + .../KeywordScriptBlockDocValuesReader.java | 78 ++ .../index/mapper/KeywordScriptFieldType.java | 5 + .../LongScriptBlockDocValuesReader.java | 72 ++ .../index/mapper/LongScriptFieldType.java | 5 + .../index/mapper/MappedFieldType.java | 31 + .../index/mapper/NumberFieldMapper.java | 86 ++ .../index/mapper/ProvidedIdFieldMapper.java | 5 + .../index/mapper/TextFieldMapper.java | 24 + .../mapper/TsidExtractingIdFieldMapper.java | 5 + .../index/mapper/VersionFieldMapper.java | 5 + .../index/mapper/BooleanFieldMapperTests.java | 7 + .../mapper/BooleanScriptFieldTypeTests.java | 13 + .../index/mapper/DateFieldMapperTests.java | 18 +- .../mapper/DateScriptFieldTypeTests.java | 12 + .../mapper/DoubleScriptFieldTypeTests.java | 12 + .../index/mapper/FloatFieldMapperTests.java | 10 + .../mapper/HalfFloatFieldMapperTests.java | 12 + .../index/mapper/IpFieldMapperTests.java | 7 + .../index/mapper/IpScriptFieldTypeTests.java | 18 + .../index/mapper/KeywordFieldMapperTests.java | 26 +- .../mapper/KeywordScriptFieldTypeTests.java | 15 + .../index/mapper/LongFieldMapperTests.java | 12 + .../mapper/LongScriptFieldTypeTests.java | 12 + .../index/mapper/NumberFieldMapperTests.java | 26 +- .../index/mapper/TextFieldMapperTests.java | 9 +- .../AbstractScriptFieldTypeTestCase.java | 49 + .../index/mapper/MapperTestCase.java | 128 ++- .../elasticsearch/index/mapper/TestBlock.java | 199 ++++ .../compute/data/BooleanBlock.java | 11 +- .../compute/data/BytesRefBlock.java | 11 +- .../compute/data/DoubleBlock.java | 11 +- .../elasticsearch/compute/data/IntBlock.java | 11 +- .../elasticsearch/compute/data/LongBlock.java | 11 +- .../org/elasticsearch/compute/data/Block.java | 3 +- .../data/SingletonOrdinalsBuilder.java | 147 +++ .../compute/data/X-Block.java.st | 11 +- .../compute/lucene/BlockDocValuesReader.java | 698 -------------- .../compute/lucene/BlockReaderFactories.java | 108 +++ .../compute/lucene/NullValueSource.java | 53 -- .../compute/lucene/NullValueSourceType.java | 50 - .../compute/lucene/ValueSourceInfo.java | 15 - .../compute/lucene/ValueSources.java | 200 ---- .../lucene/ValuesSourceReaderOperator.java | 118 ++- .../operator/OrdinalsGroupingOperator.java | 59 +- .../exchange/ExchangeSourceHandler.java | 4 +- .../elasticsearch/compute/OperatorTests.java | 70 +- .../data/SingletonOrdinalsBuilderTests.java | 140 +++ .../lucene/LuceneSourceOperatorTests.java | 11 +- .../lucene/LuceneTopNSourceOperatorTests.java | 11 +- .../ValuesSourceReaderOperatorTests.java | 90 +- .../esql/qa/server/single-node/build.gradle | 2 +- .../resources/rest-api-spec/test/30_types.yml | 2 +- .../resources/rest-api-spec/test/40_tsdb.yml | 2 +- .../rest-api-spec/test/90_non_indexed.yml | 24 +- .../xpack/esql/action/EsqlActionIT.java | 15 +- .../esql/action/EsqlActionRuntimeFieldIT.java | 3 +- .../xpack/esql/action/EsqlActionTaskIT.java | 2 +- .../xpack/esql/action/SyntheticSourceIT.java | 68 +- .../esql/enrich/EnrichLookupService.java | 7 +- .../planner/EsPhysicalOperationProviders.java | 26 +- .../mapper/ConstantKeywordFieldMapper.java | 41 + .../ConstantKeywordFieldMapperTests.java | 7 + .../unsignedlong/UnsignedLongFieldMapper.java | 23 + .../UnsignedLongFieldMapperTests.java | 15 + .../VersionStringFieldMapper.java | 8 + .../VersionStringFieldMapperTests.java | 7 + .../wildcard/mapper/WildcardFieldMapper.java | 12 + .../mapper/WildcardFieldMapperTests.java | 16 +- 92 files changed, 3595 insertions(+), 1388 deletions(-) create mode 100644 docs/changelog/101235.yaml create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java create mode 100644 server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java create mode 100644 test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java create mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilder.java delete mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java create mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockReaderFactories.java delete mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSource.java delete mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSourceType.java delete mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSourceInfo.java delete mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java create mode 100644 x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilderTests.java diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java index 9c527923fae02..1827babe6f091 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java @@ -24,28 +24,19 @@ import org.elasticsearch.compute.data.DocVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; -import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.LongVector; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.lucene.BlockReaderFactories; import org.elasticsearch.compute.lucene.LuceneSourceOperator; -import org.elasticsearch.compute.lucene.ValueSourceInfo; import org.elasticsearch.compute.lucene.ValuesSourceReaderOperator; import org.elasticsearch.compute.operator.topn.TopNOperator; import org.elasticsearch.core.IOUtils; -import org.elasticsearch.index.fielddata.FieldData; -import org.elasticsearch.index.fielddata.IndexFieldDataCache; -import org.elasticsearch.index.fielddata.IndexNumericFieldData; -import org.elasticsearch.index.fielddata.plain.SortedDoublesIndexFieldData; -import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData; -import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; +import org.elasticsearch.index.mapper.BlockLoader; import org.elasticsearch.index.mapper.KeywordFieldMapper; -import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.script.field.KeywordDocValuesField; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; -import org.elasticsearch.search.aggregations.support.FieldContext; +import org.elasticsearch.index.mapper.NumberFieldMapper; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -101,54 +92,18 @@ public class ValuesSourceReaderBenchmark { } } - private static ValueSourceInfo info(IndexReader reader, String name) { + private static BlockLoader blockLoader(String name) { return switch (name) { - case "long" -> numericInfo(reader, name, IndexNumericFieldData.NumericType.LONG, ElementType.LONG); - case "int" -> numericInfo(reader, name, IndexNumericFieldData.NumericType.INT, ElementType.INT); - case "double" -> { - SortedDoublesIndexFieldData fd = new SortedDoublesIndexFieldData( - name, - IndexNumericFieldData.NumericType.DOUBLE, - CoreValuesSourceType.NUMERIC, - null - ); - FieldContext context = new FieldContext(name, fd, null); - yield new ValueSourceInfo( - CoreValuesSourceType.NUMERIC, - CoreValuesSourceType.NUMERIC.getField(context, null), - ElementType.DOUBLE, - reader - ); - } - case "keyword" -> { - SortedSetOrdinalsIndexFieldData fd = new SortedSetOrdinalsIndexFieldData( - new IndexFieldDataCache.None(), - "keyword", - CoreValuesSourceType.KEYWORD, - new NoneCircuitBreakerService(), - (dv, n) -> new KeywordDocValuesField(FieldData.toString(dv), n) - ); - FieldContext context = new FieldContext(name, fd, null); - yield new ValueSourceInfo( - CoreValuesSourceType.KEYWORD, - CoreValuesSourceType.KEYWORD.getField(context, null), - ElementType.BYTES_REF, - reader - ); - } + case "long" -> numericBlockLoader(name, NumberFieldMapper.NumberType.LONG); + case "int" -> numericBlockLoader(name, NumberFieldMapper.NumberType.INTEGER); + case "double" -> numericBlockLoader(name, NumberFieldMapper.NumberType.DOUBLE); + case "keyword" -> new KeywordFieldMapper.KeywordFieldType(name).blockLoader(null); default -> throw new IllegalArgumentException("can't read [" + name + "]"); }; } - private static ValueSourceInfo numericInfo( - IndexReader reader, - String name, - IndexNumericFieldData.NumericType numericType, - ElementType elementType - ) { - SortedNumericIndexFieldData fd = new SortedNumericIndexFieldData(name, numericType, CoreValuesSourceType.NUMERIC, null); - FieldContext context = new FieldContext(name, fd, null); - return new ValueSourceInfo(CoreValuesSourceType.NUMERIC, CoreValuesSourceType.NUMERIC.getField(context, null), elementType, reader); + private static BlockLoader numericBlockLoader(String name, NumberFieldMapper.NumberType numberType) { + return new NumberFieldMapper.NumberFieldType(name, numberType).blockLoader(null); } /** @@ -176,7 +131,11 @@ private static ValueSourceInfo numericInfo( @Benchmark @OperationsPerInvocation(INDEX_SIZE) public void benchmark() { - ValuesSourceReaderOperator op = new ValuesSourceReaderOperator(List.of(info(reader, name)), 0, name); + ValuesSourceReaderOperator op = new ValuesSourceReaderOperator( + List.of(BlockReaderFactories.loaderToFactory(reader, blockLoader(name))), + 0, + name + ); long sum = 0; for (Page page : pages) { op.addInput(page); @@ -203,13 +162,24 @@ public void benchmark() { BytesRef scratch = new BytesRef(); BytesRefVector values = op.getOutput().getBlock(1).asVector(); for (int p = 0; p < values.getPositionCount(); p++) { - sum += Integer.parseInt(values.getBytesRef(p, scratch).utf8ToString()); + BytesRef r = values.getBytesRef(p, scratch); + r.offset++; + r.length--; + sum += Integer.parseInt(r.utf8ToString()); } } } } - long expected = INDEX_SIZE; - expected = expected * (expected - 1) / 2; + long expected; + if (name.equals("keyword")) { + expected = 0; + for (int i = 0; i < INDEX_SIZE; i++) { + expected += i % 1000; + } + } else { + expected = INDEX_SIZE; + expected = expected * (expected - 1) / 2; + } if (expected != sum) { throw new AssertionError("[" + layout + "][" + name + "] expected [" + expected + "] but was [" + sum + "]"); } @@ -225,16 +195,13 @@ private void setupIndex() throws IOException { directory = new ByteBuffersDirectory(); try (IndexWriter iw = new IndexWriter(directory, new IndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE))) { for (int i = 0; i < INDEX_SIZE; i++) { + String c = Character.toString('a' - ((i % 1000) % 26) + 26); iw.addDocument( List.of( new NumericDocValuesField("long", i), new NumericDocValuesField("int", i), new NumericDocValuesField("double", NumericUtils.doubleToSortableLong(i)), - new KeywordFieldMapper.KeywordField( - "keyword", - new BytesRef(Integer.toString(i)), - KeywordFieldMapper.Defaults.FIELD_TYPE - ) + new KeywordFieldMapper.KeywordField("keyword", new BytesRef(c + i % 1000), KeywordFieldMapper.Defaults.FIELD_TYPE) ) ); if (i % COMMIT_INTERVAL == 0) { diff --git a/docs/changelog/101235.yaml b/docs/changelog/101235.yaml new file mode 100644 index 0000000000000..53adf9527c2c4 --- /dev/null +++ b/docs/changelog/101235.yaml @@ -0,0 +1,5 @@ +pr: 101235 +summary: Load different way +area: ES|QL +type: enhancement +issues: [] diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java index fa7c61cdd80b6..ee04346591009 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java @@ -39,6 +39,9 @@ import org.elasticsearch.index.fielddata.StoredFieldSortedBinaryIndexFieldData; import org.elasticsearch.index.fieldvisitor.LeafStoredFieldLoader; import org.elasticsearch.index.fieldvisitor.StoredFieldLoader; +import org.elasticsearch.index.mapper.BlockLoader; +import org.elasticsearch.index.mapper.BlockSourceReader; +import org.elasticsearch.index.mapper.BlockStoredFieldsReader; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MapperBuilderContext; @@ -318,6 +321,14 @@ public Query phrasePrefixQuery(TokenStream stream, int slop, int maxExpansions, return toQuery(query, queryShardContext); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (textFieldType.isSyntheticSource()) { + return BlockStoredFieldsReader.bytesRefsFromStrings(storedFieldNameForSyntheticSource()); + } + return BlockSourceReader.bytesRefs(SourceValueFetcher.toString(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { if (fieldDataContext.fielddataOperation() != FielddataOperation.SCRIPT) { diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java index 43951878933fa..abed23621d5e9 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java @@ -30,6 +30,9 @@ import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; import org.elasticsearch.index.fielddata.SourceValueFetcherSortedDoubleIndexFieldData; import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; +import org.elasticsearch.index.mapper.BlockSourceReader; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MapperBuilderContext; @@ -303,6 +306,19 @@ public Query rangeQuery( return NumberFieldMapper.NumberType.LONG.rangeQuery(name(), lo, hi, true, true, hasDocValues(), context, isIndexed()); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (indexMode == IndexMode.TIME_SERIES && metricType == TimeSeriesParams.MetricType.COUNTER) { + // Counters are not supported by ESQL so we load them in null + return BlockDocValuesReader.nulls(); + } + if (hasDocValues()) { + double scalingFactorInverse = 1d / scalingFactor; + return BlockDocValuesReader.doubles(name(), l -> l * scalingFactorInverse); + } + return BlockSourceReader.doubles(sourceValueFetcher(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java index 4c738ddcffce1..e1fbc2e149441 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java @@ -20,6 +20,7 @@ import org.apache.lucene.tests.analysis.CannedTokenStream; import org.apache.lucene.tests.analysis.Token; import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Strings; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.mapper.DocumentMapper; @@ -42,6 +43,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; @@ -261,4 +263,9 @@ public void testDocValuesLoadedFromSynthetic() throws IOException { protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); } + + @Override + protected Function loadBlockExpected() { + return v -> ((BytesRef) v).utf8ToString(); + } } diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java index ad0610732b758..665e9289c3c7d 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java @@ -30,17 +30,20 @@ import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; import org.elasticsearch.xcontent.XContentType; +import org.hamcrest.Matcher; import org.junit.AssumptionViolatedException; import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.function.Function; import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notANumber; public class ScaledFloatFieldMapperTests extends MapperTestCase { @@ -368,13 +371,15 @@ private static class ScaledFloatSyntheticSourceSupport implements SyntheticSourc public SyntheticSourceExample example(int maxValues) { if (randomBoolean()) { Tuple v = generateValue(); - return new SyntheticSourceExample(v.v1(), v.v2(), this::mapping); + return new SyntheticSourceExample(v.v1(), v.v2(), roundDocValues(v.v2()), this::mapping); } List> values = randomList(1, maxValues, this::generateValue); List in = values.stream().map(Tuple::v1).toList(); List outList = values.stream().map(Tuple::v2).sorted().toList(); Object out = outList.size() == 1 ? outList.get(0) : outList; - return new SyntheticSourceExample(in, out, this::mapping); + List outBlockList = values.stream().map(v -> roundDocValues(v.v2())).sorted().toList(); + Object outBlock = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; + return new SyntheticSourceExample(in, out, outBlock, this::mapping); } private Tuple generateValue() { @@ -398,6 +403,11 @@ private double round(double d) { return decoded; } + private double roundDocValues(double d) { + long encoded = Math.round(d * scalingFactor); + return encoded * (1 / scalingFactor); + } + private void mapping(XContentBuilder b) throws IOException { b.field("type", "scaled_float"); b.field("scaling_factor", scalingFactor); @@ -427,6 +437,16 @@ public List invalidExample() throws IOException { } } + @Override + protected Function loadBlockExpected() { + return v -> (Number) v; + } + + @Override + protected Matcher blockItemMatcher(Object expected) { + return "NaN".equals(expected) ? notANumber() : equalTo(expected); + } + @Override protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java b/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java index e4030a0de9d61..27ef30ed67508 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldDataService.java @@ -91,8 +91,12 @@ public synchronized void clearField(final String fieldName) { */ @SuppressWarnings("unchecked") public > IFD getForField(MappedFieldType fieldType, FieldDataContext fieldDataContext) { + return getFromBuilder(fieldType, fieldType.fielddataBuilder(fieldDataContext)); + } + + @SuppressWarnings("unchecked") + public > IFD getFromBuilder(MappedFieldType fieldType, IndexFieldData.Builder builder) { final String fieldName = fieldType.name(); - IndexFieldData.Builder builder = fieldType.fielddataBuilder(fieldDataContext); IndexFieldDataCache cache; synchronized (this) { cache = fieldDataCaches.get(fieldName); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java new file mode 100644 index 0000000000000..049a779c97503 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java @@ -0,0 +1,857 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NumericDocValues; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.index.SortedNumericDocValues; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.UnicodeUtil; +import org.elasticsearch.core.CheckedFunction; +import org.elasticsearch.index.fielddata.SortedBinaryDocValues; +import org.elasticsearch.index.mapper.BlockLoader.BooleanBuilder; +import org.elasticsearch.index.mapper.BlockLoader.Builder; +import org.elasticsearch.index.mapper.BlockLoader.BuilderFactory; +import org.elasticsearch.index.mapper.BlockLoader.BytesRefBuilder; +import org.elasticsearch.index.mapper.BlockLoader.Docs; +import org.elasticsearch.index.mapper.BlockLoader.DoubleBuilder; +import org.elasticsearch.index.mapper.BlockLoader.IntBuilder; +import org.elasticsearch.index.mapper.BlockLoader.LongBuilder; +import org.elasticsearch.index.mapper.BlockLoader.SingletonOrdinalsBuilder; + +import java.io.IOException; + +/** + * A reader that supports reading doc-values from a Lucene segment in Block fashion. + */ +public abstract class BlockDocValuesReader { + public interface Factory { + BlockDocValuesReader build(int segment) throws IOException; + + boolean supportsOrdinals(); + + SortedSetDocValues ordinals(int segment) throws IOException; + } + + protected final Thread creationThread; + + public BlockDocValuesReader() { + this.creationThread = Thread.currentThread(); + } + + /** + * Returns the current doc that this reader is on. + */ + public abstract int docID(); + + /** + * The {@link BlockLoader.Builder} for data of this type. + */ + public abstract Builder builder(BuilderFactory factory, int expectedCount); + + /** + * Reads the values of the given documents specified in the input block + */ + public abstract Builder readValues(BuilderFactory factory, Docs docs) throws IOException; + + /** + * Reads the values of the given document into the builder + */ + public abstract void readValuesFromSingleDoc(int docId, Builder builder) throws IOException; + + /** + * Checks if the reader can be used to read a range documents starting with the given docID by the current thread. + */ + public static boolean canReuse(BlockDocValuesReader reader, int startingDocID) { + return reader != null && reader.creationThread == Thread.currentThread() && reader.docID() <= startingDocID; + } + + public static BlockLoader booleans(String fieldName) { + return context -> { + SortedNumericDocValues docValues = DocValues.getSortedNumeric(context.reader(), fieldName); + NumericDocValues singleton = DocValues.unwrapSingleton(docValues); + if (singleton != null) { + return new SingletonBooleans(singleton); + } + return new Booleans(docValues); + }; + } + + public static BlockLoader bytesRefsFromOrds(String fieldName) { + return new BlockLoader() { + @Override + public BlockDocValuesReader reader(LeafReaderContext context) throws IOException { + SortedSetDocValues docValues = ordinals(context); + SortedDocValues singleton = DocValues.unwrapSingleton(docValues); + if (singleton != null) { + return new SingletonOrdinals(singleton); + } + return new Ordinals(docValues); + } + + @Override + public boolean supportsOrdinals() { + return true; + } + + @Override + public SortedSetDocValues ordinals(LeafReaderContext context) throws IOException { + return DocValues.getSortedSet(context.reader(), fieldName); + } + }; + } + + /** + * Load {@link BytesRef} values from doc values. Prefer {@link #bytesRefsFromOrds} if + * doc values are indexed with ordinals because that's generally much faster. It's + * possible to use this with field data, but generally should be avoided because field + * data has higher per invocation overhead. + */ + public static BlockLoader bytesRefsFromDocValues(CheckedFunction fieldData) { + return context -> new Bytes(fieldData.apply(context)); + } + + /** + * Convert from the stored {@link long} into the {@link double} to load. + * Sadly, this will go megamorphic pretty quickly and slow us down, + * but it gets the job done for now. + */ + public interface ToDouble { + double convert(long v); + } + + /** + * Load {@code double} values from doc values. + */ + public static BlockLoader doubles(String fieldName, ToDouble toDouble) { + return context -> { + SortedNumericDocValues docValues = DocValues.getSortedNumeric(context.reader(), fieldName); + NumericDocValues singleton = DocValues.unwrapSingleton(docValues); + if (singleton != null) { + return new SingletonDoubles(singleton, toDouble); + } + return new Doubles(docValues, toDouble); + }; + } + + /** + * Load {@code int} values from doc values. + */ + public static BlockLoader ints(String fieldName) { + return context -> { + SortedNumericDocValues docValues = DocValues.getSortedNumeric(context.reader(), fieldName); + NumericDocValues singleton = DocValues.unwrapSingleton(docValues); + if (singleton != null) { + return new SingletonInts(singleton); + } + return new Ints(docValues); + }; + } + + /** + * Load a block of {@code long}s from doc values. + */ + public static BlockLoader longs(String fieldName) { + return context -> { + SortedNumericDocValues docValues = DocValues.getSortedNumeric(context.reader(), fieldName); + NumericDocValues singleton = DocValues.unwrapSingleton(docValues); + if (singleton != null) { + return new SingletonLongs(singleton); + } + return new Longs(docValues); + }; + } + + /** + * Load blocks with only null. + */ + public static BlockLoader nulls() { + return context -> new Nulls(); + } + + @Override + public abstract String toString(); + + private static class SingletonLongs extends BlockDocValuesReader { + private final NumericDocValues numericDocValues; + + SingletonLongs(NumericDocValues numericDocValues) { + this.numericDocValues = numericDocValues; + } + + @Override + public LongBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.longsFromDocValues(expectedCount); + } + + @Override + public LongBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (numericDocValues.advanceExact(doc)) { + blockBuilder.appendLong(numericDocValues.longValue()); + } else { + blockBuilder.appendNull(); + } + lastDoc = doc; + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + BlockLoader.LongBuilder blockBuilder = (BlockLoader.LongBuilder) builder; + if (numericDocValues.advanceExact(docId)) { + blockBuilder.appendLong(numericDocValues.longValue()); + } else { + blockBuilder.appendNull(); + } + } + + @Override + public int docID() { + return numericDocValues.docID(); + } + + @Override + public String toString() { + return "SingletonLongs"; + } + } + + private static class Longs extends BlockDocValuesReader { + private final SortedNumericDocValues numericDocValues; + private int docID = -1; + + Longs(SortedNumericDocValues numericDocValues) { + this.numericDocValues = numericDocValues; + } + + @Override + public BlockLoader.LongBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.longsFromDocValues(expectedCount); + } + + @Override + public BlockLoader.LongBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, blockBuilder); + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + read(docId, (LongBuilder) builder); + } + + private void read(int doc, LongBuilder builder) throws IOException { + this.docID = doc; + if (false == numericDocValues.advanceExact(doc)) { + builder.appendNull(); + return; + } + int count = numericDocValues.docValueCount(); + if (count == 1) { + builder.appendLong(numericDocValues.nextValue()); + return; + } + builder.beginPositionEntry(); + for (int v = 0; v < count; v++) { + builder.appendLong(numericDocValues.nextValue()); + } + builder.endPositionEntry(); + } + + @Override + public int docID() { + // There is a .docID on the numericDocValues but it is often not implemented. + return docID; + } + + @Override + public String toString() { + return "Longs"; + } + } + + private static class SingletonInts extends BlockDocValuesReader { + private final NumericDocValues numericDocValues; + + SingletonInts(NumericDocValues numericDocValues) { + this.numericDocValues = numericDocValues; + } + + @Override + public IntBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.intsFromDocValues(expectedCount); + } + + @Override + public IntBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (numericDocValues.advanceExact(doc)) { + blockBuilder.appendInt(Math.toIntExact(numericDocValues.longValue())); + } else { + blockBuilder.appendNull(); + } + lastDoc = doc; + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + IntBuilder blockBuilder = (IntBuilder) builder; + if (numericDocValues.advanceExact(docId)) { + blockBuilder.appendInt(Math.toIntExact(numericDocValues.longValue())); + } else { + blockBuilder.appendNull(); + } + } + + @Override + public int docID() { + return numericDocValues.docID(); + } + + @Override + public String toString() { + return "SingletonInts"; + } + } + + private static class Ints extends BlockDocValuesReader { + private final SortedNumericDocValues numericDocValues; + private int docID = -1; + + Ints(SortedNumericDocValues numericDocValues) { + this.numericDocValues = numericDocValues; + } + + @Override + public IntBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.intsFromDocValues(expectedCount); + } + + @Override + public IntBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, blockBuilder); + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + read(docId, (IntBuilder) builder); + } + + private void read(int doc, IntBuilder builder) throws IOException { + this.docID = doc; + if (false == numericDocValues.advanceExact(doc)) { + builder.appendNull(); + return; + } + int count = numericDocValues.docValueCount(); + if (count == 1) { + builder.appendInt(Math.toIntExact(numericDocValues.nextValue())); + return; + } + builder.beginPositionEntry(); + for (int v = 0; v < count; v++) { + builder.appendInt(Math.toIntExact(numericDocValues.nextValue())); + } + builder.endPositionEntry(); + } + + @Override + public int docID() { + // There is a .docID on on the numericDocValues but it is often not implemented. + return docID; + } + + @Override + public String toString() { + return "Ints"; + } + } + + private static class SingletonDoubles extends BlockDocValuesReader { + private final NumericDocValues docValues; + private final ToDouble toDouble; + private int docID = -1; + + SingletonDoubles(NumericDocValues docValues, ToDouble toDouble) { + this.docValues = docValues; + this.toDouble = toDouble; + } + + @Override + public DoubleBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.doublesFromDocValues(expectedCount); + } + + @Override + public DoubleBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (docValues.advanceExact(doc)) { + blockBuilder.appendDouble(toDouble.convert(docValues.longValue())); + } else { + blockBuilder.appendNull(); + } + lastDoc = doc; + this.docID = doc; + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + this.docID = docId; + DoubleBuilder blockBuilder = (DoubleBuilder) builder; + if (docValues.advanceExact(this.docID)) { + blockBuilder.appendDouble(toDouble.convert(docValues.longValue())); + } else { + blockBuilder.appendNull(); + } + } + + @Override + public int docID() { + return docID; + } + + @Override + public String toString() { + return "SingletonDoubles"; + } + } + + private static class Doubles extends BlockDocValuesReader { + private final SortedNumericDocValues docValues; + private final ToDouble toDouble; + private int docID = -1; + + Doubles(SortedNumericDocValues docValues, ToDouble toDouble) { + this.docValues = docValues; + this.toDouble = toDouble; + } + + @Override + public DoubleBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.doublesFromDocValues(expectedCount); + } + + @Override + public DoubleBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, blockBuilder); + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + read(docId, (DoubleBuilder) builder); + } + + private void read(int doc, DoubleBuilder builder) throws IOException { + this.docID = doc; + if (false == docValues.advanceExact(doc)) { + builder.appendNull(); + return; + } + int count = docValues.docValueCount(); + if (count == 1) { + builder.appendDouble(toDouble.convert(docValues.nextValue())); + return; + } + builder.beginPositionEntry(); + for (int v = 0; v < count; v++) { + builder.appendDouble(toDouble.convert(docValues.nextValue())); + } + builder.endPositionEntry(); + } + + @Override + public int docID() { + return docID; + } + + @Override + public String toString() { + return "Doubles"; + } + } + + private static class SingletonOrdinals extends BlockDocValuesReader { + private final SortedDocValues ordinals; + + SingletonOrdinals(SortedDocValues ordinals) { + this.ordinals = ordinals; + } + + @Override + public BytesRefBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.bytesRefsFromDocValues(expectedCount); + } + + @Override + public SingletonOrdinalsBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + SingletonOrdinalsBuilder builder = factory.singletonOrdinalsBuilder(ordinals, docs.count()); + + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < ordinals.docID()) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (ordinals.advanceExact(doc)) { + builder.appendOrd(ordinals.ordValue()); + } else { + builder.appendNull(); + } + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int doc, Builder builder) throws IOException { + if (ordinals.advanceExact(doc)) { + ((BytesRefBuilder) builder).appendBytesRef(ordinals.lookupOrd(ordinals.ordValue())); + } else { + builder.appendNull(); + } + } + + @Override + public int docID() { + return ordinals.docID(); + } + + @Override + public String toString() { + return "SingletonOrdinals"; + } + } + + private static class Ordinals extends BlockDocValuesReader { + private final SortedSetDocValues ordinals; + + Ordinals(SortedSetDocValues ordinals) { + this.ordinals = ordinals; + } + + @Override + public BytesRefBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.bytesRefsFromDocValues(expectedCount); + } + + @Override + public BytesRefBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + BytesRefBuilder builder = builder(factory, docs.count()); + + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < ordinals.docID()) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int doc, Builder builder) throws IOException { + read(doc, (BytesRefBuilder) builder); + } + + private void read(int doc, BytesRefBuilder builder) throws IOException { + if (false == ordinals.advanceExact(doc)) { + builder.appendNull(); + return; + } + int count = ordinals.docValueCount(); + if (count == 1) { + builder.appendBytesRef(ordinals.lookupOrd(ordinals.nextOrd())); + return; + } + builder.beginPositionEntry(); + for (int v = 0; v < count; v++) { + builder.appendBytesRef(ordinals.lookupOrd(ordinals.nextOrd())); + } + builder.endPositionEntry(); + } + + @Override + public int docID() { + return ordinals.docID(); + } + + @Override + public String toString() { + return "Ordinals"; + } + } + + private static class Bytes extends BlockDocValuesReader { + private final SortedBinaryDocValues docValues; + private int docID = -1; + + Bytes(SortedBinaryDocValues docValues) { + this.docValues = docValues; + } + + @Override + public BytesRefBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.bytesRefsFromDocValues(expectedCount); + } + + @Override + public BytesRefBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, blockBuilder); + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + read(docId, (BytesRefBuilder) builder); + } + + private void read(int doc, BytesRefBuilder builder) throws IOException { + this.docID = doc; + if (false == docValues.advanceExact(doc)) { + builder.appendNull(); + return; + } + int count = docValues.docValueCount(); + if (count == 1) { + // TODO read ords in ascending order. Buffers and stuff. + builder.appendBytesRef(docValues.nextValue()); + return; + } + builder.beginPositionEntry(); + for (int v = 0; v < count; v++) { + builder.appendBytesRef(docValues.nextValue()); + } + builder.endPositionEntry(); + } + + @Override + public int docID() { + return docID; + } + + @Override + public String toString() { + return "Bytes"; + } + } + + private static class SingletonBooleans extends BlockDocValuesReader { + private final NumericDocValues numericDocValues; + + SingletonBooleans(NumericDocValues numericDocValues) { + this.numericDocValues = numericDocValues; + } + + @Override + public BooleanBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.booleansFromDocValues(expectedCount); + } + + @Override + public BooleanBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (numericDocValues.advanceExact(doc)) { + blockBuilder.appendBoolean(numericDocValues.longValue() != 0); + } else { + blockBuilder.appendNull(); + } + lastDoc = doc; + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + BooleanBuilder blockBuilder = (BooleanBuilder) builder; + if (numericDocValues.advanceExact(docId)) { + blockBuilder.appendBoolean(numericDocValues.longValue() != 0); + } else { + blockBuilder.appendNull(); + } + } + + @Override + public int docID() { + return numericDocValues.docID(); + } + + @Override + public String toString() { + return "SingletonBooleans"; + } + } + + private static class Booleans extends BlockDocValuesReader { + private final SortedNumericDocValues numericDocValues; + private int docID = -1; + + Booleans(SortedNumericDocValues numericDocValues) { + this.numericDocValues = numericDocValues; + } + + @Override + public BooleanBuilder builder(BuilderFactory factory, int expectedCount) { + return factory.booleansFromDocValues(expectedCount); + } + + @Override + public BooleanBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { + var blockBuilder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, blockBuilder); + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) throws IOException { + read(docId, (BooleanBuilder) builder); + } + + private void read(int doc, BooleanBuilder builder) throws IOException { + this.docID = doc; + if (false == numericDocValues.advanceExact(doc)) { + builder.appendNull(); + return; + } + int count = numericDocValues.docValueCount(); + if (count == 1) { + builder.appendBoolean(numericDocValues.nextValue() != 0); + return; + } + builder.beginPositionEntry(); + for (int v = 0; v < count; v++) { + builder.appendBoolean(numericDocValues.nextValue() != 0); + } + builder.endPositionEntry(); + } + + @Override + public int docID() { + // There is a .docID on the numericDocValues but it is often not implemented. + return docID; + } + + @Override + public String toString() { + return "Booleans"; + } + } + + private static class Nulls extends BlockDocValuesReader { + private int docID = -1; + + @Override + public Builder builder(BuilderFactory factory, int expectedCount) { + return factory.nulls(expectedCount); + } + + @Override + public Builder readValues(BuilderFactory factory, Docs docs) throws IOException { + Builder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + builder.appendNull(); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, Builder builder) { + this.docID = docId; + builder.appendNull(); + } + + @Override + public int docID() { + return docID; + } + + @Override + public String toString() { + return "Nulls"; + } + } + + /** + * Convert a {@link String} into a utf-8 {@link BytesRef}. + */ + protected static BytesRef toBytesRef(BytesRef scratch, String v) { + int len = UnicodeUtil.maxUTF8Length(v.length()); + if (scratch.bytes.length < len) { + scratch.bytes = new byte[len]; + } + scratch.length = UnicodeUtil.UTF16toUTF8(v, 0, v.length(), scratch.bytes); + return scratch; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java new file mode 100644 index 0000000000000..7e973f9c32033 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java @@ -0,0 +1,191 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.util.BytesRef; + +import java.io.IOException; + +/** + * Interface for loading data in a block shape. Instances of this class + * must be immutable and thread safe. + */ +public interface BlockLoader { + /** + * Build a {@link LeafReaderContext leaf} level reader. + */ + BlockDocValuesReader reader(LeafReaderContext context) throws IOException; + + /** + * Does this loader support loading bytes via calling {@link #ordinals}. + */ + default boolean supportsOrdinals() { + return false; + } + + /** + * Load ordinals for the provided context. + */ + default SortedSetDocValues ordinals(LeafReaderContext context) throws IOException { + throw new IllegalStateException("ordinals not supported"); + } + + /** + * A list of documents to load. + */ + interface Docs { + int count(); + + int get(int i); + } + + /** + * Builds block "builders" for loading data into blocks for the compute engine. + * It's important for performance that this only have one implementation in + * production code. That implementation sits in the "compute" project. The is + * also a test implementation, but there may be no more other implementations. + */ + interface BuilderFactory { + /** + * Build a builder to load booleans as loaded from doc values. Doc values + * load booleans deduplicated and in sorted order. + */ + BooleanBuilder booleansFromDocValues(int expectedCount); + + /** + * Build a builder to load booleans without any loading constraints. + */ + BooleanBuilder booleans(int expectedCount); + + /** + * Build a builder to load {@link BytesRef}s as loaded from doc values. + * Doc values load {@linkplain BytesRef}s deduplicated and in sorted order. + */ + BytesRefBuilder bytesRefsFromDocValues(int expectedCount); + + /** + * Build a builder to load {@link BytesRef}s without any loading constraints. + */ + BytesRefBuilder bytesRefs(int expectedCount); + + /** + * Build a builder to load doubles as loaded from doc values. + * Doc values load doubles deduplicated and in sorted order. + */ + DoubleBuilder doublesFromDocValues(int expectedCount); + + /** + * Build a builder to load doubles without any loading constraints. + */ + DoubleBuilder doubles(int expectedCount); + + /** + * Build a builder to load ints as loaded from doc values. + * Doc values load ints deduplicated and in sorted order. + */ + IntBuilder intsFromDocValues(int expectedCount); + + /** + * Build a builder to load ints without any loading constraints. + */ + IntBuilder ints(int expectedCount); + + /** + * Build a builder to load longs as loaded from doc values. + * Doc values load longs deduplicated and in sorted order. + */ + LongBuilder longsFromDocValues(int expectedCount); + + /** + * Build a builder to load longs without any loading constraints. + */ + LongBuilder longs(int expectedCount); + + /** + * Build a builder that can only load null values. + * TODO this should return a block directly instead of a builder + */ + Builder nulls(int expectedCount); + + /** + * Build a reader for reading keyword ordinals. + */ + SingletonOrdinalsBuilder singletonOrdinalsBuilder(SortedDocValues ordinals, int count); + + // TODO support non-singleton ords + } + + /** + * A builder for typed values. For each document you may either call + * {@link #appendNull}, {@code append}, or + * {@link #beginPositionEntry} followed by two or more {@code append} + * calls, and then {@link #endPositionEntry}. + */ + interface Builder { + /** + * Insert a null value. + */ + Builder appendNull(); + + /** + * Start a multivalued field. + */ + Builder beginPositionEntry(); + + /** + * End a multivalued field. + */ + Builder endPositionEntry(); + } + + interface BooleanBuilder extends Builder { + /** + * Appends a boolean to the current entry. + */ + BooleanBuilder appendBoolean(boolean value); + } + + interface BytesRefBuilder extends Builder { + /** + * Appends a BytesRef to the current entry. + */ + BytesRefBuilder appendBytesRef(BytesRef value); + } + + interface DoubleBuilder extends Builder { + /** + * Appends a double to the current entry. + */ + DoubleBuilder appendDouble(double value); + } + + interface IntBuilder extends Builder { + /** + * Appends an int to the current entry. + */ + IntBuilder appendInt(int value); + } + + interface LongBuilder extends Builder { + /** + * Appends a long to the current entry. + */ + LongBuilder appendLong(long value); + } + + interface SingletonOrdinalsBuilder extends Builder { + /** + * Appends an ordinal to the builder. + */ + SingletonOrdinalsBuilder appendOrd(int value); + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java new file mode 100644 index 0000000000000..2b9daadda31d6 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java @@ -0,0 +1,198 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.fieldvisitor.LeafStoredFieldLoader; +import org.elasticsearch.index.fieldvisitor.StoredFieldLoader; +import org.elasticsearch.search.lookup.Source; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Loads values from {@code _source}. This whole process is very slow and cast-tastic, + * so it doesn't really try to avoid megamorphic invocations. It's just going to be + * slow. + * + * Note that this extends {@link BlockDocValuesReader} because it pretends to load + * doc values because, for now, ESQL only knows how to load things in a doc values + * order. + */ +public abstract class BlockSourceReader extends BlockDocValuesReader { + /** + * Read {@code boolean}s from {@code _source}. + */ + public static BlockLoader booleans(ValueFetcher fetcher) { + StoredFieldLoader loader = StoredFieldLoader.create(true, Set.of()); + return context -> new BlockSourceReader(fetcher, loader.getLoader(context, null)) { + @Override + public BlockLoader.Builder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.booleans(expectedCount); + } + + @Override + protected void append(BlockLoader.Builder builder, Object v) { + ((BlockLoader.BooleanBuilder) builder).appendBoolean((Boolean) v); + } + + @Override + public String toString() { + return "SourceBooleans"; + } + }; + } + + /** + * Read {@link BytesRef}s from {@code _source}. + */ + public static BlockLoader bytesRefs(ValueFetcher fetcher) { + StoredFieldLoader loader = StoredFieldLoader.create(true, Set.of()); + return context -> new BlockSourceReader(fetcher, loader.getLoader(context, null)) { + BytesRef scratch = new BytesRef(); + + @Override + public BlockLoader.Builder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); + } + + @Override + protected void append(BlockLoader.Builder builder, Object v) { + ((BlockLoader.BytesRefBuilder) builder).appendBytesRef(toBytesRef(scratch, (String) v)); + } + + @Override + public String toString() { + return "SourceBytes"; + } + }; + } + + /** + * Read {@code double}s from {@code _source}. + */ + public static BlockLoader doubles(ValueFetcher fetcher) { + StoredFieldLoader loader = StoredFieldLoader.create(true, Set.of()); + return context -> new BlockSourceReader(fetcher, loader.getLoader(context, null)) { + @Override + public BlockLoader.Builder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.doubles(expectedCount); + } + + @Override + protected void append(BlockLoader.Builder builder, Object v) { + ((BlockLoader.DoubleBuilder) builder).appendDouble(((Number) v).doubleValue()); + } + + @Override + public String toString() { + return "SourceDoubles"; + } + }; + } + + /** + * Read {@code int}s from {@code _source}. + */ + public static BlockLoader ints(ValueFetcher fetcher) { + StoredFieldLoader loader = StoredFieldLoader.create(true, Set.of()); + return context -> new BlockSourceReader(fetcher, loader.getLoader(context, null)) { + @Override + public BlockLoader.Builder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.ints(expectedCount); + } + + @Override + protected void append(BlockLoader.Builder builder, Object v) { + ((BlockLoader.IntBuilder) builder).appendInt(((Number) v).intValue()); + } + + @Override + public String toString() { + return "SourceInts"; + } + }; + } + + /** + * Read {@code long}s from {@code _source}. + */ + public static BlockLoader longs(ValueFetcher fetcher) { + StoredFieldLoader loader = StoredFieldLoader.create(true, Set.of()); + return context -> new BlockSourceReader(fetcher, loader.getLoader(context, null)) { + @Override + public BlockLoader.Builder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.longs(expectedCount); + } + + @Override + protected void append(BlockLoader.Builder builder, Object v) { + ((BlockLoader.LongBuilder) builder).appendLong(((Number) v).longValue()); + } + + @Override + public String toString() { + return "SourceLongs"; + } + }; + } + + private final ValueFetcher fetcher; + private final LeafStoredFieldLoader loader; + private final List ignoredValues = new ArrayList<>(); + private int docID = -1; + + BlockSourceReader(ValueFetcher fetcher, LeafStoredFieldLoader loader) { + this.fetcher = fetcher; + this.loader = loader; + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) throws IOException { + BlockLoader.Builder blockBuilder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + readValuesFromSingleDoc(doc, blockBuilder); + } + return blockBuilder; + } + + @Override + public void readValuesFromSingleDoc(int doc, BlockLoader.Builder builder) throws IOException { + this.docID = doc; + loader.advanceTo(doc); + List values = fetcher.fetchValues(Source.fromBytes(loader.source()), doc, ignoredValues); + ignoredValues.clear(); // TODO do something with these? + if (values == null) { + builder.appendNull(); + return; + } + if (values.size() == 1) { + append(builder, values.get(0)); + return; + } + builder.beginPositionEntry(); + for (Object v : values) { + append(builder, v); + } + builder.endPositionEntry(); + } + + protected abstract void append(BlockLoader.Builder builder, Object v); + + @Override + public int docID() { + return docID; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java new file mode 100644 index 0000000000000..d38d30a03b275 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.fieldvisitor.LeafStoredFieldLoader; +import org.elasticsearch.index.fieldvisitor.StoredFieldLoader; +import org.elasticsearch.index.mapper.BlockLoader.BytesRefBuilder; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * Loads values from {@link LeafReader#storedFields}. This whole process is very slow + * and cast-tastic, so it doesn't really try to avoid megamorphic invocations. It's + * just going to be slow. + * + * Note that this extends {@link BlockDocValuesReader} because it pretends to load + * doc values because, for now, ESQL only knows how to load things in a doc values + * order. + */ +public abstract class BlockStoredFieldsReader extends BlockDocValuesReader { + public static BlockLoader bytesRefsFromBytesRefs(String field) { + StoredFieldLoader loader = StoredFieldLoader.create(false, Set.of(field)); + return context -> new Bytes(loader.getLoader(context, null), field) { + @Override + protected BytesRef toBytesRef(Object v) { + return (BytesRef) v; + } + }; + } + + public static BlockLoader bytesRefsFromStrings(String field) { + StoredFieldLoader loader = StoredFieldLoader.create(false, Set.of(field)); + return context -> new Bytes(loader.getLoader(context, null), field) { + private final BytesRef scratch = new BytesRef(); + + @Override + protected BytesRef toBytesRef(Object v) { + return toBytesRef(scratch, (String) v); + } + }; + } + + public static BlockLoader id() { + StoredFieldLoader loader = StoredFieldLoader.create(false, Set.of(IdFieldMapper.NAME)); + return context -> new Id(loader.getLoader(context, null)); + } + + private final LeafStoredFieldLoader loader; + private int docID = -1; + + protected BlockStoredFieldsReader(LeafStoredFieldLoader loader) { + this.loader = loader; + } + + @Override + public final BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) throws IOException { + var builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + readValuesFromSingleDoc(docs.get(i), builder); + } + return builder; + } + + @Override + public final void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) throws IOException { + if (docId < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + this.docID = docId; + loader.advanceTo(docId); + read(loader, builder); + } + + protected abstract void read(LeafStoredFieldLoader loader, BlockLoader.Builder builder) throws IOException; + + @Override + public final int docID() { + return docID; + } + + private abstract static class Bytes extends BlockStoredFieldsReader { + private final String field; + + Bytes(LeafStoredFieldLoader loader, String field) { + super(loader); + this.field = field; + } + + @Override + public BytesRefBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); + } + + protected abstract BytesRef toBytesRef(Object v); + + @Override + protected void read(LeafStoredFieldLoader loader, BlockLoader.Builder builder) throws IOException { + List values = loader.storedFields().get(field); + if (values == null) { + builder.appendNull(); + return; + } + if (values.size() == 1) { + ((BytesRefBuilder) builder).appendBytesRef(toBytesRef(values.get(0))); + return; + } + builder.beginPositionEntry(); + for (Object v : values) { + ((BytesRefBuilder) builder).appendBytesRef(toBytesRef(v)); + } + builder.endPositionEntry(); + } + + @Override + public String toString() { + return "BlockStoredFieldsReader.Bytes"; + } + } + + private static class Id extends BlockStoredFieldsReader { + private final BytesRef scratch = new BytesRef(); + + Id(LeafStoredFieldLoader loader) { + super(loader); + } + + @Override + public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); + } + + @Override + protected void read(LeafStoredFieldLoader loader, BlockLoader.Builder builder) throws IOException { + ((BytesRefBuilder) builder).appendBytesRef(toBytesRef(scratch, loader.id())); + } + + @Override + public String toString() { + return "BlockStoredFieldsReader.Id"; + } + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java index 54961f2f489bf..1fb3f706c56a2 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java @@ -255,6 +255,14 @@ public Boolean valueForDisplay(Object value) { }; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (hasDocValues()) { + return BlockDocValuesReader.booleans(name()); + } + return BlockSourceReader.booleans(sourceValueFetcher(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java new file mode 100644 index 0000000000000..0d29bc43700e8 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.elasticsearch.script.BooleanFieldScript; + +/** + * {@link BlockDocValuesReader} implementation for {@code boolean} scripts. + */ +public class BooleanScriptBlockDocValuesReader extends BlockDocValuesReader { + public static BlockLoader blockLoader(BooleanFieldScript.LeafFactory factory) { + return context -> new BooleanScriptBlockDocValuesReader(factory.newInstance(context)); + } + + private final BooleanFieldScript script; + private int docId; + + BooleanScriptBlockDocValuesReader(BooleanFieldScript script) { + this.script = script; + } + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.BooleanBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + // Note that we don't emit falses before trues so we conform to the doc values contract and can use booleansFromDocValues + return factory.booleansFromDocValues(expectedCount); + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.BooleanBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + read(docId, (BlockLoader.BooleanBuilder) builder); + } + + private void read(int docId, BlockLoader.BooleanBuilder builder) { + script.runForDoc(docId); + int total = script.falses() + script.trues(); + switch (total) { + case 0 -> builder.appendNull(); + case 1 -> builder.appendBoolean(script.trues() > 0); + default -> { + builder.beginPositionEntry(); + for (int i = 0; i < script.falses(); i++) { + builder.appendBoolean(false); + } + for (int i = 0; i < script.trues(); i++) { + builder.appendBoolean(true); + } + builder.endPositionEntry(); + } + } + } + + @Override + public String toString() { + return "ScriptBooleans"; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptFieldType.java index fcdf733b0febc..6e3876644567f 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptFieldType.java @@ -110,6 +110,11 @@ public DocValueFormat docValueFormat(String format, ZoneId timeZone) { return DocValueFormat.BOOLEAN; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return BooleanScriptBlockDocValuesReader.blockLoader(leafFactory(blContext.lookup())); + } + @Override public BooleanScriptFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new BooleanScriptFieldData.Builder(name(), leafFactory(fieldDataContext.lookupSupplier().get()), BooleanDocValuesField::new); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java index c80df48f56977..21b9ec04c56c0 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java @@ -773,6 +773,14 @@ public Function pointReaderIfPossible() { return null; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (hasDocValues()) { + return BlockDocValuesReader.longs(name()); + } + return BlockSourceReader.longs(sourceValueFetcher(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java new file mode 100644 index 0000000000000..6e6cdd3d1f057 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.elasticsearch.script.DateFieldScript; + +/** + * {@link BlockDocValuesReader} implementation for date scripts. + */ +public class DateScriptBlockDocValuesReader extends BlockDocValuesReader { + public static BlockLoader blockLoader(DateFieldScript.LeafFactory factory) { + return context -> new DateScriptBlockDocValuesReader(factory.newInstance(context)); + } + + private final DateFieldScript script; + private int docId; + + DateScriptBlockDocValuesReader(DateFieldScript script) { + this.script = script; + } + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.LongBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.longs(expectedCount); // Note that we don't pre-sort our output so we can't use longsFromDocValues + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.LongBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + read(docId, (BlockLoader.LongBuilder) builder); + } + + private void read(int docId, BlockLoader.LongBuilder builder) { + script.runForDoc(docId); + switch (script.count()) { + case 0 -> builder.appendNull(); + case 1 -> builder.appendLong(script.values()[0]); + default -> { + builder.beginPositionEntry(); + for (int i = 0; i < script.count(); i++) { + builder.appendLong(script.values()[i]); + } + builder.endPositionEntry(); + } + } + } + + @Override + public String toString() { + return "ScriptDates"; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateScriptFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/DateScriptFieldType.java index 10c68e023baa2..8252d571dce68 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DateScriptFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateScriptFieldType.java @@ -179,6 +179,11 @@ public DocValueFormat docValueFormat(@Nullable String format, ZoneId timeZone) { return new DocValueFormat.DateTime(dateTimeFormatter, timeZone, Resolution.MILLISECONDS); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return DateScriptBlockDocValuesReader.blockLoader(leafFactory(blContext.lookup())); + } + @Override public DateScriptFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new DateScriptFieldData.Builder( diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java new file mode 100644 index 0000000000000..856321f53244d --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.elasticsearch.script.DoubleFieldScript; + +/** + * {@link BlockDocValuesReader} implementation for {@code double} scripts. + */ +public class DoubleScriptBlockDocValuesReader extends BlockDocValuesReader { + public static BlockLoader blockLoader(DoubleFieldScript.LeafFactory factory) { + return context -> new DoubleScriptBlockDocValuesReader(factory.newInstance(context)); + } + + private final DoubleFieldScript script; + private int docId; + + DoubleScriptBlockDocValuesReader(DoubleFieldScript script) { + this.script = script; + } + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.DoubleBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.doubles(expectedCount); // Note that we don't pre-sort our output so we can't use doublesFromDocValues + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.DoubleBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + read(docId, (BlockLoader.DoubleBuilder) builder); + } + + private void read(int docId, BlockLoader.DoubleBuilder builder) { + script.runForDoc(docId); + switch (script.count()) { + case 0 -> builder.appendNull(); + case 1 -> builder.appendDouble(script.values()[0]); + default -> { + builder.beginPositionEntry(); + for (int i = 0; i < script.count(); i++) { + builder.appendDouble(script.values()[i]); + } + builder.endPositionEntry(); + } + } + } + + @Override + public String toString() { + return "ScriptDoubles"; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java index 9baa6340b45c7..ef5c112ef212a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java @@ -105,6 +105,11 @@ public DocValueFormat docValueFormat(String format, ZoneId timeZone) { return new DocValueFormat.Decimal(format); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return DoubleScriptBlockDocValuesReader.blockLoader(leafFactory(blContext.lookup())); + } + @Override public DoubleScriptFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new DoubleScriptFieldData.Builder(name(), leafFactory(fieldDataContext.lookupSupplier().get()), DoubleDocValuesField::new); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java index c24b3077f700c..cb69fa4b7c50a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -10,6 +10,7 @@ import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Strings; import org.elasticsearch.index.fielddata.FieldData; import org.elasticsearch.index.fielddata.FieldDataContext; @@ -77,6 +78,45 @@ public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext ); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + // TODO build a constant block directly + BytesRef bytes = new BytesRef(blContext.indexName()); + return context -> new BlockDocValuesReader() { + private int docId; + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + builder.appendBytesRef(bytes); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + ((BlockLoader.BytesRefBuilder) builder).appendBytesRef(bytes); + } + + @Override + public String toString() { + return "Index"; + } + }; + } + @Override public ValueFetcher valueFetcher(SearchExecutionContext context, String format) { return new ValueFetcher() { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java index 4f7a8d5691c69..7d6b7711360fe 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -406,6 +406,14 @@ public static Query rangeQuery( return builder.apply(lower, upper); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (hasDocValues()) { + return BlockDocValuesReader.bytesRefsFromOrds(name()); + } + return null; + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { failIfNoDocValues(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java new file mode 100644 index 0000000000000..f05b9aff890af --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.elasticsearch.script.IpFieldScript; + +/** + * {@link BlockDocValuesReader} implementation for keyword scripts. + */ +public class IpScriptBlockDocValuesReader extends BlockDocValuesReader { + public static BlockLoader blockLoader(IpFieldScript.LeafFactory factory) { + return context -> new IpScriptBlockDocValuesReader(factory.newInstance(context)); + } + + private final IpFieldScript script; + private int docId; + + IpScriptBlockDocValuesReader(IpFieldScript script) { + this.script = script; + } + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); // Note that we don't pre-sort our output so we can't use bytesRefsFromDocValues + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + read(docId, (BlockLoader.BytesRefBuilder) builder); + } + + private void read(int docId, BlockLoader.BytesRefBuilder builder) { + script.runForDoc(docId); + switch (script.count()) { + case 0 -> builder.appendNull(); + case 1 -> { + builder.appendBytesRef(script.values()[0]); + } + default -> { + builder.beginPositionEntry(); + for (int i = 0; i < script.count(); i++) { + builder.appendBytesRef(script.values()[i]); + } + builder.endPositionEntry(); + } + } + } + + @Override + public String toString() { + return "ScriptIps"; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpScriptFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/IpScriptFieldType.java index 693322506972f..0e56b30e2d5d9 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpScriptFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpScriptFieldType.java @@ -208,4 +208,9 @@ private Query cidrQuery(String term, SearchExecutionContext context) { BytesRef upperBytes = new BytesRef(InetAddressPoint.encode(InetAddressPoint.decode(upper))); return new IpScriptFieldRangeQuery(script, leafFactory(context), name(), lowerBytes, upperBytes); } + + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return IpScriptBlockDocValuesReader.blockLoader(leafFactory(blContext.lookup())); + } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java index 1e74f90ed7393..9bc3db22365de 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java @@ -578,6 +578,24 @@ NamedAnalyzer normalizer() { return normalizer; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (hasDocValues()) { + return BlockDocValuesReader.bytesRefsFromOrds(name()); + } + if (isSyntheticSource) { + if (false == isStored()) { + throw new IllegalStateException( + "keyword field [" + + name() + + "] is only supported in synthetic _source index if it creates doc values or stored fields" + ); + } + return BlockStoredFieldsReader.bytesRefsFromBytesRefs(name()); + } + return BlockSourceReader.bytesRefs(sourceValueFetcher(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java new file mode 100644 index 0000000000000..51058b3b60bf4 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.util.BytesRefBuilder; +import org.elasticsearch.script.StringFieldScript; + +/** + * {@link BlockDocValuesReader} implementation for keyword scripts. + */ +public class KeywordScriptBlockDocValuesReader extends BlockDocValuesReader { + public static BlockLoader blockLoader(StringFieldScript.LeafFactory factory) { + return context -> new KeywordScriptBlockDocValuesReader(factory.newInstance(context)); + } + + private final BytesRefBuilder bytesBuild = new BytesRefBuilder(); + private final StringFieldScript script; + private int docId; + + KeywordScriptBlockDocValuesReader(StringFieldScript script) { + this.script = script; + } + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); // Note that we don't pre-sort our output so we can't use bytesRefsFromDocValues + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + read(docId, (BlockLoader.BytesRefBuilder) builder); + } + + private void read(int docId, BlockLoader.BytesRefBuilder builder) { + script.runForDoc(docId); + switch (script.getValues().size()) { + case 0 -> builder.appendNull(); + case 1 -> { + bytesBuild.copyChars(script.getValues().get(0)); + builder.appendBytesRef(bytesBuild.get()); + } + default -> { + builder.beginPositionEntry(); + for (String v : script.getValues()) { + bytesBuild.copyChars(v); + builder.appendBytesRef(bytesBuild.get()); + } + builder.endPositionEntry(); + } + } + } + + @Override + public String toString() { + return "ScriptKeywords"; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptFieldType.java index fb498e7eb7dcd..879a28d4c76c8 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptFieldType.java @@ -110,6 +110,11 @@ public Object valueForDisplay(Object value) { return binaryValue.utf8ToString(); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return KeywordScriptBlockDocValuesReader.blockLoader(leafFactory(blContext.lookup())); + } + @Override public StringScriptFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new StringScriptFieldData.Builder(name(), leafFactory(fieldDataContext.lookupSupplier().get()), KeywordDocValuesField::new); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java new file mode 100644 index 0000000000000..4896f7d858144 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.elasticsearch.script.LongFieldScript; + +/** + * {@link BlockDocValuesReader} implementation for {@code long} scripts. + */ +public class LongScriptBlockDocValuesReader extends BlockDocValuesReader { + public static BlockLoader blockLoader(LongFieldScript.LeafFactory factory) { + return context -> new LongScriptBlockDocValuesReader(factory.newInstance(context)); + } + + private final LongFieldScript script; + private int docId; + + LongScriptBlockDocValuesReader(LongFieldScript script) { + this.script = script; + } + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.LongBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.longs(expectedCount); // Note that we don't pre-sort our output so we can't use longsFromDocValues + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.LongBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + read(docId, (BlockLoader.LongBuilder) builder); + } + + private void read(int docId, BlockLoader.LongBuilder builder) { + script.runForDoc(docId); + switch (script.count()) { + case 0 -> builder.appendNull(); + case 1 -> builder.appendLong(script.values()[0]); + default -> { + builder.beginPositionEntry(); + for (int i = 0; i < script.count(); i++) { + builder.appendLong(script.values()[i]); + } + builder.endPositionEntry(); + } + } + } + + @Override + public String toString() { + return "ScriptLongs"; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java index 417d60533aad0..f89babe32d0a9 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java @@ -105,6 +105,11 @@ public DocValueFormat docValueFormat(String format, ZoneId timeZone) { return new DocValueFormat.Decimal(format); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return LongScriptBlockDocValuesReader.blockLoader(leafFactory(blContext.lookup())); + } + @Override public LongScriptFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new LongScriptFieldData.Builder(name(), leafFactory(fieldDataContext.lookupSupplier().get()), LongDocValuesField::new); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index 21ed56a82292c..b68bb1a2b1987 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -42,6 +42,7 @@ import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.fetch.subphase.FetchFieldsPhase; +import org.elasticsearch.search.lookup.SearchLookup; import java.io.IOException; import java.time.ZoneId; @@ -50,6 +51,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES; @@ -629,4 +631,33 @@ public void validateMatchedRoutingPath(String routingPath) { + "]." ); } + + /** + * Returns a loader for ESQL or {@code null} if the field doesn't support + * ESQL. + */ + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return null; + } + + /** + * Arguments for {@link #blockLoader}. + */ + public interface BlockLoaderContext { + /** + * The name of the index. + */ + String indexName(); + + /** + * {@link SearchLookup} used for building scripts. + */ + SearchLookup lookup(); + + /** + * Find the paths in {@code _source} that contain values for the field named {@code name}. + */ + Set sourcePaths(String name); + } + } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java index 4df3b9cf02985..7dfc5a98037d0 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -438,6 +438,16 @@ protected void writeValue(XContentBuilder b, long value) throws IOException { } }; } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.doubles(fieldName, l -> HalfFloatPoint.sortableShortToHalfFloat((short) l)); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.doubles(sourceValueFetcher); + } }, FLOAT("float", NumericType.FLOAT) { @Override @@ -590,6 +600,16 @@ protected void writeValue(XContentBuilder b, long value) throws IOException { } }; } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.doubles(fieldName, l -> NumericUtils.sortableIntToFloat((int) l)); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.doubles(sourceValueFetcher); + } }, DOUBLE("double", NumericType.DOUBLE) { @Override @@ -720,6 +740,16 @@ protected void writeValue(XContentBuilder b, long value) throws IOException { } }; } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.doubles(fieldName, NumericUtils::sortableLongToDouble); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.doubles(sourceValueFetcher); + } }, BYTE("byte", NumericType.BYTE) { @Override @@ -813,6 +843,16 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder( SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) { return NumberType.syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed); } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.ints(fieldName); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.ints(sourceValueFetcher); + } }, SHORT("short", NumericType.SHORT) { @Override @@ -902,6 +942,16 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder( SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) { return NumberType.syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed); } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.ints(fieldName); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.ints(sourceValueFetcher); + } }, INTEGER("integer", NumericType.INT) { @Override @@ -1059,6 +1109,16 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder( SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) { return NumberType.syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed); } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.ints(fieldName); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.ints(sourceValueFetcher); + } }, LONG("long", NumericType.LONG) { @Override @@ -1186,6 +1246,16 @@ public IndexFieldData.Builder getValueFetcherFieldDataBuilder( SourceLoader.SyntheticFieldLoader syntheticFieldLoader(String fieldName, String fieldSimpleName, boolean ignoreMalformed) { return syntheticLongFieldLoader(fieldName, fieldSimpleName, ignoreMalformed); } + + @Override + BlockLoader blockLoaderFromDocValues(String fieldName) { + return BlockDocValuesReader.longs(fieldName); + } + + @Override + BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher) { + return BlockSourceReader.longs(sourceValueFetcher); + } }; private final String name; @@ -1449,6 +1519,10 @@ protected void writeValue(XContentBuilder b, long value) throws IOException { } }; } + + abstract BlockLoader blockLoaderFromDocValues(String fieldName); + + abstract BlockLoader blockLoaderFromSource(SourceValueFetcher sourceValueFetcher); } public static class NumberFieldType extends SimpleMappedFieldType { @@ -1579,6 +1653,18 @@ public Function pointReaderIfPossible() { return null; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (indexMode == IndexMode.TIME_SERIES && metricType == TimeSeriesParams.MetricType.COUNTER) { + // Counters are not supported by ESQL so we load them in null + return BlockDocValuesReader.nulls(); + } + if (hasDocValues()) { + return type.blockLoaderFromDocValues(name()); + } + return type.blockLoaderFromSource(sourceValueFetcher(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ProvidedIdFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/ProvidedIdFieldMapper.java index e2821189f0564..f681d54ebbead 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ProvidedIdFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ProvidedIdFieldMapper.java @@ -117,6 +117,11 @@ public Query termsQuery(Collection values, SearchExecutionContext context) { return new TermInSetQuery(name(), bytesRefs); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return BlockStoredFieldsReader.id(); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { if (fieldDataEnabled.getAsBoolean() == false) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java index 7ac524a642401..91616041f65f6 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java @@ -936,6 +936,30 @@ public boolean isAggregatable() { return fielddata; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (syntheticSourceDelegate != null) { + return syntheticSourceDelegate.blockLoader(blContext); + } + if (isSyntheticSource) { + if (isStored()) { + return BlockStoredFieldsReader.bytesRefsFromStrings(name()); + } + /* + * We *shouldn't fall to this exception. The mapping should be + * rejected because we've enabled synthetic source but not configured + * the index properly. But we give it a nice message anyway just in + * case. + */ + throw new IllegalArgumentException( + "fetching values from a text field [" + + name() + + "] is supported because synthetic _source is enabled and we don't have a way to load the fields" + ); + } + return BlockSourceReader.bytesRefs(SourceValueFetcher.toString(blContext.sourcePaths(name()))); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TsidExtractingIdFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TsidExtractingIdFieldMapper.java index e8e4b6909fc4e..9d43ef398feac 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TsidExtractingIdFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TsidExtractingIdFieldMapper.java @@ -87,6 +87,11 @@ public Query termsQuery(Collection values, SearchExecutionContext context) { return new TermInSetQuery(name(), bytesRefs); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return BlockStoredFieldsReader.id(); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { throw new IllegalArgumentException("Fielddata is not supported on [_id] field in [time_series] indices"); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java index 0de2a27fbaac2..54a44dd55caa4 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java @@ -54,6 +54,11 @@ public ValueFetcher valueFetcher(SearchExecutionContext context, String format) return new DocValueFetcher(docValueFormat(format, null), context.getForField(this, FielddataOperation.SEARCH)); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + return BlockDocValuesReader.longs(name()); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { failIfNoDocValues(); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java index e8c2db2ab2616..70e2fee7a003a 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.List; +import java.util.function.Function; import static org.hamcrest.Matchers.equalTo; @@ -262,6 +263,12 @@ public List invalidExample() throws IOException { }; } + @Override + protected Function loadBlockExpected() { + // Just assert that we expect a boolean. Otherwise no munging. + return v -> (Boolean) v; + } + protected IngestScriptSupport ingestScriptSupport() { return new IngestScriptSupport() { @Override diff --git a/server/src/test/java/org/elasticsearch/index/mapper/BooleanScriptFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/BooleanScriptFieldTypeTests.java index 3df28170938e1..8d5a47f08c663 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/BooleanScriptFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/BooleanScriptFieldTypeTests.java @@ -410,6 +410,19 @@ public XContentParser parser() { } } + public void testBlockLoader() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [false]}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [true]}")))); + try (DirectoryReader reader = iw.getReader()) { + BooleanScriptFieldType fieldType = build("xor_param", Map.of("param", false), OnScriptError.FAIL); + List expected = List.of(false, true); + assertThat(blockLoaderReadValues(reader, fieldType), equalTo(expected)); + assertThat(blockLoaderReadValuesFromSingleDoc(reader, fieldType), equalTo(expected)); + } + } + } + private void assertSameCount(IndexSearcher searcher, String source, Object queryDescription, Query scriptedQuery, Query ootbQuery) throws IOException { assertThat( diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java index 638af8562a098..9e9437aa6b9db 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.function.Function; import static org.elasticsearch.index.mapper.DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER; import static org.hamcrest.Matchers.containsString; @@ -577,7 +578,12 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) public SyntheticSourceExample example(int maxValues) { if (randomBoolean()) { Tuple v = generateValue(); - return new SyntheticSourceExample(v.v1(), v.v2(), this::mapping); + return new SyntheticSourceExample( + v.v1(), + v.v2(), + resolution.convert(Instant.from(formatter.parse(v.v2()))), + this::mapping + ); } List> values = randomList(1, maxValues, this::generateValue); List in = values.stream().map(Tuple::v1).toList(); @@ -588,7 +594,10 @@ public SyntheticSourceExample example(int maxValues) { .map(Tuple::v2) .toList(); Object out = outList.size() == 1 ? outList.get(0) : outList; - return new SyntheticSourceExample(in, out, this::mapping); + + List outBlockList = outList.stream().map(v -> resolution.convert(Instant.from(formatter.parse(v)))).toList(); + Object outBlock = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; + return new SyntheticSourceExample(in, out, outBlock, this::mapping); } private Tuple generateValue() { @@ -696,6 +705,11 @@ public void execute() { }; } + @Override + protected Function loadBlockExpected() { + return v -> ((Number) v).longValue(); + } + public void testLegacyField() throws Exception { // check that unknown date formats are treated leniently on old indices MapperService service = createMapperService(IndexVersion.fromId(5000099), Settings.EMPTY, () -> false, mapping(b -> { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DateScriptFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DateScriptFieldTypeTests.java index cf28b46118d65..d1652b9f57716 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DateScriptFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DateScriptFieldTypeTests.java @@ -471,6 +471,18 @@ public void testLegacyDateFormatName() throws IOException { ); } + public void testBlockLoader() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"timestamp\": [1595432181354]}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"timestamp\": [1595432181355]}")))); + try (DirectoryReader reader = iw.getReader()) { + DateScriptFieldType fieldType = build("add_days", Map.of("days", 1), OnScriptError.FAIL); + assertThat(blockLoaderReadValues(reader, fieldType), equalTo(List.of(1595518581354L, 1595518581355L))); + assertThat(blockLoaderReadValuesFromSingleDoc(reader, fieldType), equalTo(List.of(1595518581354L, 1595518581355L))); + } + } + } + @Override protected Query randomTermsQuery(MappedFieldType ft, SearchExecutionContext ctx) { return ft.termsQuery(randomList(1, 100, DateScriptFieldTypeTests::randomDate), ctx); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DoubleScriptFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DoubleScriptFieldTypeTests.java index d360dc9796d89..0f05dad8098f4 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DoubleScriptFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DoubleScriptFieldTypeTests.java @@ -230,6 +230,18 @@ public void testTermsQuery() throws IOException { } } + public void testBlockLoader() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [1]}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [2]}")))); + try (DirectoryReader reader = iw.getReader()) { + DoubleScriptFieldType fieldType = build("add_param", Map.of("param", 1), OnScriptError.FAIL); + assertThat(blockLoaderReadValues(reader, fieldType), equalTo(List.of(2d, 3d))); + assertThat(blockLoaderReadValuesFromSingleDoc(reader, fieldType), equalTo(List.of(2d, 3d))); + } + } + } + @Override protected Query randomTermsQuery(MappedFieldType ft, SearchExecutionContext ctx) { return ft.termsQuery(List.of(randomLong()), ctx); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java index 36bb70210a439..3798129ccff29 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.List; +import java.util.function.Function; public class FloatFieldMapperTests extends NumberFieldMapperTests { @@ -54,6 +55,15 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) return new NumberSyntheticSourceSupport(Number::floatValue, ignoreMalformed); } + @Override + protected Function loadBlockExpected() { + return v -> { + // The test converts the float into a string so we do do + Number n = (Number) v; + return Double.parseDouble(Float.toString(n.floatValue())); + }; + } + @Override protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java index 13b6644520745..cc024efb5f307 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.util.List; +import java.util.function.Function; public class HalfFloatFieldMapperTests extends NumberFieldMapperTests { @@ -54,6 +55,17 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) ); } + @Override + protected Function loadBlockExpected() { + return v -> { + // The test converts the float into a string so we do do + Number n = (Number) v; + return Double.parseDouble( + Float.toString(HalfFloatPoint.sortableShortToHalfFloat(HalfFloatPoint.halfFloatToSortableShort(n.floatValue()))) + ); + }; + } + @Override protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java index d2510d3671d23..ba9c2e6c4a299 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java @@ -28,6 +28,7 @@ import java.net.InetAddress; import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -386,6 +387,7 @@ public List invalidExample() throws IOException { } } + @Override protected IngestScriptSupport ingestScriptSupport() { return new IngestScriptSupport() { @Override @@ -419,4 +421,9 @@ public void execute() { } }; } + + @Override + protected Function loadBlockExpected() { + return v -> InetAddresses.toAddrString(InetAddressPoint.decode(BytesRef.deepCopyOf((BytesRef) v).bytes)); + } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IpScriptFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IpScriptFieldTypeTests.java index 26e1763a12c21..56ca5f3dae89f 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IpScriptFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IpScriptFieldTypeTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.index.mapper; +import org.apache.lucene.document.InetAddressPoint; import org.apache.lucene.document.StoredField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.LeafReaderContext; @@ -25,6 +26,7 @@ import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery; +import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.fielddata.BinaryScriptFieldData; import org.elasticsearch.index.fielddata.ScriptDocValues.Strings; @@ -244,6 +246,22 @@ public void testTermsQuery() throws IOException { } } + public void testBlockLoader() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [\"192.168.0\"]}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [\"192.168.1\"]}")))); + try (DirectoryReader reader = iw.getReader()) { + IpScriptFieldType fieldType = build("append_param", Map.of("param", ".1"), OnScriptError.FAIL); + List expected = List.of( + new BytesRef(InetAddressPoint.encode(InetAddresses.forString("192.168.0.1"))), + new BytesRef(InetAddressPoint.encode(InetAddresses.forString("192.168.1.1"))) + ); + assertThat(blockLoaderReadValues(reader, fieldType), equalTo(expected)); + assertThat(blockLoaderReadValuesFromSingleDoc(reader, fieldType), equalTo(expected)); + } + } + } + @Override protected Query randomTermsQuery(MappedFieldType ft, SearchExecutionContext ctx) { return ft.termsQuery(randomList(100, () -> randomIp(randomBoolean())), ctx); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java index 94e2506d2b2a7..eafb33cd44cd4 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java @@ -50,6 +50,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; import static java.util.Collections.singletonList; @@ -648,6 +649,11 @@ protected boolean supportsIgnoreMalformed() { return false; } + @Override + protected Function loadBlockExpected() { + return v -> ((BytesRef) v).utf8ToString(); + } + @Override protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) { assertFalse("keyword doesn't support ignore_malformed", ignoreMalformed); @@ -658,6 +664,7 @@ static class KeywordSyntheticSourceSupport implements SyntheticSourceSupport { private final Integer ignoreAbove = randomBoolean() ? null : between(10, 100); private final boolean allIgnored = ignoreAbove != null && rarely(); private final boolean store; + private final boolean docValues; private final String nullValue; private final boolean exampleSortsUsingIgnoreAbove; @@ -665,13 +672,18 @@ static class KeywordSyntheticSourceSupport implements SyntheticSourceSupport { this.store = store; this.nullValue = nullValue; this.exampleSortsUsingIgnoreAbove = exampleSortsUsingIgnoreAbove; + this.docValues = store ? randomBoolean() : true; } @Override public SyntheticSourceExample example(int maxValues) { if (randomBoolean()) { Tuple v = generateValue(); - return new SyntheticSourceExample(v.v1(), v.v2(), this::mapping); + Object loadBlock = v.v2(); + if (ignoreAbove != null && v.v2().length() > ignoreAbove) { + loadBlock = null; + } + return new SyntheticSourceExample(v.v1(), v.v2(), loadBlock, this::mapping); } List> values = randomList(1, maxValues, this::generateValue); List in = values.stream().map(Tuple::v1).toList(); @@ -685,9 +697,13 @@ public SyntheticSourceExample example(int maxValues) { } }); List outList = store ? outPrimary : new HashSet<>(outPrimary).stream().sorted().collect(Collectors.toList()); + List loadBlock = docValues + ? new HashSet<>(outPrimary).stream().sorted().collect(Collectors.toList()) + : List.copyOf(outList); + Object loadBlockResult = loadBlock.size() == 1 ? loadBlock.get(0) : loadBlock; outList.addAll(outExtraValues); Object out = outList.size() == 1 ? outList.get(0) : outList; - return new SyntheticSourceExample(in, out, this::mapping); + return new SyntheticSourceExample(in, out, loadBlockResult, this::mapping); } private Tuple generateValue() { @@ -712,9 +728,9 @@ private void mapping(XContentBuilder b) throws IOException { } if (store) { b.field("store", true); - if (randomBoolean()) { - b.field("doc_values", false); - } + } + if (docValues == false) { + b.field("doc_values", false); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/KeywordScriptFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/KeywordScriptFieldTypeTests.java index c319ac51803db..65f4c2e3ea6eb 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/KeywordScriptFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/KeywordScriptFieldTypeTests.java @@ -376,6 +376,21 @@ public void testMatchQuery() throws IOException { } } + public void testBlockLoader() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [1]}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [2]}")))); + try (DirectoryReader reader = iw.getReader()) { + KeywordScriptFieldType fieldType = build("append_param", Map.of("param", "-Suffix"), OnScriptError.FAIL); + assertThat(blockLoaderReadValues(reader, fieldType), equalTo(List.of(new BytesRef("1-Suffix"), new BytesRef("2-Suffix")))); + assertThat( + blockLoaderReadValuesFromSingleDoc(reader, fieldType), + equalTo(List.of(new BytesRef("1-Suffix"), new BytesRef("2-Suffix"))) + ); + } + } + } + @Override protected KeywordScriptFieldType simpleMappedFieldType() { return build("read_foo", Map.of(), OnScriptError.FAIL); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java index ad34c407d0678..f2d4431e5c79f 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.math.BigInteger; import java.util.List; +import java.util.function.Function; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -120,6 +121,17 @@ public void testFetchCoerced() throws IOException { assertFetch(randomFetchTestMapper(), "field", 3.783147882954537E18, randomFetchTestFormat()); } + @Override + protected Function loadBlockExpected() { + return n -> { + Number number = ((Number) n); + if (Integer.MIN_VALUE <= number.longValue() && number.longValue() <= Integer.MAX_VALUE) { + return number.intValue(); + } + return number.longValue(); + }; + } + protected IngestScriptSupport ingestScriptSupport() { return new IngestScriptSupport() { @Override diff --git a/server/src/test/java/org/elasticsearch/index/mapper/LongScriptFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/LongScriptFieldTypeTests.java index 20ae732f9c5b0..1688cab24af3e 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/LongScriptFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/LongScriptFieldTypeTests.java @@ -263,6 +263,18 @@ public void testTermsQuery() throws IOException { } } + public void testBlockLoader() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [1]}")))); + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{\"foo\": [2]}")))); + try (DirectoryReader reader = iw.getReader()) { + LongScriptFieldType fieldType = build("add_param", Map.of("param", 1), OnScriptError.FAIL); + assertThat(blockLoaderReadValues(reader, fieldType), equalTo(List.of(2L, 3L))); + assertThat(blockLoaderReadValuesFromSingleDoc(reader, fieldType), equalTo(List.of(2L, 3L))); + } + } + } + @Override protected Query randomTermsQuery(MappedFieldType ft, SearchExecutionContext ctx) { return ft.termsQuery(List.of(randomLong()), ctx); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java index 45a1ac2ced32d..7b91c84a05c53 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.script.ScriptFactory; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.XContentBuilder; +import org.hamcrest.Matcher; import java.io.IOException; import java.util.ArrayList; @@ -37,6 +38,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.notANumber; public abstract class NumberFieldMapperTests extends MapperTestCase { @@ -379,6 +381,16 @@ public void testAllowMultipleValuesField() throws IOException { assertThat(e.getCause().getMessage(), containsString("Only one field can be stored per key")); } + @Override + protected Function loadBlockExpected() { + return n -> ((Number) n); // Just assert it's a number + } + + @Override + protected Matcher blockItemMatcher(Object expected) { + return "NaN".equals(expected) ? notANumber() : equalTo(expected); + } + protected abstract Number randomNumber(); protected final class NumberSyntheticSourceSupport implements SyntheticSourceSupport { @@ -398,10 +410,11 @@ public SyntheticSourceExample example(int maxVals) { if (randomBoolean()) { Tuple v = generateValue(); if (v.v2() instanceof Number n) { - return new SyntheticSourceExample(v.v1(), round.apply(n), this::mapping); + Number result = round.apply(n); + return new SyntheticSourceExample(v.v1(), result, result, this::mapping); } // ignore_malformed value - return new SyntheticSourceExample(v.v1(), v.v2(), this::mapping); + return new SyntheticSourceExample(v.v1(), v.v2(), List.of(), this::mapping); } List> values = randomList(1, maxVals, this::generateValue); List in = values.stream().map(Tuple::v1).toList(); @@ -412,7 +425,14 @@ public SyntheticSourceExample example(int maxVals) { .collect(Collectors.toCollection(ArrayList::new)); values.stream().filter(v -> false == v.v2() instanceof Number).map(v -> v.v2()).forEach(outList::add); Object out = outList.size() == 1 ? outList.get(0) : outList; - return new SyntheticSourceExample(in, out, this::mapping); + + List outBlockList = values.stream() + .filter(v -> v.v2() instanceof Number) + .map(t -> round.apply((Number) t.v2())) + .sorted() + .collect(Collectors.toCollection(ArrayList::new)); + Object outBlock = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; + return new SyntheticSourceExample(in, out, outBlock, this::mapping); } private Tuple generateValue() { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java index 02259a24a5e94..bbfeaaa8b9d69 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java @@ -82,6 +82,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -1132,10 +1133,11 @@ public SyntheticSourceExample example(int maxValues) { return new SyntheticSourceExample( delegate.inputValue(), delegate.result(), + delegate.result(), b -> b.field("type", "text").field("store", true) ); } - return new SyntheticSourceExample(delegate.inputValue(), delegate.result(), b -> { + return new SyntheticSourceExample(delegate.inputValue(), delegate.result(), delegate.blockLoaderResult(), b -> { b.field("type", "text"); b.startObject("fields"); { @@ -1181,6 +1183,11 @@ public List invalidExample() throws IOException { }; } + @Override + protected Function loadBlockExpected() { + return v -> ((BytesRef) v).utf8ToString(); + } + @Override protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java index 6b56418e14663..56ad35bee83d5 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java @@ -11,6 +11,7 @@ import org.apache.lucene.document.StoredField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; @@ -37,6 +38,7 @@ import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -381,6 +383,53 @@ public final void testCacheable() throws IOException { } } + protected final List blockLoaderReadValues(DirectoryReader reader, MappedFieldType fieldType) throws IOException { + BlockLoader loader = fieldType.blockLoader(blContext()); + List all = new ArrayList<>(); + for (LeafReaderContext ctx : reader.leaves()) { + TestBlock block = (TestBlock) loader.reader(ctx).readValues(TestBlock.FACTORY, TestBlock.docs(ctx)); + for (int i = 0; i < block.size(); i++) { + all.add(block.get(i)); + } + } + return all; + } + + protected final List blockLoaderReadValuesFromSingleDoc(DirectoryReader reader, MappedFieldType fieldType) throws IOException { + BlockLoader loader = fieldType.blockLoader(blContext()); + List all = new ArrayList<>(); + for (LeafReaderContext ctx : reader.leaves()) { + BlockDocValuesReader blockReader = loader.reader(ctx); + TestBlock block = (TestBlock) blockReader.builder(TestBlock.FACTORY, ctx.reader().numDocs()); + for (int i = 0; i < ctx.reader().numDocs(); i++) { + blockReader.readValuesFromSingleDoc(i, block); + } + for (int i = 0; i < block.size(); i++) { + all.add(block.get(i)); + } + } + return all; + } + + private MappedFieldType.BlockLoaderContext blContext() { + return new MappedFieldType.BlockLoaderContext() { + @Override + public String indexName() { + throw new UnsupportedOperationException(); + } + + @Override + public SearchLookup lookup() { + return mockContext().lookup(); + } + + @Override + public Set sourcePaths(String name) { + throw new UnsupportedOperationException(); + } + }; + } + private void assertQueryOnlyOnText(String queryName, ThrowingRunnable buildQuery) { Exception e = expectThrows(IllegalArgumentException.class, buildQuery); assertThat( diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java index 9c9d1d763fbd1..e34072fbf1668 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.CheckedConsumer; +import org.elasticsearch.core.CheckedFunction; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersions; @@ -52,6 +53,7 @@ import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.Source; import org.elasticsearch.search.lookup.SourceProvider; +import org.elasticsearch.test.ListMatcher; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.json.JsonXContent; @@ -71,6 +73,7 @@ import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; +import static org.elasticsearch.test.MapMatcher.assertMap; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -1034,10 +1037,24 @@ protected String minimalIsInvalidRoutingPathErrorMessage(Mapper mapper) { public record SyntheticSourceExample( CheckedConsumer inputValue, CheckedConsumer result, + CheckedConsumer blockLoaderResult, CheckedConsumer mapping ) { public SyntheticSourceExample(Object inputValue, Object result, CheckedConsumer mapping) { - this(b -> b.value(inputValue), b -> b.value(result), mapping); + this(b -> b.value(inputValue), b -> b.value(result), b -> b.value(result), mapping); + } + + /** + * Create an example that returns different results from doc values + * than from synthetic source. + */ + public SyntheticSourceExample( + Object inputValue, + Object result, + Object blockLoaderResults, + CheckedConsumer mapping + ) { + this(b -> b.value(inputValue), b -> b.value(result), b -> b.value(blockLoaderResults), mapping); } private void buildInput(XContentBuilder b) throws IOException { @@ -1050,6 +1067,20 @@ private String expected() throws IOException { result.accept(b); return Strings.toString(b.endObject()); } + + private Object expectedParsed() throws IOException { + return XContentHelper.convertToMap(JsonXContent.jsonXContent, expected(), false).get("field"); + } + + private String expectedBlockLoader() throws IOException { + XContentBuilder b = JsonXContent.contentBuilder().startObject().field("field"); + blockLoaderResult.accept(b); + return Strings.toString(b.endObject()); + } + + private Object expectedParsedBlockLoader() throws IOException { + return XContentHelper.convertToMap(JsonXContent.jsonXContent, expectedBlockLoader(), false).get("field"); + } } public record SyntheticSourceInvalidExample(Matcher error, CheckedConsumer mapping) {} @@ -1078,7 +1109,7 @@ public final void testSyntheticSourceIgnoreMalformedExamples() throws IOExceptio assumeTrue("type doesn't support ignore_malformed", supportsIgnoreMalformed()); CheckedConsumer mapping = syntheticSourceSupport(true).example(1).mapping(); for (ExampleMalformedValue v : exampleMalformedValues()) { - assertSyntheticSource(new SyntheticSourceExample(v.value, v.value, mapping)); + assertSyntheticSource(new SyntheticSourceExample(v.value, v.value, v.value, mapping)); } } @@ -1209,6 +1240,99 @@ public final void testSyntheticEmptyListNoDocValuesLoader() throws IOException { assertNoDocValueLoader(b -> b.startArray("field").endArray()); } + public final void testBlockLoaderReadValues() throws IOException { + testBlockLoader(blockReader -> (TestBlock) blockReader.readValues(TestBlock.FACTORY, TestBlock.docs(0))); + } + + public final void testBlockLoaderReadValuesFromSingleDoc() throws IOException { + testBlockLoader(blockReader -> { + TestBlock block = (TestBlock) blockReader.builder(TestBlock.FACTORY, 1); + blockReader.readValuesFromSingleDoc(0, block); + return block; + }); + } + + private void testBlockLoader(CheckedFunction body) throws IOException { + SyntheticSourceExample example = syntheticSourceSupport(false).example(5); + MapperService mapper = createMapperService(syntheticSourceMapping(b -> { + b.startObject("field"); + example.mapping().accept(b); + b.endObject(); + })); + BlockLoader loader = mapper.fieldType("field").blockLoader(new MappedFieldType.BlockLoaderContext() { + @Override + public String indexName() { + throw new UnsupportedOperationException(); + } + + @Override + public SearchLookup lookup() { + throw new UnsupportedOperationException(); + } + + @Override + public Set sourcePaths(String name) { + return mapper.mappingLookup().sourcePaths(name); + } + }); + Function valuesConvert = loadBlockExpected(); + if (valuesConvert == null) { + assertNull(loader); + return; + } + try (Directory directory = newDirectory()) { + RandomIndexWriter iw = new RandomIndexWriter(random(), directory); + LuceneDocument doc = mapper.documentMapper().parse(source(b -> { + b.field("field"); + example.inputValue.accept(b); + })).rootDoc(); + iw.addDocument(doc); + iw.close(); + try (DirectoryReader reader = DirectoryReader.open(directory)) { + TestBlock block = body.apply(loader.reader(reader.leaves().get(0))); + Object inBlock = block.get(0); + if (inBlock != null) { + if (inBlock instanceof List l) { + inBlock = l.stream().map(valuesConvert).toList(); + } else { + inBlock = valuesConvert.apply(inBlock); + } + } + Object expected = loader instanceof BlockSourceReader ? example.expectedParsed() : example.expectedParsedBlockLoader(); + if (List.of().equals(expected)) { + assertThat(inBlock, nullValue()); + return; + } + if (expected instanceof List l) { + ListMatcher m = ListMatcher.matchesList(); + for (Object v : l) { + m = m.item(blockItemMatcher(v)); + } + assertMap((List) inBlock, m); + return; + } + @SuppressWarnings("unchecked") + Matcher e = (Matcher) blockItemMatcher(expected); + assertThat(inBlock, e); + } + } + } + + /** + * Matcher for {@link #testBlockLoaderReadValues} and {@link #testBlockLoaderReadValuesFromSingleDoc}. + */ + protected Matcher blockItemMatcher(Object expected) { + return equalTo(expected); + } + + /** + * How {@link MappedFieldType#blockLoader} should load values or {@code null} + * if that method isn't supported by field being tested. + */ + protected Function loadBlockExpected() { + return null; + } + public final void testEmptyDocumentNoDocValueLoader() throws IOException { assumeFalse("Field will add values even if no fields are supplied", addsValueWhenNotSupplied()); assertNoDocValueLoader(b -> {}); diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java new file mode 100644 index 0000000000000..5a42d5c6890b5 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.core.Nullable; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class TestBlock + implements + BlockLoader.BooleanBuilder, + BlockLoader.BytesRefBuilder, + BlockLoader.DoubleBuilder, + BlockLoader.IntBuilder, + BlockLoader.LongBuilder, + BlockLoader.SingletonOrdinalsBuilder { + public static BlockLoader.BuilderFactory FACTORY = new BlockLoader.BuilderFactory() { + @Override + public BlockLoader.BooleanBuilder booleansFromDocValues(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.BooleanBuilder booleans(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.BytesRefBuilder bytesRefsFromDocValues(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.BytesRefBuilder bytesRefs(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.DoubleBuilder doublesFromDocValues(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.DoubleBuilder doubles(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.IntBuilder intsFromDocValues(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.IntBuilder ints(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.LongBuilder longsFromDocValues(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.LongBuilder longs(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.Builder nulls(int expectedCount) { + return new TestBlock(null); + } + + @Override + public BlockLoader.SingletonOrdinalsBuilder singletonOrdinalsBuilder(SortedDocValues ordinals, int count) { + return new TestBlock(ordinals); + } + }; + + public static final BlockLoader.Docs docs(int... docs) { + return new BlockLoader.Docs() { + @Override + public int count() { + return docs.length; + } + + @Override + public int get(int i) { + return docs[i]; + } + }; + } + + public static final BlockLoader.Docs docs(LeafReaderContext ctx) { + return new BlockLoader.Docs() { + @Override + public int count() { + return ctx.reader().numDocs(); + } + + @Override + public int get(int i) { + return i; + } + }; + } + + private final SortedDocValues sortedDocValues; + private final List values = new ArrayList<>(); + + private List currentPosition = null; + + private TestBlock(@Nullable SortedDocValues sortedDocValues) { + this.sortedDocValues = sortedDocValues; + } + + public Object get(int i) { + return values.get(i); + } + + public int size() { + return values.size(); + } + + @Override + public TestBlock appendNull() { + assertNull(currentPosition); + values.add(null); + return this; + } + + @Override + public TestBlock beginPositionEntry() { + assertNull(currentPosition); + currentPosition = new ArrayList<>(); + values.add(currentPosition); + return this; + } + + @Override + public TestBlock endPositionEntry() { + assertNotNull(currentPosition); + currentPosition = null; + return this; + } + + @Override + public TestBlock appendBoolean(boolean value) { + return add(value); + } + + @Override + public TestBlock appendBytesRef(BytesRef value) { + return add(BytesRef.deepCopyOf(value)); + } + + @Override + public TestBlock appendDouble(double value) { + return add(value); + } + + @Override + public TestBlock appendInt(int value) { + return add(value); + } + + @Override + public TestBlock appendLong(long value) { + return add(value); + } + + @Override + public TestBlock appendOrd(int value) { + try { + return add(sortedDocValues.lookupOrd(value)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private TestBlock add(Object value) { + (currentPosition == null ? values : currentPosition).add(value); + return this; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java index f8f291c2a2e69..c518b3d603319 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.mapper.BlockLoader; import java.io.IOException; @@ -185,11 +186,14 @@ static BooleanBlock newConstantBlockWith(boolean value, int positions, BlockFact return blockFactory.newConstantBooleanBlockWith(value, positions); } - sealed interface Builder extends Block.Builder permits BooleanBlockBuilder { - + /** + * Builder for {@link BooleanBlock} + */ + sealed interface Builder extends Block.Builder, BlockLoader.BooleanBuilder permits BooleanBlockBuilder { /** * Appends a boolean to the current entry. */ + @Override Builder appendBoolean(boolean value); /** @@ -213,12 +217,11 @@ sealed interface Builder extends Block.Builder permits BooleanBlockBuilder { @Override Builder mvOrdering(Block.MvOrdering mvOrdering); - // TODO boolean containsMvDups(); - /** * Appends the all values of the given block into a the current position * in this builder. */ + @Override Builder appendAllValuesToCurrentPosition(Block block); /** diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java index 488d3032b2b08..6da60fbfe011d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.mapper.BlockLoader; import java.io.IOException; @@ -190,11 +191,14 @@ static BytesRefBlock newConstantBlockWith(BytesRef value, int positions, BlockFa return blockFactory.newConstantBytesRefBlockWith(value, positions); } - sealed interface Builder extends Block.Builder permits BytesRefBlockBuilder { - + /** + * Builder for {@link BytesRefBlock} + */ + sealed interface Builder extends Block.Builder, BlockLoader.BytesRefBuilder permits BytesRefBlockBuilder { /** * Appends a BytesRef to the current entry. */ + @Override Builder appendBytesRef(BytesRef value); /** @@ -218,12 +222,11 @@ sealed interface Builder extends Block.Builder permits BytesRefBlockBuilder { @Override Builder mvOrdering(Block.MvOrdering mvOrdering); - // TODO boolean containsMvDups(); - /** * Appends the all values of the given block into a the current position * in this builder. */ + @Override Builder appendAllValuesToCurrentPosition(Block block); /** diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java index c2e63a0c6f384..9d42e5dd3f284 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.mapper.BlockLoader; import java.io.IOException; @@ -186,11 +187,14 @@ static DoubleBlock newConstantBlockWith(double value, int positions, BlockFactor return blockFactory.newConstantDoubleBlockWith(value, positions); } - sealed interface Builder extends Block.Builder permits DoubleBlockBuilder { - + /** + * Builder for {@link DoubleBlock} + */ + sealed interface Builder extends Block.Builder, BlockLoader.DoubleBuilder permits DoubleBlockBuilder { /** * Appends a double to the current entry. */ + @Override Builder appendDouble(double value); /** @@ -214,12 +218,11 @@ sealed interface Builder extends Block.Builder permits DoubleBlockBuilder { @Override Builder mvOrdering(Block.MvOrdering mvOrdering); - // TODO boolean containsMvDups(); - /** * Appends the all values of the given block into a the current position * in this builder. */ + @Override Builder appendAllValuesToCurrentPosition(Block block); /** diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java index f27e855809491..d4305e23dd4a1 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.mapper.BlockLoader; import java.io.IOException; @@ -185,11 +186,14 @@ static IntBlock newConstantBlockWith(int value, int positions, BlockFactory bloc return blockFactory.newConstantIntBlockWith(value, positions); } - sealed interface Builder extends Block.Builder permits IntBlockBuilder { - + /** + * Builder for {@link IntBlock} + */ + sealed interface Builder extends Block.Builder, BlockLoader.IntBuilder permits IntBlockBuilder { /** * Appends a int to the current entry. */ + @Override Builder appendInt(int value); /** @@ -213,12 +217,11 @@ sealed interface Builder extends Block.Builder permits IntBlockBuilder { @Override Builder mvOrdering(Block.MvOrdering mvOrdering); - // TODO boolean containsMvDups(); - /** * Appends the all values of the given block into a the current position * in this builder. */ + @Override Builder appendAllValuesToCurrentPosition(Block block); /** diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java index 287b55eac3d04..9def8475161ff 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.mapper.BlockLoader; import java.io.IOException; @@ -186,11 +187,14 @@ static LongBlock newConstantBlockWith(long value, int positions, BlockFactory bl return blockFactory.newConstantLongBlockWith(value, positions); } - sealed interface Builder extends Block.Builder permits LongBlockBuilder { - + /** + * Builder for {@link LongBlock} + */ + sealed interface Builder extends Block.Builder, BlockLoader.LongBuilder permits LongBlockBuilder { /** * Appends a long to the current entry. */ + @Override Builder appendLong(long value); /** @@ -214,12 +218,11 @@ sealed interface Builder extends Block.Builder permits LongBlockBuilder { @Override Builder mvOrdering(Block.MvOrdering mvOrdering); - // TODO boolean containsMvDups(); - /** * Appends the all values of the given block into a the current position * in this builder. */ + @Override Builder appendAllValuesToCurrentPosition(Block block); /** diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index c5d6780e84685..d7c3a5cb9bfab 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Releasable; +import org.elasticsearch.index.mapper.BlockLoader; import java.util.List; @@ -160,7 +161,7 @@ static Block constantNullBlock(int positions, BlockFactory blockFactory) { * Builds {@link Block}s. Typically, you use one of it's direct supinterfaces like {@link IntBlock.Builder}. * This is {@link Releasable} and should be released after building the block or if building the block fails. */ - interface Builder extends Releasable { + interface Builder extends BlockLoader.Builder, Releasable { /** * Appends a null value to the block. diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilder.java new file mode 100644 index 0000000000000..703d882b91029 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilder.java @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.operator.BreakingBytesRefBuilder; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.index.mapper.BlockLoader; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Arrays; + +public class SingletonOrdinalsBuilder implements BlockLoader.SingletonOrdinalsBuilder, Releasable, Block.Builder { + private final BlockFactory blockFactory; + private final SortedDocValues docValues; + private int[] ords; + private int count; + + public SingletonOrdinalsBuilder(BlockFactory blockFactory, SortedDocValues docValues, int count) { + this.blockFactory = blockFactory; + this.docValues = docValues; + blockFactory.adjustBreaker(ordsSize(count), false); + this.ords = new int[count]; + } + + @Override + public SingletonOrdinalsBuilder appendNull() { + ords[count++] = -1; // real ords can't be < 0, so we use -1 as null + return this; + } + + @Override + public SingletonOrdinalsBuilder appendOrd(int value) { + ords[count++] = value; + return this; + } + + int[] ords() { + return ords; + } + + @Override + public SingletonOrdinalsBuilder beginPositionEntry() { + throw new UnsupportedOperationException("should only have one value per doc"); + } + + @Override + public SingletonOrdinalsBuilder endPositionEntry() { + throw new UnsupportedOperationException("should only have one value per doc"); + } + + @Override + public BytesRefBlock build() { + try { + long breakerSize = ordsSize(ords.length); + // Increment breaker for sorted ords. + blockFactory.adjustBreaker(breakerSize, false); + try { + int[] sortedOrds = ords.clone(); + Arrays.sort(sortedOrds); + int uniqueCount = compactToUnique(sortedOrds); + + try (BreakingBytesRefBuilder copies = new BreakingBytesRefBuilder(blockFactory.breaker(), "ords")) { + long offsetsAndLength = RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + (uniqueCount + 1) * Integer.BYTES; + blockFactory.adjustBreaker(offsetsAndLength, false); + breakerSize += offsetsAndLength; + int[] offsets = new int[uniqueCount + 1]; + for (int o = 0; o < uniqueCount; o++) { + BytesRef v = docValues.lookupOrd(sortedOrds[o]); + offsets[o] = copies.length(); + copies.append(v); + } + offsets[uniqueCount] = copies.length(); + + /* + * It'd be better if BytesRefBlock could run off of a deduplicated list of + * blocks. It can't at the moment. So we copy many times. + */ + BytesRef scratch = new BytesRef(); + scratch.bytes = copies.bytes(); + try (BytesRefBlock.Builder builder = blockFactory.newBytesRefBlockBuilder(ords.length)) { + for (int i = 0; i < ords.length; i++) { + if (ords[i] == -1) { + builder.appendNull(); + continue; + } + int o = Arrays.binarySearch(sortedOrds, 0, uniqueCount, ords[i]); + assert 0 <= o && o < uniqueCount; + scratch.offset = offsets[o]; + scratch.length = offsets[o + 1] - scratch.offset; + builder.appendBytesRef(scratch); + } + return builder.build(); + } + } + } finally { + blockFactory.adjustBreaker(-breakerSize, false); + } + } catch (IOException e) { + throw new UncheckedIOException("error resolving ordinals", e); + } + } + + @Override + public void close() { + blockFactory.adjustBreaker(-ordsSize(ords.length), false); + } + + @Override + public Block.Builder appendAllValuesToCurrentPosition(Block block) { + throw new UnsupportedOperationException(); + } + + @Override + public Block.Builder copyFrom(Block block, int beginInclusive, int endExclusive) { + throw new UnsupportedOperationException(); + } + + @Override + public Block.Builder mvOrdering(Block.MvOrdering mvOrdering) { + throw new UnsupportedOperationException(); + } + + private static long ordsSize(int ordsCount) { + return RamUsageEstimator.NUM_BYTES_ARRAY_HEADER + ordsCount * Integer.BYTES; + } + + static int compactToUnique(int[] sortedOrds) { + Arrays.sort(sortedOrds); + int uniqueSize = 0; + int prev = -1; + for (int i = 0; i < sortedOrds.length; i++) { + if (sortedOrds[i] != prev) { + sortedOrds[uniqueSize++] = prev = sortedOrds[i]; + } + } + return uniqueSize; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st index 1dac4f1783e44..7f03e22332ccf 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st @@ -14,6 +14,7 @@ $endif$ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.index.mapper.BlockLoader; import java.io.IOException; @@ -222,11 +223,14 @@ $endif$ return blockFactory.newConstant$Type$BlockWith(value, positions); } - sealed interface Builder extends Block.Builder permits $Type$BlockBuilder { - + /** + * Builder for {@link $Type$Block} + */ + sealed interface Builder extends Block.Builder, BlockLoader.$Type$Builder permits $Type$BlockBuilder { /** * Appends a $type$ to the current entry. */ + @Override Builder append$Type$($type$ value); /** @@ -250,12 +254,11 @@ $endif$ @Override Builder mvOrdering(Block.MvOrdering mvOrdering); - // TODO boolean containsMvDups(); - /** * Appends the all values of the given block into a the current position * in this builder. */ + @Override Builder appendAllValuesToCurrentPosition(Block block); /** diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java deleted file mode 100644 index 28a9359497393..0000000000000 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockDocValuesReader.java +++ /dev/null @@ -1,698 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.compute.lucene; - -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.NumericDocValues; -import org.apache.lucene.index.SortedNumericDocValues; -import org.elasticsearch.compute.data.Block; -import org.elasticsearch.compute.data.BooleanBlock; -import org.elasticsearch.compute.data.BytesRefBlock; -import org.elasticsearch.compute.data.DoubleBlock; -import org.elasticsearch.compute.data.ElementType; -import org.elasticsearch.compute.data.IntBlock; -import org.elasticsearch.compute.data.IntVector; -import org.elasticsearch.compute.data.LongBlock; -import org.elasticsearch.index.fielddata.FieldData; -import org.elasticsearch.index.fielddata.NumericDoubleValues; -import org.elasticsearch.index.fielddata.SortedBinaryDocValues; -import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.ValuesSourceType; - -import java.io.IOException; - -/** - * A reader that supports reading doc-values from a Lucene segment in Block fashion. - */ -public abstract class BlockDocValuesReader { - - protected final Thread creationThread; - - public BlockDocValuesReader() { - this.creationThread = Thread.currentThread(); - } - - /** - * Returns the current doc that this reader is on. - */ - public abstract int docID(); - - /** - * The {@link Block.Builder} for data of this type. - */ - public abstract Block.Builder builder(int positionCount); - - /** - * Reads the values of the given documents specified in the input block - */ - public abstract Block readValues(IntVector docs) throws IOException; - - /** - * Reads the values of the given document into the builder - */ - public abstract void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException; - - /** - * Checks if the reader can be used to read a range documents starting with the given docID by the current thread. - */ - public static boolean canReuse(BlockDocValuesReader reader, int startingDocID) { - return reader != null && reader.creationThread == Thread.currentThread() && reader.docID() <= startingDocID; - } - - public static BlockDocValuesReader createBlockReader( - ValuesSource valuesSource, - ValuesSourceType valuesSourceType, - ElementType elementType, - LeafReaderContext leafReaderContext - ) throws IOException { - if (valuesSourceType instanceof UnsupportedValueSourceType) { - final UnsupportedValueSource bytesVS = (UnsupportedValueSource) valuesSource; - final SortedBinaryDocValues bytesValues = bytesVS.bytesValues(leafReaderContext); - return new BytesValuesReader(bytesValues); - } - if (CoreValuesSourceType.NUMERIC.equals(valuesSourceType) || CoreValuesSourceType.DATE.equals(valuesSourceType)) { - ValuesSource.Numeric numericVS = (ValuesSource.Numeric) valuesSource; - if (numericVS.isFloatingPoint()) { - if (elementType != ElementType.DOUBLE) { - throw new UnsupportedOperationException("can't extract [" + elementType + "] from floating point fields"); - } - final SortedNumericDoubleValues doubleValues = numericVS.doubleValues(leafReaderContext); - final NumericDoubleValues singleton = FieldData.unwrapSingleton(doubleValues); - if (singleton != null) { - return new DoubleSingletonValuesReader(singleton); - } - return new DoubleValuesReader(doubleValues); - } else { - final SortedNumericDocValues longValues = numericVS.longValues(leafReaderContext); - final NumericDocValues singleton = DocValues.unwrapSingleton(longValues); - if (singleton != null) { - return switch (elementType) { - case LONG -> new LongSingletonValuesReader(singleton); - case INT -> new IntSingletonValuesReader(singleton); - default -> throw new UnsupportedOperationException("can't extract [" + elementType + "] from integer fields"); - }; - } - return switch (elementType) { - case LONG -> new LongValuesReader(longValues); - case INT -> new IntValuesReader(longValues); - default -> throw new UnsupportedOperationException("can't extract [" + elementType + "] from integer fields"); - }; - } - } - if (CoreValuesSourceType.KEYWORD.equals(valuesSourceType) || CoreValuesSourceType.IP.equals(valuesSourceType)) { - if (elementType != ElementType.BYTES_REF) { - throw new UnsupportedOperationException("can't extract [" + elementType + "] from keywords"); - } - final ValuesSource.Bytes bytesVS = (ValuesSource.Bytes) valuesSource; - final SortedBinaryDocValues bytesValues = bytesVS.bytesValues(leafReaderContext); - return new BytesValuesReader(bytesValues); - } - if (CoreValuesSourceType.BOOLEAN.equals(valuesSourceType)) { - if (elementType != ElementType.BOOLEAN) { - throw new UnsupportedOperationException("can't extract [" + elementType + "] from booleans"); - } - ValuesSource.Numeric numericVS = (ValuesSource.Numeric) valuesSource; - final SortedNumericDocValues longValues = numericVS.longValues(leafReaderContext); - final NumericDocValues singleton = DocValues.unwrapSingleton(longValues); - if (singleton != null) { - return new BooleanSingletonValuesReader(singleton); - } - return new BooleanValuesReader(longValues); - } - if (valuesSourceType instanceof NullValueSourceType) { - return new NullValuesReader(); - } - throw new IllegalArgumentException("Field type [" + valuesSourceType.typeName() + "] is not supported"); - } - - private static class LongSingletonValuesReader extends BlockDocValuesReader { - private final NumericDocValues numericDocValues; - - LongSingletonValuesReader(NumericDocValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public LongBlock.Builder builder(int positionCount) { - return LongBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public LongBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - int lastDoc = -1; - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendLong(numericDocValues.longValue()); - } else { - blockBuilder.appendNull(); - } - lastDoc = doc; - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - LongBlock.Builder blockBuilder = (LongBlock.Builder) builder; - if (numericDocValues.advanceExact(docId)) { - blockBuilder.appendLong(numericDocValues.longValue()); - } else { - blockBuilder.appendNull(); - } - } - - @Override - public int docID() { - return numericDocValues.docID(); - } - - @Override - public String toString() { - return "LongSingletonValuesReader"; - } - } - - private static class LongValuesReader extends BlockDocValuesReader { - private final SortedNumericDocValues numericDocValues; - private int docID = -1; - - LongValuesReader(SortedNumericDocValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public LongBlock.Builder builder(int positionCount) { - return LongBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public LongBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); - } - read(doc, blockBuilder); - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - read(docId, (LongBlock.Builder) builder); - } - - private void read(int doc, LongBlock.Builder builder) throws IOException { - this.docID = doc; - if (false == numericDocValues.advanceExact(doc)) { - builder.appendNull(); - return; - } - int count = numericDocValues.docValueCount(); - if (count == 1) { - builder.appendLong(numericDocValues.nextValue()); - return; - } - builder.beginPositionEntry(); - for (int v = 0; v < count; v++) { - builder.appendLong(numericDocValues.nextValue()); - } - builder.endPositionEntry(); - } - - @Override - public int docID() { - // There is a .docID on the numericDocValues but it is often not implemented. - return docID; - } - - @Override - public String toString() { - return "LongValuesReader"; - } - } - - private static class IntSingletonValuesReader extends BlockDocValuesReader { - private final NumericDocValues numericDocValues; - - IntSingletonValuesReader(NumericDocValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public IntBlock.Builder builder(int positionCount) { - return IntBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public IntBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - int lastDoc = -1; - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendInt(Math.toIntExact(numericDocValues.longValue())); - } else { - blockBuilder.appendNull(); - } - lastDoc = doc; - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - IntBlock.Builder blockBuilder = (IntBlock.Builder) builder; - if (numericDocValues.advanceExact(docId)) { - blockBuilder.appendInt(Math.toIntExact(numericDocValues.longValue())); - } else { - blockBuilder.appendNull(); - } - } - - @Override - public int docID() { - return numericDocValues.docID(); - } - - @Override - public String toString() { - return "LongSingletonValuesReader"; - } - } - - private static class IntValuesReader extends BlockDocValuesReader { - private final SortedNumericDocValues numericDocValues; - private int docID = -1; - - IntValuesReader(SortedNumericDocValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public IntBlock.Builder builder(int positionCount) { - return IntBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public IntBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < this.docID) { - // TODO this may not be true after sorting many docs in a single segment. - throw new IllegalStateException("docs within same block must be in order"); - } - read(doc, blockBuilder); - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - read(docId, (IntBlock.Builder) builder); - } - - private void read(int doc, IntBlock.Builder builder) throws IOException { - this.docID = doc; - if (false == numericDocValues.advanceExact(doc)) { - builder.appendNull(); - return; - } - int count = numericDocValues.docValueCount(); - if (count == 1) { - builder.appendInt(Math.toIntExact(numericDocValues.nextValue())); - return; - } - builder.beginPositionEntry(); - for (int v = 0; v < count; v++) { - builder.appendInt(Math.toIntExact(numericDocValues.nextValue())); - } - builder.endPositionEntry(); - } - - @Override - public int docID() { - // There is a .docID on on the numericDocValues but it is often not implemented. - return docID; - } - - @Override - public String toString() { - return "LongValuesReader"; - } - } - - private static class DoubleSingletonValuesReader extends BlockDocValuesReader { - private final NumericDoubleValues numericDocValues; - private int docID = -1; - - DoubleSingletonValuesReader(NumericDoubleValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public DoubleBlock.Builder builder(int positionCount) { - return DoubleBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public DoubleBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - int lastDoc = -1; - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendDouble(numericDocValues.doubleValue()); - } else { - blockBuilder.appendNull(); - } - lastDoc = doc; - this.docID = doc; - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - this.docID = docId; - DoubleBlock.Builder blockBuilder = (DoubleBlock.Builder) builder; - if (numericDocValues.advanceExact(this.docID)) { - blockBuilder.appendDouble(numericDocValues.doubleValue()); - } else { - blockBuilder.appendNull(); - } - } - - @Override - public int docID() { - return docID; - } - - @Override - public String toString() { - return "DoubleSingletonValuesReader"; - } - } - - private static class DoubleValuesReader extends BlockDocValuesReader { - private final SortedNumericDoubleValues numericDocValues; - private int docID = -1; - - DoubleValuesReader(SortedNumericDoubleValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public DoubleBlock.Builder builder(int positionCount) { - return DoubleBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public DoubleBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); - } - read(doc, blockBuilder); - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - read(docId, (DoubleBlock.Builder) builder); - } - - private void read(int doc, DoubleBlock.Builder builder) throws IOException { - this.docID = doc; - if (false == numericDocValues.advanceExact(doc)) { - builder.appendNull(); - return; - } - int count = numericDocValues.docValueCount(); - if (count == 1) { - builder.appendDouble(numericDocValues.nextValue()); - return; - } - builder.beginPositionEntry(); - for (int v = 0; v < count; v++) { - builder.appendDouble(numericDocValues.nextValue()); - } - builder.endPositionEntry(); - } - - @Override - public int docID() { - return docID; - } - - @Override - public String toString() { - return "DoubleValuesReader"; - } - } - - private static class BytesValuesReader extends BlockDocValuesReader { - private final SortedBinaryDocValues binaryDV; - private int docID = -1; - - BytesValuesReader(SortedBinaryDocValues binaryDV) { - this.binaryDV = binaryDV; - } - - @Override - public BytesRefBlock.Builder builder(int positionCount) { - return BytesRefBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public BytesRefBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - for (int i = 0; i < docs.getPositionCount(); i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); - } - read(doc, blockBuilder); - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - read(docId, (BytesRefBlock.Builder) builder); - } - - private void read(int doc, BytesRefBlock.Builder builder) throws IOException { - this.docID = doc; - if (false == binaryDV.advanceExact(doc)) { - builder.appendNull(); - return; - } - int count = binaryDV.docValueCount(); - if (count == 1) { - builder.appendBytesRef(binaryDV.nextValue()); - return; - } - builder.beginPositionEntry(); - for (int v = 0; v < count; v++) { - builder.appendBytesRef(binaryDV.nextValue()); - } - builder.endPositionEntry(); - } - - @Override - public int docID() { - return docID; - } - - @Override - public String toString() { - return "BytesValuesReader"; - } - } - - private static class BooleanSingletonValuesReader extends BlockDocValuesReader { - private final NumericDocValues numericDocValues; - - BooleanSingletonValuesReader(NumericDocValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public BooleanBlock.Builder builder(int positionCount) { - return BooleanBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public BooleanBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - int lastDoc = -1; - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendBoolean(numericDocValues.longValue() != 0); - } else { - blockBuilder.appendNull(); - } - lastDoc = doc; - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - BooleanBlock.Builder blockBuilder = (BooleanBlock.Builder) builder; - if (numericDocValues.advanceExact(docId)) { - blockBuilder.appendBoolean(numericDocValues.longValue() != 0); - } else { - blockBuilder.appendNull(); - } - } - - @Override - public int docID() { - return numericDocValues.docID(); - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - } - - private static class BooleanValuesReader extends BlockDocValuesReader { - private final SortedNumericDocValues numericDocValues; - private int docID = -1; - - BooleanValuesReader(SortedNumericDocValues numericDocValues) { - this.numericDocValues = numericDocValues; - } - - @Override - public BooleanBlock.Builder builder(int positionCount) { - return BooleanBlock.newBlockBuilder(positionCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); - } - - @Override - public BooleanBlock readValues(IntVector docs) throws IOException { - final int positionCount = docs.getPositionCount(); - var blockBuilder = builder(positionCount); - for (int i = 0; i < positionCount; i++) { - int doc = docs.getInt(i); - // docs within same block must be in order - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); - } - read(doc, blockBuilder); - } - return blockBuilder.build(); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) throws IOException { - read(docId, (BooleanBlock.Builder) builder); - } - - private void read(int doc, BooleanBlock.Builder builder) throws IOException { - this.docID = doc; - if (false == numericDocValues.advanceExact(doc)) { - builder.appendNull(); - return; - } - int count = numericDocValues.docValueCount(); - if (count == 1) { - builder.appendBoolean(numericDocValues.nextValue() != 0); - return; - } - builder.beginPositionEntry(); - for (int v = 0; v < count; v++) { - builder.appendBoolean(numericDocValues.nextValue() != 0); - } - builder.endPositionEntry(); - } - - @Override - public int docID() { - // There is a .docID on the numericDocValues but it is often not implemented. - return docID; - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - } - - private static class NullValuesReader extends BlockDocValuesReader { - private int docID = -1; - - @Override - public Block.Builder builder(int positionCount) { - return ElementType.NULL.newBlockBuilder(positionCount); - } - - @Override - public Block readValues(IntVector docs) throws IOException { - return Block.constantNullBlock(docs.getPositionCount()); - } - - @Override - public void readValuesFromSingleDoc(int docId, Block.Builder builder) { - this.docID = docId; - builder.appendNull(); - } - - @Override - public int docID() { - return docID; - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - } -} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockReaderFactories.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockReaderFactories.java new file mode 100644 index 0000000000000..a0d08bc798fbb --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/BlockReaderFactories.java @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.lucene; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.SortedSetDocValues; +import org.elasticsearch.common.logging.HeaderWarning; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.query.SearchExecutionContext; +import org.elasticsearch.search.internal.SearchContext; +import org.elasticsearch.search.lookup.SearchLookup; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Resolves *how* ESQL loads field values. + */ +public final class BlockReaderFactories { + private BlockReaderFactories() {} + + /** + * Resolves *how* ESQL loads field values. + * @param searchContexts a search context per search index we're loading + * field from + * @param fieldName the name of the field to load + * @param asUnsupportedSource should the field be loaded as "unsupported"? + * These will always have {@code null} values + */ + public static List factories( + List searchContexts, + String fieldName, + boolean asUnsupportedSource + ) { + List factories = new ArrayList<>(searchContexts.size()); + + for (SearchContext searchContext : searchContexts) { + SearchExecutionContext ctx = searchContext.getSearchExecutionContext(); + if (asUnsupportedSource) { + factories.add(loaderToFactory(ctx.getIndexReader(), BlockDocValuesReader.nulls())); + continue; + } + MappedFieldType fieldType = ctx.getFieldType(fieldName); + if (fieldType == null) { + // the field does not exist in this context + factories.add(loaderToFactory(ctx.getIndexReader(), BlockDocValuesReader.nulls())); + continue; + } + BlockLoader loader = fieldType.blockLoader(new MappedFieldType.BlockLoaderContext() { + @Override + public String indexName() { + return ctx.getFullyQualifiedIndex().getName(); + } + + @Override + public SearchLookup lookup() { + return ctx.lookup(); + } + + @Override + public Set sourcePaths(String name) { + return ctx.sourcePath(name); + } + }); + if (loader == null) { + HeaderWarning.addWarning("Field [{}] cannot be retrieved, it is unsupported or not indexed; returning null", fieldName); + factories.add(loaderToFactory(ctx.getIndexReader(), BlockDocValuesReader.nulls())); + continue; + } + factories.add(loaderToFactory(ctx.getIndexReader(), loader)); + } + + return factories; + } + + /** + * Converts a {@link BlockLoader}, something defined in core elasticsearch at + * the field level, into a {@link BlockDocValuesReader.Factory} which can be + * used inside ESQL. + */ + public static BlockDocValuesReader.Factory loaderToFactory(IndexReader reader, BlockLoader loader) { + return new BlockDocValuesReader.Factory() { + @Override + public BlockDocValuesReader build(int segment) throws IOException { + return loader.reader(reader.leaves().get(segment)); + } + + @Override + public boolean supportsOrdinals() { + return loader.supportsOrdinals(); + } + + @Override + public SortedSetDocValues ordinals(int segment) throws IOException { + return loader.ordinals(reader.leaves().get(segment)); + } + }; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSource.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSource.java deleted file mode 100644 index fc9807b2e2410..0000000000000 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSource.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.compute.lucene; - -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.Rounding; -import org.elasticsearch.index.fielddata.DocValueBits; -import org.elasticsearch.index.fielddata.SortedBinaryDocValues; -import org.elasticsearch.search.aggregations.support.AggregationContext; -import org.elasticsearch.search.aggregations.support.ValuesSource; - -import java.io.IOException; -import java.util.function.Function; - -public class NullValueSource extends ValuesSource { - - @Override - public SortedBinaryDocValues bytesValues(LeafReaderContext context) throws IOException { - - return new SortedBinaryDocValues() { - @Override - public boolean advanceExact(int doc) throws IOException { - return true; - } - - @Override - public int docValueCount() { - return 1; - } - - @Override - public BytesRef nextValue() throws IOException { - return null; - } - }; - } - - @Override - public DocValueBits docsWithValue(LeafReaderContext context) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - protected Function roundingPreparer(AggregationContext context) throws IOException { - throw new UnsupportedOperationException(); - } -} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSourceType.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSourceType.java deleted file mode 100644 index fd354bd9e1a0b..0000000000000 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/NullValueSourceType.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.compute.lucene; - -import org.elasticsearch.script.AggregationScript; -import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.support.AggregationContext; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValueType; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.ValuesSourceType; - -public class NullValueSourceType implements ValuesSourceType { - - @Override - public ValuesSource getEmpty() { - throw new UnsupportedOperationException(); - } - - @Override - public ValuesSource getScript(AggregationScript.LeafFactory script, ValueType scriptValueType) { - throw new UnsupportedOperationException(); - } - - @Override - public ValuesSource getField(FieldContext fieldContext, AggregationScript.LeafFactory script) { - throw new UnsupportedOperationException(); - } - - @Override - public ValuesSource replaceMissing( - ValuesSource valuesSource, - Object rawMissing, - DocValueFormat docValueFormat, - AggregationContext context - ) { - throw new UnsupportedOperationException(); - } - - @Override - public String typeName() { - return null; - } - -} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSourceInfo.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSourceInfo.java deleted file mode 100644 index e4dffdfe72c4d..0000000000000 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSourceInfo.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.compute.lucene; - -import org.apache.lucene.index.IndexReader; -import org.elasticsearch.compute.data.ElementType; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.ValuesSourceType; - -public record ValueSourceInfo(ValuesSourceType type, ValuesSource source, ElementType elementType, IndexReader reader) {} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java deleted file mode 100644 index 29a539b1e068e..0000000000000 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValueSources.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.compute.lucene; - -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.SortedNumericDocValues; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.logging.HeaderWarning; -import org.elasticsearch.compute.data.ElementType; -import org.elasticsearch.index.fielddata.FieldDataContext; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.SortedBinaryDocValues; -import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; -import org.elasticsearch.index.fielddata.SourceValueFetcherSortedBinaryIndexFieldData; -import org.elasticsearch.index.fielddata.StoredFieldSortedBinaryIndexFieldData; -import org.elasticsearch.index.mapper.IdFieldMapper; -import org.elasticsearch.index.mapper.KeywordFieldMapper; -import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.SourceValueFetcher; -import org.elasticsearch.index.mapper.TextFieldMapper; -import org.elasticsearch.index.query.SearchExecutionContext; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.search.internal.ShardSearchRequest; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public final class ValueSources { - - public static final String MATCH_ONLY_TEXT = "match_only_text"; - - private ValueSources() {} - - public static List sources( - List searchContexts, - String fieldName, - boolean asUnsupportedSource, - ElementType elementType - ) { - List sources = new ArrayList<>(searchContexts.size()); - - for (SearchContext searchContext : searchContexts) { - // TODO: remove this workaround - // Create a separate SearchExecutionContext for each ValuesReader, as it seems that - // the synthetic source doesn't work properly with inter-segment or intra-segment parallelism. - ShardSearchRequest shardRequest = searchContext.request(); - SearchExecutionContext ctx = searchContext.readerContext() - .indexService() - .newSearchExecutionContext( - shardRequest.shardId().id(), - shardRequest.shardRequestIndex(), - searchContext.searcher(), - shardRequest::nowInMillis, - shardRequest.getClusterAlias(), - shardRequest.getRuntimeMappings() - ); - var fieldType = ctx.getFieldType(fieldName); - if (fieldType == null) { - sources.add(new ValueSourceInfo(new NullValueSourceType(), new NullValueSource(), elementType, ctx.getIndexReader())); - continue; // the field does not exist in this context - } - if (asUnsupportedSource) { - sources.add( - new ValueSourceInfo( - new UnsupportedValueSourceType(fieldType.typeName()), - new UnsupportedValueSource(null), - elementType, - ctx.getIndexReader() - ) - ); - HeaderWarning.addWarning("Field [{}] cannot be retrieved, it is unsupported or not indexed; returning null", fieldName); - continue; - } - - if (fieldType.hasDocValues() == false) { - // MatchOnlyTextFieldMapper class lives in the mapper-extras module. We use string equality - // for the field type name to avoid adding a dependency to the module - if (fieldType instanceof KeywordFieldMapper.KeywordFieldType - || fieldType instanceof TextFieldMapper.TextFieldType tft && (tft.isSyntheticSource() == false || tft.isStored()) - || MATCH_ONLY_TEXT.equals(fieldType.typeName())) { - ValuesSource vs = textValueSource(ctx, fieldType); - sources.add(new ValueSourceInfo(CoreValuesSourceType.KEYWORD, vs, elementType, ctx.getIndexReader())); - continue; - } - - if (IdFieldMapper.NAME.equals(fieldType.name())) { - ValuesSource vs = new IdValueSource(new IdFieldIndexFieldData(CoreValuesSourceType.KEYWORD)); - sources.add(new ValueSourceInfo(CoreValuesSourceType.KEYWORD, vs, elementType, ctx.getIndexReader())); - continue; - } - } - - IndexFieldData fieldData; - try { - fieldData = ctx.getForField(fieldType, MappedFieldType.FielddataOperation.SEARCH); - } catch (IllegalArgumentException e) { - sources.add(unsupportedValueSource(elementType, ctx, fieldType, e)); - HeaderWarning.addWarning("Field [{}] cannot be retrieved, it is unsupported or not indexed; returning null", fieldName); - continue; - } - var fieldContext = new FieldContext(fieldName, fieldData, fieldType); - var vsType = fieldData.getValuesSourceType(); - var vs = vsType.getField(fieldContext, null); - sources.add(new ValueSourceInfo(vsType, vs, elementType, ctx.getIndexReader())); - } - - return sources; - } - - private static ValueSourceInfo unsupportedValueSource( - ElementType elementType, - SearchExecutionContext ctx, - MappedFieldType fieldType, - IllegalArgumentException e - ) { - return switch (elementType) { - case BYTES_REF -> new ValueSourceInfo( - new UnsupportedValueSourceType(fieldType.typeName()), - new UnsupportedValueSource(null), - elementType, - ctx.getIndexReader() - ); - case LONG, INT -> new ValueSourceInfo( - CoreValuesSourceType.NUMERIC, - ValuesSource.Numeric.EMPTY, - elementType, - ctx.getIndexReader() - ); - case BOOLEAN -> new ValueSourceInfo( - CoreValuesSourceType.BOOLEAN, - ValuesSource.Numeric.EMPTY, - elementType, - ctx.getIndexReader() - ); - case DOUBLE -> new ValueSourceInfo(CoreValuesSourceType.NUMERIC, new ValuesSource.Numeric() { - @Override - public boolean isFloatingPoint() { - return true; - } - - @Override - public SortedNumericDocValues longValues(LeafReaderContext context) { - return DocValues.emptySortedNumeric(); - } - - @Override - public SortedNumericDoubleValues doubleValues(LeafReaderContext context) throws IOException { - return org.elasticsearch.index.fielddata.FieldData.emptySortedNumericDoubles(); - } - - @Override - public SortedBinaryDocValues bytesValues(LeafReaderContext context) throws IOException { - return org.elasticsearch.index.fielddata.FieldData.emptySortedBinary(); - } - }, elementType, ctx.getIndexReader()); - default -> throw e; - }; - } - - private static TextValueSource textValueSource(SearchExecutionContext ctx, MappedFieldType fieldType) { - if (fieldType.isStored()) { - IndexFieldData fieldData = new StoredFieldSortedBinaryIndexFieldData( - fieldType.name(), - CoreValuesSourceType.KEYWORD, - TextValueSource.TextDocValuesFieldWrapper::new - ) { - @Override - protected BytesRef storedToBytesRef(Object stored) { - return new BytesRef((String) stored); - } - }; - return new TextValueSource(fieldData); - } - - FieldDataContext fieldDataContext = new FieldDataContext( - ctx.getFullyQualifiedIndex().getName(), - () -> ctx.lookup().forkAndTrackFieldReferences(fieldType.name()), - ctx::sourcePath, - MappedFieldType.FielddataOperation.SEARCH - ); - IndexFieldData fieldData = new SourceValueFetcherSortedBinaryIndexFieldData.Builder( - fieldType.name(), - CoreValuesSourceType.KEYWORD, - SourceValueFetcher.toString(fieldDataContext.sourcePathsLookup().apply(fieldType.name())), - fieldDataContext.lookupSupplier().get(), - TextValueSource.TextDocValuesFieldWrapper::new - ).build(null, null); // Neither cache nor breakerService are used by SourceValueFetcherSortedBinaryIndexFieldData builder - return new TextValueSource(fieldData); - } -} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java index 83fc902bd5077..8b1c4f78825ad 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java @@ -7,18 +7,24 @@ package org.elasticsearch.compute.lucene; -import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DocBlock; import org.elasticsearch.compute.data.DocVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.data.SingletonOrdinalsBuilder; import org.elasticsearch.compute.operator.AbstractPageMappingOperator; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.Operator; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.xcontent.XContentBuilder; @@ -28,7 +34,6 @@ import java.util.Map; import java.util.Objects; import java.util.TreeMap; -import java.util.function.Supplier; /** * Operator that extracts doc_values from a Lucene index out of pages that have been produced by {@link LuceneSourceOperator} @@ -43,12 +48,12 @@ public class ValuesSourceReaderOperator extends AbstractPageMappingOperator { * @param docChannel the channel containing the shard, leaf/segment and doc id * @param field the lucene field being loaded */ - public record ValuesSourceReaderOperatorFactory(Supplier> sources, int docChannel, String field) + public record ValuesSourceReaderOperatorFactory(List sources, int docChannel, String field) implements OperatorFactory { @Override public Operator get(DriverContext driverContext) { - return new ValuesSourceReaderOperator(sources.get(), docChannel, field); + return new ValuesSourceReaderOperator(sources, docChannel, field); } @Override @@ -57,9 +62,14 @@ public String describe() { } } - private final List sources; + /** + * A list, one entry per shard, of factories for {@link BlockDocValuesReader}s + * which perform the actual reading. + */ + private final List factories; private final int docChannel; private final String field; + private final ComputeBlockLoaderFactory blockFactory; private BlockDocValuesReader lastReader; private int lastShard = -1; @@ -69,14 +79,15 @@ public String describe() { /** * Creates a new extractor - * @param sources the value source, type and index readers to use for extraction + * @param factories builds {@link BlockDocValuesReader} * @param docChannel the channel containing the shard, leaf/segment and doc id * @param field the lucene field being loaded */ - public ValuesSourceReaderOperator(List sources, int docChannel, String field) { - this.sources = sources; + public ValuesSourceReaderOperator(List factories, int docChannel, String field) { + this.factories = factories; this.docChannel = docChannel; this.field = field; + this.blockFactory = new ComputeBlockLoaderFactory(BlockFactory.getNonBreakingInstance()); // TODO breaking! } @Override @@ -95,14 +106,26 @@ protected Page process(Page page) { private Block loadFromSingleLeaf(DocVector docVector) throws IOException { setupReader(docVector.shards().getInt(0), docVector.segments().getInt(0), docVector.docs().getInt(0)); - return lastReader.readValues(docVector.docs()); + return ((Block.Builder) lastReader.readValues(blockFactory, new BlockLoader.Docs() { + private final IntVector docs = docVector.docs(); + + @Override + public int count() { + return docs.getPositionCount(); + } + + @Override + public int get(int i) { + return docs.getInt(i); + } + })).build(); } private Block loadFromManyLeaves(DocVector docVector) throws IOException { int[] forwards = docVector.shardSegmentDocMapForwards(); int doc = docVector.docs().getInt(forwards[0]); setupReader(docVector.shards().getInt(forwards[0]), docVector.segments().getInt(forwards[0]), doc); - Block.Builder builder = lastReader.builder(forwards.length); + BlockLoader.Builder builder = lastReader.builder(blockFactory, forwards.length); lastReader.readValuesFromSingleDoc(doc, builder); for (int i = 1; i < forwards.length; i++) { int shard = docVector.shards().getInt(forwards[i]); @@ -114,16 +137,15 @@ private Block loadFromManyLeaves(DocVector docVector) throws IOException { lastReader.readValuesFromSingleDoc(doc, builder); } // TODO maybe it's better for downstream consumers if we perform a copy here. - return builder.build().filter(docVector.shardSegmentDocMapBackwards()); + return ((Block.Builder) builder).build().filter(docVector.shardSegmentDocMapBackwards()); } private void setupReader(int shard, int segment, int doc) throws IOException { if (lastSegment == segment && lastShard == shard && BlockDocValuesReader.canReuse(lastReader, doc)) { return; } - var info = sources.get(shard); - LeafReaderContext leafReaderContext = info.reader().leaves().get(segment); - lastReader = BlockDocValuesReader.createBlockReader(info.source(), info.type(), info.elementType(), leafReaderContext); + + lastReader = factories.get(shard).build(segment); lastShard = shard; lastSegment = segment; readersBuilt.compute(lastReader.toString(), (k, v) -> v == null ? 1 : v + 1); @@ -203,4 +225,72 @@ public String toString() { return Strings.toString(this); } } + + private static class ComputeBlockLoaderFactory implements BlockLoader.BuilderFactory { + private final BlockFactory factory; + + private ComputeBlockLoaderFactory(BlockFactory factory) { + this.factory = factory; + } + + @Override + public BlockLoader.BooleanBuilder booleansFromDocValues(int expectedCount) { + return factory.newBooleanBlockBuilder(expectedCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); + } + + @Override + public BlockLoader.BooleanBuilder booleans(int expectedCount) { + return factory.newBooleanBlockBuilder(expectedCount); + } + + @Override + public BlockLoader.BytesRefBuilder bytesRefsFromDocValues(int expectedCount) { + return factory.newBytesRefBlockBuilder(expectedCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); + } + + @Override + public BlockLoader.BytesRefBuilder bytesRefs(int expectedCount) { + return factory.newBytesRefBlockBuilder(expectedCount); + } + + @Override + public BlockLoader.DoubleBuilder doublesFromDocValues(int expectedCount) { + return factory.newDoubleBlockBuilder(expectedCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); + } + + @Override + public BlockLoader.DoubleBuilder doubles(int expectedCount) { + return factory.newDoubleBlockBuilder(expectedCount); + } + + @Override + public BlockLoader.IntBuilder intsFromDocValues(int expectedCount) { + return factory.newIntBlockBuilder(expectedCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); + } + + @Override + public BlockLoader.IntBuilder ints(int expectedCount) { + return factory.newIntBlockBuilder(expectedCount); + } + + @Override + public BlockLoader.LongBuilder longsFromDocValues(int expectedCount) { + return factory.newLongBlockBuilder(expectedCount).mvOrdering(Block.MvOrdering.DEDUPLICATED_AND_SORTED_ASCENDING); + } + + @Override + public BlockLoader.LongBuilder longs(int expectedCount) { + return factory.newLongBlockBuilder(expectedCount); + } + + @Override + public BlockLoader.Builder nulls(int expectedCount) { + return ElementType.NULL.newBlockBuilder(expectedCount, factory); + } + + @Override + public BlockLoader.SingletonOrdinalsBuilder singletonOrdinalsBuilder(SortedDocValues ordinals, int count) { + return new SingletonOrdinalsBuilder(factory, ordinals, count); + } + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java index 216627f996cad..2c5eedb6d8bbe 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java @@ -7,11 +7,11 @@ package org.elasticsearch.compute.operator; -import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedSetDocValues; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.PriorityQueue; +import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BitArray; import org.elasticsearch.compute.Describable; @@ -24,15 +24,15 @@ import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DocBlock; import org.elasticsearch.compute.data.DocVector; +import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Page; -import org.elasticsearch.compute.lucene.ValueSourceInfo; import org.elasticsearch.compute.lucene.ValuesSourceReaderOperator; import org.elasticsearch.compute.operator.HashAggregationOperator.GroupSpec; import org.elasticsearch.core.Releasable; import org.elasticsearch.core.Releasables; -import org.elasticsearch.search.aggregations.support.ValuesSource; +import org.elasticsearch.index.mapper.BlockDocValuesReader; import java.io.IOException; import java.io.UncheckedIOException; @@ -52,7 +52,8 @@ */ public class OrdinalsGroupingOperator implements Operator { public record OrdinalsGroupingOperatorFactory( - Supplier> sources, + List readerFactories, + ElementType groupingElementType, int docChannel, String groupingField, List aggregators, @@ -63,7 +64,8 @@ public record OrdinalsGroupingOperatorFactory( @Override public Operator get(DriverContext driverContext) { return new OrdinalsGroupingOperator( - sources.get(), + readerFactories, + groupingElementType, docChannel, groupingField, aggregators, @@ -79,11 +81,12 @@ public String describe() { } } - private final List sources; + private final List readerFactories; private final int docChannel; private final String groupingField; private final List aggregatorFactories; + private final ElementType groupingElementType; private final Map ordinalAggregators; private final BigArrays bigArrays; @@ -96,7 +99,8 @@ public String describe() { private ValuesAggregator valuesAggregator; public OrdinalsGroupingOperator( - List sources, + List readerFactories, + ElementType groupingElementType, int docChannel, String groupingField, List aggregatorFactories, @@ -105,7 +109,8 @@ public OrdinalsGroupingOperator( DriverContext driverContext ) { Objects.requireNonNull(aggregatorFactories); - this.sources = sources; + this.readerFactories = readerFactories; + this.groupingElementType = groupingElementType; this.docChannel = docChannel; this.groupingField = groupingField; this.aggregatorFactories = aggregatorFactories; @@ -126,22 +131,20 @@ public void addInput(Page page) { requireNonNull(page, "page is null"); DocVector docVector = page.getBlock(docChannel).asVector(); final int shardIndex = docVector.shards().getInt(0); - final var source = sources.get(shardIndex); + final var readerFactory = readerFactories.get(shardIndex); boolean pagePassed = false; try { - if (docVector.singleSegmentNonDecreasing() && source.source() instanceof ValuesSource.Bytes.WithOrdinals withOrdinals) { + if (docVector.singleSegmentNonDecreasing() && readerFactory.supportsOrdinals()) { final IntVector segmentIndexVector = docVector.segments(); assert segmentIndexVector.isConstant(); final OrdinalSegmentAggregator ordinalAggregator = this.ordinalAggregators.computeIfAbsent( new SegmentID(shardIndex, segmentIndexVector.getInt(0)), k -> { try { - final LeafReaderContext leafReaderContext = source.reader().leaves().get(k.segmentIndex); return new OrdinalSegmentAggregator( driverContext.blockFactory(), this::createGroupingAggregators, - withOrdinals, - leafReaderContext, + () -> readerFactory.ordinals(k.segmentIndex), bigArrays ); } catch (IOException e) { @@ -155,7 +158,8 @@ public void addInput(Page page) { if (valuesAggregator == null) { int channelIndex = page.getBlockCount(); // extractor will append a new block at the end valuesAggregator = new ValuesAggregator( - sources, + readerFactories, + groupingElementType, docChannel, groupingField, channelIndex, @@ -327,29 +331,26 @@ record SegmentID(int shardIndex, int segmentIndex) { static final class OrdinalSegmentAggregator implements Releasable, SeenGroupIds { private final BlockFactory blockFactory; private final List aggregators; - private final ValuesSource.Bytes.WithOrdinals withOrdinals; - private final LeafReaderContext leafReaderContext; + private final CheckedSupplier docValuesSupplier; private final BitArray visitedOrds; private BlockOrdinalsReader currentReader; OrdinalSegmentAggregator( BlockFactory blockFactory, Supplier> aggregatorsSupplier, - ValuesSource.Bytes.WithOrdinals withOrdinals, - LeafReaderContext leafReaderContext, + CheckedSupplier docValuesSupplier, BigArrays bigArrays ) throws IOException { boolean success = false; List groupingAggregators = null; BitArray bitArray = null; try { - final SortedSetDocValues sortedSetDocValues = withOrdinals.ordinalsValues(leafReaderContext); + final SortedSetDocValues sortedSetDocValues = docValuesSupplier.get(); bitArray = new BitArray(sortedSetDocValues.getValueCount(), bigArrays); groupingAggregators = aggregatorsSupplier.get(); this.currentReader = new BlockOrdinalsReader(sortedSetDocValues, blockFactory); this.blockFactory = blockFactory; - this.withOrdinals = withOrdinals; - this.leafReaderContext = leafReaderContext; + this.docValuesSupplier = docValuesSupplier; this.aggregators = groupingAggregators; this.visitedOrds = bitArray; success = true; @@ -369,7 +370,7 @@ void addInput(IntVector docs, Page page) { } if (BlockOrdinalsReader.canReuse(currentReader, docs.getInt(0)) == false) { - currentReader = new BlockOrdinalsReader(withOrdinals.ordinalsValues(leafReaderContext), blockFactory); + currentReader = new BlockOrdinalsReader(docValuesSupplier.get(), blockFactory); } try (IntBlock ordinals = currentReader.readOrdinalsAdded1(docs)) { for (int p = 0; p < ordinals.getPositionCount(); p++) { @@ -392,7 +393,7 @@ void addInput(IntVector docs, Page page) { } AggregatedResultIterator getResultIterator() throws IOException { - return new AggregatedResultIterator(aggregators, visitedOrds, withOrdinals.ordinalsValues(leafReaderContext)); + return new AggregatedResultIterator(aggregators, visitedOrds, docValuesSupplier.get()); } boolean seenNulls() { @@ -457,7 +458,8 @@ private static class ValuesAggregator implements Releasable { private final HashAggregationOperator aggregator; ValuesAggregator( - List sources, + List factories, + ElementType groupingElementType, int docChannel, String groupingField, int channelIndex, @@ -465,15 +467,10 @@ private static class ValuesAggregator implements Releasable { int maxPageSize, DriverContext driverContext ) { - this.extractor = new ValuesSourceReaderOperator(sources, docChannel, groupingField); + this.extractor = new ValuesSourceReaderOperator(factories, docChannel, groupingField); this.aggregator = new HashAggregationOperator( aggregatorFactories, - () -> BlockHash.build( - List.of(new GroupSpec(channelIndex, sources.get(0).elementType())), - driverContext, - maxPageSize, - false - ), + () -> BlockHash.build(List.of(new GroupSpec(channelIndex, groupingElementType)), driverContext, maxPageSize, false), driverContext ); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java index 0b2cb20ecdabd..979777d4cfd03 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/exchange/ExchangeSourceHandler.java @@ -154,7 +154,7 @@ void fetchPage() { final LoopControl loopControl = new LoopControl(); while (loopControl.isRunning()) { loopControl.exiting(); - // finish other sinks if one of them failed or sources no longer need pages. + // finish other sinks if one of them failed or source no longer need pages. boolean toFinishSinks = buffer.noMoreInputs() || failure.get() != null; remoteSink.fetchPageAsync(toFinishSinks, ActionListener.wrap(resp -> { Page page = resp.takePage(); @@ -249,7 +249,7 @@ protected void closeInternal() { /** * Add a listener, which will be notified when this exchange source handler is completed. An exchange source - * handler is consider completed when all exchange sources and sinks are completed and de-attached. + * handler is consider completed when all exchange factories and sinks are completed and de-attached. */ public void addCompletionListener(ActionListener listener) { completionFuture.addListener(listener); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java index 7b29f82f085c5..6951cdf4d56ca 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java @@ -14,7 +14,6 @@ import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.SortedSetDocValues; import org.apache.lucene.index.Term; import org.apache.lucene.search.Collector; import org.apache.lucene.search.IndexSearcher; @@ -46,10 +45,10 @@ import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.lucene.BlockReaderFactories; import org.elasticsearch.compute.lucene.DataPartitioning; import org.elasticsearch.compute.lucene.LuceneOperator; import org.elasticsearch.compute.lucene.LuceneSourceOperator; -import org.elasticsearch.compute.lucene.ValueSourceInfo; import org.elasticsearch.compute.operator.AbstractPageMappingOperator; import org.elasticsearch.compute.operator.Driver; import org.elasticsearch.compute.operator.DriverContext; @@ -62,13 +61,10 @@ import org.elasticsearch.compute.operator.SequenceLongBlockSourceOperator; import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.core.Releasables; -import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MapperServiceTestCase; import org.elasticsearch.index.mapper.Uid; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; -import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ESTestCase; @@ -80,7 +76,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.LongUnaryOperator; import static org.elasticsearch.compute.aggregation.AggregatorMode.FINAL; import static org.elasticsearch.compute.aggregation.AggregatorMode.INITIAL; @@ -231,13 +226,9 @@ public String toString() { }, new OrdinalsGroupingOperator( List.of( - new ValueSourceInfo( - CoreValuesSourceType.KEYWORD, - randomBoolean() ? getOrdinalsValuesSource(gField) : getBytesValuesSource(gField), - ElementType.BYTES_REF, - reader - ) + BlockReaderFactories.loaderToFactory(reader, new KeywordFieldMapper.KeywordFieldType("g").blockLoader(null)) ), + ElementType.BYTES_REF, 0, gField, List.of(CountAggregatorFunction.supplier(bigArrays, List.of(1)).groupingAggregatorFactory(INITIAL)), @@ -333,61 +324,6 @@ public ScoreMode scoreMode() { return docIds; } - static ValuesSource.Bytes.WithOrdinals getOrdinalsValuesSource(String field) { - return new ValuesSource.Bytes.WithOrdinals() { - - @Override - public SortedBinaryDocValues bytesValues(LeafReaderContext context) throws IOException { - return getBytesValuesSource(field).bytesValues(context); - } - - @Override - public SortedSetDocValues ordinalsValues(LeafReaderContext context) throws IOException { - return context.reader().getSortedSetDocValues(field); - } - - @Override - public SortedSetDocValues globalOrdinalsValues(LeafReaderContext context) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean supportsGlobalOrdinalsMapping() { - return false; - } - - @Override - public LongUnaryOperator globalOrdinalsMapping(LeafReaderContext context) { - throw new UnsupportedOperationException(); - } - }; - } - - static ValuesSource.Bytes getBytesValuesSource(String field) { - return new ValuesSource.Bytes() { - @Override - public SortedBinaryDocValues bytesValues(LeafReaderContext context) throws IOException { - final SortedSetDocValues dv = context.reader().getSortedSetDocValues(field); - return new SortedBinaryDocValues() { - @Override - public boolean advanceExact(int doc) throws IOException { - return dv.advanceExact(doc); - } - - @Override - public int docValueCount() { - return dv.docValueCount(); - } - - @Override - public BytesRef nextValue() throws IOException { - return dv.lookupOrd(dv.nextOrd()); - } - }; - } - }; - } - /** * Creates a {@link BigArrays} that tracks releases but doesn't throw circuit breaking exceptions. */ diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilderTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilderTests.java new file mode 100644 index 0000000000000..ff231a0cc20e0 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/SingletonOrdinalsBuilderTests.java @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.document.SortedDocValuesField; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedDocValues; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.indices.CrankyCircuitBreakerService; +import org.elasticsearch.test.ESTestCase; +import org.junit.After; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.test.ListMatcher.matchesList; +import static org.elasticsearch.test.MapMatcher.assertMap; +import static org.elasticsearch.test.MapMatcher.matchesMap; +import static org.hamcrest.Matchers.equalTo; + +public class SingletonOrdinalsBuilderTests extends ESTestCase { + public void testReader() throws IOException { + testRead(breakingDriverContext().blockFactory()); + } + + public void testReadWithCranky() throws IOException { + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, new CrankyCircuitBreakerService()); + BlockFactory factory = new BlockFactory(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST), bigArrays); + try { + testRead(factory); + // If we made it this far cranky didn't fail us! + } catch (CircuitBreakingException e) { + logger.info("cranky", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + assertThat(factory.breaker().getUsed(), equalTo(0L)); + } + + private void testRead(BlockFactory factory) throws IOException { + int count = 1000; + try (Directory directory = newDirectory(); RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory)) { + for (int i = 0; i < count; i++) { + for (BytesRef v : new BytesRef[] { new BytesRef("a"), new BytesRef("b"), new BytesRef("c"), new BytesRef("d") }) { + indexWriter.addDocument(List.of(new SortedDocValuesField("f", v))); + } + } + Map counts = new HashMap<>(); + try (IndexReader reader = indexWriter.getReader()) { + for (LeafReaderContext ctx : reader.leaves()) { + SortedDocValues docValues = ctx.reader().getSortedDocValues("f"); + try (SingletonOrdinalsBuilder builder = new SingletonOrdinalsBuilder(factory, docValues, ctx.reader().numDocs())) { + for (int i = 0; i < ctx.reader().maxDoc(); i++) { + if (ctx.reader().getLiveDocs() == null || ctx.reader().getLiveDocs().get(i)) { + assertThat(docValues.advanceExact(i), equalTo(true)); + builder.appendOrd(docValues.ordValue()); + } + } + try (BytesRefBlock build = builder.build()) { + for (int i = 0; i < build.getPositionCount(); i++) { + counts.merge(build.getBytesRef(i, new BytesRef()).utf8ToString(), 1, (lhs, rhs) -> lhs + rhs); + } + } + } + } + } + assertMap(counts, matchesMap().entry("a", count).entry("b", count).entry("c", count).entry("d", count)); + } + } + + public void testCompactWithNulls() { + assertCompactToUnique(new int[] { -1, -1, -1, -1, 0, 1, 2 }, List.of(0, 1, 2)); + } + + public void testCompactNoNulls() { + assertCompactToUnique(new int[] { 0, 1, 2 }, List.of(0, 1, 2)); + } + + public void testCompactDups() { + assertCompactToUnique(new int[] { 0, 0, 0, 1, 2 }, List.of(0, 1, 2)); + } + + public void testCompactSkips() { + assertCompactToUnique(new int[] { 2, 7, 1000 }, List.of(2, 7, 1000)); + } + + private void assertCompactToUnique(int[] sortedOrds, List expected) { + int uniqueLength = SingletonOrdinalsBuilder.compactToUnique(sortedOrds); + assertMap(Arrays.stream(sortedOrds).mapToObj(Integer::valueOf).limit(uniqueLength).toList(), matchesList(expected)); + } + + private final List breakers = new ArrayList<>(); + private final List blockFactories = new ArrayList<>(); + + /** + * A {@link DriverContext} with a breaking {@link BigArrays} and {@link BlockFactory}. + */ + protected DriverContext breakingDriverContext() { // TODO move this to driverContext once everyone supports breaking + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, ByteSizeValue.ofGb(1)).withCircuitBreaking(); + CircuitBreaker breaker = bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); + breakers.add(breaker); + BlockFactory factory = new MockBlockFactory(breaker, bigArrays); + blockFactories.add(factory); + return new DriverContext(bigArrays, factory); + } + + @After + public void allBreakersEmpty() throws Exception { + // first check that all big arrays are released, which can affect breakers + MockBigArrays.ensureAllArraysAreReleased(); + + for (CircuitBreaker breaker : breakers) { + for (var factory : blockFactories) { + if (factory instanceof MockBlockFactory mockBlockFactory) { + mockBlockFactory.ensureAllBlocksAreReleased(); + } + } + assertThat("Unexpected used in breaker: " + breaker, breaker.getUsed(), equalTo(0L)); + } + } + +} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java index bbafc8ed753cc..131082859bf4c 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java @@ -17,7 +17,6 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.AnyOperatorTestCase; @@ -36,7 +35,6 @@ import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.support.NestedScope; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import org.elasticsearch.search.internal.ContextIndexSearcher; import org.elasticsearch.search.internal.SearchContext; import org.junit.After; @@ -58,7 +56,7 @@ import static org.mockito.Mockito.when; public class LuceneSourceOperatorTests extends AnyOperatorTestCase { - private static final MappedFieldType S_FIELD = new NumberFieldMapper.NumberFieldType("s", NumberFieldMapper.NumberType.INTEGER); + private static final MappedFieldType S_FIELD = new NumberFieldMapper.NumberFieldType("s", NumberFieldMapper.NumberType.LONG); private Directory directory = newDirectory(); private IndexReader reader; @@ -145,12 +143,7 @@ public void testEmpty() { private void testSimple(int size, int limit) { DriverContext ctx = driverContext(); LuceneSourceOperator.Factory factory = simple(nonBreakingBigArrays(), DataPartitioning.SHARD, size, limit); - Operator.OperatorFactory readS = ValuesSourceReaderOperatorTests.factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.LONG, - S_FIELD - ); + Operator.OperatorFactory readS = ValuesSourceReaderOperatorTests.factory(reader, S_FIELD); List results = new ArrayList<>(); OperatorTestCase.runDriver( diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java index 54853abd0cecb..f0eb49c233e7a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java @@ -16,7 +16,6 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.AnyOperatorTestCase; @@ -34,7 +33,6 @@ import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.support.NestedScope; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortBuilder; @@ -53,7 +51,7 @@ import static org.mockito.Mockito.when; public class LuceneTopNSourceOperatorTests extends AnyOperatorTestCase { - private static final MappedFieldType S_FIELD = new NumberFieldMapper.NumberFieldType("s", NumberFieldMapper.NumberType.INTEGER); + private static final MappedFieldType S_FIELD = new NumberFieldMapper.NumberFieldType("s", NumberFieldMapper.NumberType.LONG); private Directory directory = newDirectory(); private IndexReader reader; @@ -150,12 +148,7 @@ public void testEmpty() { private void testSimple(int size, int limit) { DriverContext ctx = driverContext(); LuceneTopNSourceOperator.Factory factory = simple(nonBreakingBigArrays(), DataPartitioning.SHARD, size, limit); - Operator.OperatorFactory readS = ValuesSourceReaderOperatorTests.factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.LONG, - S_FIELD - ); + Operator.OperatorFactory readS = ValuesSourceReaderOperatorTests.factory(reader, S_FIELD); List results = new ArrayList<>(); OperatorTestCase.runDriver( diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java index ec1697e9aedd2..a6e5e3bf4744d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.compute.data.BytesRefVector; import org.elasticsearch.compute.data.DoubleBlock; import org.elasticsearch.compute.data.DoubleVector; -import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; @@ -47,18 +46,10 @@ import org.elasticsearch.compute.operator.PageConsumerOperator; import org.elasticsearch.compute.operator.SourceOperator; import org.elasticsearch.core.IOUtils; -import org.elasticsearch.index.fielddata.FieldDataContext; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.mapper.BooleanFieldMapper; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.NumberFieldMapper; -import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.ValuesSourceType; import org.junit.After; import java.io.IOException; @@ -95,21 +86,12 @@ public void closeIndex() throws IOException { @Override protected Operator.OperatorFactory simple(BigArrays bigArrays) { - return factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.LONG, - new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG) - ); + return factory(reader, new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG)); } - static Operator.OperatorFactory factory(IndexReader reader, ValuesSourceType vsType, ElementType elementType, MappedFieldType ft) { - IndexFieldData fd = ft.fielddataBuilder(FieldDataContext.noRuntimeFields("test")) - .build(new IndexFieldDataCache.None(), new NoneCircuitBreakerService()); - FieldContext fc = new FieldContext(ft.name(), fd, ft); - ValuesSource vs = vsType.getField(fc, null); + static Operator.OperatorFactory factory(IndexReader reader, MappedFieldType ft) { return new ValuesSourceReaderOperator.ValuesSourceReaderOperatorFactory( - () -> List.of(new ValueSourceInfo(vsType, vs, elementType, reader)), + List.of(BlockReaderFactories.loaderToFactory(reader, ft.blockLoader(null))), 0, ft.name() ); @@ -243,54 +225,16 @@ public void testLoadAllInOnePageShuffled() { private void loadSimpleAndAssert(DriverContext driverContext, List input) { List results = new ArrayList<>(); List operators = List.of( - factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.INT, - new NumberFieldMapper.NumberFieldType("key", NumberFieldMapper.NumberType.INTEGER) - ).get(driverContext), - factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.LONG, - new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG) - ).get(driverContext), - factory(reader, CoreValuesSourceType.KEYWORD, ElementType.BYTES_REF, new KeywordFieldMapper.KeywordFieldType("kwd")).get( - driverContext - ), - factory(reader, CoreValuesSourceType.KEYWORD, ElementType.BYTES_REF, new KeywordFieldMapper.KeywordFieldType("mv_kwd")).get( - driverContext - ), - factory(reader, CoreValuesSourceType.BOOLEAN, ElementType.BOOLEAN, new BooleanFieldMapper.BooleanFieldType("bool")).get( - driverContext - ), - factory(reader, CoreValuesSourceType.BOOLEAN, ElementType.BOOLEAN, new BooleanFieldMapper.BooleanFieldType("mv_bool")).get( - driverContext - ), - factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.INT, - new NumberFieldMapper.NumberFieldType("mv_key", NumberFieldMapper.NumberType.INTEGER) - ).get(driverContext), - factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.LONG, - new NumberFieldMapper.NumberFieldType("mv_long", NumberFieldMapper.NumberType.LONG) - ).get(driverContext), - factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.DOUBLE, - new NumberFieldMapper.NumberFieldType("double", NumberFieldMapper.NumberType.DOUBLE) - ).get(driverContext), - factory( - reader, - CoreValuesSourceType.NUMERIC, - ElementType.DOUBLE, - new NumberFieldMapper.NumberFieldType("mv_double", NumberFieldMapper.NumberType.DOUBLE) - ).get(driverContext) + factory(reader, new NumberFieldMapper.NumberFieldType("key", NumberFieldMapper.NumberType.INTEGER)).get(driverContext), + factory(reader, new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG)).get(driverContext), + factory(reader, new KeywordFieldMapper.KeywordFieldType("kwd")).get(driverContext), + factory(reader, new KeywordFieldMapper.KeywordFieldType("mv_kwd")).get(driverContext), + factory(reader, new BooleanFieldMapper.BooleanFieldType("bool")).get(driverContext), + factory(reader, new BooleanFieldMapper.BooleanFieldType("mv_bool")).get(driverContext), + factory(reader, new NumberFieldMapper.NumberFieldType("mv_key", NumberFieldMapper.NumberType.INTEGER)).get(driverContext), + factory(reader, new NumberFieldMapper.NumberFieldType("mv_long", NumberFieldMapper.NumberType.LONG)).get(driverContext), + factory(reader, new NumberFieldMapper.NumberFieldType("double", NumberFieldMapper.NumberType.DOUBLE)).get(driverContext), + factory(reader, new NumberFieldMapper.NumberFieldType("mv_double", NumberFieldMapper.NumberType.DOUBLE)).get(driverContext) ); try ( Driver d = new Driver( @@ -418,10 +362,10 @@ public void testValuesSourceReaderOperatorWithNulls() throws IOException { driverContext, luceneFactory.get(driverContext), List.of( - factory(reader, CoreValuesSourceType.NUMERIC, ElementType.INT, intFt).get(driverContext), - factory(reader, CoreValuesSourceType.NUMERIC, ElementType.LONG, longFt).get(driverContext), - factory(reader, CoreValuesSourceType.NUMERIC, ElementType.DOUBLE, doubleFt).get(driverContext), - factory(reader, CoreValuesSourceType.KEYWORD, ElementType.BYTES_REF, kwFt).get(driverContext) + factory(reader, intFt).get(driverContext), + factory(reader, longFt).get(driverContext), + factory(reader, doubleFt).get(driverContext), + factory(reader, kwFt).get(driverContext) ), new PageConsumerOperator(page -> { logger.debug("New page: {}", page); diff --git a/x-pack/plugin/esql/qa/server/single-node/build.gradle b/x-pack/plugin/esql/qa/server/single-node/build.gradle index 792059639aaca..3131b4176ee25 100644 --- a/x-pack/plugin/esql/qa/server/single-node/build.gradle +++ b/x-pack/plugin/esql/qa/server/single-node/build.gradle @@ -7,7 +7,7 @@ dependencies { restResources { restApi { - include '_common', 'bulk', 'indices', 'esql', 'xpack', 'enrich', 'cluster' + include '_common', 'bulk', 'get', 'indices', 'esql', 'xpack', 'enrich', 'cluster' } } diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml index 886bb6dc60aca..7ec0f671253b9 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml @@ -112,7 +112,7 @@ keyword no doc_values: - match: {columns.0.name: card} - match: {columns.0.type: keyword} - length: {values: 1} - - match: {values.0.0: [diamonds, jack, of]} + - match: {values.0.0: [jack, of, diamonds]} --- wildcard: diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml index 33697a789cc26..6a90fc5a7b8f8 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/40_tsdb.yml @@ -111,7 +111,7 @@ load everything: - "No limit defined, adding default limit of \\[.*\\]" esql.query: body: - query: 'from test' + query: 'from test [metadata _id]' - match: {columns.0.name: "@timestamp"} - match: {columns.0.type: "date"} diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/90_non_indexed.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/90_non_indexed.yml index a673fb7a5b88d..9138a9454c571 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/90_non_indexed.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/90_non_indexed.yml @@ -1,6 +1,6 @@ setup: - skip: - features: allowed_warnings_regex + features: allowed_warnings - do: indices.create: index: test @@ -85,11 +85,11 @@ setup: } --- -unsupported: +fetch: - do: - allowed_warnings_regex: - - "Field \\[.*\\] cannot be retrieved, it is unsupported or not indexed; returning null" - - "No limit defined, adding default limit of \\[.*\\]" + allowed_warnings: + - "Field [ip_noidx] cannot be retrieved, it is unsupported or not indexed; returning null" + - "No limit defined, adding default limit of [500]" esql.query: body: query: 'from test' @@ -130,18 +130,18 @@ unsupported: - length: { values: 1 } - match: { values.0.0: true } - - match: { values.0.1: null } + - match: { values.0.1: true } - match: { values.0.2: "2021-04-28T18:50:04.467Z" } - - match: { values.0.3: null } + - match: { values.0.3: "2021-04-28T18:50:04.467Z" } - match: { values.0.4: 40 } - - match: { values.0.5: null } + - match: { values.0.5: 40 } - match: { values.0.6: 30 } - - match: { values.0.7: null } + - match: { values.0.7: 30 } - match: { values.0.8: 10 } - - match: { values.0.9: null } + - match: { values.0.9: 10 } - match: { values.0.10: "192.168.0.1" } - match: { values.0.11: null } - match: { values.0.12: "foo" } - - match: { values.0.13: "foo" } # this is a special case, ESQL can retrieve keywords from source + - match: { values.0.13: "foo" } - match: { values.0.14: 20 } - - match: { values.0.15: null } + - match: { values.0.15: 20 } diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java index 6ebd1b24c13bd..1729c9fc363e4 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java @@ -23,6 +23,7 @@ import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.ListMatcher; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.json.JsonXContent; import org.elasticsearch.xpack.esql.analysis.VerificationException; @@ -50,6 +51,8 @@ import static java.util.Comparator.comparing; import static java.util.Comparator.naturalOrder; import static java.util.Comparator.reverseOrder; +import static org.elasticsearch.test.ListMatcher.matchesList; +import static org.elasticsearch.test.MapMatcher.assertMap; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.xpack.esql.EsqlTestUtils.getValuesList; import static org.hamcrest.Matchers.allOf; @@ -81,7 +84,6 @@ public void testProjectConstant() { assertThat(getValuesList(results).size(), equalTo(40)); assertThat(getValuesList(results).get(0).get(0), equalTo(1)); } - } public void testStatsOverConstant() { @@ -1180,6 +1182,17 @@ public void testGroupingMultiValueByOrdinals() { } } + public void testLoadId() { + try (EsqlQueryResponse results = run("from test [metadata _id] | keep _id | sort _id ")) { + assertThat(results.columns(), equalTo(List.of(new ColumnInfo("_id", "keyword")))); + ListMatcher values = matchesList(); + for (int i = 10; i < 50; i++) { + values = values.item(List.of(Integer.toString(i))); + } + assertMap(getValuesList(results), values); + } + } + public void testUnsupportedTypesOrdinalGrouping() { assertAcked( client().admin().indices().prepareCreate("index-1").setMapping("f1", "type=keyword", "f2", "type=keyword", "v", "type=long") diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java index 41450be131e2a..be661b51d41d5 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionRuntimeFieldIT.java @@ -38,8 +38,7 @@ import static org.hamcrest.Matchers.equalTo; /** - * Makes sure that the circuit breaker is "plugged in" to ESQL by configuring an - * unreasonably small breaker and tripping it. + * Tests runtime fields against ESQL. */ @ESIntegTestCase.ClusterScope(scope = SUITE, numDataNodes = 1, numClientNodes = 0, supportsDedicatedMasters = false) // @TestLogging(value = "org.elasticsearch.xpack.esql:TRACE", reason = "debug") diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionTaskIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionTaskIT.java index b1fab0ab94af9..edaf9d91e9771 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionTaskIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionTaskIT.java @@ -177,7 +177,7 @@ public void testTaskContents() throws Exception { } if (o.operator().equals("ValuesSourceReaderOperator[field = pause_me]")) { ValuesSourceReaderOperator.Status oStatus = (ValuesSourceReaderOperator.Status) o.status(); - assertMap(oStatus.readersBuilt(), matchesMap().entry("LongValuesReader", greaterThanOrEqualTo(1))); + assertMap(oStatus.readersBuilt(), matchesMap().entry("ScriptLongs", greaterThanOrEqualTo(1))); assertThat(oStatus.pagesProcessed(), greaterThanOrEqualTo(1)); valuesSourceReaders++; continue; diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/SyntheticSourceIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/SyntheticSourceIT.java index f0365ce78f44a..2585b5325df18 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/SyntheticSourceIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/SyntheticSourceIT.java @@ -9,11 +9,13 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.support.WriteRequest; +import org.elasticsearch.core.CheckedFunction; import org.elasticsearch.index.mapper.extras.MapperExtrasPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.json.JsonXContent; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -30,40 +32,64 @@ protected Collection> nodePlugins() { } public void testMatchOnlyText() throws Exception { - XContentBuilder mapping = JsonXContent.contentBuilder(); - mapping.startObject(); - if (true || randomBoolean()) { - mapping.startObject("_source"); - mapping.field("mode", "synthetic"); - mapping.endObject(); + createIndex(b -> b.field("type", "match_only_text")); + + int numDocs = between(10, 1000); + for (int i = 0; i < numDocs; i++) { + IndexRequestBuilder indexRequest = client().prepareIndex("test").setSource("id", "i" + i, "field", "n" + i); + if (randomInt(100) < 5) { + indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + } + indexRequest.get(); } - { - mapping.startObject("properties"); - mapping.startObject("uid"); - mapping.field("type", "keyword"); - mapping.endObject(); - mapping.startObject("name"); - mapping.field("type", "match_only_text"); - mapping.endObject(); - mapping.endObject(); + client().admin().indices().prepareRefresh("test").get(); + + try (EsqlQueryResponse resp = run("from test | sort id asc | limit 1")) { + Iterator row = resp.values().next(); + assertThat(row.next(), equalTo("n0")); + assertThat(row.next(), equalTo("i0")); + assertFalse(row.hasNext()); } - mapping.endObject(); + } - assertAcked(client().admin().indices().prepareCreate("test").setMapping(mapping)); + public void testText() throws Exception { + createIndex(b -> b.field("type", "text").field("store", true)); int numDocs = between(10, 1000); for (int i = 0; i < numDocs; i++) { - IndexRequestBuilder indexRequest = client().prepareIndex("test").setSource("uid", "u" + i); + IndexRequestBuilder indexRequest = client().prepareIndex("test").setSource("id", "i" + i, "field", "n" + i); if (randomInt(100) < 5) { indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); } indexRequest.get(); } client().admin().indices().prepareRefresh("test").get(); - try (EsqlQueryResponse resp = run("from test | keep uid, name | sort uid asc | limit 1")) { + try (EsqlQueryResponse resp = run("from test | keep field, id | sort id asc | limit 1")) { Iterator row = resp.values().next(); - assertThat(row.next(), equalTo("u0")); - assertNull(row.next()); + assertThat(row.next(), equalTo("n0")); + assertThat(row.next(), equalTo("i0")); + assertFalse(row.hasNext()); + } + } + + private void createIndex(CheckedFunction fieldMapping) throws IOException { + XContentBuilder mapping = JsonXContent.contentBuilder(); + mapping.startObject(); + { + mapping.startObject("_source"); + mapping.field("mode", "synthetic"); + mapping.endObject(); + } + { + mapping.startObject("properties"); + mapping.startObject("id").field("type", "keyword").endObject(); + mapping.startObject("field"); + fieldMapping.apply(mapping); + mapping.endObject(); + mapping.endObject(); } + mapping.endObject(); + + assertAcked(client().admin().indices().prepareCreate("test").setMapping(mapping)); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index af8732ad9c969..bf246b5ac02d4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -30,7 +30,7 @@ import org.elasticsearch.compute.data.BlockStreamInput; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; -import org.elasticsearch.compute.lucene.ValueSources; +import org.elasticsearch.compute.lucene.BlockReaderFactories; import org.elasticsearch.compute.lucene.ValuesSourceReaderOperator; import org.elasticsearch.compute.operator.Driver; import org.elasticsearch.compute.operator.DriverContext; @@ -250,11 +250,10 @@ private void doLookup( NamedExpression extractField = extractFields.get(i); final ElementType elementType = LocalExecutionPlanner.toElementType(extractField.dataType()); mergingTypes[i] = elementType; - var sources = ValueSources.sources( + var sources = BlockReaderFactories.factories( List.of(searchContext), extractField instanceof Alias a ? ((NamedExpression) a.child()).name() : extractField.name(), - EsqlDataTypes.isUnsupported(extractField.dataType()), - elementType + EsqlDataTypes.isUnsupported(extractField.dataType()) ); intermediateOperators.add(new ValuesSourceReaderOperator(sources, 0, extractField.name())); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java index 3131b8c8c1e20..2cb739d6f068e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java @@ -12,21 +12,19 @@ import org.apache.lucene.search.Query; import org.elasticsearch.compute.aggregation.GroupingAggregator; import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.lucene.BlockReaderFactories; import org.elasticsearch.compute.lucene.LuceneOperator; import org.elasticsearch.compute.lucene.LuceneSourceOperator; import org.elasticsearch.compute.lucene.LuceneTopNSourceOperator; -import org.elasticsearch.compute.lucene.ValueSourceInfo; -import org.elasticsearch.compute.lucene.ValueSources; import org.elasticsearch.compute.lucene.ValuesSourceReaderOperator; import org.elasticsearch.compute.operator.Operator; import org.elasticsearch.compute.operator.OrdinalsGroupingOperator; +import org.elasticsearch.index.mapper.BlockDocValuesReader; import org.elasticsearch.index.mapper.NestedLookup; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.search.NestedHelper; -import org.elasticsearch.logging.LogManager; -import org.elasticsearch.logging.Logger; import org.elasticsearch.search.internal.AliasFilter; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.SortBuilder; @@ -45,13 +43,11 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Function; -import java.util.function.Supplier; import static org.elasticsearch.common.lucene.search.Queries.newNonNestedFilter; import static org.elasticsearch.compute.lucene.LuceneSourceOperator.NO_LIMIT; public class EsPhysicalOperationProviders extends AbstractPhysicalOperationProviders { - private static final Logger logger = LogManager.getLogger(EsPhysicalOperationProviders.class); private final List searchContexts; @@ -79,16 +75,18 @@ public final PhysicalOperation fieldExtractPhysicalOperation(FieldExtractExec fi DataType dataType = attr.dataType(); String fieldName = attr.name(); - Supplier> sources = () -> ValueSources.sources( + List factories = BlockReaderFactories.factories( searchContexts, fieldName, - EsqlDataTypes.isUnsupported(dataType), - LocalExecutionPlanner.toElementType(dataType) + EsqlDataTypes.isUnsupported(dataType) ); int docChannel = previousLayout.get(sourceAttr.id()).channel(); - op = op.with(new ValuesSourceReaderOperator.ValuesSourceReaderOperatorFactory(sources, docChannel, fieldName), layout.build()); + op = op.with( + new ValuesSourceReaderOperator.ValuesSourceReaderOperatorFactory(factories, docChannel, fieldName), + layout.build() + ); } return op; } @@ -175,12 +173,8 @@ public final Operator.OperatorFactory ordinalGroupingOperatorFactory( // The grouping-by values are ready, let's group on them directly. // Costin: why are they ready and not already exposed in the layout? return new OrdinalsGroupingOperator.OrdinalsGroupingOperatorFactory( - () -> ValueSources.sources( - searchContexts, - attrSource.name(), - EsqlDataTypes.isUnsupported(attrSource.dataType()), - LocalExecutionPlanner.toElementType(attrSource.dataType()) - ), + BlockReaderFactories.factories(searchContexts, attrSource.name(), EsqlDataTypes.isUnsupported(attrSource.dataType())), + groupElementType, docChannel, attrSource.name(), aggregatorFactories, diff --git a/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java b/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java index 9723276c827fc..75202749d8dca 100644 --- a/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java +++ b/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java @@ -30,6 +30,8 @@ import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; import org.elasticsearch.index.mapper.ConstantFieldType; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; @@ -133,6 +135,45 @@ public String familyTypeName() { return KeywordFieldMapper.CONTENT_TYPE; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + // TODO build a constant block directly + BytesRef bytes = new BytesRef(value); + return context -> new BlockDocValuesReader() { + private int docId; + + @Override + public int docID() { + return docId; + } + + @Override + public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, int expectedCount) { + return factory.bytesRefs(expectedCount); + } + + @Override + public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); + for (int i = 0; i < docs.count(); i++) { + builder.appendBytesRef(bytes); + } + return builder; + } + + @Override + public void readValuesFromSingleDoc(int docId, BlockLoader.Builder builder) { + this.docId = docId; + ((BlockLoader.BytesRefBuilder) builder).appendBytesRef(bytes); + } + + @Override + public String toString() { + return "ConstantKeyword"; + } + }; + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new ConstantIndexFieldData.Builder( diff --git a/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java b/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java index 859168e154ff8..5fb8f3f7dc345 100644 --- a/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java +++ b/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java @@ -9,6 +9,7 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Strings; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; @@ -30,6 +31,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.function.Function; import static org.elasticsearch.index.mapper.MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING; import static org.hamcrest.Matchers.equalTo; @@ -237,6 +239,11 @@ protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); } + @Override + protected Function loadBlockExpected() { + return v -> ((BytesRef) v).utf8ToString(); + } + public void testNullValueSyntheticSource() throws IOException { DocumentMapper mapper = createDocumentMapper(syntheticSourceMapping(b -> { b.startObject("field"); diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java index ca23799300f24..62b02f5a3d850 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapper.java @@ -24,6 +24,9 @@ import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; +import org.elasticsearch.index.mapper.BlockSourceReader; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -313,6 +316,26 @@ public Query rangeQuery( return query; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (indexMode == IndexMode.TIME_SERIES && metricType == TimeSeriesParams.MetricType.COUNTER) { + // Counters are not supported by ESQL so we load them in null + return BlockDocValuesReader.nulls(); + } + if (hasDocValues()) { + return BlockDocValuesReader.longs(name()); + } + return BlockSourceReader.longs(new SourceValueFetcher(blContext.sourcePaths(name()), nullValueFormatted) { + @Override + protected Object parseSourceValue(Object value) { + if (value.equals("")) { + return nullValueFormatted; + } + return parseUnsignedLong(value); + } + }); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { FielddataOperation operation = fieldDataContext.fielddataOperation(); diff --git a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java index 54a1edd88eb6b..95fe8f0a530ba 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java +++ b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java @@ -30,6 +30,7 @@ import java.math.BigInteger; import java.util.Collection; import java.util.List; +import java.util.function.Function; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; @@ -365,6 +366,20 @@ protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); } + @Override + protected Function loadBlockExpected() { + return v -> { + // Numbers are in the block as a long but the test needs to compare them to their BigInteger value parsed from xcontent. + if (v instanceof BigInteger ul) { + if (ul.bitLength() < Long.SIZE) { + return ul.longValue() ^ Long.MIN_VALUE; + } + return ul.subtract(BigInteger.ONE.shiftLeft(Long.SIZE - 1)).longValue(); + } + return ((Long) v).longValue() ^ Long.MIN_VALUE; + }; + } + final class NumberSyntheticSourceSupport implements SyntheticSourceSupport { private final BigInteger nullValue = usually() ? null : BigInteger.valueOf(randomNonNegativeLong()); diff --git a/x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapper.java b/x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapper.java index 9e88c516576c2..f4fb83fd9a91c 100644 --- a/x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapper.java +++ b/x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapper.java @@ -39,6 +39,8 @@ import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -290,6 +292,12 @@ protected BytesRef indexedValueForSearch(Object value) { return encodeVersion(valueAsString).bytesRef; } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + failIfNoDocValues(); + return BlockDocValuesReader.bytesRefsFromOrds(name()); + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { return new SortedSetOrdinalsIndexFieldData.Builder(name(), CoreValuesSourceType.KEYWORD, VersionStringDocValuesField::new); diff --git a/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java b/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java index d94a632ff9787..5653ed7f4302f 100644 --- a/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java +++ b/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java @@ -11,6 +11,7 @@ import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableFieldType; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.core.Tuple; @@ -31,6 +32,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; import static org.hamcrest.Matchers.equalTo; @@ -185,6 +187,11 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) return new VersionStringSyntheticSourceSupport(); } + @Override + protected Function loadBlockExpected() { + return v -> new Version((BytesRef) v).toString(); + } + static class VersionStringSyntheticSourceSupport implements SyntheticSourceSupport { @Override public SyntheticSourceExample example(int maxValues) { diff --git a/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java b/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java index 4b4dea8829791..ec206c64a2371 100644 --- a/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java +++ b/x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java @@ -60,6 +60,8 @@ import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.StringBinaryIndexFieldData; import org.elasticsearch.index.mapper.BinaryFieldMapper.CustomBinaryDocValuesField; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; import org.elasticsearch.index.mapper.DocumentParserContext; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.KeywordFieldMapper; @@ -851,6 +853,16 @@ public Query termsQuery(Collection values, SearchExecutionContext context) { return new ConstantScoreQuery(bq.build()); } + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (hasDocValues()) { + // TODO it'd almost certainly be faster to drop directly to doc values like we do with keyword but this'll do for now + IndexFieldData fd = new StringBinaryIndexFieldData(name(), CoreValuesSourceType.KEYWORD, null); + return BlockDocValuesReader.bytesRefsFromDocValues(context -> fd.load(context).getBytesValues()); + } + return null; + } + @Override public IndexFieldData.Builder fielddataBuilder(FieldDataContext fieldDataContext) { failIfNoDocValues(); diff --git a/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java b/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java index 1fade663cbe2d..a17cb7474a681 100644 --- a/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java +++ b/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java @@ -83,6 +83,7 @@ import java.util.HashSet; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Function; import static java.util.Collections.emptyMap; import static org.hamcrest.Matchers.equalTo; @@ -1215,6 +1216,11 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) return new WildcardSyntheticSourceSupport(); } + @Override + protected Function loadBlockExpected() { + return v -> ((BytesRef) v).utf8ToString(); + } + static class WildcardSyntheticSourceSupport implements SyntheticSourceSupport { private final Integer ignoreAbove = randomBoolean() ? null : between(10, 100); private final boolean allIgnored = ignoreAbove != null && rarely(); @@ -1224,7 +1230,11 @@ static class WildcardSyntheticSourceSupport implements SyntheticSourceSupport { public SyntheticSourceExample example(int maxValues) { if (randomBoolean()) { Tuple v = generateValue(); - return new SyntheticSourceExample(v.v1(), v.v2(), this::mapping); + Object loadBlock = v.v2(); + if (ignoreAbove != null && v.v2().length() > ignoreAbove) { + loadBlock = null; + } + return new SyntheticSourceExample(v.v1(), v.v2(), loadBlock, this::mapping); } List> values = randomList(1, maxValues, this::generateValue); List in = values.stream().map(Tuple::v1).toList(); @@ -1239,9 +1249,11 @@ public SyntheticSourceExample example(int maxValues) { }); List outList = new ArrayList<>(new HashSet<>(docValuesValues)); Collections.sort(outList); + List outBlockList = List.copyOf(outList); + Object outBlockResult = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; outList.addAll(outExtraValues); Object out = outList.size() == 1 ? outList.get(0) : outList; - return new SyntheticSourceExample(in, out, this::mapping); + return new SyntheticSourceExample(in, out, outBlockResult, this::mapping); } private Tuple generateValue() { From 76d1ab650901ee58170ab4d62510ba4a6413885c Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 25 Oct 2023 11:10:53 -0700 Subject: [PATCH 111/203] Support text field in count_distinct (#101334) Previously, we did not load text fields and did not register them to the count_distinct operator. However, we have now loaded the text fields and should support them with count_distinct. Closes #101330 --- .../xpack/esql/action/EsqlActionIT.java | 28 +++++++++++++++++++ .../function/aggregate/CountDistinct.java | 2 +- .../xpack/esql/planner/AggregateMapper.java | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java index 1729c9fc363e4..c2b275105bdd7 100644 --- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java +++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/EsqlActionIT.java @@ -35,11 +35,13 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.OptionalDouble; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -1303,6 +1305,32 @@ public void testStatsMissingFields() { } } + public void testCountTextField() { + assertAcked(client().admin().indices().prepareCreate("test_count").setMapping("name", "type=text")); + int numDocs = between(10, 1000); + Set names = new HashSet<>(); + for (int i = 0; i < numDocs; i++) { + String name = "name-" + randomIntBetween(1, 100); + names.add(name); + IndexRequestBuilder indexRequest = client().prepareIndex("test_count").setSource("name", name); + if (randomInt(100) < 5) { + indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + } + indexRequest.get(); + } + client().admin().indices().prepareRefresh("test_count").get(); + try (EsqlQueryResponse resp = run("FROM test_count | stats COUNT_DISTINCT(name)")) { + Iterator row = resp.values().next(); + assertThat(row.next(), equalTo((long) names.size())); + assertFalse(row.hasNext()); + } + try (EsqlQueryResponse resp = run("FROM test_count | stats COUNT(name)")) { + Iterator row = resp.values().next(); + assertThat(row.next(), equalTo((long) numDocs)); + assertFalse(row.hasNext()); + } + } + private void createNestedMappingIndex(String indexName) throws IOException { XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java index 2e20af1889773..85330c80750e7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java @@ -87,7 +87,7 @@ public AggregatorFunctionSupplier supplier(BigArrays bigArrays, List in if (type == DataTypes.DOUBLE) { return new CountDistinctDoubleAggregatorFunctionSupplier(bigArrays, inputChannels, precision); } - if (type == DataTypes.KEYWORD || type == DataTypes.IP) { + if (type == DataTypes.KEYWORD || type == DataTypes.IP || type == DataTypes.TEXT) { return new CountDistinctBytesRefAggregatorFunctionSupplier(bigArrays, inputChannels, precision); } throw EsqlIllegalArgumentException.illegalDataType(type); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java index 86ad56c115b3a..da81800c09402 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java @@ -225,7 +225,7 @@ static String dataTypeToString(DataType type, Class aggClass) { return "Long"; } else if (type.equals(DataTypes.DOUBLE)) { return "Double"; - } else if (type.equals(DataTypes.KEYWORD) || type.equals(DataTypes.IP)) { + } else if (type.equals(DataTypes.KEYWORD) || type.equals(DataTypes.IP) || type.equals(DataTypes.TEXT)) { return "BytesRef"; } else { throw new EsqlIllegalArgumentException("illegal agg type: " + type.typeName()); From 91e5259f7402de0783589fdc7d34c59bbcc6d77f Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Wed, 25 Oct 2023 14:18:33 -0400 Subject: [PATCH 112/203] [ci] Remove unused jjbb variable --- .ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml index f7fc27816c4be..2eeb08c6cff65 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+bwc.yml @@ -3,7 +3,6 @@ jjbb-template: matrix-gradle-unix-disabled.yml vars: - job-name: elastic+elasticsearch+%BRANCH%+periodic+bwc - job-display-name: "elastic / elasticsearch # %BRANCH% - backwards compatibility matrix" - - job-description: "Testing of the Elasticsearch %BRANCH% branch backwards compatibility matrix.\n" - matrix-yaml-file: ".ci/bwcVersions" - matrix-variable: BWC_VERSION - gradle-args: "-Dbwc.checkout.align=true v$BWC_VERSION#bwcTest" From aa7e92e29b54db2f71dae4eae74e6b45b3f2bf39 Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Wed, 25 Oct 2023 14:26:47 -0500 Subject: [PATCH 113/203] Metrics: Gauge Callbacks (#101286) Replace synchronous gauges with gauges that accept an observer which is called once per reporting interval. Calling code may register one observer at a time. If the observer is null, there is no observation in that reporting interval. --- .../telemetry/apm/APMMeterRegistry.java | 27 ++++++- .../internal/metrics/DoubleGaugeAdapter.java | 43 ++++++----- .../internal/metrics/LongGaugeAdapter.java | 43 ++++++----- .../apm/internal/metrics/OtelHelper.java | 8 ++ .../telemetry/apm/RecordingOtelMeter.java | 4 +- .../internal/metrics/GaugeAdapterTests.java | 69 +++++++++++++---- .../telemetry/metric/DoubleGauge.java | 23 +----- .../metric/DoubleWithAttributes.java | 15 ++++ .../telemetry/metric/LongGauge.java | 25 +----- .../telemetry/metric/LongWithAttributes.java | 15 ++++ .../telemetry/metric/MeterRegistry.java | 14 +++- .../telemetry/InstrumentType.java | 8 +- .../telemetry/MetricRecorder.java | 17 +++- .../telemetry/RecordingInstruments.java | 77 +++++++++++++------ .../telemetry/RecordingMeterRegistry.java | 20 +++-- 15 files changed, 264 insertions(+), 144 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/DoubleWithAttributes.java create mode 100644 server/src/main/java/org/elasticsearch/telemetry/metric/LongWithAttributes.java diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java index ac3ff7f28a4cf..57649f7e3dfa6 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APMMeterRegistry.java @@ -24,15 +24,18 @@ import org.elasticsearch.telemetry.metric.DoubleGauge; import org.elasticsearch.telemetry.metric.DoubleHistogram; import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.DoubleWithAttributes; import org.elasticsearch.telemetry.metric.LongCounter; import org.elasticsearch.telemetry.metric.LongGauge; import org.elasticsearch.telemetry.metric.LongHistogram; import org.elasticsearch.telemetry.metric.LongUpDownCounter; +import org.elasticsearch.telemetry.metric.LongWithAttributes; import org.elasticsearch.telemetry.metric.MeterRegistry; import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; /** * Container for registering and fetching meterRegistrar by type and name. @@ -69,82 +72,98 @@ public APMMeterRegistry(Meter meter) { // Access to registration has to be restricted when the provider is updated in ::setProvider protected final ReleasableLock registerLock = new ReleasableLock(new ReentrantLock()); + @Override public DoubleCounter registerDoubleCounter(String name, String description, String unit) { try (ReleasableLock lock = registerLock.acquire()) { return doubleCounters.register(new DoubleCounterAdapter(meter, name, description, unit)); } } + @Override public DoubleCounter getDoubleCounter(String name) { return doubleCounters.get(name); } + @Override public DoubleUpDownCounter registerDoubleUpDownCounter(String name, String description, String unit) { try (ReleasableLock lock = registerLock.acquire()) { return doubleUpDownCounters.register(new DoubleUpDownCounterAdapter(meter, name, description, unit)); } } + @Override public DoubleUpDownCounter getDoubleUpDownCounter(String name) { return doubleUpDownCounters.get(name); } - public DoubleGauge registerDoubleGauge(String name, String description, String unit) { + @Override + public DoubleGauge registerDoubleGauge(String name, String description, String unit, Supplier observer) { try (ReleasableLock lock = registerLock.acquire()) { - return doubleGauges.register(new DoubleGaugeAdapter(meter, name, description, unit)); + return doubleGauges.register(new DoubleGaugeAdapter(meter, name, description, unit, observer)); } } + @Override public DoubleGauge getDoubleGauge(String name) { return doubleGauges.get(name); } + @Override public DoubleHistogram registerDoubleHistogram(String name, String description, String unit) { try (ReleasableLock lock = registerLock.acquire()) { return doubleHistograms.register(new DoubleHistogramAdapter(meter, name, description, unit)); } } + @Override public DoubleHistogram getDoubleHistogram(String name) { return doubleHistograms.get(name); } + @Override public LongCounter registerLongCounter(String name, String description, String unit) { try (ReleasableLock lock = registerLock.acquire()) { return longCounters.register(new LongCounterAdapter(meter, name, description, unit)); } } + @Override public LongCounter getLongCounter(String name) { return longCounters.get(name); } + @Override public LongUpDownCounter registerLongUpDownCounter(String name, String description, String unit) { try (ReleasableLock lock = registerLock.acquire()) { return longUpDownCounters.register(new LongUpDownCounterAdapter(meter, name, description, unit)); } } + @Override public LongUpDownCounter getLongUpDownCounter(String name) { return longUpDownCounters.get(name); } - public LongGauge registerLongGauge(String name, String description, String unit) { + @Override + public LongGauge registerLongGauge(String name, String description, String unit, Supplier observer) { try (ReleasableLock lock = registerLock.acquire()) { - return longGauges.register(new LongGaugeAdapter(meter, name, description, unit)); + return longGauges.register(new LongGaugeAdapter(meter, name, description, unit, observer)); } } + @Override public LongGauge getLongGauge(String name) { return longGauges.get(name); } + @Override public LongHistogram registerLongHistogram(String name, String description, String unit) { try (ReleasableLock lock = registerLock.acquire()) { return longHistograms.register(new LongHistogramAdapter(meter, name, description, unit)); } } + @Override public LongHistogram getLongHistogram(String name) { return longHistograms.get(name); } diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java index 727c8c75a6f9b..faef8bd723fcf 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/DoubleGaugeAdapter.java @@ -11,25 +11,28 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import org.elasticsearch.common.util.concurrent.ReleasableLock; import org.elasticsearch.telemetry.apm.AbstractInstrument; +import org.elasticsearch.telemetry.metric.DoubleWithAttributes; -import java.util.Collections; -import java.util.Map; import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; /** - * DoubleGaugeAdapter wraps an otel ObservableDoubleMeasurement + * DoubleGaugeAdapter wraps an otel ObservableLongGauge */ public class DoubleGaugeAdapter extends AbstractInstrument implements org.elasticsearch.telemetry.metric.DoubleGauge { - private final AtomicReference valueWithAttributes; + private final Supplier observer; + private final ReleasableLock closedLock = new ReleasableLock(new ReentrantLock()); + private boolean closed = false; - public DoubleGaugeAdapter(Meter meter, String name, String description, String unit) { + public DoubleGaugeAdapter(Meter meter, String name, String description, String unit, Supplier observer) { super(meter, name, description, unit); - this.valueWithAttributes = new AtomicReference<>(new ValueWithAttributes(0.0, Collections.emptyMap())); + this.observer = observer; } @Override @@ -39,20 +42,24 @@ protected io.opentelemetry.api.metrics.ObservableDoubleGauge buildInstrument(Met .setDescription(getDescription()) .setUnit(getUnit()) .buildWithCallback(measurement -> { - var localValueWithAttributed = valueWithAttributes.get(); - measurement.record(localValueWithAttributed.value(), OtelHelper.fromMap(localValueWithAttributed.attributes())); + DoubleWithAttributes observation; + try { + observation = observer.get(); + } catch (RuntimeException err) { + assert false : "observer must not throw [" + err.getMessage() + "]"; + return; + } + measurement.record(observation.value(), OtelHelper.fromMap(observation.attributes())); }); } @Override - public void record(double value) { - record(value, Collections.emptyMap()); + public void close() throws Exception { + try (ReleasableLock lock = closedLock.acquire()) { + if (closed == false) { + getInstrument().close(); + } + closed = true; + } } - - @Override - public void record(double value, Map attributes) { - this.valueWithAttributes.set(new ValueWithAttributes(value, attributes)); - } - - private record ValueWithAttributes(double value, Map attributes) {} } diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java index 2b2f573941484..e297ba7ee963a 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/LongGaugeAdapter.java @@ -11,22 +11,25 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.ObservableLongGauge; +import org.elasticsearch.common.util.concurrent.ReleasableLock; import org.elasticsearch.telemetry.apm.AbstractInstrument; +import org.elasticsearch.telemetry.metric.LongWithAttributes; -import java.util.Collections; -import java.util.Map; import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; /** - * LongGaugeAdapter wraps an otel ObservableLongMeasurement + * LongGaugeAdapter wraps an otel ObservableLongGauge */ public class LongGaugeAdapter extends AbstractInstrument implements org.elasticsearch.telemetry.metric.LongGauge { - private final AtomicReference valueWithAttributes; + private final Supplier observer; + private final ReleasableLock closedLock = new ReleasableLock(new ReentrantLock()); + private boolean closed = false; - public LongGaugeAdapter(Meter meter, String name, String description, String unit) { + public LongGaugeAdapter(Meter meter, String name, String description, String unit, Supplier observer) { super(meter, name, description, unit); - this.valueWithAttributes = new AtomicReference<>(new ValueWithAttributes(0L, Collections.emptyMap())); + this.observer = observer; } @Override @@ -37,20 +40,24 @@ protected io.opentelemetry.api.metrics.ObservableLongGauge buildInstrument(Meter .setDescription(getDescription()) .setUnit(getUnit()) .buildWithCallback(measurement -> { - var localValueWithAttributed = valueWithAttributes.get(); - measurement.record(localValueWithAttributed.value(), OtelHelper.fromMap(localValueWithAttributed.attributes())); + LongWithAttributes observation; + try { + observation = observer.get(); + } catch (RuntimeException err) { + assert false : "observer must not throw [" + err.getMessage() + "]"; + return; + } + measurement.record(observation.value(), OtelHelper.fromMap(observation.attributes())); }); } @Override - public void record(long value) { - record(value, Collections.emptyMap()); + public void close() throws Exception { + try (ReleasableLock lock = closedLock.acquire()) { + if (closed == false) { + getInstrument().close(); + } + closed = true; + } } - - @Override - public void record(long value, Map attributes) { - this.valueWithAttributes.set(new ValueWithAttributes(value, attributes)); - } - - private record ValueWithAttributes(long value, Map attributes) {} } diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java index 673025a1a41f4..18bf66cee5391 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/metrics/OtelHelper.java @@ -23,8 +23,16 @@ static Attributes fromMap(Map attributes) { builder.put(k, value); } else if (v instanceof Long value) { builder.put(k, value); + } else if (v instanceof Integer value) { + builder.put(k, value); + } else if (v instanceof Byte value) { + builder.put(k, value); + } else if (v instanceof Short value) { + builder.put(k, value); } else if (v instanceof Double value) { builder.put(k, value); + } else if (v instanceof Float value) { + builder.put(k, value); } else if (v instanceof Boolean value) { builder.put(k, value); } else { diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java index a067d5e0f8c60..6661653499f63 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java @@ -446,7 +446,7 @@ private class DoubleGaugeRecorder extends AbstractInstrument implements Observab final Consumer callback; DoubleGaugeRecorder(String name, Consumer callback) { - super(name, InstrumentType.DOUBLE_GAUGE_OBSERVER); + super(name, InstrumentType.DOUBLE_GAUGE); this.callback = callback; } @@ -517,7 +517,7 @@ private class LongGaugeRecorder extends AbstractInstrument implements Observable final Consumer callback; LongGaugeRecorder(String name, Consumer callback) { - super(name, InstrumentType.LONG_GAUGE_OBSERVER); + super(name, InstrumentType.LONG_GAUGE); this.callback = callback; } diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java index 6e349842f7673..e8cd18521f842 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java @@ -12,12 +12,15 @@ import org.elasticsearch.telemetry.apm.APMMeterRegistry; import org.elasticsearch.telemetry.apm.RecordingOtelMeter; import org.elasticsearch.telemetry.metric.DoubleGauge; +import org.elasticsearch.telemetry.metric.DoubleWithAttributes; import org.elasticsearch.telemetry.metric.LongGauge; +import org.elasticsearch.telemetry.metric.LongWithAttributes; import org.elasticsearch.test.ESTestCase; import org.junit.Before; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -33,34 +36,68 @@ public void init() { } // testing that a value reported is then used in a callback - @SuppressWarnings("unchecked") - public void testLongGaugeRecord() { - LongGauge longGauge = registry.registerLongGauge("name", "desc", "unit"); + public void testLongGaugeRecord() throws Exception { + AtomicReference attrs = new AtomicReference<>(); + LongGauge gauge = registry.registerLongGauge("name", "desc", "unit", attrs::get); - // recording a value - Map attributes = Map.of("k", 1L); - longGauge.record(1L, attributes); + attrs.set(new LongWithAttributes(1L, Map.of("k", 1L))); otelMeter.collectMetrics(); - List metrics = otelMeter.getRecorder().getMeasurements(longGauge); + List metrics = otelMeter.getRecorder().getMeasurements(gauge); assertThat(metrics, hasSize(1)); - assertThat(metrics.get(0).attributes(), equalTo(attributes)); + assertThat(metrics.get(0).attributes(), equalTo(Map.of("k", 1L))); assertThat(metrics.get(0).getLong(), equalTo(1L)); + + attrs.set(new LongWithAttributes(2L, Map.of("k", 5L))); + + otelMeter.getRecorder().resetCalls(); + otelMeter.collectMetrics(); + + metrics = otelMeter.getRecorder().getMeasurements(gauge); + assertThat(metrics, hasSize(1)); + assertThat(metrics.get(0).attributes(), equalTo(Map.of("k", 5L))); + assertThat(metrics.get(0).getLong(), equalTo(2L)); + + gauge.close(); + + otelMeter.getRecorder().resetCalls(); + otelMeter.collectMetrics(); + + metrics = otelMeter.getRecorder().getMeasurements(gauge); + assertThat(metrics, hasSize(0)); } // testing that a value reported is then used in a callback - @SuppressWarnings("unchecked") - public void testDoubleGaugeRecord() { - DoubleGauge doubleGauge = registry.registerDoubleGauge("name", "desc", "unit"); - Map attributes = Map.of("k", 1L); - doubleGauge.record(1.0, attributes); + public void testDoubleGaugeRecord() throws Exception { + AtomicReference attrs = new AtomicReference<>(); + DoubleGauge gauge = registry.registerDoubleGauge("name", "desc", "unit", attrs::get); + + attrs.set(new DoubleWithAttributes(1.0d, Map.of("k", 1L))); otelMeter.collectMetrics(); - List metrics = otelMeter.getRecorder().getMeasurements(doubleGauge); + List metrics = otelMeter.getRecorder().getMeasurements(gauge); assertThat(metrics, hasSize(1)); - assertThat(metrics.get(0).attributes(), equalTo(attributes)); - assertThat(metrics.get(0).getDouble(), equalTo(1.0)); + assertThat(metrics.get(0).attributes(), equalTo(Map.of("k", 1L))); + assertThat(metrics.get(0).getDouble(), equalTo(1.0d)); + + attrs.set(new DoubleWithAttributes(2.0d, Map.of("k", 5L))); + + otelMeter.getRecorder().resetCalls(); + otelMeter.collectMetrics(); + + metrics = otelMeter.getRecorder().getMeasurements(gauge); + assertThat(metrics, hasSize(1)); + assertThat(metrics.get(0).attributes(), equalTo(Map.of("k", 5L))); + assertThat(metrics.get(0).getDouble(), equalTo(2.0d)); + + gauge.close(); + + otelMeter.getRecorder().resetCalls(); + otelMeter.collectMetrics(); + + metrics = otelMeter.getRecorder().getMeasurements(gauge); + assertThat(metrics, hasSize(0)); } } diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java index 797c125900bb8..47244d16924c0 100644 --- a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleGauge.java @@ -8,22 +8,10 @@ package org.elasticsearch.telemetry.metric; -import java.util.Map; - /** - * Record non-additive double values. eg number of running threads, current load + * Record non-additive double values based on a callback. eg number of running threads, current load */ -public interface DoubleGauge extends Instrument { - /** - * Record the current value for measured item - */ - void record(double value); - - /** - * Record the current value - * @param attributes key-value pairs to associate with the current measurement - */ - void record(double value, Map attributes); +public interface DoubleGauge extends Instrument, AutoCloseable { /** * Noop gauge for tests @@ -35,12 +23,7 @@ public String getName() { } @Override - public void record(double value) { - - } - - @Override - public void record(double value, Map attributes) { + public void close() { } }; diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleWithAttributes.java b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleWithAttributes.java new file mode 100644 index 0000000000000..e342b6128998d --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/DoubleWithAttributes.java @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +public record DoubleWithAttributes(double value, Map attributes) { + +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java b/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java index 71539064ce53e..a9387acd67434 100644 --- a/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/LongGauge.java @@ -8,24 +8,10 @@ package org.elasticsearch.telemetry.metric; -import java.util.Map; - /** - * Record non-additive long values. + * Record non-additive long values based on a callback */ -public interface LongGauge extends Instrument { - - /** - * Record the current value of the measured item. - * @param value - */ - void record(long value); - - /** - * Record the current value - * @param attributes key-value pairs to associate with the current measurement - */ - void record(long value, Map attributes); +public interface LongGauge extends Instrument, AutoCloseable { /** * Noop gauge for tests @@ -37,12 +23,7 @@ public String getName() { } @Override - public void record(long value) { - - } - - @Override - public void record(long value, Map attributes) { + public void close() throws Exception { } }; diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/LongWithAttributes.java b/server/src/main/java/org/elasticsearch/telemetry/metric/LongWithAttributes.java new file mode 100644 index 0000000000000..eef880431fb83 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/LongWithAttributes.java @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.telemetry.metric; + +import java.util.Map; + +public record LongWithAttributes(long value, Map attributes) { + +} diff --git a/server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java b/server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java index e6f81786bb2f2..6940795213603 100644 --- a/server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java +++ b/server/src/main/java/org/elasticsearch/telemetry/metric/MeterRegistry.java @@ -8,6 +8,8 @@ package org.elasticsearch.telemetry.metric; +import java.util.function.Supplier; + /** * Container for metering instruments. Meters with the same name and type (DoubleCounter, etc) can * only be registered once. @@ -51,9 +53,11 @@ public interface MeterRegistry { * @param name name of the gauge * @param description description of purpose * @param unit the unit (bytes, sec, hour) + * @param observer callback to use. This is called once during reporting period. + * Must not throw an exception and must be safe to call from different threads. * @return the registered meter. */ - DoubleGauge registerDoubleGauge(String name, String description, String unit); + DoubleGauge registerDoubleGauge(String name, String description, String unit, Supplier observer); /** * Retrieved a previously registered {@link DoubleGauge}. @@ -115,9 +119,11 @@ public interface MeterRegistry { * @param name name of the gauge * @param description description of purpose * @param unit the unit (bytes, sec, hour) + * @param observer callback to use. This is called once during reporting period. + * Must not throw an exception and must be safe to call from different threads. * @return the registered meter. */ - LongGauge registerLongGauge(String name, String description, String unit); + LongGauge registerLongGauge(String name, String description, String unit, Supplier observer); /** * Retrieved a previously registered {@link LongGauge}. @@ -166,7 +172,7 @@ public DoubleUpDownCounter getDoubleUpDownCounter(String name) { } @Override - public DoubleGauge registerDoubleGauge(String name, String description, String unit) { + public DoubleGauge registerDoubleGauge(String name, String description, String unit, Supplier observer) { return DoubleGauge.NOOP; } @@ -206,7 +212,7 @@ public LongUpDownCounter getLongUpDownCounter(String name) { } @Override - public LongGauge registerLongGauge(String name, String description, String unit) { + public LongGauge registerLongGauge(String name, String description, String unit, Supplier observer) { return LongGauge.NOOP; } diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java b/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java index ed99d79fd37ab..3930adf1af638 100644 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java @@ -32,9 +32,7 @@ public enum InstrumentType { DOUBLE_HISTOGRAM(true), LONG_HISTOGRAM(false), DOUBLE_GAUGE(true), - LONG_GAUGE(false), - DOUBLE_GAUGE_OBSERVER(true), - LONG_GAUGE_OBSERVER(false); + LONG_GAUGE(false); public final boolean isDouble; public final boolean isLong; @@ -59,9 +57,9 @@ public static InstrumentType fromInstrument(Instrument instrument) { } else if (instrument instanceof LongHistogram) { return InstrumentType.LONG_HISTOGRAM; } else if (instrument instanceof DoubleGauge) { - return InstrumentType.DOUBLE_GAUGE_OBSERVER; + return InstrumentType.DOUBLE_GAUGE; } else if (instrument instanceof LongGauge) { - return InstrumentType.LONG_GAUGE_OBSERVER; + return InstrumentType.LONG_GAUGE; } else { throw new IllegalArgumentException("unknown instrument [" + instrument.getClass().getName() + "]"); } diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java b/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java index faf6b0b31c837..29218783a45b7 100644 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java @@ -34,19 +34,24 @@ public class MetricRecorder { private record RegisteredMetric( Map registered, Map> called, - Map instruments + Map instruments, + List callbacks ) { void register(String name, String description, String unit, I instrument) { assert registered.containsKey(name) == false : Strings.format("unexpected [{}]: [{}][{}], already registered[{}]", name, description, unit, registered.get(name)); registered.put(name, new Registration(name, description, unit)); instruments.put(name, instrument); + if (instrument instanceof Runnable callback) { + callbacks.add(callback); + } } void call(String name, Measurement call) { assert registered.containsKey(name) : Strings.format("call for unregistered metric [{}]: [{}]", name, call); called.computeIfAbsent(Objects.requireNonNull(name), k -> new ArrayList<>()).add(call); } + } /** @@ -57,7 +62,7 @@ void call(String name, Measurement call) { public MetricRecorder() { metrics = new HashMap<>(InstrumentType.values().length); for (var instrument : InstrumentType.values()) { - metrics.put(instrument, new RegisteredMetric<>(new HashMap<>(), new HashMap<>(), new HashMap<>())); + metrics.put(instrument, new RegisteredMetric<>(new HashMap<>(), new HashMap<>(), new HashMap<>(), new ArrayList<>())); } } @@ -106,4 +111,12 @@ public Registration getRegistration(Instrument instrument) { public I getInstrument(InstrumentType instrumentType, String name) { return metrics.get(instrumentType).instruments.get(name); } + + public void resetCalls() { + metrics.forEach((it, rm) -> rm.called().clear()); + } + + public void collect() { + metrics.forEach((it, rm) -> rm.callbacks().forEach(Runnable::run)); + } } diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java index 3878c7f9e7da1..7067c390ef5ae 100644 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingInstruments.java @@ -8,19 +8,25 @@ package org.elasticsearch.telemetry; +import org.elasticsearch.common.util.concurrent.ReleasableLock; +import org.elasticsearch.core.Tuple; import org.elasticsearch.telemetry.metric.DoubleCounter; import org.elasticsearch.telemetry.metric.DoubleGauge; import org.elasticsearch.telemetry.metric.DoubleHistogram; import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.DoubleWithAttributes; import org.elasticsearch.telemetry.metric.Instrument; import org.elasticsearch.telemetry.metric.LongCounter; import org.elasticsearch.telemetry.metric.LongGauge; import org.elasticsearch.telemetry.metric.LongHistogram; import org.elasticsearch.telemetry.metric.LongUpDownCounter; +import org.elasticsearch.telemetry.metric.LongWithAttributes; import java.util.Collections; import java.util.Map; import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; /** * Recording versions of Elasticsearch {@link Instrument}s. All invocations are recorded via {@link MetricRecorder}. @@ -45,6 +51,40 @@ public String getName() { } } + protected interface NumberWithAttributesObserver extends Supplier>> { + + } + + protected abstract static class CallbackRecordingInstrument extends RecordingInstrument implements AutoCloseable, Runnable { + private final NumberWithAttributesObserver observer; + private boolean closed = false; + private final ReleasableLock closedLock = new ReleasableLock(new ReentrantLock()); + + public CallbackRecordingInstrument(String name, NumberWithAttributesObserver observer, MetricRecorder recorder) { + super(name, recorder); + this.observer = observer; + } + + @Override + public void run() { + try (ReleasableLock lock = closedLock.acquire()) { + if (closed) { + return; + } + var observation = observer.get(); + call(observation.v1(), observation.v2()); + } + } + + @Override + public void close() throws Exception { + try (ReleasableLock lock = closedLock.acquire()) { + assert closed == false : "double close"; + closed = true; + } + } + } + public static class RecordingDoubleCounter extends RecordingInstrument implements DoubleCounter { public RecordingDoubleCounter(String name, MetricRecorder recorder) { super(name, recorder); @@ -66,19 +106,12 @@ public void incrementBy(double inc, Map attributes) { } } - public static class RecordingDoubleGauge extends RecordingInstrument implements DoubleGauge { - public RecordingDoubleGauge(String name, MetricRecorder recorder) { - super(name, recorder); - } - - @Override - public void record(double value) { - record(value, Collections.emptyMap()); - } - - @Override - public void record(double value, Map attributes) { - call(value, attributes); + public static class RecordingDoubleGauge extends CallbackRecordingInstrument implements DoubleGauge { + public RecordingDoubleGauge(String name, Supplier observer, MetricRecorder recorder) { + super(name, () -> { + var observation = observer.get(); + return new Tuple<>(observation.value(), observation.attributes()); + }, recorder); } } @@ -135,19 +168,13 @@ public void incrementBy(long inc, Map attributes) { } } - public static class RecordingLongGauge extends RecordingInstrument implements LongGauge { - public RecordingLongGauge(String name, MetricRecorder recorder) { - super(name, recorder); - } + public static class RecordingLongGauge extends CallbackRecordingInstrument implements LongGauge { - @Override - public void record(long value) { - record(value, Collections.emptyMap()); - } - - @Override - public void record(long value, Map attributes) { - call(value, attributes); + public RecordingLongGauge(String name, Supplier observer, MetricRecorder recorder) { + super(name, () -> { + var observation = observer.get(); + return new Tuple<>(observation.value(), observation.attributes()); + }, recorder); } } diff --git a/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java index 1ca34758c8ffb..f552b2d001b42 100644 --- a/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java +++ b/test/framework/src/main/java/org/elasticsearch/telemetry/RecordingMeterRegistry.java @@ -12,13 +12,17 @@ import org.elasticsearch.telemetry.metric.DoubleGauge; import org.elasticsearch.telemetry.metric.DoubleHistogram; import org.elasticsearch.telemetry.metric.DoubleUpDownCounter; +import org.elasticsearch.telemetry.metric.DoubleWithAttributes; import org.elasticsearch.telemetry.metric.Instrument; import org.elasticsearch.telemetry.metric.LongCounter; import org.elasticsearch.telemetry.metric.LongGauge; import org.elasticsearch.telemetry.metric.LongHistogram; import org.elasticsearch.telemetry.metric.LongUpDownCounter; +import org.elasticsearch.telemetry.metric.LongWithAttributes; import org.elasticsearch.telemetry.metric.MeterRegistry; +import java.util.function.Supplier; + /** * A {@link MeterRegistry} that records all instrument invocations. * Tests can subclass this class and extend the build[Instrument] methods to do their @@ -64,8 +68,8 @@ protected DoubleUpDownCounter buildDoubleUpDownCounter(String name, String descr } @Override - public DoubleGauge registerDoubleGauge(String name, String description, String unit) { - DoubleGauge instrument = buildDoubleGauge(name, description, unit); + public DoubleGauge registerDoubleGauge(String name, String description, String unit, Supplier observer) { + DoubleGauge instrument = buildDoubleGauge(name, description, unit, observer); recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); return instrument; } @@ -75,8 +79,8 @@ public DoubleGauge getDoubleGauge(String name) { return (DoubleGauge) recorder.getInstrument(InstrumentType.DOUBLE_GAUGE, name); } - protected DoubleGauge buildDoubleGauge(String name, String description, String unit) { - return new RecordingInstruments.RecordingDoubleGauge(name, recorder); + protected DoubleGauge buildDoubleGauge(String name, String description, String unit, Supplier observer) { + return new RecordingInstruments.RecordingDoubleGauge(name, observer, recorder); } @Override @@ -128,8 +132,8 @@ protected LongUpDownCounter buildLongUpDownCounter(String name, String descripti } @Override - public LongGauge registerLongGauge(String name, String description, String unit) { - LongGauge instrument = buildLongGauge(name, description, unit); + public LongGauge registerLongGauge(String name, String description, String unit, Supplier observer) { + LongGauge instrument = buildLongGauge(name, description, unit, observer); recorder.register(instrument, InstrumentType.fromInstrument(instrument), name, description, unit); return instrument; } @@ -139,8 +143,8 @@ public LongGauge getLongGauge(String name) { return (LongGauge) recorder.getInstrument(InstrumentType.LONG_GAUGE, name); } - protected LongGauge buildLongGauge(String name, String description, String unit) { - return new RecordingInstruments.RecordingLongGauge(name, recorder); + protected LongGauge buildLongGauge(String name, String description, String unit, Supplier observer) { + return new RecordingInstruments.RecordingLongGauge(name, observer, recorder); } @Override From 3a91763d27b53a28c81380f35385b3c33f6c986e Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Wed, 25 Oct 2023 16:52:25 -0400 Subject: [PATCH 114/203] [DOCS] Deprecate rollups (#101265) --- .../src/main/resources/changelog-schema.json | 2 ++ docs/changelog/101265.yaml | 13 +++++++++++++ docs/reference/data-rollup-transform.asciidoc | 2 ++ docs/reference/how-to/disk-usage.asciidoc | 4 ++-- docs/reference/intro.asciidoc | 4 ++-- docs/reference/landing-page.asciidoc | 2 +- docs/reference/rollup/api-quickref.asciidoc | 5 +---- docs/reference/rollup/apis/delete-job.asciidoc | 7 ++----- docs/reference/rollup/apis/get-job.asciidoc | 9 +++------ docs/reference/rollup/apis/put-job.asciidoc | 15 ++++++--------- docs/reference/rollup/apis/rollup-caps.asciidoc | 9 +++------ .../rollup/apis/rollup-index-caps.asciidoc | 7 ++----- docs/reference/rollup/apis/rollup-search.asciidoc | 7 ++----- docs/reference/rollup/apis/start-job.asciidoc | 9 +++------ docs/reference/rollup/apis/stop-job.asciidoc | 9 +++------ docs/reference/rollup/index.asciidoc | 5 +---- docs/reference/rollup/overview.asciidoc | 5 +---- .../rollup/rollup-agg-limitations.asciidoc | 5 +---- docs/reference/rollup/rollup-apis.asciidoc | 2 ++ .../rollup/rollup-getting-started.asciidoc | 5 +---- .../rollup/rollup-search-limitations.asciidoc | 5 +---- .../rollup/understanding-groups.asciidoc | 5 +---- 22 files changed, 55 insertions(+), 81 deletions(-) create mode 100644 docs/changelog/101265.yaml diff --git a/build-tools-internal/src/main/resources/changelog-schema.json b/build-tools-internal/src/main/resources/changelog-schema.json index 644c543222274..87a9313f3eefe 100644 --- a/build-tools-internal/src/main/resources/changelog-schema.json +++ b/build-tools-internal/src/main/resources/changelog-schema.json @@ -75,6 +75,7 @@ "Ranking", "Recovery", "Reindex", + "Rollup", "SQL", "Search", "Security", @@ -277,6 +278,7 @@ "Packaging", "Painless", "REST API", + "Rollup", "System requirement", "Transform" ] diff --git a/docs/changelog/101265.yaml b/docs/changelog/101265.yaml new file mode 100644 index 0000000000000..f39b57fa9a75e --- /dev/null +++ b/docs/changelog/101265.yaml @@ -0,0 +1,13 @@ +pr: 101265 +summary: Rollup functionality is now deprecated +area: Rollup +type: deprecation +issues: [] +deprecation: + title: >- + Rollup functionality is now deprecated + area: Rollup + details: |- + {ref}/xpack-rollup[Rollup functionality] has been deprecated and will be removed in a future release. Previously, rollups were available in technical preview. + impact: |- + Use {ref}/downsampling.html[downsampling] to reduce storage costs for time series data by by storing it at reduced granularity. diff --git a/docs/reference/data-rollup-transform.asciidoc b/docs/reference/data-rollup-transform.asciidoc index 3a7b3bedc7765..3116d4117f70e 100644 --- a/docs/reference/data-rollup-transform.asciidoc +++ b/docs/reference/data-rollup-transform.asciidoc @@ -8,6 +8,8 @@ * <> + +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] ++ include::rollup/index.asciidoc[tag=rollup-intro] * <> diff --git a/docs/reference/how-to/disk-usage.asciidoc b/docs/reference/how-to/disk-usage.asciidoc index 6eef9621ed9de..55a7b33f53cb3 100644 --- a/docs/reference/how-to/disk-usage.asciidoc +++ b/docs/reference/how-to/disk-usage.asciidoc @@ -137,5 +137,5 @@ if fields always occur in the same order. === Roll up historical data Keeping older data can be useful for later analysis but is often avoided due to -storage costs. You can use data rollups to summarize and store historical data -at a fraction of the raw data's storage cost. See <>. +storage costs. You can use downsampling to summarize and store historical data +at a fraction of the raw data's storage cost. See <>. diff --git a/docs/reference/intro.asciidoc b/docs/reference/intro.asciidoc index 9c9f2774d8f2c..3ea2c96eeaf02 100644 --- a/docs/reference/intro.asciidoc +++ b/docs/reference/intro.asciidoc @@ -261,6 +261,6 @@ secondary clusters are read-only followers. As with any enterprise system, you need tools to secure, manage, and monitor your {es} clusters. Security, monitoring, and administrative features that are integrated into {es} enable you to use {kibana-ref}/introduction.html[{kib}] -as a control center for managing a cluster. Features like <> and <> +as a control center for managing a cluster. Features like <> and <> help you intelligently manage your data over time. diff --git a/docs/reference/landing-page.asciidoc b/docs/reference/landing-page.asciidoc index 489168a443bb1..1ddd0cfa28128 100644 --- a/docs/reference/landing-page.asciidoc +++ b/docs/reference/landing-page.asciidoc @@ -172,7 +172,7 @@ Data management
  • - Roll up or transform your data + Downsampling
  • Snapshot and restore diff --git a/docs/reference/rollup/api-quickref.asciidoc b/docs/reference/rollup/api-quickref.asciidoc index f6975650dc2b8..1676161e5fbcc 100644 --- a/docs/reference/rollup/api-quickref.asciidoc +++ b/docs/reference/rollup/api-quickref.asciidoc @@ -5,10 +5,7 @@ API quick reference ++++ -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] Most rollup endpoints have the following base: diff --git a/docs/reference/rollup/apis/delete-job.asciidoc b/docs/reference/rollup/apis/delete-job.asciidoc index 97e75ff7181df..c563e705039e2 100644 --- a/docs/reference/rollup/apis/delete-job.asciidoc +++ b/docs/reference/rollup/apis/delete-job.asciidoc @@ -6,12 +6,9 @@ Delete {rollup-jobs} ++++ -Deletes an existing {rollup-job}. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +Deletes an existing {rollup-job}. [[rollup-delete-job-request]] ==== {api-request-title} diff --git a/docs/reference/rollup/apis/get-job.asciidoc b/docs/reference/rollup/apis/get-job.asciidoc index be308f81d5ab7..fcafbbe95159b 100644 --- a/docs/reference/rollup/apis/get-job.asciidoc +++ b/docs/reference/rollup/apis/get-job.asciidoc @@ -5,12 +5,9 @@ Get job ++++ -Retrieves the configuration, stats, and status of {rollup-jobs}. - -experimental[] +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +Retrieves the configuration, stats, and status of {rollup-jobs}. [[rollup-get-job-request]] ==== {api-request-title} @@ -48,7 +45,7 @@ For details about a historical {rollup-job}, the ==== {api-response-body-title} `jobs`:: -(array) An array of {rollup-job} resources. +(array) An array of {rollup-job} resources. + .Properties of {rollup-job} resources [%collapsible%open] diff --git a/docs/reference/rollup/apis/put-job.asciidoc b/docs/reference/rollup/apis/put-job.asciidoc index c5e94a5b6c2f3..2392d7204df3b 100644 --- a/docs/reference/rollup/apis/put-job.asciidoc +++ b/docs/reference/rollup/apis/put-job.asciidoc @@ -6,12 +6,9 @@ Create {rollup-jobs} ++++ -Creates a {rollup-job}. - -experimental[] +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +Creates a {rollup-job}. [[rollup-put-job-api-request]] ==== {api-request-title} @@ -93,7 +90,7 @@ documents without a timestamp and a `date_histogram` group. The + .Properties of `date_histogram` [%collapsible%open] -===== +===== `calendar_interval` or `fixed_interval`:::: (Required, <>) The interval of time buckets to be generated when rolling up. For example, `60m` produces 60 minute (hourly) @@ -139,11 +136,11 @@ in `UTC`. //Begin histogram `histogram`::: (Optional, object) The histogram group aggregates one or more numeric fields -into numeric histogram intervals. +into numeric histogram intervals. + .Properties of `histogram` [%collapsible%open] -===== +===== `fields`:::: (Required, array) The set of fields that you wish to build histograms for. All fields specified must be some kind of numeric. Order does not matter. @@ -175,7 +172,7 @@ judicious which high-cardinality fields are included for that reason. + .Properties of `terms` [%collapsible%open] -===== +===== `fields`:::: (Required, string) The set of fields that you wish to collect terms for. This diff --git a/docs/reference/rollup/apis/rollup-caps.asciidoc b/docs/reference/rollup/apis/rollup-caps.asciidoc index 78563047962ca..95f652f6d4415 100644 --- a/docs/reference/rollup/apis/rollup-caps.asciidoc +++ b/docs/reference/rollup/apis/rollup-caps.asciidoc @@ -5,14 +5,11 @@ Get rollup caps ++++ +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] + Returns the capabilities of any {rollup-jobs} that have been configured for a specific index or index pattern. -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. - [[rollup-get-rollup-caps-request]] ==== {api-request-title} @@ -43,7 +40,7 @@ can be performed, and where does the data live? ==== {api-path-parms-title} ``:: - (string) Index, indices or index-pattern to return rollup capabilities for. + (string) Index, indices or index-pattern to return rollup capabilities for. `_all` may be used to fetch rollup capabilities from all jobs. diff --git a/docs/reference/rollup/apis/rollup-index-caps.asciidoc b/docs/reference/rollup/apis/rollup-index-caps.asciidoc index 9ca33d2b141de..c5b729f2e52e6 100644 --- a/docs/reference/rollup/apis/rollup-index-caps.asciidoc +++ b/docs/reference/rollup/apis/rollup-index-caps.asciidoc @@ -5,14 +5,11 @@ Get rollup index caps ++++ +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] + Returns the rollup capabilities of all jobs inside of a rollup index (e.g. the index where rollup data is stored). -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. - [[rollup-get-rollup-index-caps-request]] ==== {api-request-title} diff --git a/docs/reference/rollup/apis/rollup-search.asciidoc b/docs/reference/rollup/apis/rollup-search.asciidoc index af6be2961afee..491dcc6c38ae2 100644 --- a/docs/reference/rollup/apis/rollup-search.asciidoc +++ b/docs/reference/rollup/apis/rollup-search.asciidoc @@ -5,12 +5,9 @@ Rollup search ++++ -Enables searching rolled-up data using the standard Query DSL. - -experimental[] +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +Enables searching rolled-up data using the standard Query DSL. [[rollup-search-request]] ==== {api-request-title} diff --git a/docs/reference/rollup/apis/start-job.asciidoc b/docs/reference/rollup/apis/start-job.asciidoc index b5e1edf3c16d8..c102c26ea5d8e 100644 --- a/docs/reference/rollup/apis/start-job.asciidoc +++ b/docs/reference/rollup/apis/start-job.asciidoc @@ -6,12 +6,9 @@ Start {rollup-jobs} ++++ -Starts an existing, stopped {rollup-job}. - -experimental[] +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +Starts an existing, stopped {rollup-job}. [[rollup-start-job-request]] ==== {api-request-title} @@ -36,7 +33,7 @@ to start a job that is already started, nothing happens. ``:: (Required, string) Identifier for the {rollup-job}. - + [[rollup-start-job-response-codes]] ==== {api-response-codes-title} diff --git a/docs/reference/rollup/apis/stop-job.asciidoc b/docs/reference/rollup/apis/stop-job.asciidoc index f714159a1c099..61e561b4ceac9 100644 --- a/docs/reference/rollup/apis/stop-job.asciidoc +++ b/docs/reference/rollup/apis/stop-job.asciidoc @@ -6,12 +6,9 @@ Stop {rollup-jobs} ++++ -Stops an existing, started {rollup-job}. - -experimental[] +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +Stops an existing, started {rollup-job}. [[rollup-stop-job-request]] ==== {api-request-title} @@ -52,7 +49,7 @@ processing and eventually moves the job to `STOPPED`. The timeout simply means the API call itself timed out while waiting for the status change. -- - + `wait_for_completion`:: (Optional, Boolean) If set to `true`, causes the API to block until the indexer state completely stops. If set to `false`, the API returns immediately diff --git a/docs/reference/rollup/index.asciidoc b/docs/reference/rollup/index.asciidoc index 5f5a36d55e627..a4394c3c930fd 100644 --- a/docs/reference/rollup/index.asciidoc +++ b/docs/reference/rollup/index.asciidoc @@ -2,10 +2,7 @@ [[xpack-rollup]] == Rolling up historical data -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] Keeping historical data around for analysis is extremely useful but often avoided due to the financial cost of archiving massive amounts of data. Retention periods are thus driven by financial realities rather than by the diff --git a/docs/reference/rollup/overview.asciidoc b/docs/reference/rollup/overview.asciidoc index 3026f7f63b0e4..67a65415c6d60 100644 --- a/docs/reference/rollup/overview.asciidoc +++ b/docs/reference/rollup/overview.asciidoc @@ -5,10 +5,7 @@ Overview ++++ -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] Time-based data (documents that are predominantly identified by their timestamp) often have associated retention policies to manage data growth. For example, your system may be generating 500 documents every second. That will generate diff --git a/docs/reference/rollup/rollup-agg-limitations.asciidoc b/docs/reference/rollup/rollup-agg-limitations.asciidoc index 72374dfeb1ba3..f6e557a27184e 100644 --- a/docs/reference/rollup/rollup-agg-limitations.asciidoc +++ b/docs/reference/rollup/rollup-agg-limitations.asciidoc @@ -2,10 +2,7 @@ [[rollup-agg-limitations]] === {rollup-cap} aggregation limitations -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] There are some limitations to how fields can be rolled up / aggregated. This page highlights the major limitations so that you are aware of them. diff --git a/docs/reference/rollup/rollup-apis.asciidoc b/docs/reference/rollup/rollup-apis.asciidoc index 8707e6a0c3217..94dab153ed9c7 100644 --- a/docs/reference/rollup/rollup-apis.asciidoc +++ b/docs/reference/rollup/rollup-apis.asciidoc @@ -2,6 +2,8 @@ [[rollup-apis]] == Rollup APIs +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] + [discrete] [[rollup-jobs-endpoint]] === Jobs diff --git a/docs/reference/rollup/rollup-getting-started.asciidoc b/docs/reference/rollup/rollup-getting-started.asciidoc index 0772a2778d9c2..7e00af05526ee 100644 --- a/docs/reference/rollup/rollup-getting-started.asciidoc +++ b/docs/reference/rollup/rollup-getting-started.asciidoc @@ -5,10 +5,7 @@ Getting started ++++ -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] To use the Rollup feature, you need to create one or more "Rollup Jobs". These jobs run continuously in the background and rollup the index or indices that you specify, placing the rolled documents in a secondary index (also of your choosing). diff --git a/docs/reference/rollup/rollup-search-limitations.asciidoc b/docs/reference/rollup/rollup-search-limitations.asciidoc index c64923811e2a1..ac44bd69722c0 100644 --- a/docs/reference/rollup/rollup-search-limitations.asciidoc +++ b/docs/reference/rollup/rollup-search-limitations.asciidoc @@ -2,10 +2,7 @@ [[rollup-search-limitations]] === {rollup-cap} search limitations -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] While we feel the Rollup function is extremely flexible, the nature of summarizing data means there will be some limitations. Once live data is thrown away, you will always lose some flexibility. diff --git a/docs/reference/rollup/understanding-groups.asciidoc b/docs/reference/rollup/understanding-groups.asciidoc index 21d061c2ae7ef..24afea110bd95 100644 --- a/docs/reference/rollup/understanding-groups.asciidoc +++ b/docs/reference/rollup/understanding-groups.asciidoc @@ -2,10 +2,7 @@ [[rollup-understanding-groups]] === Understanding groups -experimental[] - -NOTE: For version 8.5 and above we recommend <> over -rollups as a way to reduce your storage costs for time series data. +deprecated::[8.11.0,"Rollups will be removed in a future version. Use <> instead."] To preserve flexibility, Rollup Jobs are defined based on how future queries may need to use the data. Traditionally, systems force the admin to make decisions about what metrics to rollup and on what interval. E.g. The average of `cpu_time` on an hourly basis. This From e11e9551c2a08af43c008ba96ddd055795141be6 Mon Sep 17 00:00:00 2001 From: Athena Brown Date: Wed, 25 Oct 2023 15:02:07 -0600 Subject: [PATCH 115/203] Unmute PkiAuthDelegationIntegTests (#101280) These tests were muted both at the suite level as well as at the test level for reasons I don't fully understand, and then were unmuted at one level but not the other. They don't appear to fail after a few thousand runs, so this PR unmutes them the rest of the way. --- .../xpack/security/authc/pki/PkiAuthDelegationIntegTests.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java index 4f9cb10a2e3bc..9115c35551585 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java @@ -149,7 +149,6 @@ void clearRealmCache() { new ClearRealmCacheRequestBuilder(client()).get(); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/97772") public void testDelegateThenAuthenticate() throws Exception { final X509Certificate clientCertificate = readCertForPkiDelegation("testClient.crt"); final X509Certificate intermediateCA = readCertForPkiDelegation("testIntermediateCA.crt"); @@ -192,7 +191,6 @@ public void testDelegateThenAuthenticate() throws Exception { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/97772") public void testTokenInvalidate() throws Exception { final X509Certificate clientCertificate = readCertForPkiDelegation("testClient.crt"); final X509Certificate intermediateCA = readCertForPkiDelegation("testIntermediateCA.crt"); @@ -296,7 +294,6 @@ public void testDelegateUnauthorized() throws Exception { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/97772") public void testDelegatePkiWithRoleMapping() throws Exception { X509Certificate clientCertificate = readCertForPkiDelegation("testClient.crt"); X509Certificate intermediateCA = readCertForPkiDelegation("testIntermediateCA.crt"); From d0571b6c2215a16454b92bbcc6b88f8333bfe2a2 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 25 Oct 2023 16:15:19 -0600 Subject: [PATCH 116/203] Cleanup apm logging config (#101291) This commit makes the apm log a little more usable. First, it makes the path to the log explicit by passing the ES logs dir through, instead of relying on the location of the apm jar file. Second, it tweaks the log level to be warn, not error. Third, it switches the apm log file to be json, which is more easily processable. --- .../server/cli/APMJvmOptions.java | 18 ++++++++++-------- .../server/cli/JvmOptionsParser.java | 2 +- .../elasticsearch/server/cli/ServerCli.java | 2 +- .../server/cli/ServerProcessTests.java | 10 +++++++++- .../windows/service/WindowsServiceDaemon.java | 2 +- .../elasticsearch/bootstrap/ServerArgs.java | 15 ++++++++++++--- 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java index 28a086cf6f8f7..b6cd680cb5816 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/APMJvmOptions.java @@ -45,12 +45,6 @@ class APMJvmOptions { // Identifies the version of Elasticsearch in the captured trace data. "service_version", Build.current().version(), - // Configures a log file to write to. `_AGENT_HOME_` is a placeholder used - // by the agent. Don't disable writing to a log file, as the agent will then - // require extra Security Manager permissions when it tries to do something - // else, and it's just painful. - "log_file", "_AGENT_HOME_/../../logs/apm.log", - // ES does not use auto-instrumentation. "instrument", "false", "enable_experimental_instrumentations", "true" @@ -80,7 +74,8 @@ class APMJvmOptions { // Logging configuration. Unless you need detailed logs about what the APM // is doing, leave this value alone. - "log_level", "error", + "log_level", "warn", + "log_format_file", "JSON", "application_packages", "org.elasticsearch,org.apache.lucene", "metrics_interval", "120s", "breakdown_metrics", "false", @@ -134,9 +129,11 @@ class APMJvmOptions { * * @param settings the Elasticsearch settings to consider * @param secrets a wrapper to access the secrets, or null if there is no secrets + * @param logsDir the directory to write the apm log into * @param tmpdir Elasticsearch's temporary directory, where the config file will be written */ - static List apmJvmOptions(Settings settings, @Nullable SecureSettings secrets, Path tmpdir) throws UserException, IOException { + static List apmJvmOptions(Settings settings, @Nullable SecureSettings secrets, Path logsDir, Path tmpdir) throws UserException, + IOException { final Path agentJar = findAgentJar(); if (agentJar == null) { @@ -145,6 +142,11 @@ static List apmJvmOptions(Settings settings, @Nullable SecureSettings se final Map propertiesMap = extractApmSettings(settings); + // Configures a log file to write to. Don't disable writing to a log file, + // as the agent will then require extra Security Manager permissions when + // it tries to do something else, and it's just painful. + propertiesMap.put("log_file", logsDir.resolve("apm-agent.log").toString()); + // No point doing anything if we don't have a destination for the trace data, and it can't be configured dynamically if (propertiesMap.containsKey("server_url") == false && propertiesMap.containsKey("server_urls") == false) { return List.of(); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index 1b92300ac3dd2..5999f618bc0ab 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -139,7 +139,7 @@ private List jvmOptions( final List ergonomicJvmOptions = JvmErgonomics.choose(substitutedJvmOptions); final List systemJvmOptions = SystemJvmOptions.systemJvmOptions(); - final List apmOptions = APMJvmOptions.apmJvmOptions(args.nodeSettings(), args.secrets(), tmpDir); + final List apmOptions = APMJvmOptions.apmJvmOptions(args.nodeSettings(), args.secrets(), args.logsDir(), tmpDir); final List finalJvmOptions = new ArrayList<>( systemJvmOptions.size() + substitutedJvmOptions.size() + ergonomicJvmOptions.size() + apmOptions.size() diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 7b3adadb29b4c..25c61c41638d1 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -219,7 +219,7 @@ private ServerArgs createArgs(OptionSet options, Environment env, SecureSettings } validatePidFile(pidFile); } - return new ServerArgs(daemonize, quiet, pidFile, secrets, env.settings(), env.configFile()); + return new ServerArgs(daemonize, quiet, pidFile, secrets, env.settings(), env.configFile(), env.logsFile()); } @Override diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java index c8dfb84363d62..57993d40391ac 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java @@ -195,7 +195,15 @@ public Process destroyForcibly() { ServerProcess startProcess(boolean daemonize, boolean quiet, String keystorePassword) throws Exception { var pinfo = new ProcessInfo(Map.copyOf(sysprops), Map.copyOf(envVars), esHomeDir); - var args = new ServerArgs(daemonize, quiet, null, secrets, nodeSettings.build(), esHomeDir.resolve("config")); + var args = new ServerArgs( + daemonize, + quiet, + null, + secrets, + nodeSettings.build(), + esHomeDir.resolve("config"), + esHomeDir.resolve("logs") + ); ServerProcess.ProcessStarter starter = pb -> { if (processValidator != null) { processValidator.validate(pb); diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java index 5d1e67bc92fb8..858787b361654 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java @@ -37,7 +37,7 @@ class WindowsServiceDaemon extends EnvironmentAwareCommand { public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { // the Windows service daemon doesn't support secure settings implementations other than the keystore try (var loadedSecrets = KeyStoreWrapper.bootstrap(env.configFile(), () -> new SecureString(new char[0]))) { - var args = new ServerArgs(false, true, null, loadedSecrets, env.settings(), env.configFile()); + var args = new ServerArgs(false, true, null, loadedSecrets, env.settings(), env.configFile(), env.logsFile()); this.server = ServerProcess.start(terminal, processInfo, args); // start does not return until the server is ready, and we do not wait for the process } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java index bc7f1928d170c..f0b1f654150a9 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java @@ -29,10 +29,17 @@ * @param secrets the provided secure settings implementation * @param nodeSettings the node settings read from {@code elasticsearch.yml}, the cli and the process environment * @param configDir the directory where {@code elasticsearch.yml} and other config exists + * @param logsDir the directory where log files should be written */ -public record ServerArgs(boolean daemonize, boolean quiet, Path pidFile, SecureSettings secrets, Settings nodeSettings, Path configDir) - implements - Writeable { +public record ServerArgs( + boolean daemonize, + boolean quiet, + Path pidFile, + SecureSettings secrets, + Settings nodeSettings, + Path configDir, + Path logsDir +) implements Writeable { /** * Arguments for running Elasticsearch. @@ -59,6 +66,7 @@ public ServerArgs(StreamInput in) throws IOException { readPidFile(in), readSecureSettingsFromStream(in), Settings.readSettingsFromStream(in), + resolvePath(in.readString()), resolvePath(in.readString()) ); } @@ -82,6 +90,7 @@ public void writeTo(StreamOutput out) throws IOException { secrets.writeTo(out); nodeSettings.writeTo(out); out.writeString(configDir.toString()); + out.writeString(logsDir.toString()); } private static SecureSettings readSecureSettingsFromStream(StreamInput in) throws IOException { From 40185bb8b21588da7da6d0546619c4c3f27714a9 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 26 Oct 2023 08:09:09 +0100 Subject: [PATCH 117/203] Register `repository_s3` settings (#101344) The settings in `S3Service` are unregistered and therefore cannot be set in `elasticsearch.yml`. This commit adds the missing registration. --- docs/changelog/101344.yaml | 5 +++++ .../s3/S3RepositoryThirdPartyTests.java | 20 +++++++++++++++++++ .../repositories/s3/S3RepositoryPlugin.java | 2 ++ .../repositories/s3/S3Service.java | 4 ++-- 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/101344.yaml diff --git a/docs/changelog/101344.yaml b/docs/changelog/101344.yaml new file mode 100644 index 0000000000000..b546e743301f6 --- /dev/null +++ b/docs/changelog/101344.yaml @@ -0,0 +1,5 @@ +pr: 101344 +summary: Register `repository_s3` settings +area: Snapshot/Restore +type: bug +issues: [] diff --git a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java index 8096eab878734..8c6ea9f6edf3b 100644 --- a/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java +++ b/modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3RepositoryThirdPartyTests.java @@ -67,6 +67,26 @@ protected SecureSettings credentials() { return secureSettings; } + @Override + protected Settings nodeSettings() { + final var settings = Settings.builder().put(super.nodeSettings()); + if (randomBoolean()) { + final var defaultMillis = S3Service.REPOSITORY_S3_CAS_TTL_SETTING.get(Settings.EMPTY).millis(); + settings.put( + S3Service.REPOSITORY_S3_CAS_TTL_SETTING.getKey(), + TimeValue.timeValueMillis(randomLongBetween(defaultMillis, defaultMillis * 2)) + ); + } + if (randomBoolean()) { + final var defaultMillis = S3Service.REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING.get(Settings.EMPTY).millis(); + settings.put( + S3Service.REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING.getKey(), + TimeValue.timeValueMillis(randomLongBetween(defaultMillis, defaultMillis * 2)) + ); + } + return settings.build(); + } + @Override protected void createRepository(String repoName) { Settings.Builder settings = Settings.builder() diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java index ce9e12aa03be2..c194fe67d106e 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java @@ -151,6 +151,8 @@ public List> getSettings() { S3ClientSettings.USE_PATH_STYLE_ACCESS, S3ClientSettings.SIGNER_OVERRIDE, S3ClientSettings.REGION, + S3Service.REPOSITORY_S3_CAS_TTL_SETTING, + S3Service.REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING, S3Repository.ACCESS_KEY_SETTING, S3Repository.SECRET_KEY_SETTING ); diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java index 736e8870f21a2..291cf84019cd1 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java @@ -54,13 +54,13 @@ class S3Service implements Closeable { private static final Logger LOGGER = LogManager.getLogger(S3Service.class); - private static final Setting REPOSITORY_S3_CAS_TTL_SETTING = Setting.timeSetting( + static final Setting REPOSITORY_S3_CAS_TTL_SETTING = Setting.timeSetting( "repository_s3.compare_and_exchange.time_to_live", StoreHeartbeatService.HEARTBEAT_FREQUENCY, Setting.Property.NodeScope ); - private static final Setting REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING = Setting.timeSetting( + static final Setting REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING = Setting.timeSetting( "repository_s3.compare_and_exchange.anti_contention_delay", TimeValue.timeValueSeconds(1), TimeValue.timeValueMillis(1), From c658891f453dab0c23dd7a3f833398f18f59eb9a Mon Sep 17 00:00:00 2001 From: eyalkoren <41850454+eyalkoren@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:48:02 +0700 Subject: [PATCH 118/203] Update EcsDynamicTemplatesIT to use renamed ECS mappings (#101353) --- .../org/elasticsearch/xpack/stack/EcsDynamicTemplatesIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/stack/src/javaRestTest/java/org/elasticsearch/xpack/stack/EcsDynamicTemplatesIT.java b/x-pack/plugin/stack/src/javaRestTest/java/org/elasticsearch/xpack/stack/EcsDynamicTemplatesIT.java index 9832c789e2a99..ea3286e96160c 100644 --- a/x-pack/plugin/stack/src/javaRestTest/java/org/elasticsearch/xpack/stack/EcsDynamicTemplatesIT.java +++ b/x-pack/plugin/stack/src/javaRestTest/java/org/elasticsearch/xpack/stack/EcsDynamicTemplatesIT.java @@ -53,7 +53,7 @@ public class EcsDynamicTemplatesIT extends ESRestTestCase { public static ElasticsearchCluster cluster = ElasticsearchCluster.local().module("mapper-extras").module("wildcard").build(); // The dynamic templates we test against - public static final String ECS_DYNAMIC_TEMPLATES_FILE = "ecs-dynamic-mappings.json"; + public static final String ECS_DYNAMIC_TEMPLATES_FILE = "ecs@mappings.json"; // The current ECS state (branch main) containing all fields in flattened form private static final String ECS_FLAT_FILE_URL = "https://raw.githubusercontent.com/elastic/ecs/main/generated/ecs/ecs_flat.yml"; From 18b1bf59b024b716bf26d74f973bbd39c68ae551 Mon Sep 17 00:00:00 2001 From: Ievgen Degtiarenko Date: Thu, 26 Oct 2023 09:58:24 +0200 Subject: [PATCH 119/203] Add integration test for shard availability health indicator (#100490) This change adds integration test for shard availability health indicator with initial scenarios that verify health level is not degrading during number of operations. --- ...sAvailabilityHealthIndicatorServiceIT.java | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceIT.java diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceIT.java new file mode 100644 index 0000000000000..e85edc5805482 --- /dev/null +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityHealthIndicatorServiceIT.java @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster.routing.allocation; + +import org.elasticsearch.action.admin.indices.shrink.ResizeType; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.routing.RoutingNodes; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.health.HealthIndicatorResult; +import org.elasticsearch.health.HealthStatus; +import org.elasticsearch.health.node.HealthInfo; +import org.elasticsearch.indices.SystemIndices; +import org.elasticsearch.test.ESIntegTestCase; +import org.hamcrest.Matcher; + +import java.util.ArrayList; +import java.util.Map; + +import static org.elasticsearch.health.HealthStatus.GREEN; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; + +public class ShardsAvailabilityHealthIndicatorServiceIT extends ESIntegTestCase { + + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99951") + public void testIsGreenDuringIndexCreate() { + internalCluster().ensureAtLeastNumDataNodes(2); + + assertHealthDuring(equalTo(GREEN), () -> { + var index = randomIdentifier(); + prepareCreate(index).setSettings(indexSettings(1, 1)).get(); + ensureGreen(index); + }); + } + + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99951") + public void testIsGreenWhenNewReplicaAdded() { + internalCluster().ensureAtLeastNumDataNodes(2); + + var index = randomIdentifier(); + prepareCreate(index).setSettings(indexSettings(1, 0)).get(); + ensureGreen(index); + + assertHealthDuring(equalTo(GREEN), () -> { + updateIndexSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1), index); + ensureGreen(index); + }); + } + + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99951") + public void testIsGreenDuringSnapshotRestore() { + + internalCluster().ensureAtLeastNumDataNodes(2); + + var index = randomIdentifier(); + prepareCreate(index).setSettings(indexSettings(1, 1)).get(); + ensureGreen(index); + + var repositoryName = "repository"; + var snapshotName = randomIdentifier(); + assertAcked( + clusterAdmin().preparePutRepository(repositoryName) + .setType("fs") + .setSettings(Settings.builder().put("location", randomRepoPath())) + ); + clusterAdmin().prepareCreateSnapshot(repositoryName, snapshotName).setIndices(index).setWaitForCompletion(true).get(); + if (randomBoolean()) { + assertAcked(indicesAdmin().prepareDelete(index)); + } else { + assertAcked(indicesAdmin().prepareClose(index)); + } + ensureGreen(); + + assertHealthDuring(equalTo(GREEN), () -> { + clusterAdmin().prepareRestoreSnapshot(repositoryName, snapshotName).setIndices(index).setWaitForCompletion(true).get(); + ensureGreen(index); + }); + } + + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99951") + public void testIsGreenDuringIndexClone() { + + internalCluster().ensureAtLeastNumDataNodes(2); + + var sourceIndex = randomIdentifier(); + var targetIndex = randomIdentifier(); + prepareCreate(sourceIndex).setSettings(indexSettings(1, 1)).get(); + ensureGreen(sourceIndex); + updateIndexSettings(Settings.builder().put("index.blocks.write", true), sourceIndex); + + assertHealthDuring(equalTo(GREEN), () -> { + indicesAdmin().prepareResizeIndex(sourceIndex, targetIndex).setResizeType(ResizeType.CLONE).get(); + ensureGreen(targetIndex); + }); + } + + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/99951") + public void testIsGreenDuringOpeningAndClosingIndex() { + + internalCluster().ensureAtLeastNumDataNodes(2); + + var index = randomIdentifier(); + prepareCreate(index).setSettings(indexSettings(1, 1)).get(); + ensureGreen(index); + + assertHealthDuring(equalTo(GREEN), () -> { + indicesAdmin().prepareClose(index).get(); + ensureGreen(index); + indicesAdmin().prepareClose(index).get(); + ensureGreen(index); + }); + } + + private void assertHealthDuring(Matcher statusMatcher, Runnable action) { + var clusterService = internalCluster().getCurrentMasterNodeInstance(ClusterService.class); + var allocationService = internalCluster().getCurrentMasterNodeInstance(AllocationService.class); + var systemIndices = internalCluster().getCurrentMasterNodeInstance(SystemIndices.class); + + var service = new ShardsAvailabilityHealthIndicatorService(clusterService, allocationService, systemIndices); + var states = new ArrayList(); + var listener = new ClusterStateListener() { + @Override + public void clusterChanged(ClusterChangedEvent event) { + states.add( + new RoutingNodesAndHealth(event.state().getRoutingNodes(), service.calculate(false, 1, new HealthInfo(Map.of()))) + ); + } + }; + + clusterService.addListener(listener); + try { + action.run(); + + for (RoutingNodesAndHealth state : states) { + state.assertHealth(statusMatcher); + } + } finally { + clusterService.removeListener(listener); + } + } + + private record RoutingNodesAndHealth(RoutingNodes routing, HealthIndicatorResult health) { + private void assertHealth(Matcher statusMatcher) { + assertThat("Health [" + health + "] for routing: " + routing, health.status(), statusMatcher); + } + } +} From 1fe9aa9467c1a6a8dcdcdd5d1d263e526e878a5d Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 26 Oct 2023 09:12:35 +0100 Subject: [PATCH 120/203] Consolidate all Plugin.createComponents arguments into a single accessor class (#101305) --- .../elasticsearch/node/NodeConstruction.java | 54 +++++--- .../org/elasticsearch/plugins/Plugin.java | 117 +++++++++++++++++- 2 files changed, 149 insertions(+), 22 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index 246a41c99c922..adcb9d29861c0 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -40,6 +40,7 @@ import org.elasticsearch.cluster.coordination.StableMasterHealthIndicatorService; import org.elasticsearch.cluster.desirednodes.DesiredNodesSettingsValidator; import org.elasticsearch.cluster.metadata.IndexMetadataVerifier; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; import org.elasticsearch.cluster.metadata.MetadataCreateDataStreamService; import org.elasticsearch.cluster.metadata.MetadataCreateIndexService; @@ -52,6 +53,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.cluster.routing.BatchedRerouteService; import org.elasticsearch.cluster.routing.RerouteService; +import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.DiskThresholdMonitor; import org.elasticsearch.cluster.routing.allocation.ShardsAvailabilityHealthIndicatorService; import org.elasticsearch.cluster.routing.allocation.WriteLoadForecaster; @@ -739,24 +741,40 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi threadPool ); - Collection pluginComponents = pluginsService.flatMap( - p -> p.createComponents( - client, - clusterService, - threadPool, - resourceWatcherService, - scriptService, - xContentRegistry, - environment, - nodeEnvironment, - namedWriteableRegistry, - clusterModule.getIndexNameExpressionResolver(), - repositoriesServiceReference::get, - telemetryProvider, - clusterModule.getAllocationService(), - indicesService - ) - ).toList(); + record PluginServiceInstances( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier, + TelemetryProvider telemetryProvider, + AllocationService allocationService, + IndicesService indicesService + ) implements Plugin.PluginServices {} + PluginServiceInstances pluginServices = new PluginServiceInstances( + client, + clusterService, + threadPool, + resourceWatcherService, + scriptService, + xContentRegistry, + environment, + nodeEnvironment, + namedWriteableRegistry, + clusterModule.getIndexNameExpressionResolver(), + repositoriesServiceReference::get, + telemetryProvider, + clusterModule.getAllocationService(), + indicesService + ); + + Collection pluginComponents = pluginsService.flatMap(p -> p.createComponents(pluginServices)).toList(); List> reservedStateHandlers = new ArrayList<>(); diff --git a/server/src/main/java/org/elasticsearch/plugins/Plugin.java b/server/src/main/java/org/elasticsearch/plugins/Plugin.java index bd5d8e9220517..b0e1946a4e5dd 100644 --- a/server/src/main/java/org/elasticsearch/plugins/Plugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/Plugin.java @@ -62,6 +62,83 @@ */ public abstract class Plugin implements Closeable { + /** + * Provides access to various Elasticsearch services. + */ + public interface PluginServices { + /** + * A client to make requests to the system + */ + Client client(); + + /** + * A service to allow watching and updating cluster state + */ + ClusterService clusterService(); + + /** + * A service to allow retrieving an executor to run an async action + */ + ThreadPool threadPool(); + + /** + * A service to watch for changes to node local files + */ + ResourceWatcherService resourceWatcherService(); + + /** + * A service to allow running scripts on the local node + */ + ScriptService scriptService(); + + /** + * The registry for extensible xContent parsing + */ + NamedXContentRegistry xContentRegistry(); + + /** + * The environment for path and setting configurations + */ + Environment environment(); + + /** + * The node environment used coordinate access to the data paths + */ + NodeEnvironment nodeEnvironment(); + + /** + * The registry for {@link NamedWriteable} object parsing + */ + NamedWriteableRegistry namedWriteableRegistry(); + + /** + * A service that resolves expression to index and alias names + */ + IndexNameExpressionResolver indexNameExpressionResolver(); + + /** + * A supplier for the service that manages snapshot repositories. + * This will return null when {@link #createComponents(PluginServices)} is called, + * but will return the repositories service once the node is initialized. + */ + Supplier repositoriesServiceSupplier(); + + /** + * An interface for distributed tracing + */ + TelemetryProvider telemetryProvider(); + + /** + * A service to manage shard allocation in the cluster + */ + AllocationService allocationService(); + + /** + * A service to manage indices in the cluster + */ + IndicesService indicesService(); + } + /** * Returns components added by this plugin. *

    @@ -69,22 +146,54 @@ public abstract class Plugin implements Closeable { * Note: To aid in the migration away from guice, all objects returned as components will be bound in guice * to themselves. * + * @param services Provides access to various Elasticsearch services + */ + public Collection createComponents(PluginServices services) { + return createComponents( + services.client(), + services.clusterService(), + services.threadPool(), + services.resourceWatcherService(), + services.scriptService(), + services.xContentRegistry(), + services.environment(), + services.nodeEnvironment(), + services.namedWriteableRegistry(), + services.indexNameExpressionResolver(), + services.repositoriesServiceSupplier(), + services.telemetryProvider(), + services.allocationService(), + services.indicesService() + ); + } + + /** + * Returns components added by this plugin. Either this method or {@link #createComponents(PluginServices)} + * should be implemented. + *

    + * Any components returned that implement {@link LifecycleComponent} will have their lifecycle managed. + * Note: To aid in the migration away from guice, all objects returned as components will be bound in guice + * to themselves. + * * @param client A client to make requests to the system * @param clusterService A service to allow watching and updating cluster state * @param threadPool A service to allow retrieving an executor to run an async action * @param resourceWatcherService A service to watch for changes to node local files * @param scriptService A service to allow running scripts on the local node - * @param xContentRegistry the registry for extensible xContent parsing - * @param environment the environment for path and setting configurations - * @param nodeEnvironment the node environment used coordinate access to the data paths - * @param namedWriteableRegistry the registry for {@link NamedWriteable} object parsing + * @param xContentRegistry The registry for extensible xContent parsing + * @param environment The environment for path and setting configurations + * @param nodeEnvironment The node environment used coordinate access to the data paths + * @param namedWriteableRegistry The registry for {@link NamedWriteable} object parsing * @param indexNameExpressionResolver A service that resolves expression to index and alias names * @param repositoriesServiceSupplier A supplier for the service that manages snapshot repositories; will return null when this method * is called, but will return the repositories service once the node is initialized. * @param telemetryProvider An interface for distributed tracing * @param allocationService A service to manage shard allocation in the cluster * @param indicesService A service to manage indices in the cluster + * + * @deprecated New services will only be added to {@link PluginServices}; this method is maintained for compatibility. */ + @Deprecated public Collection createComponents( Client client, ClusterService clusterService, From 8e96d285c0a148dc795139c67172e2482812d467 Mon Sep 17 00:00:00 2001 From: Abdon Pijpelink Date: Thu, 26 Oct 2023 10:57:05 +0200 Subject: [PATCH 121/203] [DOCS] One more round of restructuring the ES|QL documentation (#101340) * Reorg some pages * Add links to landing page --- docs/reference/esql/esql-enrich-data.asciidoc | 20 +++--- docs/reference/esql/esql-examples.asciidoc | 2 +- docs/reference/esql/esql-kibana.asciidoc | 2 +- docs/reference/esql/esql-language.asciidoc | 4 ++ docs/reference/esql/esql-rest.asciidoc | 12 ++-- docs/reference/esql/esql-using.asciidoc | 16 +++++ docs/reference/esql/index.asciidoc | 67 +++++++++++++------ docs/reference/esql/metadata-fields.asciidoc | 2 +- docs/reference/esql/task-management.asciidoc | 2 +- 9 files changed, 87 insertions(+), 40 deletions(-) create mode 100644 docs/reference/esql/esql-using.asciidoc diff --git a/docs/reference/esql/esql-enrich-data.asciidoc b/docs/reference/esql/esql-enrich-data.asciidoc index f1ebe7bc218a8..69cc7817d2224 100644 --- a/docs/reference/esql/esql-enrich-data.asciidoc +++ b/docs/reference/esql/esql-enrich-data.asciidoc @@ -1,5 +1,5 @@ [[esql-enrich-data]] -== Data enrichment +=== Data enrichment ++++ Data enrichment @@ -17,7 +17,7 @@ For example, you can use `ENRICH` to: [discrete] [[esql-how-enrich-works]] -=== How the `ENRICH` command works +==== How the `ENRICH` command works The `ENRICH` command adds new columns to a table, with data from {es} indices. It requires a few special components: @@ -65,7 +65,7 @@ include::../ingest/enrich.asciidoc[tag=enrich-index] [discrete] [[esql-set-up-enrich-policy]] -=== Set up an enrich policy +==== Set up an enrich policy To start using `ENRICH`, follow these steps: @@ -89,25 +89,25 @@ your query. [discrete] [[esql-enrich-prereqs]] -=== Prerequisites +==== Prerequisites include::{es-repo-dir}/ingest/apis/enrich/put-enrich-policy.asciidoc[tag=enrich-policy-api-prereqs] [discrete] [[esql-create-enrich-source-index]] -=== Add enrich data +==== Add enrich data include::../ingest/enrich.asciidoc[tag=create-enrich-source-index] [discrete] [[esql-create-enrich-policy]] -=== Create an enrich policy +==== Create an enrich policy include::../ingest/enrich.asciidoc[tag=create-enrich-policy] [discrete] [[esql-execute-enrich-policy]] -=== Execute the enrich policy +==== Execute the enrich policy include::../ingest/enrich.asciidoc[tag=execute-enrich-policy1] @@ -117,7 +117,7 @@ include::../ingest/enrich.asciidoc[tag=execute-enrich-policy2] [discrete] [[esql-use-enrich]] -=== Use the enrich policy +==== Use the enrich policy After the policy has been executed, you can use the <> to enrich your data. @@ -128,12 +128,12 @@ include::processing-commands/enrich.asciidoc[tag=examples] [discrete] [[esql-update-enrich-data]] -=== Update an enrich index +==== Update an enrich index include::{es-repo-dir}/ingest/apis/enrich/execute-enrich-policy.asciidoc[tag=update-enrich-index] [discrete] [[esql-update-enrich-policies]] -=== Update an enrich policy +==== Update an enrich policy include::../ingest/enrich.asciidoc[tag=update-enrich-policy] diff --git a/docs/reference/esql/esql-examples.asciidoc b/docs/reference/esql/esql-examples.asciidoc index da13608175910..569dcf1172b38 100644 --- a/docs/reference/esql/esql-examples.asciidoc +++ b/docs/reference/esql/esql-examples.asciidoc @@ -1,5 +1,5 @@ [[esql-examples]] -== Examples +== {esql} examples ++++ Examples diff --git a/docs/reference/esql/esql-kibana.asciidoc b/docs/reference/esql/esql-kibana.asciidoc index 534cba22ed1a1..16dc226da6466 100644 --- a/docs/reference/esql/esql-kibana.asciidoc +++ b/docs/reference/esql/esql-kibana.asciidoc @@ -1,5 +1,5 @@ [[esql-kibana]] -== Using {esql} in {kib} +=== Using {esql} in {kib} ++++ Kibana diff --git a/docs/reference/esql/esql-language.asciidoc b/docs/reference/esql/esql-language.asciidoc index 6f8e54ebd86cb..8ffc0af7cbeb2 100644 --- a/docs/reference/esql/esql-language.asciidoc +++ b/docs/reference/esql/esql-language.asciidoc @@ -10,11 +10,15 @@ Detailed information about the {esql} language: * <> * <> * <> +* <> * <> +* <> * <> include::esql-syntax.asciidoc[] include::esql-commands.asciidoc[] include::esql-functions-operators.asciidoc[] +include::metadata-fields.asciidoc[] include::multivalued-fields.asciidoc[] include::esql-process-data-with-dissect-grok.asciidoc[] +include::esql-enrich-data.asciidoc[] \ No newline at end of file diff --git a/docs/reference/esql/esql-rest.asciidoc b/docs/reference/esql/esql-rest.asciidoc index 55c9946ad08b4..2d47f6e46ff65 100644 --- a/docs/reference/esql/esql-rest.asciidoc +++ b/docs/reference/esql/esql-rest.asciidoc @@ -1,5 +1,5 @@ [[esql-rest]] -== {esql} REST API +=== {esql} REST API ++++ REST API @@ -38,7 +38,7 @@ James S.A. Corey |Leviathan Wakes |561 |2011-06-02T00:00:00.000Z [discrete] [[esql-kibana-console]] -=== Kibana Console +==== Kibana Console If you are using {kibana-ref}/console-kibana.html[Kibana Console] (which is highly recommended), take advantage of the triple quotes `"""` when creating the @@ -62,7 +62,7 @@ POST /_query?format=txt [discrete] [[esql-rest-format]] -=== Response formats +==== Response formats {esql} can return the data in the following human readable and binary formats. You can set the format by specifying the `format` parameter in the URL or by @@ -121,7 +121,7 @@ Use the `tsv` format instead. [discrete] [[esql-rest-filtering]] -=== Filtering using {es} Query DSL +==== Filtering using {es} Query DSL Specify a Query DSL query in the `filter` parameter to filter the set of documents that an {esql} query runs on. @@ -161,7 +161,7 @@ Douglas Adams |The Hitchhiker's Guide to the Galaxy|180 |1979-10-12T [discrete] [[esql-rest-columnar]] -=== Columnar results +==== Columnar results By default, {esql} returns results as rows. For example, `FROM` returns each individual document as one row. For the `json`, `yaml`, `cbor` and `smile` @@ -206,7 +206,7 @@ Which returns: [discrete] [[esql-rest-params]] -=== Passing parameters to a query +==== Passing parameters to a query Values, for example for a condition, can be passed to a query "inline", by integrating the value in the query string itself: diff --git a/docs/reference/esql/esql-using.asciidoc b/docs/reference/esql/esql-using.asciidoc new file mode 100644 index 0000000000000..f586f3a28de5c --- /dev/null +++ b/docs/reference/esql/esql-using.asciidoc @@ -0,0 +1,16 @@ +[[esql-using]] +== Using {esql} + +<>:: +Information about using the <>. + +<>:: +Using {esql} in {kib} to query and aggregate your data, create visualizations, +and set up alerts. + +<>:: +Using the <> to list and cancel {esql} queries. + +include::esql-rest.asciidoc[] +include::esql-kibana.asciidoc[] +include::task-management.asciidoc[] \ No newline at end of file diff --git a/docs/reference/esql/index.asciidoc b/docs/reference/esql/index.asciidoc index c86dc088ff6b8..2946f4e61d629 100644 --- a/docs/reference/esql/index.asciidoc +++ b/docs/reference/esql/index.asciidoc @@ -8,40 +8,67 @@ preview::[] -The {es} Query Language ({esql}) provides a powerful way to filter, transform, and analyze data stored in {es}, and in the future in other runtimes. -It is designed to be easy to learn and use, by end users, SRE teams, application developers, and administrators. +The {es} Query Language ({esql}) provides a powerful way to filter, transform, +and analyze data stored in {es}, and in the future in other runtimes. It is +designed to be easy to learn and use, by end users, SRE teams, application +developers, and administrators. -Users can author {esql} queries to find specific events, perform statistical analysis, and generate visualizations. -It supports a wide range of commands and functions that enable users to perform various data operations, -such as filtering, aggregation, time-series analysis, and more. +Users can author {esql} queries to find specific events, perform statistical +analysis, and generate visualizations. It supports a wide range of commands and +functions that enable users to perform various data operations, such as +filtering, aggregation, time-series analysis, and more. -The {es} Query Language ({esql}) makes use of "pipes" (|) to manipulate and transform data in a step-by-step fashion. -This approach allows users to compose a series of operations, where the output of one operation becomes the input for the next, -enabling complex data transformations and analysis. +The {es} Query Language ({esql}) makes use of "pipes" (|) to manipulate and +transform data in a step-by-step fashion. This approach allows users to compose +a series of operations, where the output of one operation becomes the input for +the next, enabling complex data transformations and analysis. [discrete] === The {esql} Compute Engine -{esql} is more than a language: it represents a significant investment in new compute capabilities within {es}. -To achieve both the functional and performance requirements for {esql}, it was necessary to build an entirely new -compute architecture. {esql} search, aggregation, and transformation functions are directly executed within Elasticsearch -itself. Query expressions are not transpiled to Query DSL for execution. This approach allows {esql} to be extremely performant and versatile. +{esql} is more than a language: it represents a significant investment in new +compute capabilities within {es}. To achieve both the functional and performance +requirements for {esql}, it was necessary to build an entirely new compute +architecture. {esql} search, aggregation, and transformation functions are +directly executed within Elasticsearch itself. Query expressions are not +transpiled to Query DSL for execution. This approach allows {esql} to be +extremely performant and versatile. -The new {esql} execution engine was designed with performance in mind — it operates on blocks at a time instead of per row, targets vectorization and cache locality, and embraces specialization and multi-threading. It is a separate component from the existing Elasticsearch aggregation framework with different performance characteristics. +The new {esql} execution engine was designed with performance in mind — it +operates on blocks at a time instead of per row, targets vectorization and cache +locality, and embraces specialization and multi-threading. It is a separate +component from the existing Elasticsearch aggregation framework with different +performance characteristics. -include::esql-get-started.asciidoc[] +The {esql} documentation is organized in these sections: -include::esql-language.asciidoc[] +<>:: +A tutorial to help you get started with {esql}. + +<>:: + +Reference documentation for the <>, +<>, and <>. Information about working with <> and <>. And guidance for +<> and <>. -include::esql-rest.asciidoc[] +<>:: +An overview of using the <>, <>, and +<>. -include::metadata-fields.asciidoc[] +<>:: +The current limitations of {esql}. -include::esql-kibana.asciidoc[] +<>:: +A few examples of what you can with {esql}. -include::task-management.asciidoc[] +include::esql-get-started.asciidoc[] + +include::esql-language.asciidoc[] -include::esql-enrich-data.asciidoc[] +include::esql-using.asciidoc[] include::esql-limitations.asciidoc[] diff --git a/docs/reference/esql/metadata-fields.asciidoc b/docs/reference/esql/metadata-fields.asciidoc index 69c9c0c04dd7b..c034d4d0dd2b3 100644 --- a/docs/reference/esql/metadata-fields.asciidoc +++ b/docs/reference/esql/metadata-fields.asciidoc @@ -1,5 +1,5 @@ [[esql-metadata-fields]] -== {esql} metadata fields +=== {esql} metadata fields ++++ Metadata fields diff --git a/docs/reference/esql/task-management.asciidoc b/docs/reference/esql/task-management.asciidoc index 96a624c89bf7d..dfaff96123035 100644 --- a/docs/reference/esql/task-management.asciidoc +++ b/docs/reference/esql/task-management.asciidoc @@ -1,5 +1,5 @@ [[esql-task-management]] -== {esql} task management +=== {esql} task management ++++ Task management From e099a2e5b557ec1621647f3161577268ce26d61b Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 26 Oct 2023 10:15:04 +0100 Subject: [PATCH 122/203] Simplify sequence of ops in S3 compare-and-exchange (#101332) Reorders things into the order in which the algorithm executes, and adds some commentary describing how and why it works. --- .../repositories/s3/S3BlobContainer.java | 141 ++++++++++-------- 1 file changed, 81 insertions(+), 60 deletions(-) diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java index 296dd81b14ce0..c0b64c5c672f6 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java @@ -32,6 +32,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRunnable; import org.elasticsearch.action.support.RefCountingListener; +import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.common.Randomness; import org.elasticsearch.common.Strings; import org.elasticsearch.common.blobstore.BlobContainer; @@ -65,7 +66,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -592,8 +592,14 @@ void run(BytesReference expected, BytesReference updated, ActionListener { - if (isComplete.compareAndSet(false, true)) { - safeAbortMultipartUpload(uploadId); - } - }; + SubscribableListener - try ( - var listeners = new RefCountingListener( - ActionListener.runAfter( - listener.delegateFailure( - (delegate1, ignored) -> getRegister( - purpose, - rawKey, - delegate1.delegateFailure((delegate2, currentValue) -> ActionListener.completeWith(delegate2, () -> { - if (currentValue.isPresent() && currentValue.bytesReference().equals(expected)) { - completeMultipartUpload(uploadId, partETag); - isComplete.set(true); - } - return currentValue; - })) - ) - ), - doCleanup - ) - ) - ) { - if (currentUploads.size() > 1) { - // This is a small optimization to improve the liveness properties of this algorithm. - // - // When there are multiple competing updates, we order them by upload id and the first one tries to cancel the competing - // updates in order to make progress. To avoid liveness issues when the winner fails, the rest wait based on their - // upload_id-based position and try to make progress. - - var delayListener = listeners.acquire(); - final Runnable cancelConcurrentUpdates = () -> { - try { - cancelOtherUploads(uploadId, currentUploads, listeners); - } finally { - delayListener.onResponse(null); - } - }; - - if (uploadIndex > 0) { - threadPool.scheduleUnlessShuttingDown( - TimeValue.timeValueMillis( - uploadIndex * blobStore.getCompareAndExchangeAntiContentionDelay().millis() + Randomness.get().nextInt(50) - ), - blobStore.getSnapshotExecutor(), - cancelConcurrentUpdates - ); + // Step 3: Ensure all other uploads in currentUploads are complete (either successfully, aborted by us or by another upload) + + .newForked(l -> ensureOtherUploadsComplete(uploadId, uploadIndex, currentUploads, l)) + + // Step 4: Read the current register value. + + .andThen((l, ignored) -> getRegister(purpose, rawKey, l)) + + // Step 5: Perform the compare-and-swap by completing our upload iff the witnessed value matches the expected value. + + .andThen((l, currentValue) -> ActionListener.completeWith(l, () -> { + if (currentValue.isPresent() && currentValue.bytesReference().equals(expected)) { + completeMultipartUpload(uploadId, partETag); } else { - cancelConcurrentUpdates.run(); + // Best-effort attempt to clean up after ourselves. + safeAbortMultipartUpload(uploadId); } - } - } + return currentValue; + })) + + // Step 6: Complete the listener. + + .addListener(listener.delegateResponse((l, e) -> { + // Best-effort attempt to clean up after ourselves. + safeAbortMultipartUpload(uploadId); + l.onFailure(e); + })); + + // No compare-and-exchange operations that started before ours can write to the register (in its step 5) after we have read the + // current value of the register (in our step 4) because we have ensured all earlier operations have completed (in our step 3). + // Conversely, if some other compare-and-exchange operation started after us then it will not read the register (in its step 4) + // until it has ensured we will not do a future write to the register (in our step 5) by cancelling all the racing uploads that + // it observed (in its step 3). Thus steps 4 and 5 can only complete successfully with no intervening writes to the register. } /** @@ -756,12 +741,48 @@ private int getUploadIndex(String targetUploadId, List multipar return found ? uploadIndex : -1; } - private void cancelOtherUploads(String uploadId, List currentUploads, RefCountingListener listeners) { - for (final var currentUpload : currentUploads) { - final var currentUploadId = currentUpload.getUploadId(); - if (uploadId.equals(currentUploadId) == false) { - blobStore.getSnapshotExecutor() - .execute(ActionRunnable.run(listeners.acquire(), () -> abortMultipartUploadIfExists(currentUploadId))); + private void ensureOtherUploadsComplete( + String uploadId, + int uploadIndex, + List currentUploads, + ActionListener listener + ) { + // This is a small optimization to improve the liveness properties of this algorithm. + // + // When there are updates racing to complete, we try and let them complete in order of their upload IDs. The one with the first + // upload ID immediately tries to cancel the competing updates in order to make progress, but the ones with greater upload IDs + // wait based on their position in the list before proceeding. + // + // Note that this does not guarantee that any of the uploads actually succeeds. Another operation could start and see a + // different collection of racing uploads and cancel all of them while they're sleeping. In theory this whole thing is provably + // impossible anyway [1] but in practice it'll eventually work with sufficient retries. + // + // [1] Michael J. Fischer, Nancy A. Lynch, and Michael S. Paterson. 1985. Impossibility of distributed consensus with one faulty + // process. J. ACM 32, 2 (April 1985), 374–382. + // + // TODO should we sort these by initiation time (and then upload ID as a tiebreaker)? + // TODO should we listMultipartUploads() while waiting, so we can fail quicker if we are concurrently cancelled? + if (uploadIndex > 0) { + threadPool.scheduleUnlessShuttingDown( + TimeValue.timeValueMillis( + uploadIndex * blobStore.getCompareAndExchangeAntiContentionDelay().millis() + Randomness.get().nextInt(50) + ), + blobStore.getSnapshotExecutor(), + ActionRunnable.wrap(listener, l -> cancelOtherUploads(uploadId, currentUploads, l)) + ); + } else { + cancelOtherUploads(uploadId, currentUploads, listener); + } + } + + private void cancelOtherUploads(String uploadId, List currentUploads, ActionListener listener) { + final var executor = blobStore.getSnapshotExecutor(); + try (var listeners = new RefCountingListener(listener)) { + for (final var currentUpload : currentUploads) { + final var currentUploadId = currentUpload.getUploadId(); + if (uploadId.equals(currentUploadId) == false) { + executor.execute(ActionRunnable.run(listeners.acquire(), () -> abortMultipartUploadIfExists(currentUploadId))); + } } } } From 43446180e25aca750413adea698c28b425a9347e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Witek?= Date: Thu, 26 Oct 2023 11:34:38 +0200 Subject: [PATCH 123/203] Relax internal checkpointing action test assertion (#101356) --- .../checkpoint/TransformGetCheckpointIT.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java index fde13c9d65324..bb159856b965d 100644 --- a/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java +++ b/x-pack/plugin/transform/src/internalClusterTest/java/org/elasticsearch/xpack/transform/checkpoint/TransformGetCheckpointIT.java @@ -25,10 +25,9 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; /** * Test suite for checkpointing using transform getcheckpoint API @@ -102,11 +101,11 @@ public void testGetCheckpoint() throws Exception { public void testGetCheckpointTimeoutExceeded() throws Exception { final String indexNamePrefix = "test_index-"; - final int indices = 5; - final int shards = 10; + final int indices = 100; + final int shards = 5; for (int i = 0; i < indices; ++i) { - indicesAdmin().prepareCreate(indexNamePrefix + i).setSettings(indexSettings(shards, 1)).get(); + indicesAdmin().prepareCreate(indexNamePrefix + i).setSettings(indexSettings(shards, 0)).get(); } final GetCheckpointAction.Request request = new GetCheckpointAction.Request( @@ -115,20 +114,25 @@ public void testGetCheckpointTimeoutExceeded() throws Exception { TimeValue.ZERO ); - CountDownLatch countDown = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); SetOnce finalException = new SetOnce<>(); - client().execute(GetCheckpointAction.INSTANCE, request, ActionListener.wrap(r -> countDown.countDown(), e -> { + client().execute(GetCheckpointAction.INSTANCE, request, ActionListener.wrap(r -> latch.countDown(), e -> { finalException.set(e); - countDown.countDown(); + latch.countDown(); })); - countDown.await(10, TimeUnit.SECONDS); + latch.await(10, TimeUnit.SECONDS); Exception e = finalException.get(); - assertThat(e, is(notNullValue())); - assertThat(e, is(instanceOf(ElasticsearchTimeoutException.class))); - assertThat( - e.getMessage(), - is(equalTo("Transform checkpointing timed out on node [node_s_0] after [0ms] having processed [0] of [50] shards")) - ); + if (e != null) { + assertThat(e, is(instanceOf(ElasticsearchTimeoutException.class))); + assertThat( + "Message was: " + e.getMessage(), + e.getMessage(), + startsWith("Transform checkpointing timed out on node [node_s_0] after [0ms]") + ); + } else { + // Due to system clock usage, the timeout does not always occur where it should. + // We cannot mock the clock so we just have to live with it. + } } } From b921c1e627cb99300eb404d9aa573af62bd725e5 Mon Sep 17 00:00:00 2001 From: Abdon Pijpelink Date: Thu, 26 Oct 2023 12:02:41 +0200 Subject: [PATCH 124/203] [DOCS] Using ES|QL in Kibana (#101324) * Initial Kibana content * More docs * Nino's feedback * Amy's feedback --- docs/reference/esql/esql-kibana.asciidoc | 251 +++++++++++++++++- .../images/esql/esql-data-view-menu.png | Bin 0 -> 78675 bytes .../images/esql/esql-expanded-query-bar.png | Bin 0 -> 46042 bytes .../esql/esql-icon-edit-visualization.svg | 1 + .../esql/esql-icon-expand-query-bar.svg | 1 + docs/reference/images/esql/esql-icon-help.svg | 1 + .../esql/esql-icon-minimize-query-bar.svg | 1 + .../images/esql/esql-icon-options.svg | 1 + .../esql/esql-icon-save-visualization.svg | 1 + .../images/esql/esql-kibana-auto-complete.png | Bin 0 -> 124634 bytes .../images/esql/esql-kibana-bar-chart.png | Bin 0 -> 232578 bytes .../images/esql/esql-kibana-create-rule.png | Bin 0 -> 239430 bytes .../esql/esql-kibana-edit-on-dashboard.png | Bin 0 -> 350811 bytes .../esql/esql-kibana-enrich-autocomplete.png | Bin 0 -> 69124 bytes .../images/esql/esql-kibana-enrich-step-1.png | Bin 0 -> 126404 bytes .../images/esql/esql-kibana-enrich-step-2.png | Bin 0 -> 50848 bytes .../images/esql/esql-kibana-enriched-data.png | Bin 0 -> 275790 bytes .../esql/esql-kibana-in-line-editor.png | Bin 0 -> 366539 bytes .../esql/esql-kibana-visualization-type.png | Bin 0 -> 33360 bytes 19 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 docs/reference/images/esql/esql-data-view-menu.png create mode 100644 docs/reference/images/esql/esql-expanded-query-bar.png create mode 100644 docs/reference/images/esql/esql-icon-edit-visualization.svg create mode 100644 docs/reference/images/esql/esql-icon-expand-query-bar.svg create mode 100644 docs/reference/images/esql/esql-icon-help.svg create mode 100644 docs/reference/images/esql/esql-icon-minimize-query-bar.svg create mode 100644 docs/reference/images/esql/esql-icon-options.svg create mode 100644 docs/reference/images/esql/esql-icon-save-visualization.svg create mode 100644 docs/reference/images/esql/esql-kibana-auto-complete.png create mode 100644 docs/reference/images/esql/esql-kibana-bar-chart.png create mode 100644 docs/reference/images/esql/esql-kibana-create-rule.png create mode 100644 docs/reference/images/esql/esql-kibana-edit-on-dashboard.png create mode 100644 docs/reference/images/esql/esql-kibana-enrich-autocomplete.png create mode 100644 docs/reference/images/esql/esql-kibana-enrich-step-1.png create mode 100644 docs/reference/images/esql/esql-kibana-enrich-step-2.png create mode 100644 docs/reference/images/esql/esql-kibana-enriched-data.png create mode 100644 docs/reference/images/esql/esql-kibana-in-line-editor.png create mode 100644 docs/reference/images/esql/esql-kibana-visualization-type.png diff --git a/docs/reference/esql/esql-kibana.asciidoc b/docs/reference/esql/esql-kibana.asciidoc index 16dc226da6466..b8709364367b2 100644 --- a/docs/reference/esql/esql-kibana.asciidoc +++ b/docs/reference/esql/esql-kibana.asciidoc @@ -2,14 +2,255 @@ === Using {esql} in {kib} ++++ -Kibana +Using {esql} in {kib} ++++ +You can use {esql} in {kib} to query and aggregate your data, create +visualizations, and set up alerts. -Use {esql} in Discover to explore a data set. From the data view dropdown, -select *Try {esql}* to get started. +This guide shows you how to use {esql} in Kibana. To follow along with the +queries, load the "Sample web logs" sample data set by clicking *Try sample +data* from the {kib} Home, selecting *Other sample data sets*, and clicking *Add +data* on the *Sample web logs* card. -NOTE: {esql} queries in Discover and Lens are subject to the time range selected -with the time filter. +[discrete] +[[esql-kibana-get-started]] +=== Get started with {esql} +To get started with {esql} in Discover, open the main menu and select +*Discover*. Next, from the Data views menu, select *Try ES|QL*. +image::images/esql/esql-data-view-menu.png[align="center",width=33%] + +The ability to select {esql} from the Data views menu can be enabled and +disabled using the `discover:enableESQL` setting from +{kibana-ref}/advanced-options.html[Advanced Settings]. + +[discrete] +[[esql-kibana-query-bar]] +=== The query bar + +After switching to {esql} mode, the query bar shows a sample query. For example: + +[source,esql] +---- +from kibana_sample_data_logs | limit 10 +---- + +Every query starts with a <>. In this query, the +source command is <>. `FROM` retrieves data from data streams, indices, or +aliases. In this example, the data is retrieved from `kibana_sample_data_logs`. + +A source command can be followed by one or more <>. In this query, the processing command is <>. `LIMIT` +limits the number of rows that are retrieved. + +TIP: Click the help icon (image:images/esql/esql-icon-help.svg[]) to open the +in-product reference documentation for all commands and functions. + +To make it easier to write queries, auto-complete offers suggestions with +possible commands and functions: + +image::images/esql/esql-kibana-auto-complete.png[align="center"] + +[NOTE] +==== +{esql} keywords are case-insensitive. The following query is identical to the +previous one: + +[source,esql] +---- +FROM kibana_sample_data_logs | LIMIT 10 +---- +==== + +[discrete] +==== Expand the query bar + +For readability, you can put each processing command on a new line. The +following query is identical to the previous one: + +[source,esql] +---- +FROM kibana_sample_data_logs +| LIMIT 10 +---- + +To make it easier to write multi-line queries, click the double-headed arrow +button (image:images/esql/esql-icon-expand-query-bar.svg[]) to expand the query +bar: + +image::images/esql/esql-expanded-query-bar.png[align="center"] + +To return to a compact query bar, click the minimize editor button +(image:images/esql/esql-icon-minimize-query-bar.svg[]). + +[discrete] +==== Warnings + +A query may result in warnings, for example when querying an unsupported field +type. When that happens, a warning symbol is shown in the query bar. To see the +detailed warning, expand the query bar, and click *warnings*. + +[discrete] +[[esql-kibana-results-table]] +=== The results table + +For the example query, the results table shows 10 rows. Omitting the `LIMIT` +command, the results table defaults to up to 500 rows. Using `LIMIT`, you can +increase the limit to up to 10,000 rows. + +NOTE: the 10,000 row limit only applies to the number of rows that are retrieved +by the query and displayed in Discover. Any query or aggregation runs on the +full data set. + +Each row shows two columns for the example query: a column with the `@timestamp` +field and a column with the full document. To display specific fields from the +documents, use the <> command: + +[source,esql] +---- +FROM kibana_sample_data_logs +| KEEP @timestamp, bytes, geo.dest +---- + +To display all fields as separate columns, use `KEEP *`: + +[source,esql] +---- +FROM kibana_sample_data_logs +| KEEP * +---- + +NOTE: The maximum number of columns in Discover is 50. If a query returns more +than 50 columns, Discover only shows the first 50. + +[discrete] +==== Sorting + +To sort on one of the columns, click the column name you want to sort on and +select the sort order. Note that this performs client-side sorting. It only +sorts the rows that were retrieved by the query, which may not be the full +dataset because of the (implicit) limit. To sort the full data set, use the +<> command: + +[source,esql] +---- +FROM kibana_sample_data_logs +| KEEP @timestamp, bytes, geo.dest +| SORT bytes DESC +---- + +[discrete] +[[esql-kibana-time-filter]] +=== Time filtering + +To display data within a specified time range, use the +{kibana-ref}/set-time-filter.html[time filter]. The time filter is only enabled +when the indices you're querying have a field called `@timestamp`. + +If your indices do not have a timestamp field called `@timestamp`, you can limit +the time range using the <> command and the <> function. +For example, if the timestamp field is called `timestamp`, to query the last 15 +minutes of data: +[source,esql] +---- +FROM kibana_sample_data_logs +| WHERE timestamp > NOW() - 15minutes +---- + +[discrete] +[[esql-kibana-visualizations]] +=== Analyze and visualize data + +Between the query bar and the results table, Discover shows a date histogram +visualization. If the indices you're querying do not contain an `@timestamp` +field, the histogram is not shown. + +The visualization adapts to the query. A query's nature determines the type of +visualization. For example, this query aggregates the total number of bytes per +destination country: + +[source,esql] +---- +FROM kibana_sample_data_logs +| STATS total_bytes = SUM(bytes) BY geo.dest +| SORT total_bytes DESC +| LIMIT 3 +---- + +The resulting visualization is a bar chart showing the top 3 countries: + +image::images/esql/esql-kibana-bar-chart.png[align="center"] + +To change the visualization into another type, click the visualization type +dropdown: + +image::images/esql/esql-kibana-visualization-type.png[align="center",width=33%] + +To make other changes to the visualization, like the axes and colors, click the +pencil button (image:images/esql/esql-icon-edit-visualization.svg[]). This opens +an in-line editor: + +image::images/esql/esql-kibana-in-line-editor.png[align="center"] + +You can save the visualization to a new or existing dashboard by clicking the +save button (image:images/esql/esql-icon-save-visualization.svg[]). Once saved +to a dashboard, you can continue to make changes to visualization. Click the +options button in the top-right (image:images/esql/esql-icon-options.svg[]) and +select *Edit ESQL visualization* to open the in-line editor: + +image::images/esql/esql-kibana-edit-on-dashboard.png[align="center"] + +[discrete] +[[esql-kibana-enrich]] +=== Create an enrich policy + +The {esql} <> command enables you to <> +your query dataset with fields from another dataset. Before you can use +`ENRICH`, you need to <>. If a policy exists, it will be suggested by auto-complete. If not, +click *Click to create* to create one. + +image::images/esql/esql-kibana-enrich-autocomplete.png[align="center"] + +Next, you can enter a policy name, the policy type, source indices, and +optionally a query: + +image::images/esql/esql-kibana-enrich-step-1.png[align="center",width="50%"] + +Click *Next* to select the match field and enrich fields: + +image::images/esql/esql-kibana-enrich-step-2.png[align="center",width="50%"] + +Finally, click *Create and execute*. + +Now, you can use the enrich policy in an {esql} query: + +image::images/esql/esql-kibana-enriched-data.png[align="center"] + +[discrete] +[[esql-kibana-alerting-rule]] +=== Create an alerting rule + +You can use {esql} queries to create alerts. From Discover, click *Alerts* and +select *Create search threshold rule*. This opens a panel that enables you to +create a rule using an {esql} query. Next, you can test the query, add a +connector, and save the rule. + +image::images/esql/esql-kibana-create-rule.png[align="center",width=50%] + +[discrete] +[[esql-kibana-limitations]] +=== Limitations + +* The user interface to filter data is not enabled when Discover is in {esql} +mode. To filter data, write a query that uses the <> command +instead. +* In {esql} mode, clicking a field in the field list in Discover does not show +quick statistics for that field. +* Discover shows no more than 10,000 rows. This limit only applies to the number +of rows that are retrieved by the query and displayed in Discover. Any query or +aggregation runs on the full data set. +* Discover shows no more than 50 columns. If a query returns +more than 50 columns, Discover only shows the first 50. \ No newline at end of file diff --git a/docs/reference/images/esql/esql-data-view-menu.png b/docs/reference/images/esql/esql-data-view-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..fbbbdf44d315cf1f9c8a9182749861c19922fc6d GIT binary patch literal 78675 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 \ No newline at end of file diff --git a/docs/reference/images/esql/esql-icon-expand-query-bar.svg b/docs/reference/images/esql/esql-icon-expand-query-bar.svg new file mode 100644 index 0000000000000..fc2641318acb2 --- /dev/null +++ b/docs/reference/images/esql/esql-icon-expand-query-bar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/reference/images/esql/esql-icon-help.svg b/docs/reference/images/esql/esql-icon-help.svg new file mode 100644 index 0000000000000..84c9b8db397c9 --- /dev/null +++ b/docs/reference/images/esql/esql-icon-help.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/reference/images/esql/esql-icon-minimize-query-bar.svg b/docs/reference/images/esql/esql-icon-minimize-query-bar.svg new file mode 100644 index 0000000000000..54b9349a05d59 --- /dev/null +++ b/docs/reference/images/esql/esql-icon-minimize-query-bar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/reference/images/esql/esql-icon-options.svg b/docs/reference/images/esql/esql-icon-options.svg new file mode 100644 index 0000000000000..6abb3ca9d0827 --- /dev/null +++ b/docs/reference/images/esql/esql-icon-options.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/reference/images/esql/esql-icon-save-visualization.svg b/docs/reference/images/esql/esql-icon-save-visualization.svg new file mode 100644 index 0000000000000..f80a5993acde1 --- /dev/null +++ b/docs/reference/images/esql/esql-icon-save-visualization.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/reference/images/esql/esql-kibana-auto-complete.png b/docs/reference/images/esql/esql-kibana-auto-complete.png new file mode 100644 index 0000000000000000000000000000000000000000..5763e569c7668bb407b4fde4927158d88ffab018 GIT binary patch literal 124634 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

    %ECo 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 z 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 Wg;&>@p8XU495!#?blYbCng0M2CLk36 literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-bar-chart.png b/docs/reference/images/esql/esql-kibana-bar-chart.png new file mode 100644 index 0000000000000000000000000000000000000000..43190a34bf3c3c906c6e6e69a6fab5c876627f8f GIT binary patch literal 232578 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�rT=gjn9F zeB&czbb;7VIPe-2xasb+j=$_H+JR$!Vo+%Qakw_T^@6PcCuzXH*zxm+csCJVH8zjA zJC@6LX^AT!>NV2LlHd1Pe4-P6R?=yh!DKfXS1zYvch;!(cvZa~(RAgj<-!}$n}z1> zcZm-NRxSxS&QUGLST|XIJdk)?dRYJ5b;0|fPivlCds~?p&oRwqi#WI;X<xA03I*)}v$r(?^rhs1BVTzXc2u~a6J9y@JH z@F)q5iN}I^&KD6y1qHjOGf&#nGoLwdfO({CthFV>OPsh^bAiAzr#|L3_95GO>JXcH zr@)HC`>->{^`8EL%2j5zERPJ%t(A|YrMqGXaMv!-~dz9a}Y$8sZbZ zZt%K=kdzU(X_j~t)E>j*>J~{CM)AycQ2C-hr^U5NX2YWUANP3xFX!Z6b61yH7S2{` zoWITB!b{^fLr9`)G}N2N~KpSCLL}FV;aocTNIR{|Z-B=X%?;-#aV~6?zZJ@(m%m zv^?6LNF3-lFVduE(eY4}4$3c8&F7v!)y$vn3vbXba5F%;R?f|6yUBBL$>~2&&Ub&4>Zh$3BfeOk^2pspODqo4=5eq$rOzqZpLxFEYh`8akY+Y{7Bu41$7D#~A$jc=C9`YjL$|9Ja^wZ~=d@060+a+aR6Pgxs! zi%(t-isflFuemuSXWg2V)SGW>$r^<$C84>FiFw)<>M6(>wJawIluo(YyS~?C6e;O^O@&9Mn#1 zS7<%oCdUKA=^X=9(BDmFb06j@Owmu(5eV^InnlqiXvbc51f_vW zY-ip{YUQciAxeBa*ZY)~DImp5=@No*-khl1M2NlPdvT{+Mz3`y{dwOP+R^z}GT1x> znp49&gv{&DOi8Wk*lHq{+D2rJ3KMOlr;G>z_Sk&hp7J%e^AUz{rGm8_;WG7xg6e7& zW}#oA&Ad-O_8YD>Us`TS@{BDr%$$%)J`Ec2)D2Avc(Tq(Ige&Z6AhJ?VHey?JPSU{ zcCZ?i6-HgB#`GBa!gI{$IAJ&V>x(QV0iIn?NU ze~Ak~gOr?hv97=*i;e0mHkTwC&kyhBDor0{I_7a}8q8L7>pOZ2)tMicro=GkqO6+) zldzn0J(h!wJyD{T9I+P)viCP&NObzu1EtH8Vx3sJlID9B>zi)LtyFcZE{uzHH!9TN zTKirSOn_3B$?Af@q8b9j^gX>8zvpM{-|2 zXNiohXkO}fBe5+mgiLvk)dAqDDR-)GUEgdt{2>=wvnerUlc6GESOLmK&hOPWD+eyK z*Wc4iUg5J{V#Sdk(?`W^M&*HO-?@blImthbg(MOvYk`FpD{gIqqXo} z$j+8WrimAhwOeVVc@F26LAi+WEhps(dJiCa;*r{Ot!p927Kmb_$&7L8!)1LXscq?G z|Ax877umDlKXWc*uTSW!0$XgubSu$qXt?Ia?EL`!1z7*mLaHX`$&(5FuE1Y%kB4y9 z?v56)YOqy3hE*xHAf-S2IeRH%PHsl_W4J!`oKH%SLBp)>yt3)fc%7+x{ms`h0{J}_ zY(g4-33?PvI?h|A#Yi0B#|L2dkV~sWB>h;!q#b^;GmE;v13B~Ju5!0h2L!)7FT2C$ zv5Rc{!K9Za%QYv{Y>T6Dg?o@t?sjmFi0zyGll$muQ|4uCATIiTwdNOiTvZLM0i3u* zRNkN9u=e;dGj5)ga^*neX@dflZpdM(g&63tY~Jf|Pq~~QsBl5$gg{lMgdf;xxrHu# zZ-J#ix`AkZkW8xM{B(EsKAdSlJuh%9{dj^fRI>QXMBJaf+` z#Gltir1$1^_lejfzm@8=ZAeDeq-K_m^_UVCCwi5vWQd7gHdCj~h*On>B=thsjuFS+ z^dlF2->`(WF${YM<51Mw(^Ky6++E5K91qz9>z-k1*vBD1T=uRHc^BS3%S^LvNck2b zu4y-M?yTI6V{^GpvYt|~@0q|juM9wrYjbKLyHk}dc&@+upqJh}zo?5Teumc~8MP2z z3M@~c=BHZwb#T|`nMQv>x8?4AFJ{GA&)9IR z=G&X2%}qZnklvOq)^L3VIhp$%Ii-_*OVM5fE%;ky z`y019D&rtWW%P{!N{sV3uwF1H4t+Ye+0K+l(~-tzOZ7i#7K+*jFkyyL+D9&5lW*IM`xGzbN5AM@pJ2Ni-Ti*v29>XC5tP=FhCsZtX@`y}=^3 zlu1g~j(}_?4x_d0zGm}L^8?P-#zQP~#C-ze)TA<&tFA_ub$eZSe!4xQYw4G$Gqdhx ztwf5mjJnXBwJw<`x&QEIq&{6MohzHP?33h2g$i;QGk2Mw&{RZUyovLiDy&R_ws2gBbfuH?f}k<_zJNw*oh?_M zrMx}P|E1%X`Oab^(8#J>Wr9hnB!A2&pDGI=6pvvv@3%SxTW_lZmLlD*Vu9~t7sNVw zO){oXYii}0$7Fh<-F2P{|#%`-C)Vc588Ao?@Bp4{ezAs(CaiS6l zC8w(DcGEI)yZSNvw`^-`7_n2ELpex90;mHD^yF(A?w-Lb@R*Q#W=m^k)UIBVn_A5D zT29jQ+i!PAV*=*o?iTv?%VquNxRngUg|}WYA%qZ@Ty0eAE4P=nP^l-6=~bjxO=bxk z@xGzAyDILjgwf+d+vgc};Y*W#0kO-3k}Bi<2E9hUaCh{bFP$x^7;9AY;CoDr$eqN) z)kqAQ>%3g&RguxYbnD$7#dpZD<2~df_w=k5Zzm5Bmrx_PgP@2>?v~KO+f%?UamIy+ zQ^MxYLBP&ZDv6dH;weZD!ElYMa*qfB3YYiLak$ICHx@IljFIBG1U6a{084lrIV zvN9|LF{2^aSmLb9H#EBL1KR9tsnn1D2X=L>;|EKTnFi*&jhy#r8sXf=s*qpzFt?{& z*D$teHGIX$bDSV7?m3h1&mw#s@|hwt_2=QtvU7CAhrOD;4R``+D(b1&_-v72ft`Ri zIUi#S>v{4R4i3mCT_&Wh1-Q}PYuE*+kRBeRl*B9E_JIax>=x=(XNe1}LU(>RFB^&e z(w+abdziCZac1+U(g`gX|u*p!TQQ z!`2;%K7B9?v(%au|A11fC;R2ZzvyKssLFf{$FLQxv5j?=1%SP^f2zzuy2wU%s>|EZ z|19FL;G0v;*v`B;@Q;`BRrL!)^e=kV3~ArNTD`-vO&4(N1-0}B1vPJ#o{t1d*@;Jd zO+O3y?%i&EGb#F}RN%oEd zXOST3m3Xnx9Hv4yYW&v0Hji3?)g)E*tuBUCcoZ%y(H zVhacnuGGp0TjMZlVy{}n*~n7&h+SrZNQo1`RTGSc5sz-?UZfeGIn-?4rJTA02YfxL zvND4D*^c=j-?2Vd^3%Qag5+C^KYB&_65qz3)ysJ^6hL6<6rnl5!%KA7N15YKIi2_z{WUxBo zUedJzm71{gs~i^kJHHv0om@LE$Gsk*MNb>!&yROyOMKF&t1qzZ{We|Y^wDtnNj4NA z*`*T1DkqQ}K)5~Gt}85(QjU3P%W?(0-6O>N>Ty}mi$Z3Nr}bGyihFtPNh=4kh0l$( zM0>jq*MwHl@RY1A{piLY8NyhQg}G~g^;g?b8PhVMKJ6FtFrIKvb47XPEUi+{s^jVh z$V>R1-OHQwDhGo!@Vz~@$T-TgK08#AU@PQ^KKIP;KiNF;G_;eF%$(c;G2wlYr#S5y z*Qinhk74}E9$uY2is1|A^QM{LTCuWZo>TwDvliQYZm?Ff<7oJdW3_?OCi4LirXhbuqenB1+1OkFeYD);$O zo1595mHU!oWxXyB9F?v&Z0s0U=Cj=oJY*M!{}$Hgpax`UJ*woV(Dq0iTtHv{!Lr4u ze_rvD7VmH7oKQdFK?afh6kKxMLWGDS9F}vDG$hKp=+9ngivZn^++{L4(xYnAkJNd1 zmExbG&f0ycOqYpnNHGP`4|B^t386+CfX^sPNN(78(XNS$ctO`K~(r&hafc`%SfLe$mdJ9Sn*XJ+*H>#g!72o7gc=U*#O2D}I^zv0! zMC*B``T>BRpg>>xOWSO90($THKf-paGc}C3=NBH2%u$(%fd$Clxj23SZ7v#uJ0*D4o*V|AJvlro3-6`IokVO1f($cCB$s0)e2Hj%qT+#G0q<* z!*oK=A8t|wzP zQWN+{M%|;lHZNMBQbmBv2C?gfGsQxp`N|E3B73A~^N=g{>;N%Y0J^&FLwIZ`6ft?l zc2x6=*MZ7|3Ed}iRM--<;&+j|euuf|{NUDZ(>@AD3xI}zSue$%u?eH~ zFOM>)7y`5OhiuG-heiv+E_`$Dqc}J>%jU)h{@U{fq0iQy8TD-|ggu~t~ zquVTW|M|h^G~gQ`#YXyj|5*VPAedJ+1&@8E%tEOU}|FIqa$;v*Kp57CPD-M zhvz?Q0$%@WkhJ$-CkzfH)N$Wu+Y%$9jBE?>Pl~{_Fd%Om)34LA1Jald3)0O0q3vJ( zqTvM7@>V1oy`CFEBLO9kCA|0j^p8`sMf4RQ>AjyE=(8@Zk6Hl>B)y&;{U4H}j)N(F zd@t@OC&dtZ3mIZDBF`w)|0AKl$0PwD%*dVGPft*Ap7(*KrVW4%^Z${{KJt+G&h7i@ zNm-S8fcz6u`RJqT|Deff(6$p`Ld0AEYkKYCPpkRU~#_%KS z0$hQBSocvK`{zf0LCXMHjMJbziUE(V>}wp_AphSc%7+1f@0s2msNp(uorwq@eU(w_xjr%5`p4VzDn!+=WX{fq=}m}n0KzjWPNUf)1;g2`~B>zI<8YB zjs3@^&m>=s4e9FYx~Xx8G&jZxyu@0G4tIETGSUpa$u1wqgQ6*#jwbmdiOpLjIcch4Tl z0iFolahPR>M2=I#bJN`K( zX;2poH87TC*I$`Mpx~nb11P4Q8e)=RD4BFQpepgU&koth##72cwNawTT2TM?NiAIV z#QTkGfPdQ;9Ug&c+I?k*(mKCSOFd<@m` zCMEm(1LCm-49Xh5KRS2pi z6_cdB1OR+}GjINZX$)%6CoIKaOq5TAJ!*Xnf8~Ui3Hl{HDsH!O(TVMXiw&W3iKfko zObU^vKx}`rS5f;mwyfN2k=?_TFi;Lc5H=w1^ZZ9=1L~Ll{{FMUGqbvrODiyl*d{RcZn-m;KK9@^=uvgNNp{DVc0zSN(S)S zU_F;(4NKic8;gIjfU?MZz-M(Ff78)Z+J1GJuyZ!`e20w3Y6x(~r~&M2+*{L{|cInseIJpOq|p(`@u>8p?n1Jw5Yy4mVpp5Q>!ZG0PT^^aB%t zlZC|);K*|B&r|_<-X1A#VTJyFZURTkBv30w2n-KGc-*bg}fO%9;XysgdP}1;Hilxlc ztLfUQZ79LUpKR$1!E>V>su3AeIC|*+ zwvrwZA$!9+@aPGO5%Ezz-WSd?8*R0MN8JpuZ)J`sm@`q>Z{AyaYdYjQVgGCAv6jdx zd?OOkVQVb3cxd@;`#u-Q!|FWVRoSQcF$k7Bb8j^3wdWlV1SS zY&P|4{O0>PE>dfQx`UaevR^$*%Dhl@PWf>;*g43>KR|3~7?R@E2~j;XkdJ}o3fq!w zBBfp49~SWRJ!>qX+>)$mkA076v#2WMWSm=m(CZ4zHewJ^UV zjw^B3uAY|c*g zjIX6m0}gt4be*~P5^IJF$>NLPcSt?Sbm-72kiP#|@6t8I<94+N19nEJ`PcU1aKWHk z-WMTHe;L21>^h7vx{fB(DEFe!rHftU@kRg^tNqa2xw=Z4XG%ToWWG3J+b^)c;uEIV zt;f9C^dB*wTqnBEKKvT?ZfCI)e*9d;1<~9w%;ZU(^=tf z5|m{%O10y_och^eykLbq_4JN;L+|3*%>fn$8X2F6bg7yJ6IV5|dw!x$P~!t*hi|p* zLJm#&DYomV#1~E)o^-TK{NZ;9{^ynKAeattlouH-Ok|d;en0tia@F;y`X@2_?oVj0 z?0DpAP*omjKv+{wfV@2KIB|o%Qu7V3u!RY2r`JsPD+&CN^{=VL{+1$317e&1*ie$< zQuI`v)=+D%n>VOFVLv<*)jk67irLF~u448BysZ9ap)Lk<-_PPog6bSccUNea5WEqUolnd1{@o1H9OYs8bb;K8CeBnJ+vq?l?wOM1?AHyR!wptGI z4#1?Q%~-mdx4uC#T->p`pCox=IJx!EhYF+kxMLShRG)3>)S?+}@9` z$bZpwnGhHLazB8jEi}WZ$76n1YG+#L>dp-6FS2^s*Y*x$5W`*|GXcPg_V72P9+}Gv ziL)D!Hc;|qVQaJf)g+#1)xO-|We-0+LToyv8R?o)*HHqJ+STu*tU-bcs*FL4#JeKYA?yMn)vytAMRjfkB_!nJ&1K5mto=5ls3Xn z-azPfESYW;rv8guhcal(#vC}c?v(Q3IPz1c38Q*KE;i-2F!XVd-G#R7P!3rYAnhg5 z0|TE0Ach7jC;?%XH!E48fQcnvgs;6E+qaYb^irXm8wndo>*&m@p`F#LVmcK3qP$ylG+E)(xF}Ls(55Rs z!+E0R)1=OFXFxo%rhiRp3g#k!LkXB;Rt;28IW5iuDK2`8p!b+ z+Z`h7b>YM);tdCW%SzsE0nW)zIP^JQ3c1_20|0f*(b-l@+P}#4$>h!4;y2lU@E6$W zU>09>oi5hU!AiR_{8ox=OZt||d!Y2Kje=^opBugMI~;&I7>LM$ zwj(}yGyu_RKhc3SZF%C>uN%WM+p4gO*MaE+9;LBw_ZCB6o|md4W7&{YcA zl!I;ssxo&q?gA%3n}+OIBC6su1nVj_qDMY*Th~5XElpP+@)Q91r_V}-D{V|>W_COF zyHG>wJlK$(?@Tfoy;Pa1HFSP)Bxp_6;hGh|49S|4+Oi$3zs<%4?HirpBk!t3&6}uv z+gn)e(OUUUD&oV3dpTnz4Y=axY-kh5;cB~S1BG&spQV<{7|>8a#0#pV_t1^}dU?Ni zBqGQB#WhWaIbqnZtws(Nl<5-xhHDoEEB)!rpdY9g1bCzAv$v0Wy!g|ve$)EROJwk@ z4;GH)4~Q-h-R%N^TBZQP1L2)BcP}9h6s0bDIuYH|SKr?n49QJ8J&NyM`wJeD$$wtT zb?#g-etHdNe3#9n%u_Eeyv|*24RYP|ci@pDf&+mL*oziaIFgT=4YhL1N zF7VHP%-%n-SqPuy6 z?i$L@cNaYZq=cw~lb>08?WN1qU*|HCcb0jN{w$xplD-Qku@0I2F2+T7Ma-tgl!Z9W70F-9`?=oM>7 za!GyvAVW3f1jw-)9SsEur=d-PHmsOeWbB9A(J&G(x)tqHxk_|&qIN?gp_{r@v2gQ ztPbC~QKu~ldcc%scq6hT6DfNyLr1AS$;{=Y9>%us*GD1iM$cSW{`E)!lQMK1;xsG< zWIOjQGF6XehxU=$)!;ixQto~Y3d;Io_JwwVW?_bcQ)?7z22^fZ5jrFL}lv}$SR7Yf`K$9^Ofk);hy2|oJ z>^qvK`XEE)D*1iEP6at}w!cM`l_3ty&7G5=aKIeeJ0K5OkIN{VpfA2fddB$hqsn3e z25~=u_Y#@pk_3EtaOO|16O_$9J-!t3+ebCp({dROJ4gR_rF!m8i*w|8qF1K-3&GK3 z_3VMg+Zx1G3nWa@7$DkH%cKQC(|2s5rQd=%5i|f#tPe<%7JMKD_g+rDBd*l5dQavY@~28*U_3C_Xh+xrS9eJDQAu!1!aGYZ}Dy z!sloKfLa<{!`AHH>JNJq3VAVLu16`}4XS*3eUM=I2xLjV$X@((eX!BBZeJlIsPlMH;+ef`YeFXXy`@<-NCG=l5Uc_8`dP4iPXy#XoMk zHoRI+uL9gz{v)r6)a_j^S*Xbee<6K(_NiSqZ(wmIR6N>z<_8A578FQwf%gSaymqJ0 z1V<>8Q?dW3IP{!;jn7Y3-zKo0{T4aR3Qnp(xmZEzTX^{+&o;tf|LXcnghI}>Q$-s_ zzRFDwkjC)p?ydPcfLU$(F!Kfz@v}V`yYAQhfJ2NHz7E~Ie{8X;)_-8A2foC|u!W;i z>ui?L=ZNuzUpQYEkr_;0UK0cD6K6`Z>jOgMOk_byxK|2}UrbEhfzH&} zBYfI7<6xwZH|OgprG5VCOMhMahq^5CMZ3w)JJM_6Wij=8N9xquQ|L*v2j(_-_^&LX z0yqH^`6Zad`p}=`fGyQJ*W=gE@*L|Tmc#Yw#R(Dm1~ z|JF=EgMYf<c5{^Us4Vc;51{=useYQiC_Q`N+!k7n$&-#Xyb?N5PCx1la0cJ zw-8t%505C^yd(bes8>K~D2z#qG{rmf8H$>8X-EqGIaK%y#IKQ>x?_haZ)P|#T2;8| zAWFIZcbYl)q0rgWlU^GH%s@8a?x$Q^#Ooj5c_{OsXz>X$1zD)g_S@}=@ z{h#1Q6k*)>z+Y>>2n^Bb)9sByODa&+5X+CMCV!F{(2fM0Vra3&_=a`)uc!i293wFE*cG$O+dxOt@8 zpV2O!fCp`@Ir;eu3Te8$od+ z-#*w18VFb7|A4;Qt^{COCL1?aC_^$prC#{&3|bdJ`8SXwFLw{#g(3sqkPKKfP4I71;BY1Cg&! zRbAaZUvi`#p|ls*Akg7K-CIYs{L8l?dS)}wa@&sYToqWUFSeC1lPw>pD2F&c(Xc6d zEloaVRgUBiGirkHU1Jvas z0#!RR4|D#r(fcGHd?hOmZcN!OJORuWgFyWc#^IA7@aWG->j8=}vjSlPF@w?@wMPDK z#B9(;a~<&X#D#1$~Eo+=CJu|H_F?UA&2L1-nlbddG>an9bbnlq3}?WtJ|Xzxbund_1M`|ksAr17oUWuaG zjsrP1ik?3O+QGAzxYfN?TB{ywP39C66T4;dd~U2&9XPCppB>v<6JS=W^64@4jdphN zKd<_$0FREJW4`N28DSpy?8P!N?pSyJgbuRtPO+nSUFl*!zSW{h^3U|DK=iiZyD4vL z+3}ri&LG!3|G2b6Ma&R5Z3?9j`p+y8M-HV|Bce3;c3ftaGQYWJMF|`{8zxg6UZ2!T zxZKd@Tvz&M&2|scY_3*EDPTLQ5_yT;66WG95xK z+%AA{Gb+z3@BZ8DuxZs9Wl=fn3hi6vhQ*Gmv8eg~E{eFJQa4ES35J6H2V1Mv*9HI4 zq2ED&jFPW6%Ql+>E)JNT4*;2HW{wVqmBL$ zR-P0yj6upZFYiAtamIV97da{lAOH~{M9Jx{0e}XQ@Rg$DvMZSvO|b8oIrudLzC&#bV$NuA?-?> zw2`cFfly=JNKL4L`@$seB36g9Q8L}8%z4Za)MQbNaq4M$@8V?H^>c}eFI_WM>;|qe zmYqMfWl^S}8Gsdhk_D7WXNkFKvV zMI(+Tki5DCP*t7jkJ%N>Am%LbU>zYZFq^Q$eV>4 zZtF>%NNLNoZaeX#Qn~p0(6LsFNR3N=jIVhY`#5#{e_}0=whVMH#}>4bU9}3Qop5FN zptnlOAZ&ni7b{^Ex9h9ew+|)Cvb%gG=f(xsaa{M=`H9Zgfvm>B38uQ52P_h)ZxB^wqiL3&(q;~I*z{st&NT=z(Mc@3OYwSAN#Ur=2 z;^*@6N~Dm$C$+~s%&Gkkwz+|9#8dsKa+{#Rl_lEQ!s`v!#PfNt4VJ$Om>+MCs7lev z&CGuc?`g`uXYkQJ#;re)RZ1X#LgZXD0H4=pZAovXkuU(}P%i#?Gv#Q(KuaYw%4AZl z338;VXP92S-Mrn%SMTvext8u@ldkv7jFUlCw`$7WDIZ!Td#i`OPj46k`X{_O;aM+! zKii5jix^7Vk}wk$#C&TFr2?uIHW2ksT>Ih+5Oj8InR=yXuU-fN&3>UbNV*!U zKYKSi0+$~rck-lArJj}!g3%89HaAAfZ2*lnt9x%=eFgDS10fJ=!GxOuGnBYW3~2Ch zjvNr*UCO1GYitnmD;HB1>|>8MGw#f~;{{4r@s$Lzd#|XXTuZc1Wa;Hze%C&pZc)V2 z=RuGW8cz3F@anj#R%z8gk*8_&uDHXgGv%jc`^EIQH%&N}xz;2k6MqbMxBp0d`ac9U z7!1Ld`q;YcC_lwqdMzIhDxExC3CmFLG=8@xZwdi_8W-Pd3YSc^WtR$~G_!ugfbjYS zs0I2vdyK0oV9CD}WW|XcAW)ix)e!GLbAu}=SIMU1=kGdSty8)4$gZ~~z>^N}W~Uk! zM(P+r-Ac&@uZb#LgF&K2W*P2|XZyDKi$h%L^?M)a`)Kmbz)I%FTCr)LlegzQxTmZJ zAdR8ucF}rqYurtzkH($RyU?<_Ahf8JhY9Cn?5(4lX!(r#nq@c9nV$E>-`scR3yYt2 zkpXJBob!nchT9^oqx9FXf6 z+HJqh2Ax(8oWzwq!-`NPt1mRh3isPMk-zIt*;NON4-fFVmBfiHrl;6vmM*-y>5*s^ zL!1#^S;8AzcBjO`#@BjE2okBUO1uY6n%`6(%6fdH?IL@?L2G~h>jyO^IT#+jZ9u*?!A5Fz1KZmwD>hI2f*5V)|ET4&JV4HANRT5}rde zB~F(N?M-baYuXfazt_`iU!slHdb#Ew9mDcrlMiskbkso`wsBO8BO`7Olj_X$|d62rWR1s{>jS0ka(=L_bqA4 zjnET57pQmbNAq6p7vnM*n_k6Y^(ET&J{Bd-5z>?@rjd zHr5KD2V-oG_0J4(e|4i(_)PG&9~tc{zL8G)=65$sfO^4>UFPJ#YL(PXP$Bl^)4khE z+E?A$6@O-_-w`dNe$}pyWHRxD^|>s1b=N*s%4E~!6^uiRPm@2ta>@m1h5%UPJMUM> zvT`yuCn@ocw0(uS9XWhX=SE>J)|Vt*c-Yctjp6SI&)L4e4smY-O-f|;=v#C z?R#iQxBA1F&;4cx$2I;P{LFK8>eYe`0r4jJo=O3se=+Je{nE6F=Q`la>_=}TU#%7ld z#n0#c9sbmNeaaBV?nxcwHY^MuC#ifbtLPmzEgeR9b86qOB|Ne!P-?!qhu4CyTM+C9 zS?|8{pP!BB&9MlSS@_TeFR`@>NXovK-mVfTz;bAA!mDESJUMmj{N1k~CN-vyI;o${ z{`%u-x`&*AO@E1|wsxRf;L&|g|6gH89thRe|5~K9sVtQ&)hpRk+4p+ol~82Ht}GET z+1D9QJ;~lCvQ7)q7@Ec!W{OZ`DcQFgVXR}Bg<<&4Wahn5J~&_;~Rp@+KN1)2}1^d1en7>fi_FAy<~?v8c(R}KQyEudWw)8= z)+!@C8M78tuh+DUycdyT);(RXc2u#4Sna7!o07_{x)dgCC21%{mU;PZ-{jY@K0Svk zaiv0GR>m%BU1ij96O=(fj>TRwFnL8Yt@xhWRDB1fUN88il5T2xfk@zJxH2UViyfH` z_Jv$6`noPXaOgKK>dj5X2c^^)|j2DH%LT2`g=Q&N*9({kZ_Q*)!(_vv#Qf#W+Q z8Wrb0wqfdf46Jj6gvg!#gJXJoYeY&&+rvZxuANOJyN?!>AZ@G%T9nx=&lHDqIUr6g zHX7c>;*Ejx$O8Z&8)pyf)4}`CKYnJwl9%sn(7rapf|@}vHc;7HFyl}2Qf=*fucaX@ zp5he%fn^pE*Ju6je+ucRf7iJnTp0NzR`YSaTU$yjpWcUFh+iG;D_B4-Bfng98?=D(9?rjWJhoam z*hm%a{q#%6cnQNf#$=vjtqH($qws4pC$V7QW#27e#*?dVbDe6OiW$SZlQ;}i!l@B8(SB(HGGW@!P4M?jwl=&Q?=E00;#8`phUu% z_~7bQT`C>otP2$vH#{{aN8=_u)L#WVj|dOqVc7&YQjgvYSN!gd}y8gIy33sYDKG>Zons)j5NFSDkEDF`63U~fQC3EE*Q;2 zr}StAjN=UpcnISf)Vgf&CV5h}v{hzBOPR{UixuoGG54muYxQ-6gIE?DF-}q1#^CMv z1X87-O=92HL;jm*4~XmFtAZ@lPjKSymaZsV-9W1^Im)(HCRkvNfjaI5y<@d!xw&Xv?>YfdV{pcNJ)3DoUr5(&EO@BR=YkS z-IB`PbC-W{@+0@a&bhQyx~HYz*Yx~?9{s*^EbxX7I6A4RDUD+Yn|}*d^;xa`FemM!B+bnZlTuH#ZzZ<>YY0Qe}EV?`7CQ zV*I@gikmnIdw;JO5xVZKB#YJ|RqKzLG?H!#xkXfq2EI7o0Yo6>NQ95$_t7%)=_3-wHAtar__#Egyb4$rrW;zQfr%){vdJ0 zbF;}GokP}Mll^9K+@eKlxaky_W_HMpqPWsIUUgH;&v&6WMv79 zI9T|B2e)!Uk-fLJJs~_t<<_l6ND!gplU>ttdL|$At zz%q;fS?X(o)vL2N%Hbe|)z5 zNJ{8U<_(_foW6727P`6EHfGRG#NEXbPVt`RYn!9tGX)BGG{rU|$4W@n9}5k0jY=fu zX*E*04y$ml3JxQ{ms<(Q8;)FC9JkUHs!jFQpkAN0yN-Zw8RefFD}-Lj^X>6BwDcH? zOCRytM}$UJaB=S4Lk6TDM~38;mzV;X$dxEXj6b`Dm~Z2D1!xU5g>|R?;e72aT$=OO zg2-CrXVS!iPiL(9YNTYd^0{2$Cl-cLxdf%^)SYr=*ni;_c*A7R$^7fN1^BOc_uBALJ8qzEw_UM9uGVFRbwI33!hbO?p$0wh zkYH)tO{C5Fa&b=NOvwS{EUFwi&O*)^uEhOB=w`m_h~~q3Rex~cMP%(k!v%FZMeB%B zn;bgTx2{nK^X;x)i%Oo)n82-2L2Kob_;NqTWPfXQYTJ%>ivM8Bu8|z9PfA02IiaPb zlE-jiXh?bJX%n{AX2CX+_|Y!j)FF=(9v&%& zGfnoSUcprD*1DA^*~=d~I3E1v(P(j`oT^-lO>noZZ+gS$g*MFMEpXPIv^Ao?xro-& ztLuDJc+I*!f>)#!ViPT<7s#1Ompi%SfOBC6Uaa$1j8R*7IvP|)X$jYpU9hUt#rh07 zX6Nrly{`Q5@TlGV%!c&Td+m?H7^)=(C?Oxx0{CM1NkOYTh^goFxP=4l)9|e;ac>iK z6Q4}i&%yjWsVX^Utty--UH?rFgy=TYmyRuI9BrxXPBqA0U>0~svat{oV%If>_Z^4_ zv6uA4sWrzcGD?M_LO25#~GEns&FBT*a?GP4{ zNsMBf1&_#`*9h+Ea|(vmfsANkE9qV0Nwe3J6BYEzg3fxL%;r~SMLU&=!#fp*sYdP| zGc^37)oz#J;I_>to_G!#q^nlmo89Gti9Q2IP}{$?%|Zk3H^cD=xLt#Wk5sb^7lSUs zJ2#GX%u-KzdYg!w6HW-#YsyF37+w3@+=73Qs%zIWp4+8H-Ca^X&yI&=tf!aF5cJ%? zov&`0rAo}ium00t%}o?;5wtKn7|OrbC$c_eVE%-jXOu7D>FH3d`KXw@h&MSUV?BGU zoH%^mRjc)750evx2pLJI9yGfdb2E&-NTJ7l^e=6axByZAbOv5R!s!X@vAFhzE3z_Q z)A%B+@ihu-G8>`vX@=S!7~AGQC_J#9TVSMgvdY4JVsDsk$KxXKN*YrWV=Abfz!k|c zEu2xYtNL)YzkDPIFW)IpWSXj3*y%Px1gveyLp7?C{}eZ)?aM*bt^()2DSAklGg!z|k6jHcqomY{1&YBSfg7V6k+nvb$|p zU_*C%o4KtCp5Tngsu=Ta!%T!_)faooAL6vroUE~=caA}T!;kSu-OsjKCz0rNjO}{v zCis(rV>>NeDYch7%auasMS>NGzA$K5vooDDIWLDLF`w`1)i<@A@z$RN8k*2&s2N^ZjZ1NDO&XTQm-B>b ze{JzLfpVA*P7}(;#B)JjGKww%+A+R+n%dS}9RZDUb$ad&_<`gn&Co@it1pK#>e@pK zRs>q?+<5?X#MDUg(jrcEz^lVv_9eUx4{fGZRn1I_R)H^S??b;+($X#Q8GC#pYzqeX z=KPQimI|uASQmR_Xs^1AZ|EQsg(FX`@$9Pwu=h8UjLvWqA%akp^{{pMi zen4DT`^RMmmmX-G0@pII9MB1}(t3HP0OnehQ9a>HJn}9Sgsrj}Z^Tv_1hz&2vzusz zTC)iH4h4o;J63@{E>52Kktxqvfnm)GHmCHK#$C7rhVkn^c(i?`QQBhFV3Ay*3K)-=%3o%fB@blhyD&x z3jaj*2JLYES)7;O;Q_;N>sWXTxx5{~c+j+t4fC&g1sToMBbW`eS4-oxYnj){`KiBt zoV{RUBEYx#^k@M1o*eT@}o)^0~ zcLmHGf^@YGJJQWbm~&eiynRt7Oo9?(Eh@vAi}ap0r^t{-4qc5v=~8Row)V2%Y4xujZ=SZ>pW3t*#< z=T%w0*v|vL-_b?^P2{3rJalsDfsj%luKlj3qQX|*ShEChGE$~5e`XPzA81*VH^1%L zw=$4F2wKQ6AR4OP|ETlIDEUFGe+2s9*8G)chxYd=6Z*FSEcs*_n*MCc?@NUHksd#* zlchU#zX66k`y!Dop5RE(Ek7x&-KaoPy2)F7guKq?_^~uqEa|nrzIYAbY!MT|%TXCE27tN{f{^#LPR+)cO4ZEdT6) zBcUncPtoA1)1?~JF~5aSL2L#nK5bVUf5DhPHu4`*_l*f=p|RaX7DMN6%zunEo<}Yb2#tpEclCbs!iwx$O+7_GX`Uw-~ zqj2!Eidh-~qb*nW;t7QZLm8){cc)m57mUbaJ{-{#gw3w(L};U!J_COf@c>J5)p;%) zA%aAOT$N|TB7Dn(8+3B=lkBXG5vbYpu>eqd>dSD$)l*Xu^oDjt9E5_pr@MDWK`@a8 zGTy(#X@-Ro1efG4vf9(dk07NQ=}p+Q%ga3*-=pf?;}TJxu3P^!?e zndb@ecBjZB-)ng;tvNZ5j`^Dn%Tbc(dJI*z@ceq6#zbHt&6}-)`4SnrdQHsKjPqXiItwo!SwcTezX11 zzO=<4C7i^31uDl1oJfHvy3EWwstmVC`(;gha13=6Qf)DJsWUBoVLsOt1CB)Yn;J?0 z)!?!^nRD6;?=yt*+q5pAe8sTce|XXj-V`$SMZR4pl&#d+Z5CgtdL>n@h-{k_Mff%fM$KoML!FMMbNy9ET z9U?0E=Rpc-UeAF={_iR&&pCuyO%YEGk{rhkF8B@PHdMOfRrdMxx(){P&N9=od!bf> zN?pnxF{)CzrMC0qT}`(AaggLb>$>aAT%t%2@#9X7fgC?l8O`qP#3Rh`^WI#ubg$Ia zo=;Bo?h>^2I+>L{a5`-bEf&X3mHumhXG5iGOlsNONuC{wUY3XxouCXb8X*n2`2a9mPfDem_DB7VC6Fwh9F|+hLAQq zvzuk+p`Ivf9Xw>Ba*Bt8_3~6h@^<&aX&O6ed<#jc-p)ge0RgfaXr0u! zHuZxB$$VS2ru?#^Z&sByE0+9jzWK$YzKGiTG&}ECRrIL9#T#u7g^b<9vL&<`QBp0W zKFOt0Ap6{=Nd-Q#5TXg~J{t#h@H~H%7(YOp**P_mG{Bpw^!B?6I16sYB zsDiJtt+A1^%BVbr3H+jl0$n8N_bB*W%IE_4!ist|q(~yD;9~~<-FR9cHLKmHxKh&v zAzM08o)1}jHQUhns4IWa#oh|+fH7~5U3H8?iDXLfpxSgnrTfsE%jyz0X8?w~mFFK6 z*?wsTMH>-+^5o1|c&6i%PCJ@S<#}CdWT=4op^}BiylXYwU+*$&Jj!pWFmu-w)}4qLB<@B>b` z9!05USlJqn8=UsuVSN;iku%fH6<@hd%UsE)!@Z;}2&>gfx zT@`B4Quou9o=1Z-3&#fT6jr9k*U=fHhOxaiJpJis#5XBxScJklT@l+lVI?L(CA?lz z>j(h3Wizz+jhAryv6?w}pNn1=)7%uQ(q-}l@n!i`{&?`z`%DbQr^LabB1dSCOE$(F zJx8%R)*_xC50N@6)tTOx8;tl(z!W(y6N=?gnLEFUE(3ZJ+y}>$T2)@ruOkF4z5JtI zjn$7n#H&-nR0;-D$dlrjF=~k;4LuAMM=#Sr-;99Sjc>9&$Fealyw84b%zbp*M(-g+ z@){H<cH330Ol}Li23D z90DSxoWNs)=+7s1555>(xUC~tO&>wuOA!o?5}u1~3&fjszRhT+eDZnOzbBe<+O=1a zUa!1ZUVF5YUhdH!^=P|y#9rLZ>!8&5aCMrVG|p7rZ!zK1;Ygi&(3)5+DvHZ&;R&xi zBc2xEQ##wyiJ_Wz+rHjV%@+x0M>5|vjdl#+Cl7U%n{0Q!P+O0iF~vKjcNCKK+A5un z9*1RpE;)k4`eB-Dc)WEAb| zVsKvusEWR<4YP-ObImSHedr}j?2oM-*SgkA^czeGK51^|_RvAKTC#W1`jSPr*`tbL z%DwUUzI(%)cYC;623ORX5hp(1i;WECqbD1+z4A%tFR21`MLz$omrBuU!u@L*Zbk0) z;3xpcMmh9AJcOcG{eDeMw6JeN{&9QA7w%=Q#|1Rdvdso5A*>Dg3p%|A8L8ulD+ZGM zMW_pS|3#7Aq8vk-#m^bscAZXxrE$&Dg+HkU&^nX?--EZK1yx{)0S&p5q9k4nAB{ z1W?BX!66jhK@)FgK5bmEZ;A#j5e6~TNNFQUsZ_obS1L#NE+#2@n@dDJsA;P4ln7EP ze6lC)bJZo?Jwgrhlwu=Kro|2-e|-AsOxRG>P-ZG`r`vR-QA%ktNGe>3?8`fDYo+_q zi;A>)J~v=t*LQ1wY;J#N{s_;*$t|6*d#*GKXj08GZ%hmioV(c+q5bS&z>?KQlP-a} z@FmBH0hw+6Ji$fTu-~?lzr?=Zu6kiV zLhTEg>2+X7zER(XL{;MkqqpF4n9xDy(6_K2Wb-!d(2Q`Q^N5ZSW(7SmBZ9t zSc563emUa%6FC+8S`UzGh;Is)mu3Ytbgo1-Vtn;&o5cJxT;r0>FiNm-*(wg&`|mg; z{tmKxX)NeTpgXN)e~Mu9lF;+F~xnU$~512`{s4l z?mF{+Hz}_opB1>)Yqmy$9;o@l#bE1Alc?7#xh5Z8E*mK0v z4<;D>>IKP$iv-QQlj!fPT8-9 zX@k#>7GAe-E7OpDWq%QZmVZ;+1K$!EZ{sf`P(UM~VY+IPRHK3u71PZEEcd&dS$UCV6e ztD=E~6b{9!{pV}xRiNszmX*WMKDq&P!N8pMj?zYLL}a3mH2kySE4Im;UIQ|GZOw+4 zl+kWEX3&sbjjdOfD4_Q@xKK358Cz}RyQ#G`Il=)avndq{F5S8QA3=pO&$-L61a?m$ z&}k9((PgpB*+;&0ddIrtZNN1&%`Sr!77Yy70!`1P@cd#@-ST z@D|3ta*w&AHCHJ3c=1xTeLt-m?(+1oDO?wv0k5S#UtQ!%PHB$_iMI(rTyp8*a#m0R zCE>Pf_heqa1o4`X25k=3NCFDC`m}i3!)~>2UwXW6ZkgNT_4_;{9cnv*F1U5nQ1l#X ztX-~V(x2Ks*hjylz)b74DP zbKvdOowOcrz3(yFO|Q0X)m2A>;j)UoSDT6^=FM<@)rPl(gY$hMv>ChDsJu^hR=Vz7 zm2psO`BTxQxE#qJ^q?a%4UoiJ!A4iCwkH?kZlex7U_juxQAo)dN>XR|;lnHzVrq@& z5T@FH);S)vQ8qDu%F29|9QrTlwDR=mM#6aDc=0##tfhmQwF-p}FzkYY?CI1A~+1n$Vibq`@1KN~S^=EkmTq39igz!a!Id?u{PK24m ztv%Y2JLNsRAP=rzVfq!)ARV+*>+2I{$Pibs7dKab2Qzv^uQkAj8tD=A=_u0|`_%R+ z;qD30sTaqv20eM%4Secnr8=TZhZ4VA2vP>2RpQ;l)hR>7;CCVI!$D)6a@#=c$qUA$%9o+dYR$+&|=Qjc$g_^jHg1Z(NvCz_Wn@bM~EVL9xob*Njzfy0j z=H#~~_YWBHxQ;*eV!fWewwx^b_Slmkp5%6+R2rUfkq`pkT3h~SWjQ&h&e5@Vs?Zch zZSa6EP{so>b0THV8mY&FC9+M`*KI9TM&vE1R@Pd=4IWN8?D1>-YG#VTh1B5!Y%$h# z7acHHsSR*yhenL*%iht-od4n-iR*|Vn>Nu;i$qoVPX)&%{+CeLmyx7f1&(%lq8r=& z*fY~W=16Jyy@Qp{&VO&uj)c?NQ77r2H*lEawTXJjo%U)AZTqn%UAu<|K^~|ELJAzq zSJG=XN=VZiNl*KEiP6H8fUznNOFr7x*7ex8eXPdK66(2t0RP9c{+OnKYaa| zUduz-GCM1{0M5~}EXy@@k5+(0t_c|5k;L$7x*CVKEjHv3Cg6pE0-+6Vu$st9dPoX5 zMIdb^DY>}IrjvQ(;)MVgU3$guJu%fHfsZG`(x9yN7ztABvFW)jAkQ<%%)dTirF!dN zo~fLvUeIIzRJ$z@^zMQ7uVbn*`S#dYEeh@H%my>9?`byZ=4=&Yp5)xyaM9}fa(HG2 zT2{d9o0k9F(j1C94}@Xi6#8+K?$d@PK8f3s^lip~$6))Qu@W1&0Z zG0Wn!WsV&aO&4>3!ty*|6vFzYQ8}aloEvUlX~Gb&%Cozg-z=Bpy0MpUDsbXW7yn0!h)e2jyJ*N@|{? z-Wuu2?i%aJPs-h(Z?PFZ|6Y8O+E$1i3oy2`D)9*y_H9GpIw-7n8kPqn|7g)ErhnS{ zP+oS48IZ$V%#IrUi{CqHKDfE$>;g@wEy-3+9B%{s2r<*0;|G;L)FC`ep@+19{tG1+ zAMvv@hkE?_>iiaOY$bpW#9Mj$dgu3CIr$p&I9Ku1%88Bq2dFKRnV*)+yAM{=J3Ydxq&azlSzO&}%Kn20i6=qz z$;?#Y3=5E3;0r!7KTWOc0@d;koyns&%r+O0M} zsJ@zfwBIGUGT}UcM8)^Lmp{f5R3=A04_t^!T^W=9pw?Skdv+(wyf+RrDvh+t|dn-9avWa`NCh~<&%PyB*x z2K=6%m_^>l;>aGrBU6g|t2p0a`58Gs7V>|zTmJ^62|x&x>z@5B)@t5BY)l4Acd=A0 z8&f|Hp8|bP@rAqOMOggby#Dhhy8b-5Heu%d{pX0ndOe?8^?E@{5hN2=IpeXuTt!$P yTk9~{QQKn2M4+QZk~@j5!k88g literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-create-rule.png b/docs/reference/images/esql/esql-kibana-create-rule.png new file mode 100644 index 0000000000000000000000000000000000000000..c9fb14b0d2ee95605dbd4fe2becc7d64ca351245 GIT binary patch literal 239430 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

    yA$T6|P`=PwX#cVe-Q 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

    g@7v7&IA(>J&QDhk`$g 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%0D-cLt_;Nk1ES@$(@^rVM&>1D9nw|+ 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.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
    7LAoJcp8iX#7=@bmdE2;7UvoRdAwqhAK0^m@0 zAWqnLTQn|QRcT%Nh{#^v^jcS221a1I3H13D5F_{L`MmyjXVN$O8!)258jXmr7JbjL zBMX|P``^c&D|i@_e0_24mn)37f%_1v#4oU!yq~BKAN$3_aCmP_oiMul`^LKRM>W$P zNqJik|K)=M>Y7sLbb0R;xo0JMEdL|66n_76x@+Sc8)yRqJunNPSaw`q#4%&J*OU1! zDrdvyKDY_q8N71`cHgAZJwCA=@qw)HIV!00!&A&nZptCVb4woS*|PDV*%+cH(Zr>~ zA;J{@G_)P#{r!|?@akiOs`8IJDuJw;fBS=&Ib9fcUq*kHac#SCM9f2Nq+@sLpdBt& zW<6<~^*F3b%{V$l!pylN^`-&d*b*UWm#A-2&J@bgL{{u;YSa9>;segt< z01Kz=^iwouYi(hB1jI>mx^Pw6F~wLkc#&8G zf!*;Z7wS3hdbpuzdQ0#dif82qHzOX-qO%L=Q6*elQ8KqZ2KxaF_lS`56# z#%Dc#oV=9v9r|t7LrR$7#B)j9~ok&4^HR z6+~#yFJpw9?&sNtmXo(*n;OawY9m41ge$PlZkm95m;* z?0~$(pMNm&cA=jP=PxcNd0)PtUv~ee=+tGsTBhTBl2P7$1yO`j6F&ekOIEyc?^WC# zyY%}iwIjoV(lcB1t^`(9IhYHEd72a6X!!KySdUz^w;$?@T2Ucdg4Vzmcj=TR0csimEnT9``nd_G{{(KuRfQ$0;{8p%BV!4+v&)HT0A@%3A z%bhMGDL`W9t*RdQE&zhK+||%AC6J5pqe^JvnferHDAQV_Vz_aYdb}KEkZP^(8P+1xt2MYG3t&0GZ8CQ>*fXGJcO#Q7= zulvuN&$z5F0qJ_y86X(EyYay1k{Jw*gQ4d`KU&N_ef3g^gY5PZ*gx*zc3H{e^H5pX z9r+0Y1Ga!b^$(ArEK-2}*fhV&>&lFAi2fS*sXnlFZ#3fFeuZ5{5V+?FMu0bqo>a&) z!I#czdl6VJD4bqq0c@HQz-UU3VZ2wK@{mmg;KX;Ze7UmR3ap?(x#v8nPv5C3{3)2c zA$)?FJF{q*t_((l4H{ys;u>ip{?5AsE{nQ)FV$S_gceA%l$Io zJpTQ#BeGCOu%$x9GekS@USGAl8FTf66$k`Q_ei;0L$Qs}Y^Y$tmD20L!&@({6R#Mq4{p&QKrXCucl0Rmw56zA9F!)?I z9R~OzpMc0Ucc%hy4W-y_L0@kHs>7z)m`n5-Haob21#)I_48(;s)bZu5<>zcT5Cl}S zMoSqvK>sPR?nV&&_K)@tSaCq?0cD2CfyOaZ;Btua;xyX=z|AUz8BoR0_oe=N(F?I< zB_Q(dsPxFO$@sBlY_hpZAJkb6CCwE`2)@!gb%t1#r~g5-u1qur1ufKg#b?RQZ}S|K zKE{GxJO1a8fGem%lo}*w!;QgdSY@Ig|FurAVL2{A*KbFqzv71m1s!h^#&d4_(R0j2 z?{=(F1-q__ZAk&PKES2TQfsY=b-Y4h4U)6JT)aM>T@2mXWxnDqSA|p^@UN+9R)^!@ zawtkI1HtDZXaQq8@mQxHS`5ZL4|vr(7!gS$z%bQl5L?hUqP%V_{q3#zg0dgo3q+|% zyJT9D=W)I?{ax4n$AH9OpvQ^T1zAq)?;GcV2aM&eML9&)t7mipW}HQdR#fIG%!6?# z3Mdn14Y`eMA(T?=r}-CZRjqAJfn5^*`~Ckqm1{Sl&CZdi?SA|A?H!Kwm%RXT*t-FQ zz=iFe)j1D}M*Mr-^5uSDj#Bgi(xM(kflC*j7_~C$ z(dM&8v+l;s+%NNh&%(<+kmU8U=@wvgDhcc^+4_ZHe?P#*D?3JWj2cDQ4(|rItE=G$ zUh1$nt$1c?9>==sJbUpAtfv)8b_>Hs&Ebv?{mWK>>%_tZiI|%VQyh8L`k1Fa8;5VH zscJ!8^%vd*W1^6Mfr3SdBXG^o(~Sy%=Tt*zMS?*^+SvMDB4b7~0@HJr`h!d31 zGxt#nWR4kEZega`p?`BNxKVmxn-?E45lL2i&@Y*zyJ@KtG5Mq#GsEc$R&FOax3=4k z>2F3h^&SW@xC|%}GQ-7I* zL+{-GuuO3NqYsx5?}9gMEEk0Ul`14u`@db}{ZIs31W~nrd;?U95)L6eT*bG`Pb#+0~hDL>IZ4`3j0DXI=~0A2iXoXvL^uEQD)Z09*b0;ZJBA+Jdv{ivcVi)cg-vl1m$gJ1!Inso55MO)qF zuf8;yE7eDYnM=23*fhv9x&)x}m`QM+J@j#^j5yYxg%f7@TC-8B?uVy$+%5r_sMOa)v!EEJW^VGbq3Nv&j|O1;RGG0Xcv?j4to&E|;F7hf=@kHGV71A6AF6T5t^xf!yjC1Z`Gj60i8 z{EuDuw5!X>D>J+D9AxhW+`aVP()Qlg+B1YYarr<{C=eDFSosw5Z|3{s&^K^Jvv%_j zS*9cl(jX00B={Qr+(11we^(D%E|uv_KtqK>ba(p9IyB$rK&(&PeTcluFeIAG?Fs*ec)uoPkNsQ__|vnvLISb*8s`r_qyJ6SE>< zb9z|Ap_oM})Ms}}nT`a_c0UL{&%iDn`Nw|C1O^Qa@JUCBl98aQ`XkOhDh7SU2PM*< zolK|R(d7!EgRl7d9Y|}gdRW+EYYO6q0S;&?kN-V9dhd5#%%xa{Gd*{Cl%{x^-$4ye z;;Juo<*@CiBGunp3OQ-`GR$ZX(&|pq!@W0hULBYr)K%K_|F<7R%*CLPaE4qmqp7KB zVBCu~khEmuupd~L#W_Omfewj>uZnU)^JSMFO>AlL6Ctc)QOrJu9n^=MKhJD>l)%8f zapbyx*&L=tyc-j_bqc}FSze<(o3c>i*_F9k0N#rN{p#`58$@E~ANc%T-RkoP?@`dI9sWr=KiH0H87EI`l^^wko8~Tv>0+(&|G^T=TzC1|j*X&1 ze|HyEZ#yFyj^1UUd(`tliIrSt68W7z&cx8wQlGO|Hm1^g$Bz-?7zRd0O^!LTaowin3l?Yp zgXThq`nbkFjYXrayuL;XqJhXtVdhUa-Df&%*LAI^g1@`!KS+T=B;(`6)T$ay`TlSaD7Z~AbJf&3i) zaFR(da)*zkJr00*9zf&|m$VGd!nI0flf>;KyfC|QSaC$_>ct`|zb%SqM1y5Rd55}; z>V=w(66=A`YiuNqgF?@Qpx|)q#AlK^P-v_A7N|?bS8NRb?uHGD>Eh05O_vjT?*87l zoFL~gb!z&%4@>WN5a4St2N&<^v3n#kGUs$>V8k&;?#^M${cwb*h1~_Igw#cch4J*l zbfT(huUv}&a!$A4&Y+=@qA;&vMDIrp8HaLT0m=#e&8rMz-R7oV*>zajL}GN5XRKLO z8#%QYShANAe6sQL9sU~ZEdckg9gg=M@%+i8h%Hoo*TD%- zQf!C6xiVvS&CSC0m&8{>vm(3&E;QkMF2)R&EO)mf^;*IkS=n_Z9)Riig6ddutTVtz zh0#}WafO-rNbhZMx=QFAF3Kz7=Lte(OX+!V_f9)wd#qWQKffn)K)Injnc5*3ze3 z^bc=w@H*%R6vUy4;N@>5)f+U(&Q1T;@qK@M?rgVutv|Z_c$^Hdw;1oVfQ)Tcb{eqllYJE5^##=d$E3|jn z;~YHntT|8`yp5Oo&PvTmQjAN|b(1{URsFnPoqFJi)Rf!(k9C=OCT%wDWwH4;kmb`G zDg|UmOyNG}k|Na?t!5}ecS}sj1Qd6(V7k=(l9;iUgj?M8)>v&vVEkalpfRkvimX3%yAgqHi{+ou6Hzb%KWiyU>N7Ep?GeApip z3iazl_lm(Z7Ie{}EW7ZN_3%gC2TSf_nZMIiGCX5&S+Etpa7 zfIw5q7w-U19VhIs3NJuZqm*2+4IX`-rpA5+hugZe^)H4^B$VDdQ|@%~VI&W9Cx3%G zxu$uO4akV6_q6fCvraPaA)17q4H zM5i46wZXmpmDDA>MKd3Ir%Jzy=CX*K@cpjGFBlVnVmh}ICmyx}^ds5FU25z5Z(ZBr zV;^(})<==A4vzC#{s?R>t{RI)-SF#L^o}~KW(!p=te}A3;8^#jH@7zyIky8C#MoW4 zbvFb|ojL`w9_!SXyQDJ^i*g>q`qiI--ibGYP<`aUY+k_Q>6w)%770R`cSX6hzAf<+ z7XvfvP0#839FPRVw>=~nSs!ei-ll8)At?h}{ov~xDtn3u^jq#ga7+AfnKbckl9auF z0IZ)$rnseLu`)nOqb>Lta7`Ja0o|=UjBf9#PC3wzt2mh>E_Tna8V|o{jbJ&rG5H~( z(DMLNv+~&Ddh>F{*M3LLc^2l9FEwF`p1F#1kgvfe6!~1Zz#*kvaZ3K*H30`~-7Mdj zqFg|hspO$`zoGW^F<)yL0jDepp&`_w)RFOTy~t^KUuQ^NlvHP)d~R}Z0c(P-M>jNw zqNH1p^5Ij=MI3fy(m}eJLv2G7XkgC*d_hdf1sgeCTHkudlhh;t$4CQmt%CkLw>w>1 zvd?{(ef0U&1Avq?t93eDo;WR7(Qh{pKId_azPLRFH2q+^d?3RD3n9%Oxo30IPWJIf zpoO{)ZaZ7uBvT-6P$F}1@F7_aui)H=^lR>^BaBJm@gg=k)(meW6-Ge1K%1k`_74t= z8|KmvcsI&bVQzbotXr;OF%CP(=8rA{b3u05khR>^7NwGExf!nzL{x?vb;n21z-}!R znLQPt9rqtTU99}A8W3b(yN$pbR#_&6AJaiv=Xhvaz!7CFlNSSAh*^C_Es;SP=Hz9C zntpo>u(O#iy`FCz@+8&??-ie}JLr{g@IP*4&^dh0*(UBBZ~$+Eeb4PPoewApRKU9) z*R%ieP|F6G%+z~I{c8)AYcRJFVyIJ6wgQNbG<+{bLy(@i2SZ#FWjMmAO{yGV=MG1}| z(p3h+g6$9))t^T`s=r*_bzot{Zah!u$m!jE>#)pC6j$AGS0%gR_4~Q_)1}`cn)U_V zlO{jy$H4A0s4EI!o$Cpx&>0Vl{c;W+T@!N#T^Sp;{8M-zr_2hWoC+VG39cmv1Pu5|j| zv>nX#rX+yf8n_sS6$qOW-aDvzZ1$rYS5iLDPd}`W;Z@zfVbt{`DDFK|592r89mJc; zOqtRaJBz)1m{D=V*QKm5v72&yn0<~W@~}y&T=(2VJCx5W=PD0fuLe6ye=)B?`5#@< zg1x$o!pZA*Evj$T4KhFbFA{Y-tXh^`XWrJD(LP*}a2o+u?0y9h0Qh%^Cddrpvb5IO z%jN!cU+aJ<13ApSkMc^#G6xApmVL{6N2#UV_yWJBe*4V9kb-Vjrr9^k$Zc)C1h&PI z`CWmYfzb*Yn>9(pwjh_Oc{pa((h>Ld!fmc+>gYI+%K;=gDYcaazt$?QSfR51Yqtao z0s}$bkni|Dzbo@*eXj^Rl?*GkRjTV%aLbv)ktcEe-TU(!k6H9;v5o)r0$_ezPkh;G z^}0M4wq8XfqyuUe9v+F03jR)2zB+O5Ppeo*8k_0&GFVI$r?^D`jBYHJy@z$XTeX!O zgnvP06F_BRxOW&$*zuO*niB{`KqKEH$E#3-HPeF!J7qu7-R(LcYo zMw{mOko|38qXu?0&xyB-0$=Qb^^GZhdIwX9Rc<($+0qJI+zq_eCFzSV*UYwm;CEcW zd@tw}DRfoCxK%`M;aEqdzy<24iR<~bZqWlZMI5U0*?qO!tKeks@kP))DbpR0kTD2Z zVixEpOSH$0V^P)4B~DrOY8i*m(8JMhyHfHoLgO%*J;DN}cstetOpgHAAiov{Kq-r6 zHXXSBLKAvBmQDJ`NM-(_Z;H0YvV46O)%FwMOT9n}h1oGBk4S{CUP=j?UyYO2MD+#u zGd~Sy!3TB$$av3%rKb$4j_Ulk`VnAk_4?U?UahE0OHWSGAYXBYhVgrNFwuSHhC`LF z000s0@?CfkvBoj{4xJPtJr&i5Hux7{Xt`n(BKk_5)J1$SqI)$(7Zy2s*v z-C0}<@k`Fyb2G`6Lxir_|ti0=ucG2 zfRUE$V+&5jn(9|KCI&k?p4)K>OyOHotbz>b<2_E-{OhVvGS~HA@R5Ix(s5np$QxLb z8#6n0J+bfuny%C8<58J^LWZfNWn`Kc;@s_~-{;ueVndb7`&8yb)b?sb1g;r|TDq4x zf4N+qshVfzHk<^x;JhBX+8WDVRbc1raeIY|f2$RmY+BDyYxTtw{mTox&tJZY*8(2; z(=zsy7bRfy^?y~V{x~^~*e+v`FeA(y8MdI#z)z157Wi;I`6n?GLJxc}HP z)e;nGO%a&|a=GuT!nPHn2zK~$GS^hm0m);TA5~=z0fgCa=nn1{=|Iz802@RC4eg_` zb`xehZvne%AWZX~1BVqqJ`eAA5{Y*b2A*yC1xZwy8^0RuEMH9)pm{D?1FibV0@pK} zyjCG1!%D2+E%q=|3UBUHm|HQ%?@}c-5HNUXZ8{uo^OaA$L9Lv4S9NV#k`KWYE1Kz1 z8Jz|;_77bc;O(5ox#og6AQ65mP#5utfC<@rYXRe@lMZC5eD}8oRMoFSNPXLPw_;&n zsQ$>#5DH$6P@RW-&!m_Yo_RKnhLMU|t;lj5j>Q4-O-%1G7$_UR52K8e?Z&+dS_mec zM~?4tpRqo)BmITdO<}M2||uCB%<)T;q_(=+^d*&?=q(45vZ8YV|LwJv&Q^bsrg=>f2|vo3w9iJQ*XW?` z?bj-vggZKlv(FWg_y0fE-a0PIZ|xg3KoA5$L=+_i1(8xIX)HQKKqSXPN?RkL6Pp9VTPGG*FC5|_xtQ;zx#8}`G*fR_r2DY-z(O&3e%4_l+U^v zKK^c1Y-N>-+I{AQAQx#%um6TnFtRspA|v!+RQE$Lvav5+nr?w!9&p}4GqT75!rbN+ zRA5csmb`^xpNV5b-cCi5U6y#qn$@`iQ-Y~zU55Cn?1pX)s|?(M^n8l;!FSa}+m9QN z%?|E=Lpe1*m*X~A`F4+$*`sgbFS6)D@x}H5Ete!??6>wVVAS8(-b9CeYur6<$^yae zJu#GE_k+6|WZH5#m%5xeSvq_P3x2CoXJy>H@f;bM%l%cK4?dQqy4~ntrhSruE6rFEm6#OP~tj~5>I#6 zSPD#aXIV3JRC1)&!qv_qwz006lLhZ(=c_d^b>ydfGJ_txX!8p1ds}#XLJO`RXTrJm zB~zS@3~UfBzqYVh8nR(ZtzlD%CnQKt6ET9gPdTeL*{dV9T9i5%lukp*n*8yzIZc67 z!dxEJM&ik>OF}yBul75~{hCrP46~!M4*&RB5>r=9r7XC}#$eG)x4Z;8XV8Y(eg#gR z6SOw8=cHt7{zb(@Wd&s;c}{*>p7S*oNYx z!-vZ+)(^UL5+iY|lZ=#;3RzuMX9*((88xa?lA;e7Nwt}?jbF6bL@i%$j5JaRN(tHGW**}QuXNrmnWUzG^>;J zf%4zA^52MK7i}PYwm_T9m9T`l#`zXei0K>kBdw@~m9efr9uEiSR8c%%EKOW3y-H9s zbLq?W;P3VBshn?Zq(;2tT8xBYq~~k9Q*4|TQc-9UdU<-I+I@i;5&>>*M04~$n92R^JDzUlq?#x6s;XQ5L01}2j2 zi;E4DUl$iAJsbT=0ZB$49!sf2>nksCXUPXlt^LXuN2|gtUB8=6w{}!zOLi(mC&r2F z#(b>SMfr{%{K~a;EWOTHqbXaM5ScA39|rd@(iz+sKoM0o_u#P8!q59#ivAmRrW)qi&DZ^2IQ(Gu6+=vQh z?0FMS*`Z=BM&zpHDurjU6-KxbVe#pOhNs!D8jRwLo_3F?ZIU<+w|(9>N+1uMV5AH+ z7dA9m@V0-8ls$_f#rAjR^PQ>_+!p#Q!=o*_%CrVAaBwuWF=haD`4rn}oF-oj&M&T%0`xal{5SS|=vS7TAM~!!CMl=vBx2i;x zPx~3efmcC!+s`5$A>rkY!TyV1xaRSKn`-<0mUZ4Wsh*4X=(p*x3+jBxD*@dJ&rV^< z5`vQD)zP(hRI#N!S#7f7v+iJd{n?nhU8yXt@p;9SZOwb83U`~(PHGsu z(v!JF{#s;~s_=X~R-{-{Z>iiaoqMh^qTh{y+Z|ED{1@*+&fN-r-Vfqkrm$g zA6!h0d|mcQY-zokM=$%Vlt)ydNQ^xy((U>6z~@aPiz?Ns;?==_(fKGs=jWy!uN$f3 z7^)>zL337e56_I3XiZo-s%phEC{}t@v-!K?$o%YJ^E_FH`s}4`?7m7l+y|9x zIf?S|9V=raw_L~Xp(A;Ua0+9wZo;4f8F3A=w&i-Wk`^Mhdl5O1z5|yI51)1+`fXcv z^mUuQ3%~Pb=O;wQMU!w9Emjffv7{(B)yOGhn|$Rf^i*pBJe)SNT1Jg7oB_2-oBzVjhwg#`4uYDtCkm*J;0=eu~TD% zuL`aSE<##1vGtAB*!$_xWhVvj11Kf-r{~1>D*I*b7Taw8H1aJoVChEvKobCuSr4lS zdmOVwR~Hu~hn5xj3dXD4f1VyI(l>Rq2xs(JUWv3wB=Qtct>=q_Nai(>VrD{-)*ktg z@Bh-QvE>oPZ~7unCt8=>xW(sU%Xa%dU(V7WPu!BN%G~k7mt03N7NnPWE_2q!F_imt_>j4VQGro$UHGj54B!2>AY}Qc55Uccx1O+^t%M6- z+xZGM!Nr}L!fLaUak}%@@m$}(*qTl!AB-G}TJTSq(jD>8ITK;FxEM1(0hdn68b=yY zbE=>GcqH;PuR{I{H*%lyTe+YmiKA!Dy=*_I;?x#0KkxE&GmRMUm6EKqp(lHGdm724 zV^|jo&%x!fg5bFNsM-4D?o#$+_7KG@|VT-~h+`r%7Z&IEo zKfJo-Wkw5$j{5MRGuex58P-Bc;*-P~HNPiof z?~7j{@u9nz=6T+Xcy@SkvCDkSiBFR|lajJ*^0Lw{31nEg471RES0Otj*jGy-`$Uj$ z_IbD7?zUsqi^o();8Qt3SC0>^c#YvTe=S`Y0s{ zLUH_&gv$-dq4mDHtRRQ>*r=(ND6N535dU;2xh{HHg>hoI|39ot6G zO9nT_RY$gk*JN2KJH)6&YJO-p-^UxSB@oZ1zUt<{IZ`x@#|5IqGP1kXy9=D-+E1K) z-MpSk%s8(1rRQb!*0i*PP57o(LXlGph1c{oJ0q4lXS*xl%OSIJ$M^Qz^3vW1%p5Oh z4cM9kT2fJaorflaMsVe>Bh%0@mI18m%tmv>a$Ob#1>c=aJVL~==2^8wcf#yec%^SaD$eSwQbDR#4cs#>r`DY|NuV|=IFRkb zCU^>u_B?jL9enmGR(_hIlz`t%*~PQQDH`_~=}>RoW^>A94NWa9dItD*NYPV;46>*U zHPs=T=umfd+%`WyYbFTIO3z(;LL=`G<@21BsmV7T|^&-21RN{x~oKPC>b)K}mG_+`p#a7C;5^q_0l}p_s_AZs>*skpv z6%;QRcIksES}P!}o+sVS)S&@~KNm1Nb3>V=Szt*|ZPP^i(y1v&>eS&?zhAQi-I{b zHRH)>-K&bkFKa0&RAZlvUg6yaTw%-tZEf}2?&Dc`Dve7j4) ztU+YE$9R`tqNw^t_uG`VFG*~ulkLgbO_5zX>9RI{U$aj-m8cqwy19)=y6<35A?p1& z&+tbn)&;3;_GYD+yiN<`7)LSpscwgh=Z&e)uU-l?E744;I()(t=cECSvGd6_Fag{8 zzMc>Ov3<$OPvGj#u1ot)M|J)2>JV_W=_+v9HPwqsGQA{|BpNey-K*3R@6od;r?yl#|f&*_qLYO2P%aqoz@-g*DHw^J<}I?iT3O?dS7ely8|ePXdz{XcP?H1ZO^F zOUpB=`S*&r;=VQ}N zdPfs5sQ1j|-?-Uw4L5fKZf+j9vjOni)K&Ady`a$d%h5gxz##rf_~BiDNLL|d0OhIw z3{l-^iyH$NRpkN7tBPE6sR%tUmCnI3mSJwk4_MPHvUbm-v5D2WS6QQc^F?%atudLd zgU?4vkTRq`>^Ek)!43LpC#HQCPfauh`7J-zd@e{CV-|2AS}O}A_%(Yd^jrd8e`(rr zF)(GJ!BN)?kDEcvw9$R1-}()!2p0c^I@MR1mzj@Eyb}d>RRn0%A-a5q4U863rP%S= z9BJZ<)RXIiW|@I4Ia~Ws&u!C7bLRsw7KF>g?9|?=C2l77&i5*aAgMWT*!^(NzKqOH znc@cr`Yvw}+7~%}dwKwM2^MHbnLM?h7+bA667Zzh<8{sjwWhW~F~=~OBG2buXbM7I z?$*IQf2-~`P~A)Exbq(SvX0#t;G3bTwAv%!KYgj!s^jizRjOFpW`w~>>#9}J9(|}N z+jM2475@gsoc3xgu0Lqp%Dkjf zjaA<)fA5!-K{lU&M5Yz`ONu_@V$A1f6V?X>Cmd$HX$9~5=J#kBk(-a=AkHPEP9>gd zRl#Z`)?h4xl~YMI!JwbF-9+{|fJN4-7jjOp z8q2@T+ zGP~f0T3mzCrFMoOv21oKNPJu9MYS-K@Twc?*0sqRz}3xY&trRsTDWTwT)`+E0UJ$u zBJnj~7>y5!{$$FqTD#wzSC5G@))coHFCT&?mMp}U+cdnfCf^NWNhNxqo;u*n4AK|Y zL=eer6V8;Yy$AZ-tkPOXzf`^ z$f%q`%-U&?^zXTSs8=JdHI_Mgru}r(mcAIZ^9sAd5aHL81mPd6U#Lu!It?+4p>hj` ziBhVu>UNvtKHKOM2g_4ViM5UaqhfPpDn@}@bcC|!C7#Sm5x1TeFZgz@T>j2K>b7pf z(gvQhS^0rouPZFcO0U%SbUIc>VN}nM1)iv&&KjB|rVz6P38^yY*cfjBZPvMO(4%od z6R~sRdWLZz2F86~-h<|{gKcw+Zty@qj-Rlqw4A9BGJ*z#<)niK-iIQXYW{Jv&l@?r z=zh4SsT$2TK3ufzNOA6CUDzKBe!OAOIvCxhlDLo`QdQh9OtAG0`d1Y4OH%K}hM?z8 z>R!yfxj32YJnLb%cf(%5OjPM8(Lx1E5K&IIT1`5J7?e|Vr&G0P$KLXCp^Ec;wrbQVDSgOvKdS2B*(5y@ z_r1~*B1vo8afho@6w3rWnRhN69EkE;`M-SEJO_}I%cvTA6nx@HV>=@Oa6+S;_CdrX zu^mA8y&+-K8*NzK_D(M;k)>6CA73h!aXF|XLpyv-5x+md95qffCc7(1&ZANtnp(nDwAXukX{yZ zkT5dhcA5*Gb2=Tt3w;Y+fFtYbp7ORe4qzgiFwPH2QgqeWB#DcEhahih*2K8IV?SK0 zyykao5WiFNGKle}7VIX<_dyY29+Dv&297*-0A;@@WdKg=I~PBp(?4L8;#$cjei#p` zz-Tpo64u59VRg=-Y&{dlOVz5%uQ0pX8 zvTVC#HTGJ?&VnQU$QC+n3w9#B*bmf5ZtkI@YA0fRw87zl6TzNed`WuXiM2=$K|in# zteJ60+lt6X!aDdQ(~a(%CgTGbRvgPHw({gF_Iv7eSz|tek6(LKuvKII-v#P(<44%R zKk=-^eZQb(l^Sm~w!6$bmYo`m`&jVC7$_g1ZLkoFsMS%>qFebmS0_lFSbjVZ4lI)n zhlLDZcbUwdmh9UNahE(Ou(ly9EW|Dl7F~~H7&CvQ_jWqOVEFYL)C>2qZ&e8poOeH& zmT%$lLT}njh}6G7_>2FD5YQR%hYuh|JzvM(Bn?sI_V|%f#He2b3(u^fI@ALaxx(bC z42*7)hP_6A>Z-HqMOFEwy|+9u+o(ws#9+xc)vq~{JsPvCT@HxriX3vO-g?G58^ZEu zT>A)I_nxJZ#;L2QUW_s$WyM7|^sg0cKZE2O(uK})J^1*MeBziAZ*g~wW?T5CD9zC$ z0(0KF+Z=AHr4Yq0d!6wT0i!26)IZXNEN}pRU=HM4X+9o|4m@y%0g*&+Ai^!V20ZID z+GXJkU$v^FgJsA+gu_o{-(sS?V5hW+y>r-07%@c~ZiSE0v;n?++(*j-fJ^+iC@?FA z2pkth<>zL^3S0#DYWlUZe7=j3l2ve?8A#5U2i)Mt>$~>MZ(g@;Rj24OA`!oTJ6yEF zv)US@%0X=Pf&@Y7)&sP}@7lZ6(wNqqYWrPuZmb4ef^c!$gZ)Xi;T}5eu_NjU{D@G! z(@v(XO$7|l!sa6b?XX{OhGr#@y7RZ-)jzyBkKEd@nZl`|y09HK&yC1%Un7UGLGe2u za#4@iL|UL;iR89>N&eo!Xc4ocNNWGjW+iBWT7=9vj%$LEQmWQvy!U;pE*Dx?GdM+9jMQ>ZXdzalKiz5es>+=0$kuF^PPTT{ia2n$|X4W z)?_M#ECpY{3lI8@BZhY%O7}WFO=lmx#*4ufP4^J&^BSg91@PW0j z+}MTooLQl%d_;Ko*|xy{SC2B}WR*;OC$dvFEziVU|DZYo!YnWI?XMKXy7iJ9^mtce zlsuldH$cA3+g`F0Xv7U4w?BXMA?;a=@I%n(?O?g!0aW$9()IBCfd@bb)Xt44t#oi3 zp+eigMX`<`LiY|)$jltRLdDckk50M3ciShxoRkc)J@~lqvF2T~;)$j%y-&0_<~M5z zhzu838SqW^6AIk?gw8(|4stoKY)G+M$#3TI(7!5=@XML0p^*h#`Rj?52)}e1vuv!UvAyvyEvVWj9a|ac+DPP2<%n=-3F> zp3M=g#0Tq_ddT`tz*<=J?$0q+V=qpc#6z%j-vqc<;`I0J>1e!JFt5w@0gl26a;C@FAZ1w#|vd`0e z8j@B!xTrBJ`He?+LR$kqJ+>%gK+D)5&)(@Lj+cduP2WRNgtf!q+&lM-!x+wBw4O@? z4_LJIIk2jVTKYkSV6^Vi?uT-Z&;HR?$dDju2oEPqy`|j{K-_m>Y22fUh|=T^?pp}+ z-yq6jU%~XRUTYSALDm4tI`6MVo>dKR<$4%6+szb=hIdsdTK)x3)v6H6nK-25qVA*x z4&IC69#%w3YB4T7rJy%RW_Qodt2F3Nhvahx%98k5APizVdl0c0{s=1-?fFH4m7kqo z8X~OJh82pK9e2C-W1%%EQ#T2PvDRJ6@U7RKbL@aEMw`C4O?w_~pEpl~CvYi^9yc{u zh?ziS_c-Bs)6dh0BRVD5tO*29xC~A)LcNm%0B7JZjC12(g;l-7-RV+V4}R|10YqQv z5Y8PURy&G*GKjiprSJv~RVwXwbd;~r1DH;^2NQo;iO*t)$u~(sv6@3p3u}56x zFEbxS5ApTEbccy;_lR2;^ly|zQ%og?Ed^e_52CrYxtoW6V2mC(K_VE4huJSfi)RQB zbu^+|`8%Lae7Ev=3?eVLVV~U5+Xn$eeEgQ71Y*2B1AM?WyjZnrF#6xU_E%V6=LqoX zf@k6s<1eP&+(diH8uq>K{d54u?j;cp3V#ZWM9%9nsj2sDt2#RGHzPLm8oYKx=YDya zy90dH*jVeGOn>E)1`b{ETni0K#K7hUg)h5%d<+oY^vRd(yE#t_+fP zV2^}ZF5iNOc8N%h4A{*$HHL!zFwZWo2ba4vUoD73Qa5OZ~4i_H9F= z_Iut+&QsBDS5$XAFdM2bv%1sJQ1`E1fbVyRnNh;TBm;R2%^G=5O0ylS5Xu^z-L7E4e%;K z;O6gF_#**P!j9Y{!&S@xU#Hhk zBUtt_QtehueG@4_;9BNQA_IWYhR?j~1KbV3m&B5?R*s}m=K>8oWb3uHcXSNzLEw@@ z)C?^gjq0;g-0+KdFOSjwnH5x)^`SsNfRqn$b%}f7WC#+%rBCaU4ncU|a%}@_fH4*b z@zz62pCy{GDlH<)G1i&NWkfzGFZD6UOsd^QKZgUU==|R5XDtM!zDifuTe1KXFYL@D zc={;3=T?656`HuO0t{Ckrw$R2-)0RQy(D+X3oMYzfYUdT*|auRL4L1 zd(#ZrQ(jq-1g|iGYq%kG>LX$SN{CNWky7e?J&=yvCA;wvh#Ihx}T!mq8!Gz@q8A*$-qn z66jGuC_!Q_wFE>_nYQeCG!DX^Bxna$qVqq33@U0uYFlNzupPiVH*S%?5k z=yf^tH_`IpX@~H(8t;oF9)AuVb zxm(q41fhG3fVY+~59J-zs$7ia2>EURd+1Bt^6a`9Fkc_r73h|^UzL1pchV_R$LxjX zzHKHGM+(Ev6`o9NPU?;wd7}TCC!j%WDMl1ue_gf1!^10Q8rO@b2N^&z!6km)G!SG-PZ1cRqZ*q=zr~nwtS-!u zDZ?=?KO;=7BeIJ7wLQD-Q1Nrf=5F>j$kv5TZvXlP*}dg6AhQ|S2X8J~Wi_m^As~OC3NsJmk8h|QMhKkJ zuS*pJo6~a*Y4Blu0wTk|r2f4dEW@>;HY6Y*KzhB=r8y-F)Z|SF8ZJ0}eu7oLEl?y( zl|oPt@y7%b)Aj@s9(%4l)mJc753BgxFWL5q554+$`)Q@|*Mi9bq~+zQ;jb?QH!|>E zoC)7~{bj6c?U&_1BPZx!N+0;yu{h$dF{DFm4be%+<=nJUtk3ZI26uB=NnVe>GUM_Za(Z{aE;n2W-r)v7$bO4QZwa1--`%(p45r}Nf!4-5TBgM(&MPDo?Z$5n;7 ztM!0Sa^#mvaoP3CH}7nJsNk*=KBYM0mJ?Dts`yLyplydx{7f89KCfObe9T^2AK*x z)(o1!U$_5;J_y@{uX%GI?e;xB30W&zoO$+&? zLGwCq>@#KnJLLy8+#}-W-l!4rxX!?w7CjtkWdsE?;CQb@?p9d5y6ebKT|bnIv5CnS zxUO6f7l5y9>*z?gcPV}(Ui9s_d9ibxvHpyhMUif(1fj@7X`9GX@^X*Cb1^dkDtRUb zt}n;cs*n~Vtt)-gu6^Hqc6UzYvO;;-_xFulJW7+jrTLs^+Kv^a8KH`UBg&dQdz?hA z%G`77VnRPY;L2F86ji0JJ}tP~Rhy0mU42~S)DtZr(h2JF;%gj9)vV;&%gmzg@x1A> ziO*q*)87V4=UFH=ei#_2@MQ)oS=8N~qPM#%0N?kdn+)<(%fRPv$JYaIfot13pRFu# z!PV-4_B;@dx7)y#Hbr$&p`dhH?oDT?sgZ@9))qxwR~*e8vkGOz?D;7=R;T~$)>0PJ&BTmGd_=M)K_MSbfWVJQ2X_^V6_&b-vk?rBP|2U~#&#v{Kc5RDS-)14%PdnJUU}ij(u4 zrCw~n>k}^wlx4nDT^Vb!aUDyJ?C$ImK6`Zww|lIm|J>&!7585)PY4)dj7A*TDHA;_ zfyaKcF7s_>&#MQHZ4AD0%#V$+5a@Q=i!T&`P4}%q37frgmu~!V5VV}_e~5m+Gg6d+ zst2RIZuQk0D9x3O8vgkDib1iQuEdJU^Ll5Yu|ZZCFfxy5!h$V0viK zMuu7zIZIoHnqDN?9Q#839BRgP)@7AY{`1pr7wvX2(zhT6UjE{K-v`R{*{md%J>G8u zRQf)_hjKRDzjH1XIzB6Ug?Ym>{CFK0wt02iYxs=5WXO{BhtYVhKObxKz~S-dF^JpD zB)eCu_F+j=#n`Vga=0pq_D@tYF8AOyZ?klxJtuzS#SJ3As^Xx>{#)&g*^x9|u>MbW z#Lo*LdCL!R6W-DQinqvui4Izar0lMZ5Wc~$R4$%DAo}U^VA$w4NnBe31kS!{QDFBt zdf9`E=R7GqrQ6w*-YfDF1^J~jO&?&+1Sryx&KNTBbRPxBn0miSdr9>0Gtj`>}{x!<6 zZF_;0%~E^k8}%|rv1@WRwcB^ePAVByjyC;BON|#plM5|Fz3~O339)92jNZQclqy#T z`9f^E3+y|JC?~=_i~Bs`s1Vx(+sn-&<7vUd27&ztJI^Uj-nZOsJ)M~0O(o;o?Lw%w zot}-#UUlHb9YUk+Qdasd+^ITp;oS+(sKZvXGY=%IP9J+0(Ry^EN_q0l`?j|Fh=ZpI zZ+xr1EW`9^lDN!AS7AR43j&=Gv zjDOdlhmboKlzvEeM=dkxeqN8AG$htugX~`OS_KejC|D7p6&W_Qe_QD5DOb+p+hmd* z+L#1S39wJBPi(ep33;A&eAu#Ob75lRi{wDJp(^?58uRerVM|(68h0lNuWq&f@$0R4 zd4o~*uEmQDFUaS_uJA}rc=9MHN;-B-JvC4{)$i|iyM2?g@??)o;~UcX$!|SB!xSkt z#WVGCsSkKx8c6yQQ9K7)AH-9Z`@C?^P0pHq?1ql#=*4GD>|q7VODlLBX8+L8&<|G1 zRBjEg>q5Ppxls$u()dOoPUH;T*f_3{)q2O;XJ|ii@8#6RQ2Oab&6~a_k5`U9>IYn= zInj}oMTQ%pyDo)S{>0Ba z3~tS`b;;ye{+P`oi9c)-3k^p;)1}0w@3(h59{{W-G7@XVm)T}x?K?)_TvPu^@#!WUqtjJ!{NFJz?i-?$mU z@7_LzK;GbLP^lf@WaG~|zi@I+6yRhA5dwZPzcH<;hkm(y;pbC>yS$YKl73U|xcFhy zZ~^@6dyYxh2KEVkr8zoIrQHsg*4;8PGA+e3Vata()ZG}U@qW|0#@aI?tr|+d%fRJ) zkulC?c?~_zyE8W2Bq?STsjrge>m;)-#VL3^4_F? zow}py%>G1+FFBCx{aOTmpHv(~J1R1(weLqdz{2dn_rmdPDMXe&#p59-Ab%#gkLQ#W zBK+HE$ugdQl4VFw4l|nX$^so7HP7%B421tHy$&gmu(UuAssw-rJ)82%zV_63-*ijw z@7G>Pjfn0#I(wc}5`P7MSCF-Mc?ll}_v2|imqa;Z8Vg>g#`j&X;o*Krz<<9ple%Zh zpmRiqAZOdKr+xXvq>x@5F@15g`F%h9r^W_<;+SpbwS;(;&WBq@t0Zs(sBHZ8ermtp z%u~ml$_5|J#k-+pdcHSLjet-UyN?~|a>0^~R4}~zuQw{g70??ueFjtiGc(KyQF@v^ znEfZ|3~4NaN=ndvB#n4dA-V=BQ64`CG)dx#yp#`(){N@c5kYzZhC_zKp9A-N0L2mF zrU<{|-wI~fXMld-3zRmx{e13>arqxa&=^<>d(mKWuKOh>`Up z9f@5LCa(`elT9S)Xb%~HSb9_c`i2v(-!9xqH{5Kh-{+{G!b}-k=q9{l)_83ns4Dew zccw;om1UUna@3|8d0Mc5e4#TzaOZKorI^`6?Ji2}M(_QFkt$Y*!|`%9#{c)Zri=fQ zFJ`OXj=jQ@1d_pL5JxPF4J7XJ^fHK+ZjPpS^;fKP9On7fTCF^iHyEbaBlwYp$G3=k z{k7dAW1D(_1i9|xZE0G;Xs``&la+WC&)q@>f!2ED@_UsS=M!6}UumdH3+@_!yuhk7 zyROTl(<&M8jsx+BgFL{4sk+Kd)QbL^s@(3TYIUA#?viOK`p#km+idE12K5VsDWVwv z`LqfEww}1?h2Pkx%jVB9D>`$8LS?eqDT(}98NX)%5JQKQen3K14ZBrZ;sf}SZ~8LMz8Uh|steCRJ&?-o_nG%s;iFB;HJ;P$Jf2w6));*?t zB!PGT8{JejQ!%M4Umro3`EfFR%w6^BA(<@sG2XeE}dkms)A_nS{A7pz0j0I74mOT$ZZ{Z5`x)=qp8=3OUV{OBGB=8>JW!+jm3p8udP z&jzjSWV6O0GbqPOG}VyI8lc;o=;5sIOE z#}%O7g=yt(%q(<`Vhle~%NP9D&b|?_a(rER-@iZ=gy(heS5+(-aclBggDr zm-zga+9R_J=e`Er;R`&e9$xjXdkR80rR8sedBf3;!_q%+5XfEz@sl&D1?TjcU1N{) zOe(0?24A`p%1>TeFMbXPSEan>UUD&VL{nDj^BPr1<~_u4NLV}`4CmYDON7f58w$g* zEW$5ppIBB zx~Pu=#-Ju(G(VW^dhD$vbkF-Zls*f;slQbP>QcEA-gB&qlZQ&D%OcjR8mWaJ z^7?wX=Kc%Tb9}1H-!5fd!^BH;$g^be9-D>evp}NW<^wi5n!C$?LHw(D))K4YqDkV` zX~`NMJlbAkwUr(TSm0VGyOu)V?*9AtwO`0OnYYZ1wNeGaycd0au@J%3!gCeVdf{k5 zj^|YOmn5yHmxsTaEz4JehaFi`A;vebVfv{v=v1xFY%7xk^X8?Hk679(I{LaNMPozT zg9UD7fdL}}y}U4-TcdKii_4p$CB8BYXoQ?&(g!YU@4aa*p^vSi{=tiH*AgqpNlVtH zp&d{nE~JF~7&s=1-Yxi6MVck338`}FnKmQo`-FXfF(TB9|H~>D;7UnCD#PKiP8UZn+A61R1dXMHnN%=Rf<>$~HYz zs!{1ggS@TX|A=8s4NWX;)tB5<%u?8)2JrujfMKt9TKmQILR7b0mzw%HfJ!zyV{Prs z=jI{?sSW3>{QPkki|YWS{f zp^VJ~5D4o9KC`@Y@^vrYd!pf4RRAg4A*+zJDU?U?tdOqvHA}i5FYgg;FUe?nvCB-`YPkv_tRZIBXRY6|4BC-_|?6uGb_1$hCX!5OR#Qip4@u0 z+Kwi83ww9j|E{+4<3AP0(vd(!{D0)5r_CzYN@MBMNH+QkKbzDcO|c#MwJ{#$tZ`4r zMQ`))1d@g#QAuTG>Ss{_GLE${HJl>ll?kBi*bQQGZ&5F z1`p?~-5F}CjezAP#Vv~7sEbO^T)oukE#GO3|I#KqRqj1^(X3?F)V`-E%YCBASv^tL z!}U*_i5=3_0zDs2*%E_Qj7dmK>{{hw-S5)(0e3icz-b;uM3R+gfM6?wI~UC%-Bo$V zjt{`-8z`{hv>zODuD6&C;oh(R3(GSgId3lLZFx8&0v)*s=0m<5&|iC>cmr2(Die~* znO)`)M^X~nNbN#oF%Oy;{=4ED>S!u)sT=n%1^GvGzq+11Iu60Dyhl#>#9YAst|Y#! z9Nc^%9rgdYE`+o*Z2G&M;Slq$vLUTxty_)t7D&0hqOaM=JE0(XY4>`%d8G0x%pXY$ z4z?S^MvwVyG?IsUnmauU0#CRB^>-dHXpV(yh_uz05J-J(M(mrHuz`PI^s|V2ixaI6 z(gICPqeTMi;qc!3f7eg(tK%5M-O!i|ebVRO+*9KK*Wq~lsxz(O;So~siT!fi2P*$- z70sY+NS`!!95kr>Isx#jG`~mn!u_Rn8%g;6gw%by1C5!Rp}{7XB7DN=6BVxqL8qr7AVT8*(! zQycQ`Z{?MK>$9r|Dr%`fGo|&Vdk_ImNPtH%)$Wd}g_2u}s2yB)&EJHiCe(fI5AUnS zz8cf!g^g;f!5T{xUmy(t*Ra)px7vJNi+Tz@=8i(`%}d2eQUYl5u^pqtf+UrYQ)2hC zG>lZYR%6>F_8?6n!O-+7X{UT9Y8kfrP_z)C?JY<;n=-biWIRP)k>b}=LW2{4*O0vY zmkmC2)QblRT#Y_R?M-KT-7?#(4wODoPughRS*MYXs#~X7tmA+%ox%l36RRc^G9@Fl z=XjvXeY2Rcna^MGDo=cv10S`YwJ?$G^WG@$J`mVqNS;OA zy@m9*r6H%&)ZQXZX@Ods%AvGcJHKpx=KwlF;m-s?Gj{ zxe?cqzO5EZaef_Xd0i>HwM|S4A7b(B$e@EHqZ-yD!}po}%Q0zpVSdGVT42R?65eoZ zl9SqV5*lTU9B5s@5EA9ErB{O#p=OCUcOV)y2KGJq>E@d2NVn6wxs(=Y=KU2d1YO*| zR)U1{vt!Nmu{zLq>RE4hW15}%<4@<)M8!iVy8G$_;}bKFRvhf8$7=Qnl~1*F24+Kj z?7fDyI9fdC5%X=9=b^Hb_LzlBA^+6@|LZ)^DsM^t@>lz${(ol{!7eYzB5~t+EL(wu zY7k5sc(x!}W-jpfZm(zSP)oW^T@n+&m>0%fM5#F|2yblgG`)pp;{ zw;3Rjts1RKP8STwk1&l;^bNIyLBfJH@?QycWbpf=-$0B z?cHs?GYIZHG=>|fU&Ew97kM&YjZ6tteyDo|B`Wp1i!DxMV~MXejV#E6rQlP}ruDoY zD1fIQXJ$wYr1g86BOSY!!3{)ey*}CiXgn4%AakpVFe>t)% za84$#+repQ*iq8{nhItkoRc-+xlmrTOuCgv=(u;mQFaO;llGbO5m|CiWHwEqo-|r( zZ>=1CQohg7%t|}_Yd&HpUT#0&*r#1)>0y4LM?W#2>zu-O&NLzc-Gx%qLRbZ-q(`qc z7`D9XiLUpg4wqaXVRecbETS(JMq1hEeR5wT2OxjzsTcxsp@nXI+vZ@j*-oRRn{*U8 zyafz3EWbP$+7bDEM0$1GP&r(WC7M%d%}2&sa*p-*u3xfNNEy>g9b)Qhdf*x;MN;n$UE@9l-8S}uvKd&v`kyTkZ;;8{%*}+N4C7} z^3v4{#3)*~F3k%AjeZ$KS_d5%N#ZwVV-x8O4+2S6>eikiG7&Ra=h7o%cihO2t{fb2 zD(bduIO=q|SxIi|Uvp*tJZ0^9Y9dTu0kb}f%y^4RiXFiCu3tks5kII0y=hHKw0*;1 z1^{QqExoCi=^FTa3dO95sauo>_l;Gq2x&D2beR-?Hc*v>P-wM$@(sB|Yvk{_3cq#j z4|lFX=^u_eUaQP4K#qN_F)bnLcO8@+`(nH}%6e1$N4}sm>WjSwy3qnz7C8c@maf?a zj>Tmrb8be~PemjvC9=#5pY=Fb^z!!OFy&{Me-r2*QvLZ(8Y+JvCwk#D!s2s&E&29_B?Q!!>=RpJ+6?iTok2S{mbr zHZ?EbN9FvR)+vLcwHafHL4Zzc9bt$)dlQv(Zn5Vg1Y4m6_I=$toV3#vf7&JfaqG12 z)&myc%7!%Uw;Y=Xc4|1GXc|zbagObBP;__6DBJl*J7}l$N}|W*)Mq6%E43{CSJKk{ z5UmTK1I{mqw;e!>*KQ`ubWx9%u>alwOoJ-SEon<{rUvIB=sGPe@DgtdF%CNLmnNap zjPd^Z|3oW+(RJ;{SYPf|%T(fiLgaAP05SsM zk2wC1@6=v{d!Sb(aEXXMH8GF4GhX@XFZ@%zZ(((SCDKP)Rowq@;ak-V6sK zxZpsc~_=|MhSrbbkF=36}1nLm7@c zBF1t0BV@8)r582$M|r8-TsiO3uz#Dvo&&#{T7NY%|M}cMPx$j)&>NbC;yn039RaTT z2yk_#44~o)%#v-+p(MNiWX<{6&IjF3=WZ{1xB5iCD11sD38%WY8U59I`qvVFzaU{J zcuVM8!PzD0nipP<#$8;+@ix*9)gtVYE>`o<)BSO8IBvSrZ8khDFov}Bo${Ypxo!i} z{v%leX=uf+gIVU9rP0AJ{)IU77afLs(#Os3os6DYNLlNK{-<+(qF8u|7TNs|A^Bsw zq4SLcgAuZE@XQGe@P#=CXXK1lqdnYU4nqjq$-Ma4-D&>qGL{$Od>Kb*({0EX%rQFqemIE3Py1dz34_4^=6}y-_%|YZ)8zc>O_8&$49hb* zvj$p7!BWn2Hix2Edo#0W{NROvLOWXH6K%O_jC2%BYa4oMZS+{A-p)BmI?2s?2=xBn zdCnLQSmiuzFTQV{wD%OT_UoK@@X!C>S3lo{ir5ya-hN=9UaGbLETM^Bro@ic#1&{` zYOV-XLW0tNv+!TPQ|kbP2(d9a?=!LXo)0$HFvlNc)_fYfv)!tfr4osRwR&=ThUsT{NeOjK{r` zJk?ksv`xw0|GZo_#571ji4H6Y*G+0f6RjB&pQZEz($kTKucA0Q#GM+lh(h#mJzBk3J1PmM)6eLv0>ug2b&j zE~z8&Y3F7dGL|91gxlFnHlf6`>y<>bN-CdP|Q=z+#kn6pMTsGn(RC=7p zl;8-_+Pg2a5f*ZWTwxfn_MPRapS}48g*rIjn#%c}8T4lu-S$je7DnMs_rA^w{la$u zL00s$qsH0^KH7TMvol_wl}rgn`%0_I#9}VN`NOy8X`%nR_50t5E6O^oPGyv8GnUi&Qg~Mxenr8tcP>lURC2za+l?LDsAlh^5I-K$f-wuYS*9Fk2j0 zN%5%?nj&QJx4=ydC@J3*n+zqBqRTrd_b0u4eT%J_A~-lKCJ>)jq<;1727~&i-pi={ zb3^wbv*teytwtDCB*fKUh`CG5HjgvX;Mz@9;_ZahxNw`|cL(`T-+xYC4Xe@5;OAa7 zt=YW%d@Yh5Xl0I#djg#2<~v}nx-X|LGXgUReiP17EDK(UoeO51Kubq50M&%2Q>3tRbot4Vxm7P{r~PyVdj(|zO$lkNo= zxjyjfVux2wQRC*;1EFQdW&T|$71>?&ol@i{ZE<*Cz&gDlZ}j?PZvC%zNDR$$^Dc~<3vo>=0cMhES8SqQVLUh~fQKgmgKmc9Ub&*Eq0Q3tjh$|ZJp3HPZ$0{AHbxa zLOagruDN3>BU~1HyKr7Uo6VNXfwi(Zp8QO%zK5<^k*l!}yBdb03eJf zDxVe2&BM=lEL#|e@Yuxxb~9c`Z$v}E2gtL%(E9^IQb$Lf*KgXmPcw?lHs_Z zD>_E=UR!B(Lq&*?<7!gKVfeQ?6Q{wX-R&1q{SAzpOm1U}(ck;4x&iGtnk2tv^}Z(Q z_OCSN#7&BA$&9%NxpXoTG@Wro-C`%HCWy6iOF#PZI0gMP1%EfV=QB`@l_F2@h)zFC zHgBvMnOc3c$S=K$DjOJG;*Um*?|rD|dlU30Y5(lg3*ra@%`+;q0n2@(j?T7gibf*XVx0v+>kGewWqhiGR` zw@o?UR=6sD0!vI3Of$xYSAM_akNkZQV(PSPCKI zh>`5@b~vuF&)19Pk%2`Gu7KituP%LrJ6FnO@=KHM72cJ9Q`tce7N}7*7m+rzel8!|H6g zYekNkJU!IpTe@2Z=fT{U@IWb&!5DE#Gb1;*Ky3}>%pgkG2i=9d1|H3~4pgbwfv`oa z9Oz9`1-)z?ukQRbe;SW`ztH?tOFqR8MiVoCrQ80mv-FSVha#t@2hvmkp)wd~@P_qh zrVy7Wk-DtSKsWhpu;R_!Dw}7wJifa|OWjXflk+3pc={`gC}d~?MXBA?KqlGH

    Nb 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 CyO6yA literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-enrich-autocomplete.png b/docs/reference/images/esql/esql-kibana-enrich-autocomplete.png new file mode 100644 index 0000000000000000000000000000000000000000..15b95c650ea881715ac2acc5b2bef5357e3e418b GIT binary patch literal 69124 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-enrich-step-1.png b/docs/reference/images/esql/esql-kibana-enrich-step-1.png new file mode 100644 index 0000000000000000000000000000000000000000..22f71d987d3be02e38440d234e86dc2c47dd1b81 GIT binary patch literal 126404 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�cN0_M$<8&CfEZ7XI|2sqQ@y^^ua>~hR& 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-enrich-step-2.png b/docs/reference/images/esql/esql-kibana-enrich-step-2.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d7f5c6a05e40479c97cc05fcc772221e72096e GIT binary patch literal 50848 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

    55Cm*j}t3iomqeS%r?SbuF74SDVi5W===!TNR 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 Sb#V*$yL8^{T*28}q5lPTatI^< literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-enriched-data.png b/docs/reference/images/esql/esql-kibana-enriched-data.png new file mode 100644 index 0000000000000000000000000000000000000000..85df6a629eca910fe48b33fcd11c243d55052ea8 GIT binary patch literal 275790 zcmc$`c{r5q-v`{HY@w1QYg95qS+kB3vJPg(&Qy}@vW;y_lC;{nI=XIUm_56H4-+69isLjT5mgT^K18h3C zZkil8z)U-EfT5G=DDX+_Ql=B|a?r~}`^JI7PQF>-p9Fhzo%;p`2SC7krUMKI&mK6k zdkFAUJIMR*_gV)p9XR~g=Z6j)2zNZd@Xs-Z!0YZK26*m{`TLb2?&$$W;J=f=^U1q| z|BM8_e|P9#BbjOM4*%;tLnmP71 z3%k#QIwt3rfw=}8VdmcE2Kq{N?yiz|?z!K!m-KV>*geky6+b25t*gEF9U(tg7dI~@ zKULwsj!**L?|v*LEcDkQ-p;DR<_1PW8t$I!0Dk zf2zU{yuCe?q@<8Yq$E;C(%sWR>Z+omqSO^>DQRg5;0OsXe>d+teiCk8B7dLcU+1}L z?`7xd=;7_??k2Q*-aB{QeY{nLg?A_V_v7#DwD)uT&rEJ!|J)XEgHpTSNL`h@BK7Zc z14C7IKUFev^s{%dxasH$tQjze+Er<;?gEWcl_sSUu4yYf{xp^JtcW_~p(fd@NK+6jK=?`Yj z8^l}i6R_i4TwJ`q#>TOCxvyUq5*EHB43GHmi0%3dmRn|mAMPGz(8T7zW*KIWTiz=1 z_i}%8$G0?PQAd8eQ&CEJsxws?lVqdXLLeiwYl@jz4>1WnI?aiD5D2+4p=6>c>S7$v= znb6$pCH{S!&fY%ieqXVt-dk<9hcSK|^hhDDHh-fg3!pt1DRf-(5RCHjL-<#teNA~>XsbneUlRd0$9%0Dc-Y;& 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

    THe)pX~^L$E6T?eWk{%|2DOj|II*r(RA>5zf*x) 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%0DwjRBBEzaAW(powpI`co}cQ|b)dmhJ?} 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

    d(9EA@-I2O-2~AbnZ9}dS$8B-wb7hJ4poHyCX>I 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%0Ax1!TKLpfam~y@;Td(_MNxu*EAxX?Bs7fv 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 z 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 K86L&lA;A_w;z`~jPc@e*aUFXA0)`zY%YM1F|AF z{=05q4|2>Z*~bI263(kzcHewWfD`gE&!ECsc4wpK^VYPU_4}(}?t`UfR9!p8Uhlhu ze@@ol{|eS6?>c^We+tjz`&@k?&=#G%x1537T^l=8vuQjqElJh;@mIL@i{Me-9eZ-L+*+U}KH-yXg8lJyDSkiI{?9*G2|QZn3M0k(-=gChIt+qDYd!*#z&Hs3#&azu6iH2yT^(h7l+ zpTs4VG*LoNscr1%=foxYpimMZzdVr@wH>`jhT@oiQK%RHtST3ugHR^sBb&s2dC%cs zU1|~0OSt2QQ@~XKXhCR6K#TNr?Z$qak^Kcy zl<2q6PVJl9Q+vWTf9^@r!J-pH&pD3@#yvdOmF~C7pQgVaZ$Dmr?UWGcH*p2Ew-$a{ zsZ4If*kBWs{YSo$a&oq^UjKjElOo~r9Wl34zxN$|l>AFn>s?$Qs1J@vYLPzb?KpQ& zW&f0RIG-w6o={p^YO4BjTN-rv)QN&M{7Lfv*G6I##-rrunyu5IW@^)~dvfo+Ie+(Z zy{#+Vqcc7vK=-ZxAvQgNCGwE=)}!8Dd2}mBGy5X#LSNj4DCzUfcdc>%Sx(m};I!B@ z6;ldn<-e$Np`SvBYjP6$U-+#$fBn{^^igFtGVpvC;cV`t5-x90>i0>Sx?>7j2dwp| zk7VATzF*~J<@_G9*(dgKB-O9EY3B01?=;hjPPvQoZLuj?;57ApLW?)0_RXM`A z;_po|^s?2G*jueXB>8gexFX5_7-zRjTMafTlD2Hw_)h`3;fJR zD-x*B2g$q904U3Ci_iM`Z_Dd?g@L{f)d1@dy;)A?x{B{9?8nLvxq?9~9!prvEsfy5Vk-|yc`yFub{fPT6$YvPGaHnmt+~g3mX#?pQe|+(e#60Vi68AaF}t6(zg-a7TW%KNxuv034vq&?W>2)26-Tltj- z7U=8uqcSe$;1wBs^AA;LF#1RAtufAnwz}L1ra-7}_Z~G?7`WQ_fOe@D`OB I;ZP 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 literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-kibana-in-line-editor.png b/docs/reference/images/esql/esql-kibana-in-line-editor.png new file mode 100644 index 0000000000000000000000000000000000000000..14caf02e60ea2a9401094a0c24729bee19a202b5 GIT binary patch literal 366539 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%0AplEe4_moSzcdUM|F- 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 rf-Y4EX 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 TgCrouBS}jrh?j{Pz5hP|)XOWP literal 0 HcmV?d00001 From d2f97e7e884afac506381864a4f3d32335e802b5 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 26 Oct 2023 12:03:40 +0200 Subject: [PATCH 125/203] Enable query phase parallelism by default (#101230) Inter-segment search concurrency is enabled only in the DFS phase so far, and affects knn searches. This commit enables it by default for the query phase too. --- docs/changelog/101230.yaml | 12 ++++++++++++ .../java/org/elasticsearch/search/SearchService.java | 2 +- .../java/org/elasticsearch/test/ESIntegTestCase.java | 3 ++- .../org/elasticsearch/test/ESSingleNodeTestCase.java | 5 +++-- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 docs/changelog/101230.yaml diff --git a/docs/changelog/101230.yaml b/docs/changelog/101230.yaml new file mode 100644 index 0000000000000..3ed7eacb3fce0 --- /dev/null +++ b/docs/changelog/101230.yaml @@ -0,0 +1,12 @@ +pr: 101230 +summary: Enable query phase parallelism within a single shard +area: Search +type: enhancement +issues: + - 80693 +highlight: + title: Enable query phase parallelism within a single shard + body: |- + Activate inter-segment search concurrency by default in the query phase, in order to + enable parallelizing search execution across segments that a single shard is made of. + notable: true diff --git a/server/src/main/java/org/elasticsearch/search/SearchService.java b/server/src/main/java/org/elasticsearch/search/SearchService.java index dafcf45454aaf..6919cfdbc00b4 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchService.java +++ b/server/src/main/java/org/elasticsearch/search/SearchService.java @@ -224,7 +224,7 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv public static final Setting QUERY_PHASE_PARALLEL_COLLECTION_ENABLED = Setting.boolSetting( "search.query_phase_parallel_collection_enabled", - false, + true, Property.NodeScope, Property.Dynamic ); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java index 78ea21f117114..832902f52deb2 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java @@ -2075,8 +2075,9 @@ private NodeConfigurationSource getNodeConfigSource() { } boolean enableConcurrentSearch = enableConcurrentSearch(); if (enableConcurrentSearch) { - initialNodeSettings.put(SearchService.QUERY_PHASE_PARALLEL_COLLECTION_ENABLED.getKey(), true); initialNodeSettings.put(SearchService.MINIMUM_DOCS_PER_SLICE.getKey(), 1); + } else { + initialNodeSettings.put(SearchService.QUERY_PHASE_PARALLEL_COLLECTION_ENABLED.getKey(), false); } return new NodeConfigurationSource() { @Override diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java index ca7cb72417fac..b5ac94b53d3ca 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java @@ -254,8 +254,9 @@ private Node newNode() { boolean enableConcurrentSearch = enableConcurrentSearch(); if (enableConcurrentSearch) { - settingBuilder.put(SearchService.QUERY_PHASE_PARALLEL_COLLECTION_ENABLED.getKey(), true) - .put(SearchService.MINIMUM_DOCS_PER_SLICE.getKey(), 1); + settingBuilder.put(SearchService.MINIMUM_DOCS_PER_SLICE.getKey(), 1); + } else { + settingBuilder.put(SearchService.QUERY_PHASE_PARALLEL_COLLECTION_ENABLED.getKey(), false); } Settings settings = settingBuilder.build(); From 41ab9495ba8ffa9f11df66434691fad8e1a3ae30 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 26 Oct 2023 12:33:48 +0200 Subject: [PATCH 126/203] Don't fork slices creation in ContextIndexSearcher (#101282) We used to have to fork the slices creation with Lucene 9.7, as slices were used for knn searches too. With Lucene 9.8, we can customize how IndexSearcher creates slices, and rely on leafSlices retrieved via IndexSearcher#getSlices. --- .../search/internal/ContextIndexSearcher.java | 31 ++++++++----------- .../internal/ContextIndexSearcherTests.java | 4 +-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java b/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java index 1733029af9024..3c69db98c7588 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java +++ b/server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java @@ -87,7 +87,7 @@ public class ContextIndexSearcher extends IndexSearcher implements Releasable { private QueryProfiler profiler; private final MutableQueryTimeout cancellable; - private final LeafSlice[] leafSlices; + private final int maximumNumberOfSlices; // don't create slices with less than this number of docs private final int minimumDocsPerSlice; @@ -150,13 +150,15 @@ public ContextIndexSearcher( setQueryCachingPolicy(queryCachingPolicy); this.cancellable = cancellable; this.minimumDocsPerSlice = minimumDocsPerSlice; - if (executor == null) { - this.leafSlices = null; - } else { - // we offload to the executor unconditionally, including requests that don't support concurrency - this.leafSlices = computeSlices(getLeafContexts(), maximumNumberOfSlices, minimumDocsPerSlice); - assert this.leafSlices.length <= maximumNumberOfSlices : "more slices created than the maximum allowed"; - } + this.maximumNumberOfSlices = maximumNumberOfSlices; + } + + @Override + protected LeafSlice[] slices(List leaves) { + // we offload to the executor unconditionally, including requests that don't support concurrency + LeafSlice[] leafSlices = computeSlices(getLeafContexts(), maximumNumberOfSlices, minimumDocsPerSlice); + assert leafSlices.length <= maximumNumberOfSlices : "more slices created than the maximum allowed"; + return leafSlices; } // package private for testing @@ -238,15 +240,6 @@ public Weight createWeight(Query query, ScoreMode scoreMode, float boost) throws } } - /** - * Returns the slices created by this {@link ContextIndexSearcher}, different from those created by the base class and - * returned by {@link IndexSearcher#getSlices()}. The former are used for parallelizing the collection, while the latter are used - * for now to parallelize rewrite (e.g. knn query rewrite) - */ - final LeafSlice[] getSlicesForCollection() { - return leafSlices; - } - /** * Each computed slice contains at least 10% of the total data in the leaves with a * minimum given by the minDocsPerSlice parameter and the final number @@ -346,7 +339,9 @@ private T search(Weight weight, CollectorManager if (getExecutor() == null) { search(leafContexts, weight, firstCollector); return collectorManager.reduce(Collections.singletonList(firstCollector)); - } else if (leafSlices.length == 0) { + } + LeafSlice[] leafSlices = getSlices(); + if (leafSlices.length == 0) { assert leafContexts.isEmpty(); doAggregationPostCollection(firstCollector); return collectorManager.reduce(Collections.singletonList(firstCollector)); diff --git a/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java b/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java index 3f7d67d292761..120982bea36de 100644 --- a/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java +++ b/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java @@ -564,7 +564,7 @@ public void testCancelSliceTasksOnException() throws Exception { 1 ) ) { - leafSlices = contextIndexSearcher.getSlicesForCollection(); + leafSlices = contextIndexSearcher.getSlices(); int numThrowingLeafSlices = randomIntBetween(1, 3); for (int i = 0; i < numThrowingLeafSlices; i++) { LeafSlice throwingLeafSlice = leafSlices[randomIntBetween(0, Math.min(leafSlices.length, numAvailableThreads) - 1)]; @@ -700,7 +700,7 @@ public void testCancelSliceTasksOnTimeout() throws Exception { 1 ) ) { - leafSlices = contextIndexSearcher.getSlicesForCollection(); + leafSlices = contextIndexSearcher.getSlices(); int numThrowingLeafSlices = randomIntBetween(1, 3); for (int i = 0; i < numThrowingLeafSlices; i++) { LeafSlice throwingLeafSlice = leafSlices[randomIntBetween(0, Math.min(leafSlices.length, numAvailableThreads) - 1)]; From f7e39817536e77611d5380b9862e2bd6915375e1 Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Thu, 26 Oct 2023 12:59:08 +0200 Subject: [PATCH 127/203] Make DISSECT parameter append_separator case insensitive (#101358) --- docs/changelog/101358.yaml | 6 ++++++ .../src/main/resources/dissect.csv-spec | 7 +++++++ .../xpack/esql/parser/LogicalPlanBuilder.java | 2 +- .../xpack/esql/parser/StatementParserTests.java | 14 ++++++++------ 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 docs/changelog/101358.yaml diff --git a/docs/changelog/101358.yaml b/docs/changelog/101358.yaml new file mode 100644 index 0000000000000..3ae2a44e15e5e --- /dev/null +++ b/docs/changelog/101358.yaml @@ -0,0 +1,6 @@ +pr: 101358 +summary: Make DISSECT parameter `append_separator` case insensitive +area: ES|QL +type: bug +issues: + - 101138 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec index f4eaa44f15408..8091f7a18463e 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/dissect.csv-spec @@ -48,6 +48,13 @@ a:keyword | b:keyword | c:keyword | d:keyword foo 1 bar 2 baz | foo,bar,baz | 1 | 2 ; +appendSeparatorUppercase +row a = "foo 1 bar 2 baz" | dissect a "%{+b} %{c} %{+b} %{d} %{+b}" APPEND_SEPARATOR=","; + +a:keyword | b:keyword | c:keyword | d:keyword +foo 1 bar 2 baz | foo,bar,baz | 1 | 2 +; + namedSkip row a = "foo bar baz" | dissect a "%{b} %{?c} %{d}"; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java index 418e11f248ab7..3b1ab30f0bd80 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java @@ -110,7 +110,7 @@ public PlanFactory visitDissectCommand(EsqlBaseParser.DissectCommandContext ctx) Map options = visitCommandOptions(ctx.commandOptions()); String appendSeparator = ""; for (Map.Entry item : options.entrySet()) { - if (item.getKey().equals("append_separator") == false) { + if (item.getKey().equalsIgnoreCase("append_separator") == false) { throw new ParsingException(source(ctx), "Invalid option for dissect: [{}]", item.getKey()); } if (item.getValue() instanceof String == false) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java index 5047688610120..985127731a0d1 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java @@ -613,12 +613,14 @@ public void testDissectPattern() { assertEquals("", dissect.parser().appendSeparator()); assertEquals(List.of(referenceAttribute("foo", KEYWORD)), dissect.extractedFields()); - cmd = processingCommand("dissect a \"%{foo}\" append_separator=\",\""); - assertEquals(Dissect.class, cmd.getClass()); - dissect = (Dissect) cmd; - assertEquals("%{foo}", dissect.parser().pattern()); - assertEquals(",", dissect.parser().appendSeparator()); - assertEquals(List.of(referenceAttribute("foo", KEYWORD)), dissect.extractedFields()); + for (String separatorName : List.of("append_separator", "APPEND_SEPARATOR", "AppEnd_SeparAtor")) { + cmd = processingCommand("dissect a \"%{foo}\" " + separatorName + "=\",\""); + assertEquals(Dissect.class, cmd.getClass()); + dissect = (Dissect) cmd; + assertEquals("%{foo}", dissect.parser().pattern()); + assertEquals(",", dissect.parser().appendSeparator()); + assertEquals(List.of(referenceAttribute("foo", KEYWORD)), dissect.extractedFields()); + } for (Tuple queryWithUnexpectedCmd : List.of( Tuple.tuple("from a | dissect foo \"\"", "[]"), From 7b436bae2c5f5ce0931270feca755ff37d8d4c54 Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Thu, 26 Oct 2023 12:06:15 +0100 Subject: [PATCH 128/203] [DOCS] DSL: More visible tech preview tags (#101313) --- docs/reference/data-streams/data-stream-apis.asciidoc | 2 ++ .../data-streams/lifecycle/apis/delete-lifecycle.asciidoc | 2 +- .../data-streams/lifecycle/apis/explain-lifecycle.asciidoc | 2 +- .../data-streams/lifecycle/apis/get-lifecycle.asciidoc | 2 +- .../data-streams/lifecycle/apis/put-lifecycle.asciidoc | 2 +- docs/reference/data-streams/lifecycle/index.asciidoc | 2 +- .../lifecycle/tutorial-manage-existing-data-stream.asciidoc | 2 +- .../lifecycle/tutorial-manage-new-data-stream.asciidoc | 2 +- .../tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc | 2 +- 9 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/reference/data-streams/data-stream-apis.asciidoc b/docs/reference/data-streams/data-stream-apis.asciidoc index a25c5728be597..d3580ca4448a7 100644 --- a/docs/reference/data-streams/data-stream-apis.asciidoc +++ b/docs/reference/data-streams/data-stream-apis.asciidoc @@ -15,6 +15,8 @@ The following APIs are available for managing <>: [[data-stream-lifecycle-api]] The following APIs are available for managing the built-in lifecycle of data streams: +preview::[] + * <> preview:[] * <> diff --git a/docs/reference/data-streams/lifecycle/apis/delete-lifecycle.asciidoc b/docs/reference/data-streams/lifecycle/apis/delete-lifecycle.asciidoc index a3bdf20b85715..fd481d7ca4815 100644 --- a/docs/reference/data-streams/lifecycle/apis/delete-lifecycle.asciidoc +++ b/docs/reference/data-streams/lifecycle/apis/delete-lifecycle.asciidoc @@ -4,7 +4,7 @@ Delete Data Stream Lifecycle ++++ -preview:[] +preview::[] Deletes the lifecycle from a set of data streams. diff --git a/docs/reference/data-streams/lifecycle/apis/explain-lifecycle.asciidoc b/docs/reference/data-streams/lifecycle/apis/explain-lifecycle.asciidoc index 1ccd36f5468e2..a2609dcb78ecf 100644 --- a/docs/reference/data-streams/lifecycle/apis/explain-lifecycle.asciidoc +++ b/docs/reference/data-streams/lifecycle/apis/explain-lifecycle.asciidoc @@ -4,7 +4,7 @@ Explain Data Stream Lifecycle ++++ -preview:[] +preview::[] Retrieves the current data stream lifecycle status for one or more data stream backing indices. diff --git a/docs/reference/data-streams/lifecycle/apis/get-lifecycle.asciidoc b/docs/reference/data-streams/lifecycle/apis/get-lifecycle.asciidoc index 66b30b7975c11..f20a3393c191c 100644 --- a/docs/reference/data-streams/lifecycle/apis/get-lifecycle.asciidoc +++ b/docs/reference/data-streams/lifecycle/apis/get-lifecycle.asciidoc @@ -4,7 +4,7 @@ Get Data Stream Lifecycle ++++ -preview:[] +preview::[] Gets the lifecycle of a set of data streams. diff --git a/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc b/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc index dd893d0abe757..89b8bbeb880c3 100644 --- a/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc +++ b/docs/reference/data-streams/lifecycle/apis/put-lifecycle.asciidoc @@ -4,7 +4,7 @@ Put Data Stream Lifecycle ++++ -preview:[] +preview::[] Configures the data stream lifecycle for the targeted data streams. diff --git a/docs/reference/data-streams/lifecycle/index.asciidoc b/docs/reference/data-streams/lifecycle/index.asciidoc index 5f7596087cb5b..6c0220ef0a80f 100644 --- a/docs/reference/data-streams/lifecycle/index.asciidoc +++ b/docs/reference/data-streams/lifecycle/index.asciidoc @@ -2,7 +2,7 @@ [[data-stream-lifecycle]] == Data stream lifecycle -preview:[] +preview::[] A data stream lifecycle is the built-in mechanism data streams use to manage their lifecycle. It enables you to easily automate the management of your data streams according to your retention requirements. For example, you could configure diff --git a/docs/reference/data-streams/lifecycle/tutorial-manage-existing-data-stream.asciidoc b/docs/reference/data-streams/lifecycle/tutorial-manage-existing-data-stream.asciidoc index e7b4d99d25d80..5670faaade3ce 100644 --- a/docs/reference/data-streams/lifecycle/tutorial-manage-existing-data-stream.asciidoc +++ b/docs/reference/data-streams/lifecycle/tutorial-manage-existing-data-stream.asciidoc @@ -2,7 +2,7 @@ [[tutorial-manage-existing-data-stream]] === Tutorial: Update existing data stream -preview:[] +preview::[] To update the lifecycle of an existing data stream you do the following actions: diff --git a/docs/reference/data-streams/lifecycle/tutorial-manage-new-data-stream.asciidoc b/docs/reference/data-streams/lifecycle/tutorial-manage-new-data-stream.asciidoc index f22a0ae736d9e..6f1d81ab6ead2 100644 --- a/docs/reference/data-streams/lifecycle/tutorial-manage-new-data-stream.asciidoc +++ b/docs/reference/data-streams/lifecycle/tutorial-manage-new-data-stream.asciidoc @@ -2,7 +2,7 @@ [[tutorial-manage-new-data-stream]] === Tutorial: Create a data stream with a lifecycle -preview:[] +preview::[] To create a data stream with a built-in lifecycle, follow these steps: diff --git a/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc b/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc index c96bfa75516a8..de11bbcfc2d4e 100644 --- a/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc +++ b/docs/reference/data-streams/lifecycle/tutorial-migrate-data-stream-from-ilm-to-dsl.asciidoc @@ -2,7 +2,7 @@ [[tutorial-migrate-data-stream-from-ilm-to-dsl]] === Tutorial: Migrate ILM managed data stream to Data stream lifecycle -preview:[] +preview::[] In this tutorial we'll look at migrating an existing data stream from {ilm-init} to Data stream lifecycle. The existing {ilm-init} managed backing indices will continue From 7aad314cc12801cc6d79a7e5c35c61830f82cd78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Thu, 26 Oct 2023 14:30:43 +0200 Subject: [PATCH 129/203] Cleanup Nodes class (#101363) Following comments from a previous PR, Nodes class has been renamed, moved to a separate file, switched to inheritance to composition --- .../elasticsearch/backwards/HotThreadsIT.java | 4 +- .../elasticsearch/backwards/IndexingIT.java | 122 ++++-------------- .../backwards/MixedClusterTestNode.java | 13 ++ .../backwards/MixedClusterTestNodes.java | 72 +++++++++++ .../SearchWithMinCompatibleSearchNodeIT.java | 33 +++-- 5 files changed, 135 insertions(+), 109 deletions(-) create mode 100644 qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNode.java create mode 100644 qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNodes.java diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/HotThreadsIT.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/HotThreadsIT.java index 9c931e15eeee3..04c59e1ce9214 100644 --- a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/HotThreadsIT.java +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/HotThreadsIT.java @@ -17,8 +17,10 @@ public class HotThreadsIT extends ESRestTestCase { + private static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + public void testHotThreads() throws Exception { - final IndexingIT.Nodes nodes = IndexingIT.buildNodeAndVersions(client()); + final MixedClusterTestNodes nodes = MixedClusterTestNodes.buildNodes(client(), BWC_NODES_VERSION); assumeFalse("no new node found", nodes.getNewNodes().isEmpty()); assumeFalse("no bwc node found", nodes.getBWCNodes().isEmpty()); assumeTrue( diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java index 75a55e6e69af1..aac4b6a020d4b 100644 --- a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/IndexingIT.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,7 +39,7 @@ import static org.hamcrest.Matchers.oneOf; public class IndexingIT extends ESRestTestCase { - protected static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + private static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); private int indexDocs(String index, final int idStart, final int numDocs) throws IOException { for (int i = 0; i < numDocs; i++) { @@ -78,10 +77,10 @@ private int indexDocWithConcurrentUpdates(String index, final int docId, int nUp } public void testIndexVersionPropagation() throws Exception { - Nodes nodes = buildNodeAndVersions(); + MixedClusterTestNodes nodes = buildNodeAndVersions(); assumeFalse("new nodes is empty", nodes.getNewNodes().isEmpty()); logger.info("cluster discovered: {}", nodes.toString()); - final List bwcNamesList = nodes.getBWCNodes().stream().map(Node::nodeName).collect(Collectors.toList()); + final List bwcNamesList = nodes.getBWCNodes().stream().map(MixedClusterTestNode::nodeName).collect(Collectors.toList()); final String bwcNames = bwcNamesList.stream().collect(Collectors.joining(",")); Settings.Builder settings = Settings.builder() .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) @@ -94,7 +93,7 @@ public void testIndexVersionPropagation() throws Exception { try ( RestClient newNodeClient = buildClient( restClientSettings(), - nodes.getNewNodes().stream().map(Node::publishAddress).toArray(HttpHost[]::new) + nodes.getNewNodes().stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) ) ) { @@ -168,10 +167,10 @@ public void testIndexVersionPropagation() throws Exception { } public void testSeqNoCheckpoints() throws Exception { - Nodes nodes = buildNodeAndVersions(); + MixedClusterTestNodes nodes = buildNodeAndVersions(); assumeFalse("new nodes is empty", nodes.getNewNodes().isEmpty()); logger.info("cluster discovered: {}", nodes.toString()); - final List bwcNamesList = nodes.getBWCNodes().stream().map(Node::nodeName).collect(Collectors.toList()); + final List bwcNamesList = nodes.getBWCNodes().stream().map(MixedClusterTestNode::nodeName).collect(Collectors.toList()); final String bwcNames = bwcNamesList.stream().collect(Collectors.joining(",")); Settings.Builder settings = Settings.builder() .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) @@ -183,7 +182,7 @@ public void testSeqNoCheckpoints() throws Exception { try ( RestClient newNodeClient = buildClient( restClientSettings(), - nodes.getNewNodes().stream().map(Node::publishAddress).toArray(HttpHost[]::new) + nodes.getNewNodes().stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) ) ) { int numDocs = 0; @@ -228,14 +227,14 @@ public void testSeqNoCheckpoints() throws Exception { assertOK(client().performRequest(new Request("POST", index + "/_refresh"))); for (Shard shard : buildShards(index, nodes, newNodeClient)) { - assertCount(index, "_only_nodes:" + shard.node.nodeName, numDocs); + assertCount(index, "_only_nodes:" + shard.node.nodeName(), numDocs); } assertSeqNoOnShards(index, nodes, numDocs, newNodeClient); } } public void testUpdateSnapshotStatus() throws Exception { - Nodes nodes = buildNodeAndVersions(); + MixedClusterTestNodes nodes = buildNodeAndVersions(); assumeFalse("new nodes is empty", nodes.getNewNodes().isEmpty()); logger.info("cluster discovered: {}", nodes.toString()); @@ -256,7 +255,7 @@ public void testUpdateSnapshotStatus() throws Exception { assertOK(client().performRequest(request)); - String bwcNames = nodes.getBWCNodes().stream().map(Node::nodeName).collect(Collectors.joining(",")); + String bwcNames = nodes.getBWCNodes().stream().map(MixedClusterTestNode::nodeName).collect(Collectors.joining(",")); // Allocating shards on the BWC nodes to makes sure that taking snapshot happens on those nodes. Settings.Builder settings = Settings.builder() @@ -310,7 +309,7 @@ private static boolean syncedFlushRemoved() { } public void testSyncedFlushTransition() throws Exception { - Nodes nodes = buildNodeAndVersions(); + MixedClusterTestNodes nodes = buildNodeAndVersions(); assumeTrue( "bwc version is on 7.x (synced flush deprecated but not removed yet)", syncedFlushDeprecated() && syncedFlushRemoved() == false @@ -318,7 +317,7 @@ public void testSyncedFlushTransition() throws Exception { assumeFalse("no new node found", nodes.getNewNodes().isEmpty()); assumeFalse("no bwc node found", nodes.getBWCNodes().isEmpty()); // Allocate shards to new nodes then verify synced flush requests processed by old nodes/new nodes - String newNodes = nodes.getNewNodes().stream().map(Node::nodeName).collect(Collectors.joining(",")); + String newNodes = nodes.getNewNodes().stream().map(MixedClusterTestNode::nodeName).collect(Collectors.joining(",")); int numShards = randomIntBetween(1, 10); int numOfReplicas = randomIntBetween(0, nodes.getNewNodes().size() - 1); int totalShards = numShards * (numOfReplicas + 1); @@ -336,7 +335,7 @@ public void testSyncedFlushTransition() throws Exception { try ( RestClient oldNodeClient = buildClient( restClientSettings(), - nodes.getBWCNodes().stream().map(Node::publishAddress).toArray(HttpHost[]::new) + nodes.getBWCNodes().stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) ) ) { Request request = new Request("POST", index + "/_flush/synced"); @@ -364,7 +363,7 @@ public void testSyncedFlushTransition() throws Exception { try ( RestClient newNodeClient = buildClient( restClientSettings(), - nodes.getNewNodes().stream().map(Node::publishAddress).toArray(HttpHost[]::new) + nodes.getNewNodes().stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) ) ) { Request request = new Request("POST", index + "/_flush/synced"); @@ -393,11 +392,11 @@ public void testSyncedFlushTransition() throws Exception { } public void testFlushTransition() throws Exception { - Nodes nodes = buildNodeAndVersions(); + MixedClusterTestNodes nodes = buildNodeAndVersions(); assumeFalse("no new node found", nodes.getNewNodes().isEmpty()); assumeFalse("no bwc node found", nodes.getBWCNodes().isEmpty()); // Allocate shards to new nodes then verify flush requests processed by old nodes/new nodes - String newNodes = nodes.getNewNodes().stream().map(Node::nodeName).collect(Collectors.joining(",")); + String newNodes = nodes.getNewNodes().stream().map(MixedClusterTestNode::nodeName).collect(Collectors.joining(",")); int numShards = randomIntBetween(1, 10); int numOfReplicas = randomIntBetween(0, nodes.getNewNodes().size() - 1); int totalShards = numShards * (numOfReplicas + 1); @@ -415,7 +414,7 @@ public void testFlushTransition() throws Exception { try ( RestClient oldNodeClient = buildClient( restClientSettings(), - nodes.getBWCNodes().stream().map(Node::publishAddress).toArray(HttpHost[]::new) + nodes.getBWCNodes().stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) ) ) { Request request = new Request("POST", index + "/_flush"); @@ -432,7 +431,7 @@ public void testFlushTransition() throws Exception { try ( RestClient newNodeClient = buildClient( restClientSettings(), - nodes.getNewNodes().stream().map(Node::publishAddress).toArray(HttpHost[]::new) + nodes.getNewNodes().stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) ) ) { Request request = new Request("POST", index + "/_flush"); @@ -466,7 +465,7 @@ private void assertVersion(final String index, final int docId, final String pre assertThat("version mismatch for doc [" + docId + "] preference [" + preference + "]", actualVersion, equalTo(expectedVersion)); } - private void assertSeqNoOnShards(String index, Nodes nodes, int numDocs, RestClient client) throws Exception { + private void assertSeqNoOnShards(String index, MixedClusterTestNodes nodes, int numDocs, RestClient client) throws Exception { assertBusy(() -> { try { List shards = buildShards(index, nodes, client); @@ -496,7 +495,7 @@ private void assertSeqNoOnShards(String index, Nodes nodes, int numDocs, RestCli }); } - private List buildShards(String index, Nodes nodes, RestClient client) throws IOException { + private List buildShards(String index, MixedClusterTestNodes nodes, RestClient client) throws IOException { Request request = new Request("GET", index + "/_stats"); request.addParameter("level", "shards"); Response response = client.performRequest(request); @@ -505,7 +504,7 @@ private List buildShards(String index, Nodes nodes, RestClient client) th for (Object shard : shardStats) { final String nodeId = ObjectPath.evaluate(shard, "routing.node"); final Boolean primary = ObjectPath.evaluate(shard, "routing.primary"); - final Node node = nodes.getSafe(nodeId); + final MixedClusterTestNode node = nodes.getSafe(nodeId); final SeqNoStats seqNoStats; Integer maxSeqNo = ObjectPath.evaluate(shard, "seq_no.max_seq_no"); Integer localCheckpoint = ObjectPath.evaluate(shard, "seq_no.local_checkpoint"); @@ -517,82 +516,9 @@ private List buildShards(String index, Nodes nodes, RestClient client) th return shards; } - private Nodes buildNodeAndVersions() throws IOException { - return buildNodeAndVersions(client()); + private MixedClusterTestNodes buildNodeAndVersions() throws IOException { + return MixedClusterTestNodes.buildNodes(client(), BWC_NODES_VERSION); } - static Nodes buildNodeAndVersions(RestClient client) throws IOException { - Response response = client.performRequest(new Request("GET", "_nodes")); - ObjectPath objectPath = ObjectPath.createFromResponse(response); - Map nodesAsMap = objectPath.evaluate("nodes"); - Nodes nodes = new Nodes(BWC_NODES_VERSION); - for (String id : nodesAsMap.keySet()) { - nodes.add( - new Node( - id, - objectPath.evaluate("nodes." + id + ".name"), - objectPath.evaluate("nodes." + id + ".version"), - HttpHost.create(objectPath.evaluate("nodes." + id + ".http.publish_address")) - ) - ); - } - response = client.performRequest(new Request("GET", "_cluster/state")); - nodes.setMasterNodeId(ObjectPath.createFromResponse(response).evaluate("master_node")); - return nodes; - } - - static final class Nodes extends HashMap { - - private final String bwcNodesVersion; - private String masterNodeId = null; - - Nodes(String bwcNodesVersion) { - this.bwcNodesVersion = bwcNodesVersion; - } - - public Node getMaster() { - return get(masterNodeId); - } - - public void setMasterNodeId(String id) { - if (get(id) == null) { - throw new IllegalArgumentException("node with id [" + id + "] not found. got:" + toString()); - } - masterNodeId = id; - } - - public void add(Node node) { - put(node.id(), node); - } - - public List getNewNodes() { - return values().stream().filter(n -> n.version().equals(bwcNodesVersion) == false).collect(Collectors.toList()); - } - - public List getBWCNodes() { - return values().stream().filter(n -> n.version().equals(bwcNodesVersion)).collect(Collectors.toList()); - } - - public Node getSafe(String id) { - Node node = get(id); - if (node == null) { - throw new IllegalArgumentException("node with id [" + id + "] not found"); - } - return node; - } - - @Override - public String toString() { - return "Nodes{" - + "masterNodeId='" - + masterNodeId - + "'\n" - + values().stream().map(Node::toString).collect(Collectors.joining("\n")) - + '}'; - } - } - - record Node(String id, String nodeName, String version, HttpHost publishAddress) {} - - record Shard(Node node, boolean primary, SeqNoStats seqNoStats) {} + private record Shard(MixedClusterTestNode node, boolean primary, SeqNoStats seqNoStats) {} } diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNode.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNode.java new file mode 100644 index 0000000000000..c9cdf9dd41735 --- /dev/null +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNode.java @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.backwards; + +import org.apache.http.HttpHost; + +record MixedClusterTestNode(String id, String nodeName, String version, HttpHost publishAddress) {} diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNodes.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNodes.java new file mode 100644 index 0000000000000..d4bfea19fd1ff --- /dev/null +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/MixedClusterTestNodes.java @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.backwards; + +import org.apache.http.HttpHost; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.test.rest.ObjectPath; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +final class MixedClusterTestNodes { + private final Map nodesById; + private final String bwcNodesVersion; + + private MixedClusterTestNodes(String bwcNodesVersion, Map nodesById) { + this.bwcNodesVersion = bwcNodesVersion; + this.nodesById = nodesById; + } + + public List getNewNodes() { + return nodesById.values().stream().filter(n -> n.version().equals(bwcNodesVersion) == false).collect(Collectors.toList()); + } + + public List getBWCNodes() { + return nodesById.values().stream().filter(n -> n.version().equals(bwcNodesVersion)).collect(Collectors.toList()); + } + + public MixedClusterTestNode getSafe(String id) { + MixedClusterTestNode node = nodesById.get(id); + if (node == null) { + throw new IllegalArgumentException("node with id [" + id + "] not found"); + } + return node; + } + + static MixedClusterTestNodes buildNodes(RestClient client, String bwcNodesVersion) throws IOException { + Response response = client.performRequest(new Request("GET", "_nodes")); + ObjectPath objectPath = ObjectPath.createFromResponse(response); + Map nodesAsMap = objectPath.evaluate("nodes"); + + Map nodesById = new HashMap<>(); + for (var id : nodesAsMap.keySet()) { + nodesById.put( + id, + new MixedClusterTestNode( + id, + objectPath.evaluate("nodes." + id + ".name"), + objectPath.evaluate("nodes." + id + ".version"), + HttpHost.create(objectPath.evaluate("nodes." + id + ".http.publish_address")) + ) + ); + } + return new MixedClusterTestNodes(bwcNodesVersion, Collections.unmodifiableMap(nodesById)); + } + + public int size() { + return nodesById.size(); + } +} diff --git a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java index f28e014837dda..461b731e518fb 100644 --- a/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java +++ b/qa/mixed-cluster/src/test/java/org/elasticsearch/backwards/SearchWithMinCompatibleSearchNodeIT.java @@ -8,8 +8,6 @@ package org.elasticsearch.backwards; import org.apache.http.HttpHost; -import org.elasticsearch.backwards.IndexingIT.Node; -import org.elasticsearch.backwards.IndexingIT.Nodes; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; @@ -32,19 +30,19 @@ public class SearchWithMinCompatibleSearchNodeIT extends ESRestTestCase { - protected static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); - protected static final String NEW_NODES_VERSION = System.getProperty("tests.new_nodes_version"); + private static final String BWC_NODES_VERSION = System.getProperty("tests.bwc_nodes_version"); + private static final String NEW_NODES_VERSION = System.getProperty("tests.new_nodes_version"); private static String index = "test_min_version"; private static int numShards; private static int numReplicas = 1; private static int numDocs; - private static Nodes nodes; - private static List allNodes; + private static MixedClusterTestNodes nodes; + private static List allNodes; @Before public void prepareTestData() throws IOException { - nodes = IndexingIT.buildNodeAndVersions(client()); + nodes = MixedClusterTestNodes.buildNodes(client(), BWC_NODES_VERSION); numShards = nodes.size(); numDocs = randomIntBetween(numShards, 16); allNodes = new ArrayList<>(); @@ -69,7 +67,12 @@ public void prepareTestData() throws IOException { } public void testMinVersionAsNewVersion() throws Exception { - try (RestClient client = buildClient(restClientSettings(), allNodes.stream().map(Node::publishAddress).toArray(HttpHost[]::new))) { + try ( + RestClient client = buildClient( + restClientSettings(), + allNodes.stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) + ) + ) { Request newVersionRequest = new Request( "POST", index + "/_search?min_compatible_shard_node=" + NEW_NODES_VERSION + "&ccs_minimize_roundtrips=false" @@ -90,7 +93,12 @@ public void testMinVersionAsNewVersion() throws Exception { } public void testMinVersionAsOldVersion() throws Exception { - try (RestClient client = buildClient(restClientSettings(), allNodes.stream().map(Node::publishAddress).toArray(HttpHost[]::new))) { + try ( + RestClient client = buildClient( + restClientSettings(), + allNodes.stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) + ) + ) { Request oldVersionRequest = new Request( "POST", index + "/_search?min_compatible_shard_node=" + BWC_NODES_VERSION + "&ccs_minimize_roundtrips=false" @@ -112,7 +120,12 @@ public void testMinVersionAsOldVersion() throws Exception { } public void testCcsMinimizeRoundtripsIsFalse() throws Exception { - try (RestClient client = buildClient(restClientSettings(), allNodes.stream().map(Node::publishAddress).toArray(HttpHost[]::new))) { + try ( + RestClient client = buildClient( + restClientSettings(), + allNodes.stream().map(MixedClusterTestNode::publishAddress).toArray(HttpHost[]::new) + ) + ) { String version = randomBoolean() ? NEW_NODES_VERSION : BWC_NODES_VERSION; Request request = new Request( From a6ce91a714eaf6d8d06d01ccef2c964bd95b8b55 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 26 Oct 2023 13:50:00 +0100 Subject: [PATCH 130/203] [ML] Persist data counts on job close before results index refresh (#101147) On job close we aim to refresh the results index before returning so that the caller knows that all results are searchable. It turns out that we were writing the final data counts _after_ refreshing the indices, so that a user who searched for final data counts immediately after receiving the `_close` response would potentially get inaccurate data counts. This change writes the final data counts before refreshing the indices. --- docs/changelog/101147.yaml | 5 +++++ .../ml/job/process/autodetect/AutodetectCommunicator.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101147.yaml diff --git a/docs/changelog/101147.yaml b/docs/changelog/101147.yaml new file mode 100644 index 0000000000000..cb556af35eead --- /dev/null +++ b/docs/changelog/101147.yaml @@ -0,0 +1,5 @@ +pr: 101147 +summary: Persist data counts on job close before results index refresh +area: Machine Learning +type: bug +issues: [] diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectCommunicator.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectCommunicator.java index 550742ef04b5a..139eceb09bd02 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectCommunicator.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectCommunicator.java @@ -170,6 +170,7 @@ public void close() { killProcess(false, false); stateStreamer.cancel(); } + dataCountsReporter.writeUnreportedCounts(); autodetectResultProcessor.awaitCompletion(); } finally { onFinishHandler.accept(null, true); @@ -180,7 +181,6 @@ public void close() { try { future.get(); autodetectWorkerExecutor.shutdownNow(); - dataCountsReporter.writeUnreportedCounts(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { From f68cd1f1e29381f177338102f1d4289e430e52e8 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Thu, 26 Oct 2023 13:53:18 +0100 Subject: [PATCH 131/203] [ML] Return a no-scale autoscaling response if job size unavailable (#101335) It's possible that a job size can be unavailable, if some other problem within a cluster is preventing searches from working. In this situation we should not return an autoscaling response that ignores the jobs in question, as that can lead to a request to scale down inappropriately. Instead we should return no-scale events until the cluster can successfully perform searches again. --- .../MlAutoscalingResourceTracker.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java index 2dcfdf7603c7f..5c89c29a70cdd 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/autoscaling/MlAutoscalingResourceTracker.java @@ -141,9 +141,9 @@ static void getMemoryAndProcessors( Long jobMemory = mlMemoryTracker.getAnomalyDetectorJobMemoryRequirement(jobId); if (jobMemory == null) { - // TODO: this indicates a bug, should we indicate that the result is incomplete? - logger.debug("could not find memory requirement for job [{}], skipping", jobId); - continue; + logger.debug("could not find memory requirement for job [{}], returning no-scale", jobId); + listener.onResponse(noScaleStats(numberMlNodes)); + return; } if (AWAITING_LAZY_ASSIGNMENT.equals(task.getAssignment())) { @@ -169,9 +169,9 @@ static void getMemoryAndProcessors( Long jobMemory = mlMemoryTracker.getDataFrameAnalyticsJobMemoryRequirement(jobId); if (jobMemory == null) { - // TODO: this indicates a bug, should we indicate that the result is incomplete? - logger.debug("could not find memory requirement for job [{}], skipping", jobId); - continue; + logger.debug("could not find memory requirement for job [{}], returning no-scale", jobId); + listener.onResponse(noScaleStats(numberMlNodes)); + return; } if (AWAITING_LAZY_ASSIGNMENT.equals(task.getAssignment())) { @@ -291,6 +291,10 @@ static void getMemoryAndProcessors( */ public static MlAutoscalingStats noScaleStats(ClusterState clusterState) { int numberMlNodes = (int) clusterState.nodes().stream().filter(node -> node.getRoles().contains(DiscoveryNodeRole.ML_ROLE)).count(); + return noScaleStats(numberMlNodes); + } + + private static MlAutoscalingStats noScaleStats(int numberMlNodes) { return new MlAutoscalingStats( numberMlNodes, 0, From 3e2f17fe84a345f8a8668f2c836cc2d5598c78b8 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 26 Oct 2023 15:34:11 +0100 Subject: [PATCH 132/203] Refactor uses of old Plugin.createComponents method to new services method (#101376) --- .../PredicateTokenScriptFilterTests.java | 31 ++++----- .../ScriptedConditionTokenFilterTests.java | 27 +++----- .../azure/AzureStorageServiceTests.java | 8 ++- .../systemd/SystemdPluginTests.java | 18 ++++-- .../metadata/TemplateUpgradeServiceIT.java | 50 +-------------- .../core/LocalStateCompositeXPackPlugin.java | 64 +------------------ .../xpack/core/XPackPluginTests.java | 43 ++++--------- .../LocalStateEnterpriseSearch.java | 47 +------------- .../xpack/watcher/WatcherPluginTests.java | 7 +- 9 files changed, 61 insertions(+), 234 deletions(-) diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java index 7b60c1a64abb6..3a519f594a57f 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/PredicateTokenScriptFilterTests.java @@ -24,11 +24,11 @@ import org.elasticsearch.index.analysis.IndexAnalyzers; import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.indices.analysis.AnalysisModule; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.scanners.StablePluginsRegistry; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTokenStreamTestCase; import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.threadpool.ThreadPool; @@ -36,6 +36,9 @@ import java.io.IOException; import java.util.Collections; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class PredicateTokenScriptFilterTests extends ESTokenStreamTestCase { public void testSimpleFilter() throws IOException { @@ -57,9 +60,9 @@ public boolean execute(Token token) { } }; - @SuppressWarnings("unchecked") ScriptService scriptService = new ScriptService(indexSettings, Collections.emptyMap(), Collections.emptyMap(), () -> 1L) { @Override + @SuppressWarnings("unchecked") public FactoryType compile(Script script, ScriptContext context) { assertEquals(context, AnalysisPredicateScript.CONTEXT); assertEquals(new Script("my_script"), script); @@ -67,23 +70,13 @@ public FactoryType compile(Script script, ScriptContext FactoryType compile(Script script, ScriptContext FactoryType compile(Script script, ScriptContext createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - clusterService.getClusterSettings().addSettingsUpdateConsumer(UPDATE_TEMPLATE_DUMMY_SETTING, integer -> { + public Collection createComponents(PluginServices services) { + services.clusterService().getClusterSettings().addSettingsUpdateConsumer(UPDATE_TEMPLATE_DUMMY_SETTING, integer -> { logger.debug("the template dummy setting was updated to {}", integer); }); - return super.createComponents( - client, - clusterService, - threadPool, - resourceWatcherService, - scriptService, - xContentRegistry, - environment, - nodeEnvironment, - namedWriteableRegistry, - expressionResolver, - repositoriesServiceSupplier, - telemetryProvider, - allocationService, - indicesService - ); + return super.createComponents(services); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java index afc64140004c7..26436e497a644 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/LocalStateCompositeXPackPlugin.java @@ -25,7 +25,6 @@ import org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.ExistingShardsAllocator; import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider; import org.elasticsearch.cluster.service.ClusterService; @@ -44,7 +43,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.http.HttpPreRequest; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.index.IndexModule; @@ -54,7 +52,6 @@ import org.elasticsearch.index.analysis.TokenizerFactory; import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.mapper.MetadataFieldMapper; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.indices.breaker.CircuitBreakerService; @@ -81,22 +78,18 @@ import org.elasticsearch.plugins.ShutdownAwarePlugin; import org.elasticsearch.plugins.SystemIndexPlugin; import org.elasticsearch.plugins.interceptor.RestServerActionPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.rest.RestHeaderDefinition; import org.elasticsearch.script.ScriptContext; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.snapshots.Snapshot; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportInterceptor; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.ssl.SSLService; @@ -193,61 +186,10 @@ protected void setEpochMillisSupplier(LongSupplier epochMillisSupplier) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - List components = new ArrayList<>( - super.createComponents( - client, - clusterService, - threadPool, - resourceWatcherService, - scriptService, - xContentRegistry, - environment, - nodeEnvironment, - namedWriteableRegistry, - expressionResolver, - repositoriesServiceSupplier, - telemetryProvider, - allocationService, - indicesService - ) - ); + public Collection createComponents(PluginServices services) { + List components = new ArrayList<>(super.createComponents(services)); - filterPlugins(Plugin.class).forEach( - p -> components.addAll( - p.createComponents( - client, - clusterService, - threadPool, - resourceWatcherService, - scriptService, - xContentRegistry, - environment, - nodeEnvironment, - namedWriteableRegistry, - expressionResolver, - repositoriesServiceSupplier, - telemetryProvider, - allocationService, - indicesService - ) - ) - ); + filterPlugins(Plugin.class).forEach(p -> components.addAll(p.createComponents(services))); return components; } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java index 4f15d719d4193..d4c8dfa5fd0a7 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/XPackPluginTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.license.PutLicenseRequest; import org.elasticsearch.license.internal.MutableLicenseService; import org.elasticsearch.plugins.ExtensiblePlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.protocol.xpack.license.PutLicenseResponse; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; @@ -143,22 +144,11 @@ public List loadExtensions(Class extensionPointType) { when(mockEnvironment.settings()).thenReturn(Settings.builder().build()); when(mockEnvironment.configFile()).thenReturn(PathUtils.get("")); // ensure createComponents does not influence the results - xpackPlugin.createComponents( - null, - mock(ClusterService.class), - mock(ThreadPool.class), - null, - null, - null, - mockEnvironment, - null, - null, - null, - null, - null, - null, - null - ); + Plugin.PluginServices services = mock(Plugin.PluginServices.class); + when(services.clusterService()).thenReturn(mock(ClusterService.class)); + when(services.threadPool()).thenReturn(mock(ThreadPool.class)); + when(services.environment()).thenReturn(mockEnvironment); + xpackPlugin.createComponents(services); assertEquals(license, XPackPlugin.getSharedLicenseService().getLicense()); assertEquals(License.OperationMode.resolve(licenseType), XPackPlugin.getSharedLicenseState().getOperationMode()); } @@ -197,22 +187,11 @@ public List loadExtensions(Class extensionPointType) { Environment mockEnvironment = mock(Environment.class); when(mockEnvironment.settings()).thenReturn(Settings.builder().build()); when(mockEnvironment.configFile()).thenReturn(PathUtils.get("")); - xpackPlugin.createComponents( - null, - mock(ClusterService.class), - mock(ThreadPool.class), - null, - null, - null, - mockEnvironment, - null, - null, - null, - null, - null, - null, - null - ); + Plugin.PluginServices services = mock(Plugin.PluginServices.class); + when(services.clusterService()).thenReturn(mock(ClusterService.class)); + when(services.threadPool()).thenReturn(mock(ThreadPool.class)); + when(services.environment()).thenReturn(mockEnvironment); + xpackPlugin.createComponents(services); assertThat(XPackPlugin.getSharedLicenseService(), instanceOf(ClusterStateLicenseService.class)); assertEquals(License.OperationMode.TRIAL, XPackPlugin.getSharedLicenseState().getOperationMode()); } diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java index f10480e9f64a7..2e181fda1ef88 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/LocalStateEnterpriseSearch.java @@ -9,31 +9,18 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.license.MockLicenseState; import org.elasticsearch.license.XPackLicenseState; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; import org.mockito.Mockito; @@ -102,38 +89,8 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - return entSearchPlugin.createComponents( - client, - clusterService, - threadPool, - resourceWatcherService, - scriptService, - xContentRegistry, - environment, - nodeEnvironment, - namedWriteableRegistry, - indexNameExpressionResolver, - repositoriesServiceSupplier, - telemetryProvider, - allocationService, - indicesService - ); + public Collection createComponents(PluginServices services) { + return entSearchPlugin.createComponents(services); } @Override diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java index e1f1933242414..d3af489a77a2a 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginTests.java @@ -14,8 +14,8 @@ import org.elasticsearch.index.engine.InternalEngineFactory; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.TestIndexNameExpressionResolver; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.internal.DocumentParsingObserver; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.threadpool.ExecutorBuilder; @@ -74,10 +74,7 @@ public void testWatcherDisabledTests() throws Exception { watcher.onIndexModule(indexModule); // also no component creation if not enabled - assertThat( - watcher.createComponents(null, null, null, null, null, null, null, null, null, null, null, TelemetryProvider.NOOP, null, null), - hasSize(0) - ); + assertThat(watcher.createComponents(mock(Plugin.PluginServices.class)), hasSize(0)); watcher.close(); } From d52f74ff22a5c3574c4b44d159ec8a6cdf2d2a7c Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Thu, 26 Oct 2023 16:28:40 +0100 Subject: [PATCH 133/203] Fix assertion in ContextIndexSearcherTests (#101366) If the random index writer commits before we run deletes, then it's possible that a query bitset will match all documents in a segment, and so will be realised as a MatchAllBitSet implementation. This commit adds checking for this scenario to doTestContextIndexSearcher. Fixes #94615 --- .../search/internal/ContextIndexSearcherTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java b/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java index 120982bea36de..9e6b6330d2f23 100644 --- a/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java +++ b/server/src/test/java/org/elasticsearch/search/internal/ContextIndexSearcherTests.java @@ -70,6 +70,7 @@ import org.elasticsearch.index.cache.bitset.BitsetFilterCache; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.lucene.util.CombinedBitSet; +import org.elasticsearch.lucene.util.MatchAllBitSet; import org.elasticsearch.search.aggregations.BucketCollector; import org.elasticsearch.search.aggregations.LeafBucketCollector; import org.elasticsearch.test.ESTestCase; @@ -96,6 +97,7 @@ import static org.elasticsearch.search.internal.ExitableDirectoryReader.ExitableLeafReader; import static org.elasticsearch.search.internal.ExitableDirectoryReader.ExitablePointValues; import static org.elasticsearch.search.internal.ExitableDirectoryReader.ExitableTerms; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.instanceOf; @@ -279,7 +281,6 @@ public void testContextIndexSearcherSparseWithDeletions() throws IOException { doTestContextIndexSearcher(true, true); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/94615") public void testContextIndexSearcherDenseWithDeletions() throws IOException { doTestContextIndexSearcher(false, true); } @@ -332,7 +333,7 @@ public void onRemoval(ShardId shardId, Accountable accountable) { if (sparse) { assertThat(bitSet, instanceOf(SparseFixedBitSet.class)); } else { - assertThat(bitSet, instanceOf(FixedBitSet.class)); + assertThat(bitSet, anyOf(instanceOf(FixedBitSet.class), instanceOf(MatchAllBitSet.class))); } DocumentSubsetDirectoryReader filteredReader = new DocumentSubsetDirectoryReader(reader, cache, roleQuery); From 22381fd6a72687dbbaf3cfb76ce158625fc44b0c Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 26 Oct 2023 16:58:14 +0100 Subject: [PATCH 134/203] Refactor overrides of old Plugin.createComponents method to new services method (#101381) --- .../analysis/common/CommonAnalysisPlugin.java | 34 +--------- .../org/elasticsearch/telemetry/apm/APM.java | 37 ++--------- .../datastreams/DataStreamsPlugin.java | 46 ++++---------- .../ingest/geoip/IngestGeoIpPlugin.java | 36 +++-------- .../painless/PainlessPlugin.java | 30 +-------- .../elasticsearch/reindex/ReindexPlugin.java | 33 ++-------- .../ReindexFromRemoteWithAuthTests.java | 33 +--------- .../azure/AzureRepositoryPlugin.java | 31 +-------- .../repositories/s3/S3RepositoryPlugin.java | 33 +--------- .../repository/url/URLRepositoryPlugin.java | 29 +-------- .../RuntimeFieldsCommonPlugin.java | 34 +--------- .../elasticsearch/systemd/SystemdPlugin.java | 34 +--------- .../cluster/SimpleClusterStateIT.java | 31 +-------- .../health/GetHealthActionIT.java | 36 ++--------- .../elasticsearch/health/HealthServiceIT.java | 32 +--------- .../elasticsearch/index/FinalPipelineIT.java | 35 ----------- .../index/SettingsListenerIT.java | 32 +--------- .../ingest/IngestAsyncProcessorIT.java | 33 +--------- .../org/elasticsearch/node/NodeTests.java | 28 +-------- .../plugins/PluginIntrospectorTests.java | 31 +-------- .../test/seektracker/SeekTrackerPlugin.java | 30 +-------- .../test/MockIndexEventListener.java | 32 +--------- .../xpack/analytics/AnalyticsPlugin.java | 31 +-------- .../xpack/async/AsyncResultsIndexPlugin.java | 42 ++----------- .../xpack/autoscaling/Autoscaling.java | 36 +++-------- .../java/org/elasticsearch/xpack/ccr/Ccr.java | 44 ++++--------- .../elasticsearch/xpack/core/XPackPlugin.java | 38 +++-------- .../xpack/deprecation/Deprecation.java | 53 ++++------------ .../xpack/enrich/EnrichPlugin.java | 46 +++----------- .../xpack/application/EnterpriseSearch.java | 46 +++----------- .../xpack/eql/plugin/EqlPlugin.java | 30 +-------- .../xpack/esql/plugin/EsqlPlugin.java | 48 +++++--------- .../org/elasticsearch/xpack/fleet/Fleet.java | 38 ++--------- .../xpack/idp/IdentityProviderPlugin.java | 52 +++++---------- .../xpack/ilm/UpdateSettingsStepTests.java | 30 +-------- .../xpack/ilm/IndexLifecycle.java | 63 +++++++------------ .../xpack/inference/InferencePlugin.java | 37 ++--------- .../xpack/ml/MachineLearning.java | 31 +++------ .../xpack/monitoring/Monitoring.java | 42 +++++-------- .../xpack/lucene/bwc/OldLuceneVersions.java | 35 ++--------- .../xpack/profiling/ProfilingPlugin.java | 33 ++-------- .../SearchableSnapshots.java | 34 +++------- .../xpack/security/Security.java | 40 +++--------- .../xpack/shutdown/NodeShutdownTasksIT.java | 28 +-------- .../xpack/shutdown/ShutdownPlugin.java | 32 +--------- .../xpack/slm/SnapshotLifecycle.java | 34 +++------- .../xpack/sql/plugin/SqlPlugin.java | 35 +++-------- .../xpack/stack/StackPlugin.java | 48 +++----------- .../xpack/transform/Transform.java | 38 +++-------- .../votingonly/VotingOnlyNodePlugin.java | 31 +-------- .../elasticsearch/xpack/watcher/Watcher.java | 31 +++------ 51 files changed, 291 insertions(+), 1565 deletions(-) diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index 42bd1296f0b69..90a8d3379775f 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -99,17 +99,11 @@ import org.apache.lucene.analysis.tr.TurkishAnalyzer; import org.apache.lucene.analysis.util.ElisionFilter; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersions; import org.elasticsearch.index.analysis.AnalyzerProvider; @@ -120,21 +114,15 @@ import org.elasticsearch.index.analysis.PreConfiguredTokenizer; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.index.analysis.TokenizerFactory; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy; import org.elasticsearch.lucene.analysis.miscellaneous.DisableGraphAttribute; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ScriptPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; import org.elasticsearch.synonyms.SynonymsManagementAPIService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.tartarus.snowball.ext.DutchStemmer; import org.tartarus.snowball.ext.FrenchStemmer; @@ -144,7 +132,6 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import java.util.function.Supplier; import static org.elasticsearch.plugins.AnalysisPlugin.requiresAnalysisSettings; @@ -156,24 +143,9 @@ public class CommonAnalysisPlugin extends Plugin implements AnalysisPlugin, Scri private final SetOnce synonymsManagementServiceHolder = new SetOnce<>(); @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - this.scriptServiceHolder.set(scriptService); - this.synonymsManagementServiceHolder.set(new SynonymsManagementAPIService(client)); + public Collection createComponents(PluginServices services) { + this.scriptServiceHolder.set(services.scriptService()); + this.synonymsManagementServiceHolder.set(new SynonymsManagementAPIService(services.client())); return Collections.emptyList(); } diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java index 80e77a78ac011..bd751f95b2eef 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/APM.java @@ -9,33 +9,19 @@ package org.elasticsearch.telemetry.apm; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.NetworkPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.TelemetryPlugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.apm.internal.APMAgentSettings; import org.elasticsearch.telemetry.apm.internal.APMMeterService; import org.elasticsearch.telemetry.apm.internal.APMTelemetryProvider; import org.elasticsearch.telemetry.apm.internal.tracing.APMTracer; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Collection; import java.util.List; -import java.util.function.Supplier; /** * This module integrates Elastic's APM product with Elasticsearch. Elasticsearch has @@ -73,31 +59,16 @@ public TelemetryProvider getTelemetryProvider(Settings settings) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider unused, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { final APMTracer apmTracer = telemetryProvider.get().getTracer(); - apmTracer.setClusterName(clusterService.getClusterName().value()); - apmTracer.setNodeName(clusterService.getNodeName()); + apmTracer.setClusterName(services.clusterService().getClusterName().value()); + apmTracer.setNodeName(services.clusterService().getNodeName()); final APMAgentSettings apmAgentSettings = new APMAgentSettings(); apmAgentSettings.syncAgentSystemProperties(settings); final APMMeterService apmMeter = new APMMeterService(settings); - apmAgentSettings.addClusterSettingsListeners(clusterService, telemetryProvider.get(), apmMeter); + apmAgentSettings.addClusterSettingsListeners(services.clusterService(), telemetryProvider.get(), apmMeter); return List.of(apmTracer, apmMeter); } diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java index de128c685ae98..a845b75450366 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java @@ -18,13 +18,9 @@ import org.elasticsearch.action.datastreams.MigrateToDataStreamAction; import org.elasticsearch.action.datastreams.ModifyDataStreamsAction; import org.elasticsearch.action.datastreams.PromoteDataStreamAction; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.OriginSettingClient; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; @@ -60,20 +56,11 @@ import org.elasticsearch.datastreams.rest.RestMigrateToDataStreamAction; import org.elasticsearch.datastreams.rest.RestModifyDataStreamsAction; import org.elasticsearch.datastreams.rest.RestPromoteDataStreamAction; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexSettingProvider; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.io.IOException; import java.time.Clock; @@ -158,38 +145,27 @@ public List> getSettings() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { Collection components = new ArrayList<>(); - var updateTimeSeriesRangeService = new UpdateTimeSeriesRangeService(environment.settings(), threadPool, clusterService); + var updateTimeSeriesRangeService = new UpdateTimeSeriesRangeService( + services.environment().settings(), + services.threadPool(), + services.clusterService() + ); this.updateTimeSeriesRangeService.set(updateTimeSeriesRangeService); components.add(this.updateTimeSeriesRangeService.get()); errorStoreInitialisationService.set(new DataStreamLifecycleErrorStore()); dataLifecycleInitialisationService.set( new DataStreamLifecycleService( settings, - new OriginSettingClient(client, DATA_STREAM_LIFECYCLE_ORIGIN), - clusterService, + new OriginSettingClient(services.client(), DATA_STREAM_LIFECYCLE_ORIGIN), + services.clusterService(), getClock(), - threadPool, - threadPool::absoluteTimeInMillis, + services.threadPool(), + services.threadPool()::absoluteTimeInMillis, errorStoreInitialisationService.get(), - allocationService + services.allocationService() ) ); dataLifecycleInitialisationService.get().init(); diff --git a/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java b/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java index afc6fa8a1c92a..26ddbaa7ba854 100644 --- a/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java +++ b/modules/ingest-geoip/src/main/java/org/elasticsearch/ingest/geoip/IngestGeoIpPlugin.java @@ -16,7 +16,6 @@ import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; @@ -25,9 +24,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.common.settings.SettingsModule; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.ingest.IngestService; import org.elasticsearch.ingest.Processor; @@ -43,14 +39,10 @@ import org.elasticsearch.plugins.PersistentTaskPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xcontent.XContentBuilder; @@ -107,30 +99,20 @@ public Map getProcessors(Processor.Parameters paramet } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { try { - String nodeId = nodeEnvironment.nodeId(); - databaseRegistry.get().initialize(nodeId, resourceWatcherService, ingestService.get()); + String nodeId = services.nodeEnvironment().nodeId(); + databaseRegistry.get().initialize(nodeId, services.resourceWatcherService(), ingestService.get()); } catch (IOException e) { throw new UncheckedIOException(e); } - geoIpDownloaderTaskExecutor = new GeoIpDownloaderTaskExecutor(client, new HttpClient(), clusterService, threadPool); + geoIpDownloaderTaskExecutor = new GeoIpDownloaderTaskExecutor( + services.client(), + new HttpClient(), + services.clusterService(), + services.threadPool() + ); geoIpDownloaderTaskExecutor.init(); return List.of(databaseRegistry.get(), geoIpDownloaderTaskExecutor); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java index 52eccbe0dce90..62302331b38d8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java @@ -11,20 +11,13 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.painless.action.PainlessContextAction; import org.elasticsearch.painless.action.PainlessExecuteAction; import org.elasticsearch.painless.spi.PainlessExtension; @@ -36,17 +29,11 @@ import org.elasticsearch.plugins.ExtensiblePlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ScriptPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.ScriptModule; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.ArrayList; import java.util.Arrays; @@ -135,22 +122,7 @@ public ScriptEngine getScriptEngine(Settings settings, Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { // this is a hack to bind the painless script engine in guice (all components are added to guice), so that // the painless context api. this is a temporary measure until transport actions do no require guice return Collections.singletonList(painlessScriptEngine.get()); diff --git a/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java index f8f87011405fc..b07eb1b158087 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/elasticsearch/reindex/ReindexPlugin.java @@ -12,35 +12,23 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionType; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.reindex.BulkByScrollTask; import org.elasticsearch.index.reindex.DeleteByQueryAction; import org.elasticsearch.index.reindex.ReindexAction; import org.elasticsearch.index.reindex.UpdateByQueryAction; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.ArrayList; import java.util.Arrays; @@ -95,23 +83,10 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - return Collections.singletonList(new ReindexSslConfig(environment.settings(), environment, resourceWatcherService)); + public Collection createComponents(PluginServices services) { + return Collections.singletonList( + new ReindexSslConfig(services.environment().settings(), services.environment(), services.resourceWatcherService()) + ); } @Override diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java index 55b7b2cc902cc..5509e44b52a3e 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/ReindexFromRemoteWithAuthTests.java @@ -19,38 +19,25 @@ import org.elasticsearch.action.support.ActionFilter; import org.elasticsearch.action.support.ActionFilterChain; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.http.HttpInfo; import org.elasticsearch.index.reindex.ReindexAction; import org.elasticsearch.index.reindex.ReindexRequestBuilder; import org.elasticsearch.index.reindex.RemoteInfo; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestHeaderDefinition; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.root.MainRestPlugin; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.netty4.Netty4Plugin; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.junit.Before; import java.util.Arrays; @@ -58,7 +45,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; @@ -164,23 +150,8 @@ public static class TestPlugin extends Plugin implements ActionPlugin { private final SetOnce testFilter = new SetOnce<>(); @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - testFilter.set(new ReindexFromRemoteWithAuthTests.TestFilter(threadPool)); + public Collection createComponents(PluginServices services) { + testFilter.set(new ReindexFromRemoteWithAuthTests.TestFilter(services.threadPool())); return Collections.emptyList(); } diff --git a/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java b/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java index f88a96e765827..6ff9a40940e8c 100644 --- a/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java +++ b/modules/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java @@ -11,30 +11,19 @@ import com.azure.core.util.serializer.JacksonAdapter; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ReloadablePlugin; import org.elasticsearch.plugins.RepositoryPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Arrays; @@ -42,7 +31,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Supplier; /** * A plugin to add a repository type that writes to and from the Azure cloud storage service. @@ -84,23 +72,8 @@ public Map getRepositories( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - AzureClientProvider azureClientProvider = AzureClientProvider.create(threadPool, settings); + public Collection createComponents(PluginServices services) { + AzureClientProvider azureClientProvider = AzureClientProvider.create(services.threadPool(), settings); azureStoreService.set(createAzureStorageService(settings, azureClientProvider)); return List.of(azureClientProvider); } diff --git a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java index c194fe67d106e..97c065e771ffd 100644 --- a/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java +++ b/modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java @@ -12,29 +12,18 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.SpecialPermission; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.RepositoryMetadata; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ReloadablePlugin; import org.elasticsearch.plugins.RepositoryPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.metric.MeterRegistry; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import java.io.IOException; @@ -45,7 +34,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Supplier; /** * A plugin to add a repository type that writes to and from the AWS S3. @@ -92,25 +80,10 @@ protected S3Repository createRepository( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - service.set(s3Service(environment, clusterService.getSettings())); + public Collection createComponents(PluginServices services) { + service.set(s3Service(services.environment(), services.clusterService().getSettings())); this.service.get().refreshAndClearCache(S3ClientSettings.load(settings)); - meterRegistry.set(telemetryProvider.getMeterRegistry()); + meterRegistry.set(services.telemetryProvider().getMeterRegistry()); return List.of(service); } diff --git a/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java b/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java index 8057684375d69..fe33051df342e 100644 --- a/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java +++ b/modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java @@ -9,28 +9,17 @@ package org.elasticsearch.plugin.repository.url; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.blobstore.url.http.URLHttpClient; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.RepositoryPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.url.URLRepository; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import java.io.IOException; @@ -39,7 +28,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Supplier; public class URLRepositoryPlugin extends Plugin implements RepositoryPlugin { private final SetOnce httpClientFactory = new SetOnce<>(); @@ -76,22 +64,7 @@ public Map getRepositories( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { final URLHttpClient.Factory apacheURLHttpClientFactory = new URLHttpClient.Factory(); diff --git a/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java b/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java index 6cad1e057ef74..b0adda3b2062b 100644 --- a/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java +++ b/modules/runtime-fields-common/src/main/java/org/elasticsearch/runtimefields/RuntimeFieldsCommonPlugin.java @@ -8,28 +8,13 @@ package org.elasticsearch.runtimefields; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Collection; import java.util.List; -import java.util.function.Supplier; /** * The plugin class for all the runtime fields common functionality that requires large dependencies. @@ -64,23 +49,8 @@ public List> getSettings() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - grokHelper.finishInitializing(threadPool); + public Collection createComponents(PluginServices services) { + grokHelper.finishInitializing(services.threadPool()); return List.of(); } diff --git a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java index f425de279129b..e3dca57472ade 100644 --- a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java +++ b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java @@ -12,29 +12,14 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.util.SetOnce; import org.elasticsearch.Build; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ClusterPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.Scheduler; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Collection; import java.util.List; -import java.util.function.Supplier; public class SystemdPlugin extends Plugin implements ClusterPlugin { @@ -80,22 +65,7 @@ Scheduler.Cancellable extender() { } @Override - public Collection createComponents( - final Client client, - final ClusterService clusterService, - final ThreadPool threadPool, - final ResourceWatcherService resourceWatcherService, - final ScriptService scriptService, - final NamedXContentRegistry xContentRegistry, - final Environment environment, - final NodeEnvironment nodeEnvironment, - final NamedWriteableRegistry namedWriteableRegistry, - final IndexNameExpressionResolver expressionResolver, - final Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { if (enabled == false) { extender.set(null); return List.of(); @@ -107,7 +77,7 @@ public Collection createComponents( * Therefore, every fifteen seconds we send systemd a message via sd_notify to extend the timeout by thirty seconds. We will cancel * this scheduled task after we successfully notify systemd that we are ready. */ - extender.set(threadPool.scheduleWithFixedDelay(() -> { + extender.set(services.threadPool().scheduleWithFixedDelay(() -> { final int rc = sd_notify(0, "EXTEND_TIMEOUT_USEC=30000000"); if (rc < 0) { logger.warn("extending startup timeout via sd_notify failed with [{}]", rc); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java index fe24ed320d057..f4457a7db8b7c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/SimpleClusterStateIT.java @@ -15,11 +15,9 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexMetadata; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MappingMetadata; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.routing.RoutingTable; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; @@ -28,20 +26,11 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.ByteSizeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ClusterPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; @@ -55,7 +44,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -402,22 +390,9 @@ public List getNamedWriteables() { private final AtomicBoolean installed = new AtomicBoolean(); @Override - public Collection createComponents( - final Client client, - final ClusterService clusterService, - final ThreadPool threadPool, - final ResourceWatcherService resourceWatcherService, - final ScriptService scriptService, - final NamedXContentRegistry xContentRegistry, - final Environment environment, - final NodeEnvironment nodeEnvironment, - final NamedWriteableRegistry namedWriteableRegistry, - final IndexNameExpressionResolver expressionResolver, - final Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + ClusterService clusterService = services.clusterService(); + clusterService.addListener(event -> { final ClusterState state = event.state(); if (state.getBlocks().hasGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java index 11d32bb231a01..a31c3a08b8a4f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/health/GetHealthActionIT.java @@ -11,27 +11,15 @@ import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterName; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.metrics.Counters; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.health.node.HealthInfo; import org.elasticsearch.health.stats.HealthApiStatsAction; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.HealthPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.ArrayList; import java.util.Collection; @@ -40,7 +28,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.ExecutionException; -import java.util.function.Supplier; import java.util.stream.Stream; import static org.elasticsearch.common.util.CollectionUtils.appendToCopy; @@ -95,25 +82,10 @@ public List> getSettings() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - healthIndicatorServices.add(new IlmHealthIndicatorService(clusterService)); - healthIndicatorServices.add(new SlmHealthIndicatorService(clusterService)); - healthIndicatorServices.add(new ClusterCoordinationHealthIndicatorService(clusterService)); + public Collection createComponents(PluginServices services) { + healthIndicatorServices.add(new IlmHealthIndicatorService(services.clusterService())); + healthIndicatorServices.add(new SlmHealthIndicatorService(services.clusterService())); + healthIndicatorServices.add(new ClusterCoordinationHealthIndicatorService(services.clusterService())); return new ArrayList<>(healthIndicatorServices); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java index 707644e28228a..2e741d6691d24 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/health/HealthServiceIT.java @@ -9,37 +9,22 @@ package org.elasticsearch.health; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.health.node.DiskHealthInfo; import org.elasticsearch.health.node.FetchHealthInfoCacheAction; import org.elasticsearch.health.node.HealthInfo; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.HealthPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalTestCluster; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; import static org.elasticsearch.common.util.CollectionUtils.appendToCopy; import static org.hamcrest.Matchers.equalTo; @@ -124,22 +109,7 @@ public static final class TestHealthPlugin extends Plugin implements HealthPlugi private final List healthIndicatorServices = new ArrayList<>(); @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { healthIndicatorServices.add(new TestHealthIndicatorService()); return new ArrayList<>(healthIndicatorServices); } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java index ab4706c865be9..24372978834c6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/FinalPipelineIT.java @@ -17,17 +17,9 @@ import org.elasticsearch.action.ingest.PutPipelineRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.WriteRequest; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.ingest.AbstractProcessor; import org.elasticsearch.ingest.ConfigurationUtils; import org.elasticsearch.ingest.IngestDocument; @@ -35,14 +27,8 @@ import org.elasticsearch.ingest.Processor; import org.elasticsearch.plugins.IngestPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.XContentType; import org.junit.After; @@ -52,7 +38,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.BiConsumer; -import java.util.function.Supplier; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; @@ -392,26 +377,6 @@ public void testHighOrderFinalPipelinePreferred() throws IOException { public static class TestPlugin extends Plugin implements IngestPlugin { - @Override - public Collection createComponents( - final Client client, - final ClusterService clusterService, - final ThreadPool threadPool, - final ResourceWatcherService resourceWatcherService, - final ScriptService scriptService, - final NamedXContentRegistry xContentRegistry, - final Environment environment, - final NodeEnvironment nodeEnvironment, - final NamedWriteableRegistry namedWriteableRegistry, - final IndexNameExpressionResolver expressionResolver, - final Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - return List.of(); - } - @Override public Map getProcessors(Processor.Parameters parameters) { return Map.of( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java index fb0e3478c2cde..de783a28bce1d 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/SettingsListenerIT.java @@ -7,33 +7,18 @@ */ package org.elasticsearch.index; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.AbstractModule; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -63,22 +48,7 @@ public void onIndexModule(IndexModule module) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { return Collections.singletonList(service); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java index f8d2b0e464b72..fa4d4c0fbb669 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/ingest/IngestAsyncProcessorIT.java @@ -13,32 +13,18 @@ import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.ingest.PutPipelineRequest; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.IngestPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.XContentType; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import java.util.function.Supplier; import static org.hamcrest.Matchers.equalTo; @@ -85,23 +71,8 @@ public static class TestPlugin extends Plugin implements IngestPlugin { private ThreadPool threadPool; @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - this.threadPool = threadPool; + public Collection createComponents(PluginServices services) { + this.threadPool = services.threadPool(); return List.of(); } diff --git a/server/src/test/java/org/elasticsearch/node/NodeTests.java b/server/src/test/java/org/elasticsearch/node/NodeTests.java index 9a9ffeea84610..218073a1eb3f1 100644 --- a/server/src/test/java/org/elasticsearch/node/NodeTests.java +++ b/server/src/test/java/org/elasticsearch/node/NodeTests.java @@ -11,23 +11,17 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterName; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.LifecycleComponent; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.core.RestApiVersion; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.gateway.PersistedClusterStateService; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexService; @@ -43,18 +37,14 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.PluginsServiceTests; import org.elasticsearch.plugins.RecoveryPlannerPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.MockHttpTransport; import org.elasticsearch.test.rest.FakeRestRequest; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.ContextParser; import org.elasticsearch.xcontent.MediaType; import org.elasticsearch.xcontent.NamedObjectNotFoundException; @@ -77,7 +67,6 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; import static org.elasticsearch.test.NodeRoles.dataNode; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -442,22 +431,7 @@ protected void doClose() throws IOException { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { List components = new ArrayList<>(); components.add(new PluginComponentBinding<>(MyInterface.class, getRandomBool() ? new Foo() : new Bar())); return components; diff --git a/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java b/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java index c5e974c4abcfd..5e80b6d217a55 100644 --- a/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java +++ b/server/src/test/java/org/elasticsearch/plugins/PluginIntrospectorTests.java @@ -8,38 +8,24 @@ package org.elasticsearch.plugins; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.breaker.CircuitBreaker; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.health.HealthIndicatorService; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.TokenFilterFactory; import org.elasticsearch.index.engine.EngineFactory; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.indices.breaker.BreakerSettings; import org.elasticsearch.indices.recovery.plan.RecoveryPlannerService; import org.elasticsearch.indices.recovery.plan.ShardSnapshotsService; import org.elasticsearch.ingest.Processor; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.PrivilegedOperations; import org.elasticsearch.test.compiler.InMemoryJavaCompiler; import org.elasticsearch.test.jar.JarUtils; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.net.URL; import java.net.URLClassLoader; @@ -259,22 +245,7 @@ public final class FooPlugin extends q.AbstractFooPlugin { } public void testOverriddenMethodsBasic() { class FooPlugin extends Plugin { @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { return null; } } diff --git a/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java b/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java index de2ac43f7fb51..aa9ff52b00824 100644 --- a/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java +++ b/test/external-modules/seek-tracking-directory/src/main/java/org/elasticsearch/test/seektracker/SeekTrackerPlugin.java @@ -11,31 +11,18 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionType; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexModule; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Collection; import java.util.Collections; @@ -66,22 +53,7 @@ public List> getSettings() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { return Collections.singletonList(seekStatsService); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java b/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java index 9b9f464d8dff3..d504bebaa33e7 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java +++ b/test/framework/src/main/java/org/elasticsearch/test/MockIndexEventListener.java @@ -7,18 +7,11 @@ */ package org.elasticsearch.test; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.routing.ShardRouting; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.Nullable; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexService; @@ -27,21 +20,13 @@ import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.ShardId; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; /** * This is a testing plugin that registers a generic @@ -76,22 +61,7 @@ public void onIndexModule(IndexModule module) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { return Collections.singletonList(listener); } } diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java index 9360f97990c82..28d9d87f147d3 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java @@ -8,27 +8,14 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.mapper.Mapper; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SearchPlugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.analytics.action.AnalyticsInfoTransportAction; import org.elasticsearch.xpack.analytics.action.AnalyticsUsageTransportAction; import org.elasticsearch.xpack.analytics.action.TransportAnalyticsStatsAction; @@ -64,7 +51,6 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; -import java.util.function.Supplier; public class AnalyticsPlugin extends Plugin implements SearchPlugin, ActionPlugin, MapperPlugin { private final AnalyticsUsage usage = new AnalyticsUsage(); @@ -175,22 +161,7 @@ public List> getAggregationExtentions() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { return List.of(usage); } diff --git a/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java b/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java index a0e6d0daac322..3dbe59dc825ff 100644 --- a/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java +++ b/x-pack/plugin/async/src/main/java/org/elasticsearch/xpack/async/AsyncResultsIndexPlugin.java @@ -7,33 +7,18 @@ package org.elasticsearch.xpack.async; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.OriginSettingClient; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.async.AsyncTaskIndexService; import org.elasticsearch.xpack.core.async.AsyncTaskMaintenanceService; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.function.Supplier; import static org.elasticsearch.xpack.core.ClientHelper.ASYNC_SEARCH_ORIGIN; @@ -61,31 +46,16 @@ public String getFeatureDescription() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { List components = new ArrayList<>(); - if (DiscoveryNode.canContainData(environment.settings())) { + if (DiscoveryNode.canContainData(services.environment().settings())) { // only data nodes should be eligible to run the maintenance service. AsyncTaskMaintenanceService maintenanceService = new AsyncTaskMaintenanceService( - clusterService, - nodeEnvironment.nodeId(), + services.clusterService(), + services.nodeEnvironment().nodeId(), settings, - threadPool, - new OriginSettingClient(client, ASYNC_SEARCH_ORIGIN) + services.threadPool(), + new OriginSettingClient(services.client(), ASYNC_SEARCH_ORIGIN) ); components.add(maintenanceService); } diff --git a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java index c832a3c7eb461..e9d54826436c2 100644 --- a/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java +++ b/x-pack/plugin/autoscaling/src/main/java/org/elasticsearch/xpack/autoscaling/Autoscaling.java @@ -10,7 +10,6 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; @@ -23,22 +22,14 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.License; import org.elasticsearch.license.LicensedFeature; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.ExtensiblePlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xpack.autoscaling.action.DeleteAutoscalingPolicyAction; @@ -109,27 +100,16 @@ public Autoscaling() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - this.clusterServiceHolder.set(clusterService); - this.allocationServiceHolder.set(allocationService); + public Collection createComponents(PluginServices services) { + this.clusterServiceHolder.set(services.clusterService()); + this.allocationServiceHolder.set(services.allocationService()); var capacityServiceHolder = new AutoscalingCalculateCapacityService.Holder(this); this.reservedAutoscalingPolicyAction.set(new ReservedAutoscalingPolicyAction(capacityServiceHolder)); - return List.of(capacityServiceHolder, autoscalingLicenseChecker, new AutoscalingNodeInfoService(clusterService, client)); + return List.of( + capacityServiceHolder, + autoscalingLicenseChecker, + new AutoscalingNodeInfoService(services.clusterService(), services.client()) + ); } @Override diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java index a139b9a55f1be..7234b7babffdc 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/Ccr.java @@ -18,7 +18,6 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -30,11 +29,9 @@ import org.elasticsearch.common.settings.SettingsModule; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.engine.EngineFactory; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.persistent.PersistentTaskParams; import org.elasticsearch.persistent.PersistentTasksExecutor; @@ -44,17 +41,13 @@ import org.elasticsearch.plugins.PersistentTaskPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.RepositoryPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xpack.ccr.action.AutoFollowCoordinator; @@ -177,44 +170,29 @@ public Ccr(final Settings settings) { @Override @SuppressWarnings("HiddenField") - public Collection createComponents( - final Client client, - final ClusterService clusterService, - final ThreadPool threadPool, - final ResourceWatcherService resourceWatcherService, - final ScriptService scriptService, - final NamedXContentRegistry xContentRegistry, - final Environment environment, - final NodeEnvironment nodeEnvironment, - final NamedWriteableRegistry namedWriteableRegistry, - final IndexNameExpressionResolver expressionResolver, - final Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - this.client = client; + public Collection createComponents(PluginServices services) { + this.client = services.client(); if (enabled == false) { return emptyList(); } - CcrSettings ccrSettings = new CcrSettings(settings, clusterService.getClusterSettings()); + CcrSettings ccrSettings = new CcrSettings(settings, services.clusterService().getClusterSettings()); this.ccrSettings.set(ccrSettings); - CcrRestoreSourceService restoreSourceService = new CcrRestoreSourceService(threadPool, ccrSettings); + CcrRestoreSourceService restoreSourceService = new CcrRestoreSourceService(services.threadPool(), ccrSettings); this.restoreSourceService.set(restoreSourceService); - return Arrays.asList( + return List.of( ccrLicenseChecker, restoreSourceService, - new CcrRepositoryManager(settings, clusterService, client), - new ShardFollowTaskCleaner(clusterService, threadPool, client), + new CcrRepositoryManager(settings, services.clusterService(), client), + new ShardFollowTaskCleaner(services.clusterService(), services.threadPool(), client), new AutoFollowCoordinator( settings, client, - clusterService, + services.clusterService(), ccrLicenseChecker, - threadPool::relativeTimeInMillis, - threadPool::absoluteTimeInMillis, - threadPool.executor(Ccr.CCR_THREAD_POOL_NAME) + services.threadPool()::relativeTimeInMillis, + services.threadPool()::absoluteTimeInMillis, + services.threadPool().executor(Ccr.CCR_THREAD_POOL_NAME) ) ); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java index 1c8881637d4c6..d02e3f43d80cb 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackPlugin.java @@ -13,17 +13,14 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.support.TransportAction; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.DataTier; import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.ClusterSettings; @@ -35,12 +32,10 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.core.Booleans; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexSettingProvider; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.engine.EngineFactory; import org.elasticsearch.index.mapper.MetadataFieldMapper; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.recovery.RecoverySettings; import org.elasticsearch.license.ClusterStateLicenseService; import org.elasticsearch.license.DeleteLicenseAction; @@ -83,14 +78,10 @@ import org.elasticsearch.protocol.xpack.XPackInfoRequest; import org.elasticsearch.protocol.xpack.XPackInfoResponse; import org.elasticsearch.protocol.xpack.XPackUsageRequest; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.repositories.Repository; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.snapshots.sourceonly.SourceOnlySnapshotRepository; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; @@ -321,33 +312,24 @@ public Settings additionalSettings() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { List components = new ArrayList<>(); - final SSLService sslService = createSSLService(environment, resourceWatcherService); + final SSLService sslService = createSSLService(services.environment(), services.resourceWatcherService()); LicenseService licenseService = getLicenseService(); if (licenseService == null) { - licenseService = new ClusterStateLicenseService(settings, threadPool, clusterService, getClock(), getLicenseState()); + licenseService = new ClusterStateLicenseService( + settings, + services.threadPool(), + services.clusterService(), + getClock(), + getLicenseState() + ); setLicenseService(licenseService); } - setEpochMillisSupplier(threadPool::absoluteTimeInMillis); + setEpochMillisSupplier(services.threadPool()::absoluteTimeInMillis); // It is useful to override these as they are what guice is injecting into actions components.add(sslService); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 349ff1042d3a9..dd060653a4f34 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -8,31 +8,18 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.logging.RateLimitingFilter; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.deprecation.logging.DeprecationCacheResetAction; import org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent; import org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingTemplateRegistry; @@ -88,43 +75,29 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { final DeprecationIndexingTemplateRegistry templateRegistry = new DeprecationIndexingTemplateRegistry( - environment.settings(), - clusterService, - threadPool, - client, - xContentRegistry + services.environment().settings(), + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); templateRegistry.initialize(); final RateLimitingFilter rateLimitingFilterForIndexing = new RateLimitingFilter(); // enable on start. - rateLimitingFilterForIndexing.setUseXOpaqueId(USE_X_OPAQUE_ID_IN_FILTERING.get(environment.settings())); - clusterService.getClusterSettings() + rateLimitingFilterForIndexing.setUseXOpaqueId(USE_X_OPAQUE_ID_IN_FILTERING.get(services.environment().settings())); + services.clusterService() + .getClusterSettings() .addSettingsUpdateConsumer(USE_X_OPAQUE_ID_IN_FILTERING, rateLimitingFilterForIndexing::setUseXOpaqueId); final DeprecationIndexingComponent component = DeprecationIndexingComponent.createDeprecationIndexingComponent( - client, - environment.settings(), + services.client(), + services.environment().settings(), rateLimitingFilterForIndexing, - WRITE_DEPRECATION_LOGS_TO_INDEX.get(environment.settings()), // pass the default on startup - clusterService + WRITE_DEPRECATION_LOGS_TO_INDEX.get(services.environment().settings()), // pass the default on startup + services.clusterService() ); return List.of(component, rateLimitingFilterForIndexing); diff --git a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java index 5fb20a883560f..8e0c96c6ee245 100644 --- a/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java +++ b/x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichPlugin.java @@ -8,13 +8,10 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; @@ -22,22 +19,14 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.ingest.Processor; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.IngestPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xpack.core.XPackPlugin; @@ -189,44 +178,29 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { EnrichPolicyLocks enrichPolicyLocks = new EnrichPolicyLocks(); EnrichPolicyExecutor enrichPolicyExecutor = new EnrichPolicyExecutor( settings, - clusterService, - indicesService, - client, - threadPool, - expressionResolver, + services.clusterService(), + services.indicesService(), + services.client(), + services.threadPool(), + services.indexNameExpressionResolver(), enrichPolicyLocks, System::currentTimeMillis ); EnrichPolicyMaintenanceService enrichPolicyMaintenanceService = new EnrichPolicyMaintenanceService( settings, - client, - clusterService, - threadPool, + services.client(), + services.clusterService(), + services.threadPool(), enrichPolicyLocks ); enrichPolicyMaintenanceService.initialize(); return List.of( enrichPolicyLocks, - new EnrichCoordinatorProxyAction.Coordinator(client, settings), + new EnrichCoordinatorProxyAction.Coordinator(services.client(), settings), enrichPolicyMaintenanceService, enrichPolicyExecutor, enrichCache diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index ad2c033b0ee0c..cfe6e65b20263 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -9,20 +9,13 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.logging.LogManager; @@ -31,14 +24,8 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.application.analytics.AnalyticsTemplateRegistry; import org.elasticsearch.xpack.application.analytics.action.DeleteAnalyticsCollectionAction; import org.elasticsearch.xpack.application.analytics.action.GetAnalyticsCollectionAction; @@ -197,41 +184,26 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { if (enabled == false) { return Collections.emptyList(); } // Behavioral analytics components final AnalyticsTemplateRegistry analyticsTemplateRegistry = new AnalyticsTemplateRegistry( - clusterService, - threadPool, - client, - xContentRegistry + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); analyticsTemplateRegistry.initialize(); // Connector components final ConnectorTemplateRegistry connectorTemplateRegistry = new ConnectorTemplateRegistry( - clusterService, - threadPool, - client, - xContentRegistry + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); connectorTemplateRegistry.initialize(); diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java index d65a81db11266..881cb083a48f2 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPlugin.java @@ -12,32 +12,21 @@ import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.breaker.CircuitBreaker; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.breaker.BreakerSettings; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.CircuitBreakerPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; @@ -71,23 +60,8 @@ public class EqlPlugin extends Plugin implements ActionPlugin, CircuitBreakerPlu public EqlPlugin() {} @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - return createComponents(client, environment.settings(), clusterService); + public Collection createComponents(PluginServices services) { + return createComponents(services.client(), services.environment().settings(), services.clusterService()); } private Collection createComponents(Client client, Settings settings, ClusterService clusterService) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java index fba2f00f0b314..4c05a70e607f9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java @@ -8,11 +8,8 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; @@ -33,21 +30,13 @@ import org.elasticsearch.compute.operator.exchange.ExchangeSinkOperator; import org.elasticsearch.compute.operator.exchange.ExchangeSourceOperator; import org.elasticsearch.compute.operator.topn.TopNOperatorStatus; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; import org.elasticsearch.xpack.esql.EsqlInfoTransportAction; @@ -88,28 +77,25 @@ public class EsqlPlugin extends Plugin implements ActionPlugin { ); @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - CircuitBreaker circuitBreaker = indicesService.getBigArrays().breakerService().getBreaker("request"); + public Collection createComponents(PluginServices services) { + CircuitBreaker circuitBreaker = services.indicesService().getBigArrays().breakerService().getBreaker("request"); Objects.requireNonNull(circuitBreaker, "request circuit breaker wasn't set"); - BlockFactory blockFactory = new BlockFactory(circuitBreaker, indicesService.getBigArrays().withCircuitBreaking()); + BlockFactory blockFactory = new BlockFactory(circuitBreaker, services.indicesService().getBigArrays().withCircuitBreaking()); return List.of( - new PlanExecutor(new IndexResolver(client, clusterService.getClusterName().value(), EsqlDataTypeRegistry.INSTANCE, Set::of)), - new ExchangeService(clusterService.getSettings(), threadPool, EsqlPlugin.ESQL_THREAD_POOL_NAME, blockFactory), + new PlanExecutor( + new IndexResolver( + services.client(), + services.clusterService().getClusterName().value(), + EsqlDataTypeRegistry.INSTANCE, + Set::of + ) + ), + new ExchangeService( + services.clusterService().getSettings(), + services.threadPool(), + EsqlPlugin.ESQL_THREAD_POOL_NAME, + blockFactory + ), blockFactory ); } diff --git a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java index 21f0e846ebfa6..fb8d68541d4d1 100644 --- a/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java +++ b/x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/Fleet.java @@ -23,30 +23,19 @@ import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.indices.ExecutorNames; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemDataStreamDescriptor; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.SystemIndexDescriptor.Type; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentParserConfiguration; import org.elasticsearch.xcontent.XContentType; @@ -100,28 +89,13 @@ public class Fleet extends Plugin implements SystemIndexPlugin { private static final int FLEET_ACTIONS_RESULTS_MAPPINGS_VERSION = 1; @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { FleetTemplateRegistry registry = new FleetTemplateRegistry( - environment.settings(), - clusterService, - threadPool, - client, - xContentRegistry + services.environment().settings(), + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); registry.initialize(); return List.of(); diff --git a/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java b/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java index 943394f326653..c4c91dac9a513 100644 --- a/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java +++ b/x-pack/plugin/identity-provider/src/main/java/org/elasticsearch/xpack/idp/IdentityProviderPlugin.java @@ -11,31 +11,18 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.SecurityContext; import org.elasticsearch.xpack.core.ssl.X509KeyPairSettings; @@ -86,35 +73,24 @@ public class IdentityProviderPlugin extends Plugin implements ActionPlugin { private Settings settings; @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - settings = environment.settings(); + public Collection createComponents(PluginServices services) { + settings = services.environment().settings(); enabled = ENABLED_SETTING.get(settings); if (enabled == false) { return List.of(); } SamlInit.initialize(); - final SamlServiceProviderIndex index = new SamlServiceProviderIndex(client, clusterService); - final SecurityContext securityContext = new SecurityContext(settings, threadPool.getThreadContext()); + final SamlServiceProviderIndex index = new SamlServiceProviderIndex(services.client(), services.clusterService()); + final SecurityContext securityContext = new SecurityContext(settings, services.threadPool().getThreadContext()); final ServiceProviderDefaults serviceProviderDefaults = ServiceProviderDefaults.forSettings(settings); - final ApplicationActionsResolver actionsResolver = new ApplicationActionsResolver(settings, serviceProviderDefaults, client); - final UserPrivilegeResolver userPrivilegeResolver = new UserPrivilegeResolver(client, securityContext, actionsResolver); + final ApplicationActionsResolver actionsResolver = new ApplicationActionsResolver( + settings, + serviceProviderDefaults, + services.client() + ); + final UserPrivilegeResolver userPrivilegeResolver = new UserPrivilegeResolver(services.client(), securityContext, actionsResolver); final SamlServiceProviderFactory serviceProviderFactory = new SamlServiceProviderFactory(serviceProviderDefaults); final SamlServiceProviderResolver registeredServiceProviderResolver = new SamlServiceProviderResolver( @@ -123,13 +99,13 @@ public Collection createComponents( serviceProviderFactory ); final WildcardServiceProviderResolver wildcardServiceProviderResolver = WildcardServiceProviderResolver.create( - environment, - resourceWatcherService, - scriptService, + services.environment(), + services.resourceWatcherService(), + services.scriptService(), serviceProviderFactory ); final SamlIdentityProvider idp = SamlIdentityProvider.builder(registeredServiceProviderResolver, wildcardServiceProviderResolver) - .fromSettings(environment) + .fromSettings(services.environment()) .serviceProviderDefaults(serviceProviderDefaults) .build(); diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java index 06ac4673264f3..4e8df685a25a7 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/UpdateSettingsStepTests.java @@ -7,30 +7,18 @@ package org.elasticsearch.xpack.ilm; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateObserver; import org.elasticsearch.cluster.metadata.IndexMetadata; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.AbstractModule; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexModule; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.ilm.Step.StepKey; import org.elasticsearch.xpack.core.ilm.UpdateSettingsStep; import org.junit.After; @@ -39,7 +27,6 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.xpack.ilm.UpdateSettingsStepTests.SettingsTestingService.INVALID_VALUE; @@ -62,22 +49,7 @@ public void onIndexModule(IndexModule module) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { return List.of(service); } diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java index 6409c2b72e1f1..53e4d3de463fd 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycle.java @@ -10,37 +10,25 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.OriginSettingClient; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.core.IOUtils; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.health.HealthIndicatorService; import org.elasticsearch.index.IndexModule; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.HealthPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xpack.cluster.action.MigrateToDataTiersAction; @@ -154,48 +142,39 @@ protected XPackLicenseState getLicenseState() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { final List components = new ArrayList<>(); ILMHistoryTemplateRegistry ilmTemplateRegistry = new ILMHistoryTemplateRegistry( settings, - clusterService, - threadPool, - client, - xContentRegistry + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); ilmTemplateRegistry.initialize(); - ilmHistoryStore.set(new ILMHistoryStore(new OriginSettingClient(client, INDEX_LIFECYCLE_ORIGIN), clusterService, threadPool)); + ilmHistoryStore.set( + new ILMHistoryStore( + new OriginSettingClient(services.client(), INDEX_LIFECYCLE_ORIGIN), + services.clusterService(), + services.threadPool() + ) + ); /* * Here we use threadPool::absoluteTimeInMillis rather than System::currentTimeInMillis because snapshot start time is set using * ThreadPool.absoluteTimeInMillis(). ThreadPool.absoluteTimeInMillis() returns a cached time that can be several hundred * milliseconds behind System.currentTimeMillis(). The result is that a snapshot taken after a policy is created can have a start * time that is before the policy's (or action's) start time if System::currentTimeInMillis is used here. */ - LongSupplier nowSupplier = threadPool::absoluteTimeInMillis; + LongSupplier nowSupplier = services.threadPool()::absoluteTimeInMillis; indexLifecycleInitialisationService.set( new IndexLifecycleService( settings, - client, - clusterService, - threadPool, + services.client(), + services.clusterService(), + services.threadPool(), getClock(), nowSupplier, - xContentRegistry, + services.xContentRegistry(), ilmHistoryStore.get(), getLicenseState() ) @@ -204,15 +183,17 @@ public Collection createComponents( ilmHealthIndicatorService.set( new IlmHealthIndicatorService( - clusterService, + services.clusterService(), new IlmHealthIndicatorService.StagnatingIndicesFinder( - clusterService, + services.clusterService(), IlmHealthIndicatorService.RULES_BY_ACTION_CONFIG.values(), System::currentTimeMillis ) ) ); - reservedLifecycleAction.set(new ReservedLifecycleAction(xContentRegistry, client, XPackPlugin.getSharedLicenseState())); + reservedLifecycleAction.set( + new ReservedLifecycleAction(services.xContentRegistry(), services.client(), XPackPlugin.getSharedLicenseState()) + ); return components; } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java index f38f90f773b7f..2f0f95cf8a911 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/InferencePlugin.java @@ -10,11 +10,8 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; @@ -23,24 +20,15 @@ import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.InferenceServicePlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.inference.action.DeleteInferenceModelAction; import org.elasticsearch.xpack.inference.action.GetInferenceModelAction; @@ -108,25 +96,12 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - httpManager.set(HttpClientManager.create(settings, threadPool, clusterService)); - httpRequestSenderFactory.set(new HttpRequestSenderFactory(threadPool, httpManager.get(), clusterService, settings)); - ModelRegistry modelRegistry = new ModelRegistry(client); + public Collection createComponents(PluginServices services) { + httpManager.set(HttpClientManager.create(settings, services.threadPool(), services.clusterService())); + httpRequestSenderFactory.set( + new HttpRequestSenderFactory(services.threadPool(), httpManager.get(), services.clusterService(), settings) + ); + ModelRegistry modelRegistry = new ModelRegistry(services.client()); return List.of(modelRegistry); } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index cb38e23c7f8eb..f4bce4906c0b0 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -29,7 +29,6 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -45,12 +44,10 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.analysis.CharFilterFactory; import org.elasticsearch.index.analysis.TokenizerFactory; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.indices.AssociatedIndexDescriptor; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.elasticsearch.indices.breaker.BreakerSettings; @@ -73,15 +70,11 @@ import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.plugins.ShutdownAwarePlugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.ContextParser; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; @@ -884,22 +877,14 @@ public List> getRescorers() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); + Environment environment = services.environment(); + NamedXContentRegistry xContentRegistry = services.xContentRegistry(); + IndexNameExpressionResolver indexNameExpressionResolver = services.indexNameExpressionResolver(); + if (enabled == false) { // Holders for @link(MachineLearningFeatureSetUsage) which needs access to job manager and ML extension, // both empty if ML is disabled diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java index 47e24b60896da..bab8e5b22c37a 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java @@ -12,17 +12,12 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.License; import org.elasticsearch.license.LicenseService; import org.elasticsearch.license.LicensedFeature; @@ -30,14 +25,9 @@ import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ReloadablePlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; @@ -118,22 +108,11 @@ protected LicenseService getLicenseService() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); + final ClusterSettings clusterSettings = clusterService.getClusterSettings(); final CleanerService cleanerService = new CleanerService(settings, clusterSettings, threadPool); final SSLService dynamicSSLService = getSslService().createDynamicSSLService(); @@ -157,7 +136,14 @@ public Collection createComponents( Set collectors = new HashSet<>(); collectors.add(new IndexStatsCollector(clusterService, getLicenseState(), client)); collectors.add( - new ClusterStatsCollector(settings, clusterService, getLicenseState(), client, getLicenseService(), expressionResolver) + new ClusterStatsCollector( + settings, + clusterService, + getLicenseState(), + client, + getLicenseService(), + services.indexNameExpressionResolver() + ) ); collectors.add(new ShardsCollector(clusterService, getLicenseState())); collectors.add(new NodeStatsCollector(clusterService, getLicenseState(), client)); @@ -175,7 +161,7 @@ public Collection createComponents( clusterService, threadPool, client, - xContentRegistry + services.xContentRegistry() ); templateRegistry.initialize(); diff --git a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java index 5fc5d20461fe8..955cf0396326b 100644 --- a/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java +++ b/x-pack/plugin/old-lucene-versions/src/main/java/org/elasticsearch/xpack/lucene/bwc/OldLuceneVersions.java @@ -14,23 +14,17 @@ import org.elasticsearch.Build; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexMetadata; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.Strings; import org.elasticsearch.common.UUIDs; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexVersion; @@ -41,7 +35,6 @@ import org.elasticsearch.index.shard.IndexEventListener; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.translog.TranslogStats; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.License; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.LicensedFeature; @@ -52,15 +45,10 @@ import org.elasticsearch.plugins.IndexStorePlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.RepositoryPlugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotRestoreException; import org.elasticsearch.snapshots.sourceonly.SourceOnlySnapshotRepository; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; @@ -75,7 +63,6 @@ import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.function.Supplier; public class OldLuceneVersions extends Plugin implements IndexStorePlugin, ClusterPlugin, RepositoryPlugin, ActionPlugin, EnginePlugin { @@ -90,24 +77,12 @@ public class OldLuceneVersions extends Plugin implements IndexStorePlugin, Clust private final SetOnce failShardsListener = new SetOnce<>(); @Override - public Collection createComponents( - final Client client, - final ClusterService clusterService, - final ThreadPool threadPool, - final ResourceWatcherService resourceWatcherService, - final ScriptService scriptService, - final NamedXContentRegistry xContentRegistry, - final Environment environment, - final NodeEnvironment nodeEnvironment, - final NamedWriteableRegistry registry, - final IndexNameExpressionResolver resolver, - final Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); + this.failShardsListener.set(new FailShardsOnInvalidLicenseClusterListener(getLicenseState(), clusterService.getRerouteService())); - if (DiscoveryNode.isMasterNode(environment.settings())) { + if (DiscoveryNode.isMasterNode(services.environment().settings())) { // We periodically look through the indices and identify if there are any archive indices, // then marking the feature as used. We do this on each master node so that if one master fails, the // continue reporting usage state. diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java index d37a5be7543bd..f98c22b3bde30 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingPlugin.java @@ -15,30 +15,20 @@ import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; @@ -81,24 +71,13 @@ public ProfilingPlugin(Settings settings) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); + logger.info("Profiling is {}", enabled ? "enabled" : "disabled"); - registry.set(new ProfilingIndexTemplateRegistry(settings, clusterService, threadPool, client, xContentRegistry)); + registry.set(new ProfilingIndexTemplateRegistry(settings, clusterService, threadPool, client, services.xContentRegistry())); indexStateResolver.set(new IndexStateResolver(PROFILING_CHECK_OUTDATED_INDICES.get(settings))); clusterService.getClusterSettings().addSettingsUpdateConsumer(PROFILING_CHECK_OUTDATED_INDICES, this::updateCheckOutdatedIndices); diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java index f74dba71ebb83..98f6da9ba6a58 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshots.java @@ -23,13 +23,11 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.RerouteService; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.DataTier; import org.elasticsearch.cluster.routing.allocation.ExistingShardsAllocator; import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; @@ -41,7 +39,6 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Releasables; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettings; @@ -51,7 +48,6 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.index.translog.TranslogStats; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.XPackLicenseState; @@ -67,15 +63,11 @@ import org.elasticsearch.repositories.blobstore.BlobStoreRepository; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.snapshots.SearchableSnapshotsSettings; import org.elasticsearch.snapshots.sourceonly.SourceOnlySnapshotRepository; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.ScalingExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; @@ -319,24 +311,14 @@ public List> getSettings() { } @Override - public Collection createComponents( - final Client client, - final ClusterService clusterService, - final ThreadPool threadPool, - final ResourceWatcherService resourceWatcherService, - final ScriptService scriptService, - final NamedXContentRegistry xContentRegistry, - final Environment environment, - final NodeEnvironment nodeEnvironment, - final NamedWriteableRegistry registry, - final IndexNameExpressionResolver resolver, - final Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); + NodeEnvironment nodeEnvironment = services.nodeEnvironment(); + final List components = new ArrayList<>(); - this.repositoriesServiceSupplier = repositoriesServiceSupplier; + this.repositoriesServiceSupplier = services.repositoriesServiceSupplier(); this.threadPool.set(threadPool); this.failShardsListener.set(new FailShardsOnInvalidLicenseClusterListener(getLicenseState(), clusterService.getRerouteService())); if (DiscoveryNode.canContainData(settings)) { @@ -364,7 +346,7 @@ public Collection createComponents( PersistentCache.cleanUp(settings, nodeEnvironment); } - if (DiscoveryNode.isMasterNode(environment.settings())) { + if (DiscoveryNode.isMasterNode(services.environment().settings())) { // Tracking usage of searchable snapshots is too costly to do on each individually mounted snapshot. // Instead, we periodically look through the indices and identify if any are searchable snapshots, // then marking the feature as used. We do this on each master node so that if one master fails, the diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index fd38b40683f71..42b4c8c459eb0 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -28,7 +28,6 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.Strings; @@ -53,7 +52,6 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Nullable; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.env.NodeMetadata; import org.elasticsearch.http.HttpPreRequest; import org.elasticsearch.http.HttpServerTransport; @@ -61,7 +59,6 @@ import org.elasticsearch.http.netty4.internal.HttpHeadersAuthenticatorUtils; import org.elasticsearch.http.netty4.internal.HttpValidator; import org.elasticsearch.index.IndexModule; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.ingest.Processor; @@ -81,7 +78,6 @@ import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.plugins.SystemIndexPlugin; import org.elasticsearch.plugins.interceptor.RestServerActionPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; @@ -90,7 +86,6 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.internal.ShardSearchRequest; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.telemetry.tracing.Tracer; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; @@ -624,33 +619,18 @@ protected XPackLicenseState getLicenseState() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { try { return createComponents( - client, - threadPool, - clusterService, - resourceWatcherService, - scriptService, - xContentRegistry, - environment, - nodeEnvironment.nodeMetadata(), - expressionResolver + services.client(), + services.threadPool(), + services.clusterService(), + services.resourceWatcherService(), + services.scriptService(), + services.xContentRegistry(), + services.environment(), + services.nodeEnvironment().nodeMetadata(), + services.indexNameExpressionResolver() ); } catch (final Exception e) { throw new IllegalStateException("security initialization failed", e); diff --git a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java index 6c5e1e6af69ce..15e16d2a86910 100644 --- a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java +++ b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownTasksIT.java @@ -21,15 +21,11 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.SettingsModule; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.persistent.AllocatedPersistentTask; import org.elasticsearch.persistent.PersistentTaskParams; import org.elasticsearch.persistent.PersistentTaskState; @@ -38,12 +34,8 @@ import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.plugins.PersistentTaskPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ObjectParser; import org.elasticsearch.xcontent.ParseField; @@ -57,7 +49,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Supplier; import java.util.stream.Collectors; import static org.hamcrest.Matchers.contains; @@ -134,23 +125,8 @@ public static class TaskPlugin extends Plugin implements PersistentTaskPlugin { TaskExecutor taskExecutor; @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - taskExecutor = new TaskExecutor(client, clusterService, threadPool); + public Collection createComponents(PluginServices services) { + taskExecutor = new TaskExecutor(services.client(), services.clusterService(), services.threadPool()); return Collections.singletonList(taskExecutor); } diff --git a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java index 754ba7970eaa2..8c85bdb11dfa2 100644 --- a/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java +++ b/x-pack/plugin/shutdown/src/main/java/org/elasticsearch/xpack/shutdown/ShutdownPlugin.java @@ -9,29 +9,16 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Arrays; import java.util.Collection; @@ -41,24 +28,9 @@ public class ShutdownPlugin extends Plugin implements ActionPlugin { @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { - NodeSeenService nodeSeenService = new NodeSeenService(clusterService); + NodeSeenService nodeSeenService = new NodeSeenService(services.clusterService()); return Collections.singletonList(nodeSeenService); } diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java index 3664b4efbcf9a..74094c83d4bcb 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SnapshotLifecycle.java @@ -15,31 +15,22 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.core.IOUtils; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.health.HealthIndicatorService; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.HealthPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.ParseField; import org.elasticsearch.xpack.core.XPackPlugin; @@ -83,6 +74,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -123,22 +115,10 @@ protected XPackLicenseState getLicenseState() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); final List components = new ArrayList<>(); SnapshotLifecycleTemplateRegistry templateRegistry = new SnapshotLifecycleTemplateRegistry( @@ -146,7 +126,7 @@ public Collection createComponents( clusterService, threadPool, client, - xContentRegistry + services.xContentRegistry() ); templateRegistry.initialize(); snapshotHistoryStore.set(new SnapshotHistoryStore(new OriginSettingClient(client, INDEX_LIFECYCLE_ORIGIN), clusterService)); @@ -167,7 +147,7 @@ public Collection createComponents( ) ); snapshotRetentionService.get().init(clusterService); - components.addAll(Arrays.asList(snapshotLifecycleService.get(), snapshotHistoryStore.get(), snapshotRetentionService.get())); + Collections.addAll(components, snapshotLifecycleService.get(), snapshotHistoryStore.get(), snapshotRetentionService.get()); slmHealthIndicatorService.set(new SlmHealthIndicatorService(clusterService)); return components; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java index c5f8eee643858..c6e0b5067ee08 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPlugin.java @@ -11,30 +11,20 @@ import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.license.License; import org.elasticsearch.license.LicenseUtils; import org.elasticsearch.license.LicensedFeature; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; @@ -88,24 +78,13 @@ protected XPackLicenseState getLicenseState() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - - return createComponents(client, environment.settings(), clusterService, namedWriteableRegistry); + public Collection createComponents(PluginServices services) { + return createComponents( + services.client(), + services.environment().settings(), + services.clusterService(), + services.namedWriteableRegistry() + ); } /** diff --git a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java index a665170f8dcdb..1fac8a28aa5da 100644 --- a/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java +++ b/x-pack/plugin/stack/src/main/java/org/elasticsearch/xpack/stack/StackPlugin.java @@ -6,29 +6,14 @@ */ package org.elasticsearch.xpack.stack; -import org.elasticsearch.client.internal.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; public class StackPlugin extends Plugin implements ActionPlugin { private final Settings settings; @@ -43,36 +28,21 @@ public List> getSettings() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { LegacyStackTemplateRegistry legacyStackTemplateRegistry = new LegacyStackTemplateRegistry( settings, - clusterService, - threadPool, - client, - xContentRegistry + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); legacyStackTemplateRegistry.initialize(); StackTemplateRegistry stackTemplateRegistry = new StackTemplateRegistry( settings, - clusterService, - threadPool, - client, - xContentRegistry + services.clusterService(), + services.threadPool(), + services.client(), + services.xContentRegistry() ); stackTemplateRegistry.initialize(); return List.of(legacyStackTemplateRegistry, stackTemplateRegistry); diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java index 81a719e24f633..61cc0e2c072ad 100644 --- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java +++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/Transform.java @@ -24,9 +24,7 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; @@ -34,25 +32,17 @@ import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.common.settings.SettingsModule; import org.elasticsearch.core.TimeValue; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.indices.AssociatedIndexDescriptor; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.persistent.PersistentTasksExecutor; import org.elasticsearch.plugins.PersistentTaskPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.NamedXContentRegistry.Entry; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.SetResetModeActionRequest; @@ -229,27 +219,15 @@ public List getRestHandlers( } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + TransformConfigManager configManager = new IndexBasedTransformConfigManager( clusterService, - expressionResolver, + services.indexNameExpressionResolver(), client, - xContentRegistry + services.xContentRegistry() ); TransformAuditor auditor = new TransformAuditor( client, @@ -265,12 +243,12 @@ public Collection createComponents( configManager, auditor ); - TransformScheduler scheduler = new TransformScheduler(clock, threadPool, settings); + TransformScheduler scheduler = new TransformScheduler(clock, services.threadPool(), settings); scheduler.start(); transformServices.set(new TransformServices(configManager, checkpointService, auditor, scheduler)); - return Arrays.asList( + return List.of( transformServices.get(), new TransformClusterStateListener(clusterService, client), new TransformExtensionHolder(getTransformExtension()) diff --git a/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java b/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java index 1fee6eda5ad6d..df089381e70b2 100644 --- a/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java +++ b/x-pack/plugin/voting-only-node/src/main/java/org/elasticsearch/cluster/coordination/votingonly/VotingOnlyNodePlugin.java @@ -11,32 +11,22 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.client.internal.Client; import org.elasticsearch.cluster.coordination.CoordinationMetadata.VotingConfiguration; import org.elasticsearch.cluster.coordination.CoordinationState.VoteCollection; import org.elasticsearch.cluster.coordination.ElectionStrategy; import org.elasticsearch.cluster.coordination.Join; import org.elasticsearch.cluster.coordination.PublicationTransportHandler; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodeRole; -import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.discovery.DiscoveryModule; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.ClusterCoordinationPlugin; import org.elasticsearch.plugins.NetworkPlugin; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportException; @@ -45,8 +35,6 @@ import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportResponseHandler; -import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; @@ -84,23 +72,8 @@ public static boolean isFullMasterNode(DiscoveryNode discoveryNode) { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { - this.threadPool.set(threadPool); + public Collection createComponents(PluginServices services) { + this.threadPool.set(services.threadPool()); return Collections.emptyList(); } diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java index e5fd1fcbf2035..a46b42e2153bd 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/Watcher.java @@ -24,10 +24,8 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; @@ -39,26 +37,21 @@ import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexModule; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.ReloadablePlugin; import org.elasticsearch.plugins.ScriptPlugin; import org.elasticsearch.plugins.SystemIndexPlugin; -import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.TemplateScript; -import org.elasticsearch.telemetry.TelemetryProvider; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xpack.core.XPackPlugin; @@ -305,26 +298,18 @@ protected Clock getClock() { } @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver expressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { + public Collection createComponents(PluginServices services) { if (enabled == false) { return Collections.emptyList(); } + Client client = services.client(); + ClusterService clusterService = services.clusterService(); + ThreadPool threadPool = services.threadPool(); + Environment environment = services.environment(); + ScriptService scriptService = services.scriptService(); + NamedXContentRegistry xContentRegistry = services.xContentRegistry(); + // only initialize these classes if Watcher is enabled, and only after the plugin security policy for Watcher is in place BodyPartSource.init(); Account.init(); From 703ee53f3f01f77ffc29ddeca3621d2043e2e0a1 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 26 Oct 2023 16:58:59 +0100 Subject: [PATCH 135/203] Use switch expressions in MessagesTests (#101360) Remove superfluous references to Eclipse --- .../cluster/coordination/MessagesTests.java | 393 ++++++++---------- 1 file changed, 180 insertions(+), 213 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java index f779d5ea56dfa..f4eb05b573463 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java @@ -15,7 +15,6 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; -import org.elasticsearch.test.EqualsHashCodeTestUtils.CopyFunction; import org.elasticsearch.test.TransportVersionUtils; import java.util.Map; @@ -36,59 +35,56 @@ public void testJoinEqualsHashCodeSerialization() { randomNonNegativeLong(), randomNonNegativeLong() ); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialJoin, - (CopyFunction) join -> copyWriteable(join, writableRegistry(), Join::new), - join -> { - return switch (randomInt(4)) { - case 0 -> - // change sourceNode - new Join( - createNode(randomAlphaOfLength(20)), - join.getTargetNode(), - join.getTerm(), - join.getLastAcceptedTerm(), - join.getLastAcceptedVersion() - ); - case 1 -> - // change targetNode - new Join( - join.getSourceNode(), - createNode(randomAlphaOfLength(20)), - join.getTerm(), - join.getLastAcceptedTerm(), - join.getLastAcceptedVersion() - ); - case 2 -> - // change term - new Join( - join.getSourceNode(), - join.getTargetNode(), - randomValueOtherThan(join.getTerm(), ESTestCase::randomNonNegativeLong), - join.getLastAcceptedTerm(), - join.getLastAcceptedVersion() - ); - case 3 -> - // change last accepted term - new Join( - join.getSourceNode(), - join.getTargetNode(), - join.getTerm(), - randomValueOtherThan(join.getLastAcceptedTerm(), ESTestCase::randomNonNegativeLong), - join.getLastAcceptedVersion() - ); - case 4 -> - // change version - new Join( - join.getSourceNode(), - join.getTargetNode(), - join.getTerm(), - join.getLastAcceptedTerm(), - randomValueOtherThan(join.getLastAcceptedVersion(), ESTestCase::randomNonNegativeLong) - ); - default -> throw new AssertionError(); - }; + join -> copyWriteable(join, writableRegistry(), Join::new), + join -> switch (randomInt(4)) { + case 0 -> + // change sourceNode + new Join( + createNode(randomAlphaOfLength(20)), + join.getTargetNode(), + join.getTerm(), + join.getLastAcceptedTerm(), + join.getLastAcceptedVersion() + ); + case 1 -> + // change targetNode + new Join( + join.getSourceNode(), + createNode(randomAlphaOfLength(20)), + join.getTerm(), + join.getLastAcceptedTerm(), + join.getLastAcceptedVersion() + ); + case 2 -> + // change term + new Join( + join.getSourceNode(), + join.getTargetNode(), + randomValueOtherThan(join.getTerm(), ESTestCase::randomNonNegativeLong), + join.getLastAcceptedTerm(), + join.getLastAcceptedVersion() + ); + case 3 -> + // change last accepted term + new Join( + join.getSourceNode(), + join.getTargetNode(), + join.getTerm(), + randomValueOtherThan(join.getLastAcceptedTerm(), ESTestCase::randomNonNegativeLong), + join.getLastAcceptedVersion() + ); + case 4 -> + // change version + new Join( + join.getSourceNode(), + join.getTargetNode(), + join.getTerm(), + join.getLastAcceptedTerm(), + randomValueOtherThan(join.getLastAcceptedVersion(), ESTestCase::randomNonNegativeLong) + ); + default -> throw new AssertionError(); } ); } @@ -104,26 +100,23 @@ public void testPublishRequestEqualsHashCode() { public void testPublishResponseEqualsHashCodeSerialization() { PublishResponse initialPublishResponse = new PublishResponse(randomNonNegativeLong(), randomNonNegativeLong()); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialPublishResponse, - (CopyFunction) publishResponse -> copyWriteable(publishResponse, writableRegistry(), PublishResponse::new), - publishResponse -> { - return switch (randomInt(1)) { - case 0 -> - // change term - new PublishResponse( - randomValueOtherThan(publishResponse.getTerm(), ESTestCase::randomNonNegativeLong), - publishResponse.getVersion() - ); - case 1 -> - // change version - new PublishResponse( - publishResponse.getTerm(), - randomValueOtherThan(publishResponse.getVersion(), ESTestCase::randomNonNegativeLong) - ); - default -> throw new AssertionError(); - }; + publishResponse -> copyWriteable(publishResponse, writableRegistry(), PublishResponse::new), + publishResponse -> switch (randomInt(1)) { + case 0 -> + // change term + new PublishResponse( + randomValueOtherThan(publishResponse.getTerm(), ESTestCase::randomNonNegativeLong), + publishResponse.getVersion() + ); + case 1 -> + // change version + new PublishResponse( + publishResponse.getTerm(), + randomValueOtherThan(publishResponse.getVersion(), ESTestCase::randomNonNegativeLong) + ); + default -> throw new AssertionError(); } ); } @@ -141,61 +134,51 @@ public void testPublishWithJoinResponseEqualsHashCodeSerialization() { initialPublishResponse, randomBoolean() ? Optional.empty() : Optional.of(initialJoin) ); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialPublishWithJoinResponse, - (CopyFunction) publishWithJoinResponse -> copyWriteable( - publishWithJoinResponse, - writableRegistry(), - PublishWithJoinResponse::new - ), - publishWithJoinResponse -> { - switch (randomInt(1)) { - case 0: - // change publish response - return new PublishWithJoinResponse( - new PublishResponse(randomNonNegativeLong(), randomNonNegativeLong()), - publishWithJoinResponse.getJoin() - ); - case 1: - // change optional join - Join newJoin = new Join( - createNode(randomAlphaOfLength(10)), - createNode(randomAlphaOfLength(10)), - randomNonNegativeLong(), - randomNonNegativeLong(), - randomNonNegativeLong() - ); - return new PublishWithJoinResponse( - publishWithJoinResponse.getPublishResponse(), - publishWithJoinResponse.getJoin().isPresent() && randomBoolean() ? Optional.empty() : Optional.of(newJoin) - ); - default: - throw new AssertionError(); + publishWithJoinResponse -> copyWriteable(publishWithJoinResponse, writableRegistry(), PublishWithJoinResponse::new), + publishWithJoinResponse -> switch (randomInt(1)) { + case 0 -> + // change publish response + new PublishWithJoinResponse( + new PublishResponse(randomNonNegativeLong(), randomNonNegativeLong()), + publishWithJoinResponse.getJoin() + ); + case 1 -> { + // change optional join + Join newJoin = new Join( + createNode(randomAlphaOfLength(10)), + createNode(randomAlphaOfLength(10)), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong() + ); + yield new PublishWithJoinResponse( + publishWithJoinResponse.getPublishResponse(), + publishWithJoinResponse.getJoin().isPresent() && randomBoolean() ? Optional.empty() : Optional.of(newJoin) + ); } + default -> throw new AssertionError(); } ); } public void testStartJoinRequestEqualsHashCodeSerialization() { StartJoinRequest initialStartJoinRequest = new StartJoinRequest(createNode(randomAlphaOfLength(10)), randomNonNegativeLong()); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialStartJoinRequest, - (CopyFunction) startJoinRequest -> copyWriteable(startJoinRequest, writableRegistry(), StartJoinRequest::new), - startJoinRequest -> { - return switch (randomInt(1)) { - case 0 -> - // change sourceNode - new StartJoinRequest(createNode(randomAlphaOfLength(20)), startJoinRequest.getTerm()); - case 1 -> - // change term - new StartJoinRequest( - startJoinRequest.getSourceNode(), - randomValueOtherThan(startJoinRequest.getTerm(), ESTestCase::randomNonNegativeLong) - ); - default -> throw new AssertionError(); - }; + startJoinRequest -> copyWriteable(startJoinRequest, writableRegistry(), StartJoinRequest::new), + startJoinRequest -> switch (randomInt(1)) { + case 0 -> + // change sourceNode + new StartJoinRequest(createNode(randomAlphaOfLength(20)), startJoinRequest.getTerm()); + case 1 -> + // change term + new StartJoinRequest( + startJoinRequest.getSourceNode(), + randomValueOtherThan(startJoinRequest.getTerm(), ESTestCase::randomNonNegativeLong) + ); + default -> throw new AssertionError(); } ); } @@ -206,31 +189,28 @@ public void testApplyCommitEqualsHashCodeSerialization() { randomNonNegativeLong(), randomNonNegativeLong() ); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialApplyCommit, - (CopyFunction) applyCommit -> copyWriteable(applyCommit, writableRegistry(), ApplyCommitRequest::new), - applyCommit -> { - return switch (randomInt(2)) { - case 0 -> - // change sourceNode - new ApplyCommitRequest(createNode(randomAlphaOfLength(20)), applyCommit.getTerm(), applyCommit.getVersion()); - case 1 -> - // change term - new ApplyCommitRequest( - applyCommit.getSourceNode(), - randomValueOtherThan(applyCommit.getTerm(), ESTestCase::randomNonNegativeLong), - applyCommit.getVersion() - ); - case 2 -> - // change version - new ApplyCommitRequest( - applyCommit.getSourceNode(), - applyCommit.getTerm(), - randomValueOtherThan(applyCommit.getVersion(), ESTestCase::randomNonNegativeLong) - ); - default -> throw new AssertionError(); - }; + applyCommit -> copyWriteable(applyCommit, writableRegistry(), ApplyCommitRequest::new), + applyCommit -> switch (randomInt(2)) { + case 0 -> + // change sourceNode + new ApplyCommitRequest(createNode(randomAlphaOfLength(20)), applyCommit.getTerm(), applyCommit.getVersion()); + case 1 -> + // change term + new ApplyCommitRequest( + applyCommit.getSourceNode(), + randomValueOtherThan(applyCommit.getTerm(), ESTestCase::randomNonNegativeLong), + applyCommit.getVersion() + ); + case 2 -> + // change version + new ApplyCommitRequest( + applyCommit.getSourceNode(), + applyCommit.getTerm(), + randomValueOtherThan(applyCommit.getVersion(), ESTestCase::randomNonNegativeLong) + ); + default -> throw new AssertionError(); } ); } @@ -249,58 +229,51 @@ public void testJoinRequestEqualsHashCodeSerialization() { randomNonNegativeLong(), randomBoolean() ? Optional.empty() : Optional.of(initialJoin) ); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialJoinRequest, - (CopyFunction) joinRequest -> copyWriteable(joinRequest, writableRegistry(), JoinRequest::new), - joinRequest -> { - if (randomBoolean() && joinRequest.getOptionalJoin().isPresent() == false) { - return new JoinRequest( + joinRequest -> copyWriteable(joinRequest, writableRegistry(), JoinRequest::new), + joinRequest -> switch (randomInt(3)) { + case 0 -> { + assumeTrue("Optional join needs to be empty", joinRequest.getOptionalJoin().isEmpty()); + yield new JoinRequest( createNode(randomAlphaOfLength(10)), joinRequest.getCompatibilityVersions(), joinRequest.getMinimumTerm(), joinRequest.getOptionalJoin() ); - } else if (randomBoolean()) { - return new JoinRequest( - joinRequest.getSourceNode(), - new CompatibilityVersions( - TransportVersionUtils.randomVersion(Set.of(joinRequest.getCompatibilityVersions().transportVersion())), - Map.of() - ), - joinRequest.getMinimumTerm(), - joinRequest.getOptionalJoin() - ); - } else if (randomBoolean()) { - return new JoinRequest( + } + case 1 -> new JoinRequest( + joinRequest.getSourceNode(), + new CompatibilityVersions( + TransportVersionUtils.randomVersion(Set.of(joinRequest.getCompatibilityVersions().transportVersion())), + Map.of() + ), + joinRequest.getMinimumTerm(), + joinRequest.getOptionalJoin() + ); + case 2 -> new JoinRequest( + joinRequest.getSourceNode(), + joinRequest.getCompatibilityVersions(), + randomValueOtherThan(joinRequest.getMinimumTerm(), ESTestCase::randomNonNegativeLong), + joinRequest.getOptionalJoin() + ); + case 3 -> { + // change OptionalJoin + Join newJoin = new Join( joinRequest.getSourceNode(), - joinRequest.getCompatibilityVersions(), - randomValueOtherThan(joinRequest.getMinimumTerm(), ESTestCase::randomNonNegativeLong), - joinRequest.getOptionalJoin() + createNode(randomAlphaOfLength(10)), + randomNonNegativeLong(), + randomNonNegativeLong(), + randomNonNegativeLong() ); - } else { - // change OptionalJoin - final Optional newOptionalJoin; - if (joinRequest.getOptionalJoin().isPresent() && randomBoolean()) { - newOptionalJoin = Optional.empty(); - } else { - newOptionalJoin = Optional.of( - new Join( - joinRequest.getSourceNode(), - createNode(randomAlphaOfLength(10)), - randomNonNegativeLong(), - randomNonNegativeLong(), - randomNonNegativeLong() - ) - ); - } - return new JoinRequest( + yield new JoinRequest( joinRequest.getSourceNode(), joinRequest.getCompatibilityVersions(), joinRequest.getMinimumTerm(), - newOptionalJoin + joinRequest.getOptionalJoin().isPresent() && randomBoolean() ? Optional.empty() : Optional.of(newJoin) ); } + default -> throw new AssertionError(); } ); } @@ -318,16 +291,13 @@ public ClusterState randomClusterState() { public void testPreVoteRequestEqualsHashCodeSerialization() { PreVoteRequest initialPreVoteRequest = new PreVoteRequest(createNode(randomAlphaOfLength(10)), randomNonNegativeLong()); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialPreVoteRequest, - (CopyFunction) preVoteRequest -> copyWriteable(preVoteRequest, writableRegistry(), PreVoteRequest::new), - preVoteRequest -> { - if (randomBoolean()) { - return new PreVoteRequest(createNode(randomAlphaOfLength(10)), preVoteRequest.getCurrentTerm()); - } else { - return new PreVoteRequest(preVoteRequest.getSourceNode(), randomNonNegativeLong()); - } + preVoteRequest -> copyWriteable(preVoteRequest, writableRegistry(), PreVoteRequest::new), + preVoteRequest -> switch (randomInt(1)) { + case 0 -> new PreVoteRequest(createNode(randomAlphaOfLength(10)), preVoteRequest.getCurrentTerm()); + case 1 -> new PreVoteRequest(preVoteRequest.getSourceNode(), randomNonNegativeLong()); + default -> throw new AssertionError(); } ); } @@ -339,41 +309,38 @@ public void testPreVoteResponseEqualsHashCodeSerialization() { randomLongBetween(1, currentTerm), randomNonNegativeLong() ); - // Note: the explicit cast of the CopyFunction is needed for some IDE (specifically Eclipse 4.8.0) to infer the right type EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialPreVoteResponse, - (CopyFunction) preVoteResponse -> copyWriteable(preVoteResponse, writableRegistry(), PreVoteResponse::new), - preVoteResponse -> { - switch (randomInt(2)) { - case 0: - assumeTrue("last-accepted term is Long.MAX_VALUE", preVoteResponse.getLastAcceptedTerm() < Long.MAX_VALUE); - return new PreVoteResponse( - randomValueOtherThan( - preVoteResponse.getCurrentTerm(), - () -> randomLongBetween(preVoteResponse.getLastAcceptedTerm(), Long.MAX_VALUE) - ), - preVoteResponse.getLastAcceptedTerm(), - preVoteResponse.getLastAcceptedVersion() - ); - case 1: - assumeTrue("current term is 1", 1 < preVoteResponse.getCurrentTerm()); - return new PreVoteResponse( - preVoteResponse.getCurrentTerm(), - randomValueOtherThan( - preVoteResponse.getLastAcceptedTerm(), - () -> randomLongBetween(1, preVoteResponse.getCurrentTerm()) - ), - preVoteResponse.getLastAcceptedVersion() - ); - case 2: - return new PreVoteResponse( + preVoteResponse -> copyWriteable(preVoteResponse, writableRegistry(), PreVoteResponse::new), + preVoteResponse -> switch (randomInt(2)) { + case 0 -> { + assumeTrue("last-accepted term is Long.MAX_VALUE", preVoteResponse.getLastAcceptedTerm() < Long.MAX_VALUE); + yield new PreVoteResponse( + randomValueOtherThan( preVoteResponse.getCurrentTerm(), + () -> randomLongBetween(preVoteResponse.getLastAcceptedTerm(), Long.MAX_VALUE) + ), + preVoteResponse.getLastAcceptedTerm(), + preVoteResponse.getLastAcceptedVersion() + ); + } + case 1 -> { + assumeTrue("current term is 1", 1 < preVoteResponse.getCurrentTerm()); + yield new PreVoteResponse( + preVoteResponse.getCurrentTerm(), + randomValueOtherThan( preVoteResponse.getLastAcceptedTerm(), - randomValueOtherThan(preVoteResponse.getLastAcceptedVersion(), ESTestCase::randomNonNegativeLong) - ); - default: - throw new AssertionError(); + () -> randomLongBetween(1, preVoteResponse.getCurrentTerm()) + ), + preVoteResponse.getLastAcceptedVersion() + ); } + case 2 -> new PreVoteResponse( + preVoteResponse.getCurrentTerm(), + preVoteResponse.getLastAcceptedTerm(), + randomValueOtherThan(preVoteResponse.getLastAcceptedVersion(), ESTestCase::randomNonNegativeLong) + ); + default -> throw new AssertionError(); } ); } From a64390b5d0358f6ace9e5d1f058a16fd4acff9b1 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 26 Oct 2023 17:16:43 +0100 Subject: [PATCH 136/203] Avoid recovery failures triggering unnecessary shard failures (#101382) Part of ES-6938: if a recovery fails with an `IndexNotFoundException` or a `ShardNotFoundException` then the relevant shards are already being failed, there's no need for the recovery to send more notifications and it's best not to so that we don't emit spurious `WARN` logs. --- .../indices/recovery/PeerRecoveryTargetService.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java index 2cdd383114497..a570c88ddaba7 100644 --- a/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java +++ b/server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java @@ -336,8 +336,18 @@ public void onResponse(ActionResponse.Empty ignored) { @Override public void onFailure(Exception e) { + final var cause = ExceptionsHelper.unwrapCause(e); + final var sendShardFailure = + // these indicate the source shard has already failed, which will independently notify the master and fail + // the target shard + false == (cause instanceof ShardNotFoundException || cause instanceof IndexNotFoundException); + // TODO retries? See RecoveryResponseHandler#handleException - onGoingRecoveries.failRecovery(recoveryId, new RecoveryFailedException(recoveryState, null, e), true); + onGoingRecoveries.failRecovery( + recoveryId, + new RecoveryFailedException(recoveryState, null, e), + sendShardFailure + ); } } ); From 54485c99ecca8937d78296563a3f8837abf4f941 Mon Sep 17 00:00:00 2001 From: Artem Prigoda Date: Thu, 26 Oct 2023 18:24:11 +0200 Subject: [PATCH 137/203] Enable RemoteClusterClientTests#testConnectAndExecuteRequest (#101355) The test was muted in #97080 without a follow up. Failures are not reproducible anymore, and couldn't find any specific commits from 21.06.2023 that could cause the issue. The only suspicious part from me is that failure logs contain extremely verbose TRACE logs, so there might have been some infra problem causing a node to be unreachable and throwing NodeDisconnectedException --- .../org/elasticsearch/transport/RemoteClusterClientTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java b/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java index 47d75a78d65d4..3e05743741f73 100644 --- a/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java +++ b/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java @@ -52,7 +52,6 @@ public void tearDown() throws Exception { ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/97080") public void testConnectAndExecuteRequest() throws Exception { Settings remoteSettings = Settings.builder() .put(ClusterName.CLUSTER_NAME_SETTING.getKey(), "foo_bar_cluster") From e99f3c3ee66669d5542dd19a5fd3cea9c4b2aac4 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 26 Oct 2023 10:34:24 -0600 Subject: [PATCH 138/203] Report full stack trace for non-state file settings transforms (#101346) In most cases file based settings apply transformations from configuration to cluster state. However, there exists "non state" transformations as well. When these are run, any errors are stored in cluster state. This commit fixes a bug in handling of these exception cases where just the exception messages, instead of the full stack trace, was captured. --- docs/changelog/101346.yaml | 5 +++++ .../reservedstate/service/ReservedClusterStateService.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101346.yaml diff --git a/docs/changelog/101346.yaml b/docs/changelog/101346.yaml new file mode 100644 index 0000000000000..b32b123c506d1 --- /dev/null +++ b/docs/changelog/101346.yaml @@ -0,0 +1,5 @@ +pr: 101346 +summary: Report full stack trace for non-state file settings transforms +area: Infra/Settings +type: bug +issues: [] diff --git a/server/src/main/java/org/elasticsearch/reservedstate/service/ReservedClusterStateService.java b/server/src/main/java/org/elasticsearch/reservedstate/service/ReservedClusterStateService.java index 3adf32454cc20..f6d5ab3ead6af 100644 --- a/server/src/main/java/org/elasticsearch/reservedstate/service/ReservedClusterStateService.java +++ b/server/src/main/java/org/elasticsearch/reservedstate/service/ReservedClusterStateService.java @@ -239,7 +239,7 @@ public void onFailure(Exception e) { @Override public void onFailure(Exception e) { // If we encounter an error while runnin the non-state transforms, we avoid saving any cluster state. - errorListener.accept(checkAndReportError(namespace, List.of(e.getMessage()), reservedStateVersion)); + errorListener.accept(checkAndReportError(namespace, List.of(stackTrace(e)), reservedStateVersion)); } }); } From 328ebc4145c106f3b12edfe101d62782a53c6c9b Mon Sep 17 00:00:00 2001 From: Mark Vieira Date: Thu, 26 Oct 2023 11:29:44 -0700 Subject: [PATCH 139/203] Update IronBank docker image base to ubi:9.2 (#101393) --- distribution/docker/build.gradle | 2 +- distribution/docker/src/docker/Dockerfile | 4 ++-- .../docker/src/docker/iron_bank/hardening_manifest.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/distribution/docker/build.gradle b/distribution/docker/build.gradle index 7b7040dfd7098..96e577d5635ab 100644 --- a/distribution/docker/build.gradle +++ b/distribution/docker/build.gradle @@ -407,7 +407,7 @@ void addBuildDockerImageTask(Architecture architecture, DockerBase base) { if (base == DockerBase.IRON_BANK) { Map buildArgsMap = [ 'BASE_REGISTRY': 'docker.elastic.co', - 'BASE_IMAGE' : 'ubi8/ubi', + 'BASE_IMAGE' : 'ubi9/ubi', 'BASE_TAG' : 'latest' ] diff --git a/distribution/docker/src/docker/Dockerfile b/distribution/docker/src/docker/Dockerfile index a6ecfdaf417e1..b62fa983dd480 100644 --- a/distribution/docker/src/docker/Dockerfile +++ b/distribution/docker/src/docker/Dockerfile @@ -21,8 +21,8 @@ <% if (docker_base == 'iron_bank') { %> ARG BASE_REGISTRY=registry1.dso.mil -ARG BASE_IMAGE=ironbank/redhat/ubi/ubi8 -ARG BASE_TAG=8.6 +ARG BASE_IMAGE=redhat/ubi/ubi9 +ARG BASE_TAG=9.2 <% } %> ################################################################################ diff --git a/distribution/docker/src/docker/iron_bank/hardening_manifest.yaml b/distribution/docker/src/docker/iron_bank/hardening_manifest.yaml index 9fce16efad328..7152f6d18f1d2 100644 --- a/distribution/docker/src/docker/iron_bank/hardening_manifest.yaml +++ b/distribution/docker/src/docker/iron_bank/hardening_manifest.yaml @@ -13,8 +13,8 @@ tags: # Build args passed to Dockerfile ARGs args: - BASE_IMAGE: "redhat/ubi/ubi8" - BASE_TAG: "8.6" + BASE_IMAGE: "redhat/ubi/ubi9" + BASE_TAG: "9.2" # Docker image labels labels: From ee1467d4ffc88e3b25d2d488c7d4dd856038279f Mon Sep 17 00:00:00 2001 From: Mark Vieira Date: Thu, 26 Oct 2023 12:16:06 -0700 Subject: [PATCH 140/203] Improve test avoidance (#101081) --- plugins/discovery-ec2/build.gradle | 4 ++-- plugins/repository-hdfs/build.gradle | 4 ++-- .../qa/downgrade-to-basic-license/build.gradle | 6 +++--- x-pack/plugin/ccr/qa/multi-cluster/build.gradle | 7 ++++--- .../ccr/qa/non-compliant-license/build.gradle | 5 +++-- x-pack/plugin/ccr/qa/restart/build.gradle | 3 ++- x-pack/plugin/ccr/qa/security/build.gradle | 3 ++- .../qa/multi-cluster-with-security/build.gradle | 3 ++- x-pack/plugin/ilm/qa/multi-cluster/build.gradle | 13 +++++++------ x-pack/plugin/ilm/qa/multi-node/build.gradle | 5 +++-- .../build.gradle | 3 ++- .../searchable-snapshots/qa/rest/build.gradle | 6 ++++-- .../searchable-snapshots/qa/url/build.gradle | 4 ++-- x-pack/plugin/slm/qa/multi-node/build.gradle | 5 +++-- .../snapshot-based-recoveries/qa/fs/build.gradle | 6 ++++-- .../qa/license-enforcing/build.gradle | 4 ++-- .../snapshot-repo-test-kit/qa/rest/build.gradle | 6 ++++-- .../multi-cluster-with-security/build.gradle | 3 ++- .../build.gradle | 3 ++- .../vector-tile/qa/multi-cluster/build.gradle | 4 ++-- x-pack/qa/kerberos-tests/build.gradle | 15 ++++++++++----- .../legacy-with-basic-license/build.gradle | 5 +++-- .../legacy-with-full-license/build.gradle | 5 +++-- .../legacy-with-restricted-trust/build.gradle | 5 +++-- x-pack/qa/repository-old-versions/build.gradle | 5 +++-- x-pack/qa/saml-idp-tests/build.gradle | 6 ++++++ 26 files changed, 85 insertions(+), 53 deletions(-) diff --git a/plugins/discovery-ec2/build.gradle b/plugins/discovery-ec2/build.gradle index b21f6224c9fc2..5107bb9051bd1 100644 --- a/plugins/discovery-ec2/build.gradle +++ b/plugins/discovery-ec2/build.gradle @@ -95,9 +95,9 @@ tasks.named("test").configure { // this is needed to manipulate com.amazonaws.sdk.ec2MetadataServiceEndpointOverride system property // it is better rather disable security manager at all with `systemProperty 'tests.security.manager', 'false'` if (BuildParams.inFipsJvm){ - systemProperty 'java.security.policy', "=file://${buildDir}/tmp/java.policy" + nonInputProperties.systemProperty 'java.security.policy', "=file://${buildDir}/tmp/java.policy" } else { - systemProperty 'java.security.policy', "file://${buildDir}/tmp/java.policy" + nonInputProperties.systemProperty 'java.security.policy', "file://${buildDir}/tmp/java.policy" } } diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index 78809a170fbbd..5db01ed636995 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -281,7 +281,7 @@ tasks.withType(RestIntegTestTask).configureEach { testTask -> if (disabledIntegTestTaskNames.contains(name) == false) { nonInputProperties.systemProperty "test.krb5.principal.es", "elasticsearch@${realm}" nonInputProperties.systemProperty "test.krb5.principal.hdfs", "hdfs/hdfs.build.elastic.co@${realm}" - jvmArgs "-Djava.security.krb5.conf=${project.configurations.krb5Config.getSingleFile().getPath()}" + nonInputProperties.systemProperty "java.security.krb5.conf", "${project.configurations.krb5Config.getSingleFile().getPath()}" nonInputProperties.systemProperty( "test.krb5.keytab.hdfs", new File(project.configurations.krb5Keytabs.singleFile, "hdfs_hdfs.build.elastic.co.keytab").getPath() @@ -291,7 +291,7 @@ tasks.withType(RestIntegTestTask).configureEach { testTask -> testClusters.matching { it.name == testTask.name }.configureEach { if (testTask.name.contains("Secure")) { - systemProperty "java.security.krb5.conf", configurations.krb5Config.singleFile.getPath() + systemProperty "java.security.krb5.conf", { configurations.krb5Config.singleFile.getPath() }, IGNORE_VALUE extraConfigFile( "repository-hdfs/krb5.keytab", new File(project.configurations.krb5Keytabs.singleFile, "elasticsearch.keytab"), diff --git a/x-pack/plugin/ccr/qa/downgrade-to-basic-license/build.gradle b/x-pack/plugin/ccr/qa/downgrade-to-basic-license/build.gradle index 3eaf6f267a545..da39d221f92f1 100644 --- a/x-pack/plugin/ccr/qa/downgrade-to-basic-license/build.gradle +++ b/x-pack/plugin/ccr/qa/downgrade-to-basic-license/build.gradle @@ -1,5 +1,6 @@ import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -23,8 +24,7 @@ def followCluster = testClusters.register("follow-cluster") { setting 'xpack.license.self_generated.type', 'trial' setting 'xpack.security.enabled', 'true' user username: 'admin', password: 'admin-password', role: 'superuser' - setting 'cluster.remote.leader_cluster.seeds', { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" - } + setting 'cluster.remote.leader_cluster.seeds', { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" }, IGNORE_VALUE } tasks.register("leader-cluster", RestIntegTestTask) { @@ -51,8 +51,8 @@ tasks.register("writeJavaPolicy") { tasks.register("follow-cluster", RestIntegTestTask) { dependsOn 'writeJavaPolicy', "leader-cluster" useCluster leaderCluster - systemProperty 'java.security.policy', "file://${policyFile}" systemProperty 'tests.target_cluster', 'follow' + nonInputProperties.systemProperty 'java.security.policy', "file://${policyFile}" nonInputProperties.systemProperty 'tests.leader_host', leaderCluster.map(c -> c.allHttpSocketURI.get(0)) nonInputProperties.systemProperty 'log', followCluster.map(c -> c.getFirstNode().getServerLog()) } diff --git a/x-pack/plugin/ccr/qa/multi-cluster/build.gradle b/x-pack/plugin/ccr/qa/multi-cluster/build.gradle index 31cdf04f6dc94..2475a56aa87aa 100644 --- a/x-pack/plugin/ccr/qa/multi-cluster/build.gradle +++ b/x-pack/plugin/ccr/qa/multi-cluster/build.gradle @@ -1,6 +1,7 @@ import org.elasticsearch.gradle.Version import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -25,7 +26,7 @@ def middleCluster = testClusters.register('middle-cluster') { setting 'xpack.security.enabled', 'true' user username: 'admin', password: 'admin-password', role: 'superuser' setting 'cluster.remote.leader_cluster.seeds', - { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" } + { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" }, IGNORE_VALUE } tasks.register("leader-cluster", RestIntegTestTask) { @@ -60,9 +61,9 @@ testClusters.matching {it.name == "follow-cluster" }.configureEach { setting 'xpack.security.enabled', 'true' user username: 'admin', password: 'admin-password', role: 'superuser' setting 'cluster.remote.leader_cluster.seeds', - { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" } + { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" }, IGNORE_VALUE setting 'cluster.remote.middle_cluster.seeds', - { "\"${middleCluster.get().getAllTransportPortURI().join(",")}\"" } + { "\"${middleCluster.get().getAllTransportPortURI().join(",")}\"" }, IGNORE_VALUE } diff --git a/x-pack/plugin/ccr/qa/non-compliant-license/build.gradle b/x-pack/plugin/ccr/qa/non-compliant-license/build.gradle index 67465bc782ad9..7661ea08b057d 100644 --- a/x-pack/plugin/ccr/qa/non-compliant-license/build.gradle +++ b/x-pack/plugin/ccr/qa/non-compliant-license/build.gradle @@ -1,4 +1,5 @@ import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -21,7 +22,7 @@ def followerCluster = testClusters.register('follow-cluster') { setting 'xpack.security.enabled', 'true' user username: 'admin', password: 'admin-password', role: 'superuser' setting 'cluster.remote.leader_cluster.seeds', - { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" } + { "\"${leaderCluster.get().getAllTransportPortURI().join(",")}\"" }, IGNORE_VALUE } tasks.register('leader-cluster', RestIntegTestTask) { @@ -36,4 +37,4 @@ tasks.register('follow-cluster', RestIntegTestTask) { nonInputProperties.systemProperty 'tests.leader_host', followerCluster.map(c -> c.allHttpSocketURI.get(0)) } -tasks.named("check").configure { dependsOn "follow-cluster" } \ No newline at end of file +tasks.named("check").configure { dependsOn "follow-cluster" } diff --git a/x-pack/plugin/ccr/qa/restart/build.gradle b/x-pack/plugin/ccr/qa/restart/build.gradle index d354cd911f2f8..47d37801e2dcf 100644 --- a/x-pack/plugin/ccr/qa/restart/build.gradle +++ b/x-pack/plugin/ccr/qa/restart/build.gradle @@ -1,5 +1,6 @@ import org.elasticsearch.gradle.internal.test.RestIntegTestTask import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -22,7 +23,7 @@ def followCluster = testClusters.register('follow-cluster') { setting 'xpack.security.enabled', 'true' user username: 'admin', password: 'admin-password', role: 'superuser' setting 'cluster.remote.leader_cluster.seeds', - { "\"${leaderCluster.get().getAllTransportPortURI().get(0)}\"" } + { "\"${leaderCluster.get().getAllTransportPortURI().get(0)}\"" }, IGNORE_VALUE nameCustomization = { 'follow' } } diff --git a/x-pack/plugin/ccr/qa/security/build.gradle b/x-pack/plugin/ccr/qa/security/build.gradle index ff4a4f857fe32..5515aefeaa091 100644 --- a/x-pack/plugin/ccr/qa/security/build.gradle +++ b/x-pack/plugin/ccr/qa/security/build.gradle @@ -1,4 +1,5 @@ import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -22,7 +23,7 @@ testClusters.register('follow-cluster') { testDistribution = 'DEFAULT' setting 'cluster.remote.leader_cluster.seeds', { "\"${leadCluster.get().getAllTransportPortURI().join(",")}\"" - } + }, IGNORE_VALUE setting 'xpack.license.self_generated.type', 'trial' setting 'xpack.security.enabled', 'true' setting 'xpack.monitoring.collection.enabled', 'false' // will be enabled by tests diff --git a/x-pack/plugin/eql/qa/multi-cluster-with-security/build.gradle b/x-pack/plugin/eql/qa/multi-cluster-with-security/build.gradle index 5fc247693c453..47a405517a309 100644 --- a/x-pack/plugin/eql/qa/multi-cluster-with-security/build.gradle +++ b/x-pack/plugin/eql/qa/multi-cluster-with-security/build.gradle @@ -1,4 +1,5 @@ import org.elasticsearch.gradle.testclusters.DefaultTestClustersTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.legacy-java-rest-test' @@ -24,7 +25,7 @@ def integTestClusterReg = testClusters.register('javaRestTest') { setting 'xpack.watcher.enabled', 'false' setting 'cluster.remote.my_remote_cluster.seeds', { remoteClusterReg.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE setting 'cluster.remote.connections_per_cluster', "1" setting 'xpack.security.enabled', 'true' setting 'xpack.security.autoconfiguration.enabled', 'false' diff --git a/x-pack/plugin/ilm/qa/multi-cluster/build.gradle b/x-pack/plugin/ilm/qa/multi-cluster/build.gradle index a1a2a5129e3f7..111496669afe3 100644 --- a/x-pack/plugin/ilm/qa/multi-cluster/build.gradle +++ b/x-pack/plugin/ilm/qa/multi-cluster/build.gradle @@ -1,5 +1,6 @@ import org.elasticsearch.gradle.internal.test.RestIntegTestTask import org.elasticsearch.gradle.internal.info.BuildParams +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -16,12 +17,12 @@ tasks.register('leader-cluster', RestIntegTestTask) { mustRunAfter("precommit") systemProperty 'tests.target_cluster', 'leader' /* To support taking index snapshots, we have to set path.repo setting */ - systemProperty 'tests.path.repo', repoDir.absolutePath + nonInputProperties.systemProperty 'tests.path.repo', repoDir.absolutePath } testClusters.matching { it.name == 'leader-cluster' }.configureEach { testDistribution = 'DEFAULT' - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.ccr.enabled', 'true' setting 'xpack.security.enabled', 'false' setting 'xpack.watcher.enabled', 'false' @@ -39,12 +40,12 @@ tasks.register('follow-cluster', RestIntegTestTask) { nonInputProperties.systemProperty 'tests.leader_remote_cluster_seed', "${-> testClusters.'leader-cluster'.getAllTransportPortURI().get(0)}" /* To support taking index snapshots, we have to set path.repo setting */ - systemProperty 'tests.path.repo', repoDir.absolutePath + nonInputProperties.systemProperty 'tests.path.repo', repoDir.absolutePath } testClusters.matching{ it.name == 'follow-cluster' }.configureEach { testDistribution = 'DEFAULT' - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.ccr.enabled', 'true' setting 'xpack.security.enabled', 'false' setting 'xpack.watcher.enabled', 'false' @@ -52,11 +53,11 @@ testClusters.matching{ it.name == 'follow-cluster' }.configureEach { setting 'xpack.license.self_generated.type', 'trial' setting 'indices.lifecycle.poll_interval', '1000ms' setting 'cluster.remote.leader_cluster.seeds', - { "\"${testClusters.'leader-cluster'.getAllTransportPortURI().get(0)}\"" } + { "\"${testClusters.'leader-cluster'.getAllTransportPortURI().get(0)}\"" }, IGNORE_VALUE } tasks.named("check").configure { dependsOn 'follow-cluster' } // Security is explicitly disabled for follow-cluster and leader-cluster, do not run these in FIPS mode tasks.withType(Test).configureEach { enabled = BuildParams.inFipsJvm == false -} \ No newline at end of file +} diff --git a/x-pack/plugin/ilm/qa/multi-node/build.gradle b/x-pack/plugin/ilm/qa/multi-node/build.gradle index 523bf5d04ae4f..ef20d57d83f59 100644 --- a/x-pack/plugin/ilm/qa/multi-node/build.gradle +++ b/x-pack/plugin/ilm/qa/multi-node/build.gradle @@ -1,4 +1,5 @@ import org.elasticsearch.gradle.internal.info.BuildParams +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.legacy-java-rest-test' @@ -12,14 +13,14 @@ File repoDir = file("$buildDir/testclusters/repo") tasks.named("javaRestTest").configure { /* To support taking index snapshots, we have to set path.repo setting */ - systemProperty 'tests.path.repo', repoDir + nonInputProperties.systemProperty 'tests.path.repo', repoDir } testClusters.configureEach { testDistribution = 'DEFAULT' numberOfNodes = 4 - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.searchable.snapshot.shared_cache.size', '16MB' setting 'xpack.searchable.snapshot.shared_cache.region_size', '256KB' setting 'xpack.security.enabled', 'false' diff --git a/x-pack/plugin/ml/qa/multi-cluster-tests-with-security/build.gradle b/x-pack/plugin/ml/qa/multi-cluster-tests-with-security/build.gradle index 368aab4615fc7..1cc37f5c4ffc0 100644 --- a/x-pack/plugin/ml/qa/multi-cluster-tests-with-security/build.gradle +++ b/x-pack/plugin/ml/qa/multi-cluster-tests-with-security/build.gradle @@ -1,6 +1,7 @@ import org.elasticsearch.gradle.internal.test.RestIntegTestTask import org.elasticsearch.gradle.Version import org.elasticsearch.gradle.VersionProperties +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -39,7 +40,7 @@ testClusters.register('mixed-cluster') { setting 'xpack.license.self_generated.type', 'trial' setting 'cluster.remote.my_remote_cluster.seeds', { remoteCluster.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE setting 'cluster.remote.connections_per_cluster', "1" user username: "test_user", password: "x-pack-test-password" diff --git a/x-pack/plugin/searchable-snapshots/qa/rest/build.gradle b/x-pack/plugin/searchable-snapshots/qa/rest/build.gradle index b8c0127d0586a..595d05e05b410 100644 --- a/x-pack/plugin/searchable-snapshots/qa/rest/build.gradle +++ b/x-pack/plugin/searchable-snapshots/qa/rest/build.gradle @@ -1,3 +1,5 @@ +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE + apply plugin: 'elasticsearch.legacy-java-rest-test' apply plugin: 'elasticsearch.legacy-yaml-rest-test' apply plugin: 'elasticsearch.legacy-yaml-rest-compat-test' @@ -15,12 +17,12 @@ restResources { } tasks.withType(Test).configureEach { - systemProperty 'tests.path.repo', repoDir + nonInputProperties.systemProperty 'tests.path.repo', repoDir } testClusters.configureEach { testDistribution = 'DEFAULT' - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.license.self_generated.type', 'trial' setting 'xpack.searchable.snapshot.shared_cache.size', '16MB' diff --git a/x-pack/plugin/searchable-snapshots/qa/url/build.gradle b/x-pack/plugin/searchable-snapshots/qa/url/build.gradle index 160eb8d28cbcc..12fc0873958e1 100644 --- a/x-pack/plugin/searchable-snapshots/qa/url/build.gradle +++ b/x-pack/plugin/searchable-snapshots/qa/url/build.gradle @@ -29,13 +29,13 @@ File repositoryDir = fixture.fsRepositoryDir as File tasks.named("javaRestTest").configure { dependsOn fixture.getTasks().named("postProcessFixture") - systemProperty 'test.url.fs.repo.dir', repositoryDir.absolutePath + nonInputProperties.systemProperty 'test.url.fs.repo.dir', repositoryDir.absolutePath nonInputProperties.systemProperty 'test.url.http', "${-> fixtureAddress('nginx-fixture')}" } testClusters.matching { it.name == "javaRestTest" }.configureEach { testDistribution = 'DEFAULT' - setting 'path.repo', repositoryDir.absolutePath + setting 'path.repo', repositoryDir.absolutePath, IGNORE_VALUE setting 'repositories.url.allowed_urls', { "${-> fixtureAddress('nginx-fixture')}" }, IGNORE_VALUE setting 'xpack.license.self_generated.type', 'trial' diff --git a/x-pack/plugin/slm/qa/multi-node/build.gradle b/x-pack/plugin/slm/qa/multi-node/build.gradle index 999f6e6a79c7c..b02fe7cd44fbd 100644 --- a/x-pack/plugin/slm/qa/multi-node/build.gradle +++ b/x-pack/plugin/slm/qa/multi-node/build.gradle @@ -1,4 +1,5 @@ import org.elasticsearch.gradle.internal.info.BuildParams +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.legacy-java-rest-test' @@ -12,14 +13,14 @@ File repoDir = file("$buildDir/testclusters/repo") tasks.named("javaRestTest").configure { /* To support taking index snapshots, we have to set path.repo setting */ - systemProperty 'tests.path.repo', repoDir + nonInputProperties.systemProperty 'tests.path.repo', repoDir } testClusters.configureEach { testDistribution = 'DEFAULT' numberOfNodes = 4 - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.searchable.snapshot.shared_cache.size', '16MB' setting 'xpack.searchable.snapshot.shared_cache.region_size', '256KB' setting 'xpack.security.enabled', 'false' diff --git a/x-pack/plugin/snapshot-based-recoveries/qa/fs/build.gradle b/x-pack/plugin/snapshot-based-recoveries/qa/fs/build.gradle index 0c0c1930f8601..d116a2d11e22d 100644 --- a/x-pack/plugin/snapshot-based-recoveries/qa/fs/build.gradle +++ b/x-pack/plugin/snapshot-based-recoveries/qa/fs/build.gradle @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE + apply plugin: 'elasticsearch.legacy-java-rest-test' apply plugin: 'elasticsearch.rest-resources' @@ -25,7 +27,7 @@ tasks.withType(Test).configureEach { doFirst { delete(repoDir) } - systemProperty 'tests.path.repo', repoDir + nonInputProperties.systemProperty 'tests.path.repo', repoDir } testClusters.matching { it.name == "javaRestTest" }.configureEach { @@ -33,6 +35,6 @@ testClusters.matching { it.name == "javaRestTest" }.configureEach { numberOfNodes = 3 setting 'xpack.license.self_generated.type', 'trial' - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.security.enabled', 'false' } diff --git a/x-pack/plugin/snapshot-based-recoveries/qa/license-enforcing/build.gradle b/x-pack/plugin/snapshot-based-recoveries/qa/license-enforcing/build.gradle index 1c9a877507791..d2121daa1ba93 100644 --- a/x-pack/plugin/snapshot-based-recoveries/qa/license-enforcing/build.gradle +++ b/x-pack/plugin/snapshot-based-recoveries/qa/license-enforcing/build.gradle @@ -27,7 +27,7 @@ tasks.withType(Test).configureEach { doFirst { delete(repoDir) } - systemProperty 'tests.path.repo', repoDir + nonInputProperties.systemProperty 'tests.path.repo', repoDir } testClusters.matching { it.name == "javaRestTest" }.configureEach { @@ -37,6 +37,6 @@ testClusters.matching { it.name == "javaRestTest" }.configureEach { // This project tests that enterprise licensing is enforced, // therefore we use a basic license setting 'xpack.license.self_generated.type', 'basic' - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.security.enabled', 'false' } diff --git a/x-pack/plugin/snapshot-repo-test-kit/qa/rest/build.gradle b/x-pack/plugin/snapshot-repo-test-kit/qa/rest/build.gradle index 6b32e11ef81d4..17df249b08cf6 100644 --- a/x-pack/plugin/snapshot-repo-test-kit/qa/rest/build.gradle +++ b/x-pack/plugin/snapshot-repo-test-kit/qa/rest/build.gradle @@ -1,3 +1,5 @@ +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE + apply plugin: 'elasticsearch.legacy-yaml-rest-test' apply plugin: 'elasticsearch.rest-resources' @@ -9,12 +11,12 @@ dependencies { final File repoDir = file("$buildDir/testclusters/repo") tasks.named("yamlRestTest").configure { - systemProperty 'tests.path.repo', repoDir + nonInputProperties.systemProperty 'tests.path.repo', repoDir } testClusters.matching { it.name == "yamlRestTest" }.configureEach { testDistribution = 'DEFAULT' - setting 'path.repo', repoDir.absolutePath + setting 'path.repo', repoDir.absolutePath, IGNORE_VALUE setting 'xpack.security.enabled', 'false' } diff --git a/x-pack/plugin/sql/qa/server/multi-cluster-with-security/build.gradle b/x-pack/plugin/sql/qa/server/multi-cluster-with-security/build.gradle index b56d8ff211990..b42ae29e257f0 100644 --- a/x-pack/plugin/sql/qa/server/multi-cluster-with-security/build.gradle +++ b/x-pack/plugin/sql/qa/server/multi-cluster-with-security/build.gradle @@ -1,4 +1,5 @@ import org.elasticsearch.gradle.testclusters.DefaultTestClustersTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.legacy-java-rest-test' @@ -25,7 +26,7 @@ def javaRestTestClusterReg = testClusters.register('javaRestTest') { setting 'xpack.watcher.enabled', 'false' setting 'cluster.remote.my_remote_cluster.seeds', { remoteClusterReg.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE setting 'cluster.remote.connections_per_cluster', "1" setting 'xpack.security.enabled', 'true' setting 'xpack.license.self_generated.type', 'trial' diff --git a/x-pack/plugin/transform/qa/multi-cluster-tests-with-security/build.gradle b/x-pack/plugin/transform/qa/multi-cluster-tests-with-security/build.gradle index 7c0eaf1450567..ef34db62e5e03 100644 --- a/x-pack/plugin/transform/qa/multi-cluster-tests-with-security/build.gradle +++ b/x-pack/plugin/transform/qa/multi-cluster-tests-with-security/build.gradle @@ -1,6 +1,7 @@ import org.elasticsearch.gradle.internal.test.RestIntegTestTask import org.elasticsearch.gradle.Version import org.elasticsearch.gradle.VersionProperties +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.internal-testclusters' apply plugin: 'elasticsearch.standalone-rest-test' @@ -43,7 +44,7 @@ testClusters.register('mixed-cluster') { setting 'xpack.license.self_generated.type', 'trial' setting 'cluster.remote.my_remote_cluster.seeds', { remoteCluster.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE setting 'cluster.remote.connections_per_cluster', "1" user username: "test_user", password: "x-pack-test-password" diff --git a/x-pack/plugin/vector-tile/qa/multi-cluster/build.gradle b/x-pack/plugin/vector-tile/qa/multi-cluster/build.gradle index ea20c8eacd172..30b032c1cae1a 100644 --- a/x-pack/plugin/vector-tile/qa/multi-cluster/build.gradle +++ b/x-pack/plugin/vector-tile/qa/multi-cluster/build.gradle @@ -6,7 +6,7 @@ */ import org.elasticsearch.gradle.testclusters.DefaultTestClustersTask - +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.legacy-java-rest-test' dependencies { @@ -27,7 +27,7 @@ def localCluster = testClusters.register('local') { setting 'xpack.security.enabled', 'true' user username: 'admin', password: 'admin-password', role: 'superuser' setting 'cluster.remote.other.seeds', - { "\"${remoteCluster.get().getAllTransportPortURI().join(",")}\"" } + { "\"${remoteCluster.get().getAllTransportPortURI().join(",")}\"" }, IGNORE_VALUE } diff --git a/x-pack/qa/kerberos-tests/build.gradle b/x-pack/qa/kerberos-tests/build.gradle index 30fac49096679..62d6f0a1e34b8 100644 --- a/x-pack/qa/kerberos-tests/build.gradle +++ b/x-pack/qa/kerberos-tests/build.gradle @@ -12,6 +12,13 @@ dependencies { javaRestTestImplementation(testArtifact(project(xpackModule('security')))) } +normalization { + runtimeClasspath { + ignore 'krb5.conf' + ignore '*.keytab' + } +} + tasks.register("copyKeytabToGeneratedResources", Copy) { from project(':test:fixtures:krb5kdc-fixture').ext.krb5Keytabs("peppa", "peppa.keytab") into "$buildDir/generated-resources/keytabs" @@ -28,6 +35,7 @@ tasks.register("copyConfToGeneratedResources", Copy) { String realm = "BUILD.ELASTIC.CO" tasks.named("javaRestTest").configure { + dependsOn "copyKeytabToGeneratedResources", "copyConfToGeneratedResources" usesDefaultDistribution() Path peppaKeytab = Paths.get("${project.buildDir}", "generated-resources", "keytabs", "peppa.keytab") Path krb5Conf = Paths.get("${project.buildDir}", "generated-resources", "conf", "krb5.conf") @@ -35,12 +43,9 @@ tasks.named("javaRestTest").configure { nonInputProperties.systemProperty 'test.userkt.keytab', "${peppaKeytab}" nonInputProperties.systemProperty 'test.userpwd', "george@${realm}" nonInputProperties.systemProperty 'test.krb5.conf', "${krb5Conf}" + nonInputProperties.systemProperty 'java.security.krb5.conf', "${krb5Conf}" systemProperty 'test.userpwd.password', "dino_but_longer_than_14_chars" - jvmArgs([ - "-Djava.security.krb5.conf=${krb5Conf}", - "-Dsun.security.krb5.debug=true" - ]) - dependsOn "copyKeytabToGeneratedResources", "copyConfToGeneratedResources" + systemProperty 'sun.security.krb5.debug', true classpath += files("$buildDir/generated-resources/keytabs") classpath += files("$buildDir/generated-resources/conf") } diff --git a/x-pack/qa/multi-cluster-search-security/legacy-with-basic-license/build.gradle b/x-pack/qa/multi-cluster-search-security/legacy-with-basic-license/build.gradle index f9d60181fc815..cce18a4bd1579 100644 --- a/x-pack/qa/multi-cluster-search-security/legacy-with-basic-license/build.gradle +++ b/x-pack/qa/multi-cluster-search-security/legacy-with-basic-license/build.gradle @@ -1,5 +1,6 @@ import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-resources' @@ -40,11 +41,11 @@ def queryingCluster = testClusters.register('querying-cluster') { setting 'cluster.remote.my_remote_cluster.mode', 'proxy' setting 'cluster.remote.my_remote_cluster.proxy_address', { "\"${fulfillingCluster.get().getAllTransportPortURI().get(0)}\"" - } + }, IGNORE_VALUE } else { setting 'cluster.remote.my_remote_cluster.seeds', { fulfillingCluster.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE } } diff --git a/x-pack/qa/multi-cluster-search-security/legacy-with-full-license/build.gradle b/x-pack/qa/multi-cluster-search-security/legacy-with-full-license/build.gradle index 35b7a981fca36..69c0e8b20c2c4 100644 --- a/x-pack/qa/multi-cluster-search-security/legacy-with-full-license/build.gradle +++ b/x-pack/qa/multi-cluster-search-security/legacy-with-full-license/build.gradle @@ -1,5 +1,6 @@ import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-resources' @@ -40,11 +41,11 @@ def queryingCluster = testClusters.register('querying-cluster') { setting 'cluster.remote.my_remote_cluster.mode', 'proxy' setting 'cluster.remote.my_remote_cluster.proxy_address', { "\"${fulfillingCluster.get().getAllTransportPortURI().get(0)}\"" - } + }, IGNORE_VALUE } else { setting 'cluster.remote.my_remote_cluster.seeds', { fulfillingCluster.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE } } diff --git a/x-pack/qa/multi-cluster-search-security/legacy-with-restricted-trust/build.gradle b/x-pack/qa/multi-cluster-search-security/legacy-with-restricted-trust/build.gradle index 810895abe1e86..1164aa240ee22 100644 --- a/x-pack/qa/multi-cluster-search-security/legacy-with-restricted-trust/build.gradle +++ b/x-pack/qa/multi-cluster-search-security/legacy-with-restricted-trust/build.gradle @@ -1,5 +1,6 @@ import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.internal.test.RestIntegTestTask +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-resources' @@ -63,11 +64,11 @@ def queryingCluster = testClusters.register('querying-cluster') { setting 'cluster.remote.my_remote_cluster.mode', 'proxy' setting 'cluster.remote.my_remote_cluster.proxy_address', { "\"${fulfillingCluster.get().getAllTransportPortURI().get(0)}\"" - } + }, IGNORE_VALUE } else { setting 'cluster.remote.my_remote_cluster.seeds', { fulfillingCluster.get().getAllTransportPortURI().collect { "\"$it\"" }.toString() - } + }, IGNORE_VALUE } } diff --git a/x-pack/qa/repository-old-versions/build.gradle b/x-pack/qa/repository-old-versions/build.gradle index da8802e452c43..3c55aa8aa4663 100644 --- a/x-pack/qa/repository-old-versions/build.gradle +++ b/x-pack/qa/repository-old-versions/build.gradle @@ -13,6 +13,7 @@ import org.elasticsearch.gradle.internal.info.BuildParams import org.elasticsearch.gradle.internal.test.AntFixture import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask import org.elasticsearch.gradle.transform.UnzipTransform +import static org.elasticsearch.gradle.PropertyNormalization.IGNORE_VALUE apply plugin: 'elasticsearch.jdk-download' apply plugin: 'elasticsearch.internal-testclusters' @@ -86,7 +87,7 @@ if (OS.current() == OS.WINDOWS) { numberOfNodes = 2 versions = [project.version, project.version] // to test full cluster restart - setting 'path.repo', repoLocation + setting 'path.repo', repoLocation, IGNORE_VALUE setting 'xpack.license.self_generated.type', 'trial' setting 'xpack.security.enabled', 'true' @@ -151,7 +152,7 @@ if (OS.current() == OS.WINDOWS) { } tasks.matching { it.name.startsWith("javaRestTest") && it.name.endsWith(versionNoDots) }.configureEach { - it.systemProperty "tests.repo.location", repoLocation + it.nonInputProperties.systemProperty "tests.repo.location", repoLocation it.systemProperty "tests.es.version", version.toString() /* Use a closure on the string to delay evaluation until right before we diff --git a/x-pack/qa/saml-idp-tests/build.gradle b/x-pack/qa/saml-idp-tests/build.gradle index c2dffe2470b84..6027a421b62f2 100644 --- a/x-pack/qa/saml-idp-tests/build.gradle +++ b/x-pack/qa/saml-idp-tests/build.gradle @@ -31,6 +31,12 @@ tasks.register("copyIdpFiles", Sync) { } } +normalization { + runtimeClasspath { + ignore 'idp-metadata.xml' + } +} + tasks.named("javaRestTest").configure { usesDefaultDistribution() classpath += files(tasks.named("copyIdpFiles")) From e89d245d53787fb22f43e2fa5f66848ac4479bc9 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Thu, 26 Oct 2023 16:17:20 -0400 Subject: [PATCH 141/203] [ci] Disable remaining periodic jobs in Jenkins, except third-party tests (#101403) --- ...asticsearch+periodic+concurrent-search-tests-trigger.yml | 6 ------ ...astic+elasticsearch+periodic+concurrent-search-tests.yml | 3 ++- .ci/jobs.t/elastic+elasticsearch+periodic+ear-trigger.yml | 6 ------ .ci/jobs.t/elastic+elasticsearch+periodic+ear.yml | 3 ++- ...astic+elasticsearch+periodic+eql-correctness-trigger.yml | 6 ------ .../elastic+elasticsearch+periodic+eql-correctness.yml | 3 ++- ...astic+elasticsearch+periodic+example-plugins-trigger.yml | 6 ------ .../elastic+elasticsearch+periodic+example-plugins.yml | 3 ++- ...elastic+elasticsearch+periodic+release-tests-trigger.yml | 6 ------ .ci/jobs.t/elastic+elasticsearch+periodic+release-tests.yml | 3 ++- ...c+elasticsearch+periodic+single-processor-node-tests.yml | 3 ++- ...lasticsearch+periodic+single-processor-tests-trigger.yml | 6 ------ ...icsearch+periodic+snyk-dependency-monitoring-trigger.yml | 6 ------ ...ic+elasticsearch+periodic+snyk-dependency-monitoring.yml | 3 ++- 14 files changed, 14 insertions(+), 49 deletions(-) delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+ear-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+example-plugins-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+release-tests-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+single-processor-tests-trigger.yml delete mode 100644 .ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring-trigger.yml diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests-trigger.yml deleted file mode 100644 index 1816e7143355f..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+concurrent-search-tests - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests.yml index 99758cb9d088c..ad48635654459 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+concurrent-search-tests.yml @@ -2,7 +2,8 @@ - job: name: elastic+elasticsearch+%BRANCH%+periodic+concurrent-search-tests display-name: "elastic / elasticsearch # %BRANCH% - concurrent search tests" - description: "Testing concurrent search enabled for the Elasticsearch %BRANCH% branch.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true node: "general-purpose && docker" builders: - inject: diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+ear-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+ear-trigger.yml deleted file mode 100644 index a50a1f96358ad..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+ear-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+ear - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+ear.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+ear.yml index b1b5a39f92a5a..67462d3a2a809 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+ear.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+ear.yml @@ -2,7 +2,8 @@ - job: name: elastic+elasticsearch+%BRANCH%+periodic+ear display-name: "elastic / elasticsearch # %BRANCH% - encryption at rest" - description: "The Elasticsearch %BRANCH% branch encryption at rest compatibility tests.\n\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true node: packaging-large builders: - inject: diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness-trigger.yml deleted file mode 100644 index 986c52a137de3..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+eql-correctness - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/8 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness.yml index 2652732974661..a23bae19134fc 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+eql-correctness.yml @@ -3,7 +3,8 @@ name: elastic+elasticsearch+%BRANCH%+periodic+eql-correctness workspace: /dev/shm/elastic+elasticsearch+%BRANCH%+periodic+eql-correctness display-name: "elastic / elasticsearch # %BRANCH% - eql correctness tests" - description: "Testing of Elasticsearch %BRANCH% EQL.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true builders: - inject: properties-file: '.ci/java-versions.properties' diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins-trigger.yml deleted file mode 100644 index 909d175f11882..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+example-plugins - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins.yml index 2423a85b2a6bd..ee496690e82ce 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+example-plugins.yml @@ -3,7 +3,8 @@ name: elastic+elasticsearch+%BRANCH%+periodic+example-plugins workspace: /dev/shm/elastic+elasticsearch+%BRANCH%+periodic+example-plugins display-name: "elastic / elasticsearch # %BRANCH% - example plugin tests" - description: "Testing of Elasticsearch %BRANCH% example plugins.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true builders: - inject: properties-file: '.ci/java-versions.properties' diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests-trigger.yml deleted file mode 100644 index c624c929b3dd6..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+release-tests - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests.yml index 5fc2d5cd1ca5d..abaf4242e1648 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+release-tests.yml @@ -4,7 +4,8 @@ # Don't use ramdisk since this build generates lots of large artifacts and results in oomkiller issues # workspace: /dev/shm/elastic+elasticsearch+%BRANCH%+periodic+release-tests display-name: "elastic / elasticsearch # %BRANCH% - release tests" - description: "Release version tests for the Elasticsearch %BRANCH% branch.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true node: "general-purpose && docker" builders: - inject: diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-node-tests.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-node-tests.yml index c67baa07da1ee..02240bf1bb339 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-node-tests.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-node-tests.yml @@ -2,7 +2,8 @@ - job: name: elastic+elasticsearch+%BRANCH%+periodic+single-processor-node-tests display-name: "elastic / elasticsearch # %BRANCH% - single processor node tests" - description: "Testing with node.processors set to '1' for the Elasticsearch %BRANCH% branch.\n" + description: "This job has been migrated to Buildkite.\n" + disabled: true node: "general-purpose && docker" builders: - inject: diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-tests-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-tests-trigger.yml deleted file mode 100644 index 40ad9e9dd5446..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+single-processor-tests-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+periodic+single-processor-node-tests - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H/12 * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring-trigger.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring-trigger.yml deleted file mode 100644 index fa0f06c3315af..0000000000000 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring-trigger.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -jjbb-template: periodic-trigger-lgc.yml -vars: - - periodic-job: elastic+elasticsearch+%BRANCH%+snyk-dependency-monitoring - - lgc-job: elastic+elasticsearch+%BRANCH%+intake - - cron: "H H * * *" diff --git a/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring.yml b/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring.yml index 1a76003f3d13c..6190937cc6490 100644 --- a/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring.yml +++ b/.ci/jobs.t/elastic+elasticsearch+periodic+snyk-dependency-monitoring.yml @@ -3,7 +3,8 @@ name: elastic+elasticsearch+%BRANCH%+snyk-dependency-monitoring workspace: /dev/shm/elastic+elasticsearch+%BRANCH%+snyk-dependency-monitoring display-name: "elastic / elasticsearch # %BRANCH% - snyk dependency monitoring" - description: "Publishing of the Elasticsearch %BRANCH% dependencies graph to snyk dependency monitoring" + description: "This job has been migrated to Buildkite.\n" + disabled: true builders: - inject: properties-file: '.ci/java-versions.properties' From 3b0b48427cb011acde45f80fc6390aed3d5eb011 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Thu, 26 Oct 2023 16:29:14 -0400 Subject: [PATCH 142/203] Removing trace logging for issue #100502 (#101404) Test hasn't failed for over a week, the original issue seems resolved as the at least one of the tests were failing once a day. closes https://github.com/elastic/elasticsearch/issues/100502 --- .../60_dense_vector_dynamic_mapping.yml | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml index ffc7f749b8fd3..151698482368a 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.vectors/60_dense_vector_dynamic_mapping.yml @@ -3,34 +3,6 @@ setup: version: ' - 8.10.99' reason: 'Dynamic mapping of floats to dense_vector was added in 8.11' - # Additional logging for issue: https://github.com/elastic/elasticsearch/issues/100502 - - do: - cluster.put_settings: - body: > - { - "persistent": { - "logger.org.elasticsearch.index": "TRACE", - "logger.org.elasticsearch.action.admin.indices.mapping.get": "TRACE", - "logger.org.elasticsearch.cluster.service.ClusterApplierService": "TRACE" - } - } - ---- -teardown: - - skip: - version: ' - 8.10.99' - reason: 'Dynamic mapping of floats to dense_vector was added in 8.11' - - - do: - cluster.put_settings: - body: > - { - "persistent": { - "logger.org.elasticsearch.index": null, - "logger.org.elasticsearch.action.admin.indices.mapping.get": null, - "logger.org.elasticsearch.cluster.service.ClusterApplierService": null - } - } --- "Fields with float arrays below the threshold still map as float": From 81ace015b1a40ee02817fbe2a782c6ea1bcfa3a6 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 26 Oct 2023 19:20:19 -0400 Subject: [PATCH 143/203] ESQL: Track blocks emitted from lucene (#101396) This enables memory tracking for blocks emitted by all of the `LuceneOperator` subclasses. They don't use a ton of memory, but we'd like to get everything tracked. --- docs/changelog/101396.yaml | 5 + .../compute/lucene/LuceneCountOperator.java | 24 +++-- .../compute/lucene/LuceneOperator.java | 6 +- .../compute/lucene/LuceneSourceOperator.java | 39 +++++--- .../lucene/LuceneTopNSourceOperator.java | 94 +++++++++++-------- .../AggregatorFunctionTestCase.java | 4 +- .../SumDoubleAggregatorFunctionTests.java | 12 +-- .../lucene/LuceneCountOperatorTests.java | 45 +++++++-- .../lucene/LuceneSourceOperatorTests.java | 54 +++++++++-- .../lucene/LuceneTopNSourceOperatorTests.java | 48 ++++++++-- .../compute/operator/AnyOperatorTestCase.java | 13 ++- .../operator/ForkingOperatorTestCase.java | 10 +- .../compute/operator/OperatorTestCase.java | 10 +- ...r.java => TestResultPageSinkOperator.java} | 4 +- 14 files changed, 261 insertions(+), 107 deletions(-) create mode 100644 docs/changelog/101396.yaml rename x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/{ResultPageSinkOperator.java => TestResultPageSinkOperator.java} (85%) diff --git a/docs/changelog/101396.yaml b/docs/changelog/101396.yaml new file mode 100644 index 0000000000000..a486b2bed9237 --- /dev/null +++ b/docs/changelog/101396.yaml @@ -0,0 +1,5 @@ +pr: 101396 +summary: "ESQL: Track blocks emitted from lucene" +area: ES|QL +type: enhancement +issues: [] diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneCountOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneCountOperator.java index 7e0b5297b629b..75bd230638928 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneCountOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneCountOperator.java @@ -13,11 +13,13 @@ import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.SourceOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -62,7 +64,7 @@ public Factory( @Override public SourceOperator get(DriverContext driverContext) { - return new LuceneCountOperator(sliceQueue, limit); + return new LuceneCountOperator(driverContext.blockFactory(), sliceQueue, limit); } @Override @@ -80,8 +82,8 @@ public String describe() { } } - public LuceneCountOperator(LuceneSliceQueue sliceQueue, int limit) { - super(PAGE_SIZE, sliceQueue); + public LuceneCountOperator(BlockFactory blockFactory, LuceneSliceQueue sliceQueue, int limit) { + super(blockFactory, PAGE_SIZE, sliceQueue); this.remainingDocs = limit; this.leafCollector = new LeafCollector() { @Override @@ -155,11 +157,17 @@ public Page getOutput() { // emit only one page if (remainingDocs <= 0 && pagesEmitted == 0) { pagesEmitted++; - page = new Page( - PAGE_SIZE, - LongBlock.newConstantBlockWith(totalHits, PAGE_SIZE), - BooleanBlock.newConstantBlockWith(true, PAGE_SIZE) - ); + LongBlock count = null; + BooleanBlock seen = null; + try { + count = LongBlock.newConstantBlockWith(totalHits, PAGE_SIZE, blockFactory); + seen = BooleanBlock.newConstantBlockWith(true, PAGE_SIZE, blockFactory); + page = new Page(PAGE_SIZE, count, seen); + } finally { + if (page == null) { + Releasables.closeExpectNoException(count, seen); + } + } } return page; } catch (IOException e) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneOperator.java index abb96446bb831..6536b08cd2419 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneOperator.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.operator.Operator; import org.elasticsearch.compute.operator.SourceOperator; import org.elasticsearch.logging.LogManager; @@ -37,6 +38,8 @@ public abstract class LuceneOperator extends SourceOperator { public static final int NO_LIMIT = Integer.MAX_VALUE; + protected final BlockFactory blockFactory; + private int processSlices; final int maxPageSize; private final LuceneSliceQueue sliceQueue; @@ -49,7 +52,8 @@ public abstract class LuceneOperator extends SourceOperator { int pagesEmitted; boolean doneCollecting; - public LuceneOperator(int maxPageSize, LuceneSliceQueue sliceQueue) { + public LuceneOperator(BlockFactory blockFactory, int maxPageSize, LuceneSliceQueue sliceQueue) { + this.blockFactory = blockFactory; this.maxPageSize = maxPageSize; this.sliceQueue = sliceQueue; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneSourceOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneSourceOperator.java index 0bbb6571dc4fd..7b2b276a619c6 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneSourceOperator.java @@ -11,12 +11,14 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreMode; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DocVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.SourceOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -61,7 +63,7 @@ public Factory( @Override public SourceOperator get(DriverContext driverContext) { - return new LuceneSourceOperator(maxPageSize, sliceQueue, limit); + return new LuceneSourceOperator(driverContext.blockFactory(), maxPageSize, sliceQueue, limit); } @Override @@ -89,11 +91,11 @@ public String describe() { } } - public LuceneSourceOperator(int maxPageSize, LuceneSliceQueue sliceQueue, int limit) { - super(maxPageSize, sliceQueue); + public LuceneSourceOperator(BlockFactory blockFactory, int maxPageSize, LuceneSliceQueue sliceQueue, int limit) { + super(blockFactory, maxPageSize, sliceQueue); this.minPageSize = Math.max(1, maxPageSize / 2); this.remainingDocs = limit; - this.docsBuilder = IntVector.newVectorBuilder(Math.min(limit, maxPageSize)); + this.docsBuilder = IntVector.newVectorBuilder(Math.min(limit, maxPageSize), blockFactory); this.leafCollector = new LeafCollector() { @Override public void setScorer(Scorable scorer) { @@ -143,16 +145,20 @@ public Page getOutput() { Page page = null; if (currentPagePos >= minPageSize || remainingDocs <= 0 || scorer.isDone()) { pagesEmitted++; - page = new Page( - currentPagePos, - new DocVector( - IntBlock.newConstantBlockWith(scorer.shardIndex(), currentPagePos).asVector(), - IntBlock.newConstantBlockWith(scorer.leafReaderContext().ord, currentPagePos).asVector(), - docsBuilder.build(), - true - ).asBlock() - ); - docsBuilder = IntVector.newVectorBuilder(Math.min(remainingDocs, maxPageSize)); + IntBlock shard = null; + IntBlock leaf = null; + IntVector docs = null; + try { + shard = IntBlock.newConstantBlockWith(scorer.shardIndex(), currentPagePos, blockFactory); + leaf = IntBlock.newConstantBlockWith(scorer.leafReaderContext().ord, currentPagePos, blockFactory); + docs = docsBuilder.build(); + docsBuilder = IntVector.newVectorBuilder(Math.min(remainingDocs, maxPageSize), blockFactory); + page = new Page(currentPagePos, new DocVector(shard.asVector(), leaf.asVector(), docs, true).asBlock()); + } finally { + if (page == null) { + Releasables.closeExpectNoException(shard, leaf, docs); + } + } currentPagePos = 0; } return page; @@ -161,6 +167,11 @@ public Page getOutput() { } } + @Override + public void close() { + docsBuilder.close(); + } + @Override protected void describe(StringBuilder sb) { sb.append(", remainingDocs=").append(remainingDocs); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperator.java index 4c6bb50ce9f7f..4ce0af3bd0ffe 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperator.java @@ -16,12 +16,14 @@ import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TopFieldCollector; import org.elasticsearch.common.Strings; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.DocVector; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.SourceOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.SortAndFormats; import org.elasticsearch.search.sort.SortBuilder; @@ -38,25 +40,6 @@ * Source operator that builds Pages out of the output of a TopFieldCollector (aka TopN) */ public final class LuceneTopNSourceOperator extends LuceneOperator { - /** - * Collected docs. {@code null} until we're {@link #emit(boolean)}. - */ - private ScoreDoc[] scoreDocs; - /** - * The offset in {@link #scoreDocs} of the next page. - */ - private int offset = 0; - - private PerShardCollector perShardCollector; - private final List> sorts; - private final int limit; - - public LuceneTopNSourceOperator(int maxPageSize, List> sorts, int limit, LuceneSliceQueue sliceQueue) { - super(maxPageSize, sliceQueue); - this.sorts = sorts; - this.limit = limit; - } - public static final class Factory implements LuceneOperator.Factory { private final int taskConcurrency; private final int maxPageSize; @@ -85,7 +68,7 @@ public Factory( @Override public SourceOperator get(DriverContext driverContext) { - return new LuceneTopNSourceOperator(maxPageSize, sorts, limit, sliceQueue); + return new LuceneTopNSourceOperator(driverContext.blockFactory(), maxPageSize, sorts, limit, sliceQueue); } @Override @@ -116,6 +99,31 @@ public String describe() { } } + /** + * Collected docs. {@code null} until we're {@link #emit(boolean)}. + */ + private ScoreDoc[] scoreDocs; + /** + * The offset in {@link #scoreDocs} of the next page. + */ + private int offset = 0; + + private PerShardCollector perShardCollector; + private final List> sorts; + private final int limit; + + public LuceneTopNSourceOperator( + BlockFactory blockFactory, + int maxPageSize, + List> sorts, + int limit, + LuceneSliceQueue sliceQueue + ) { + super(blockFactory, maxPageSize, sliceQueue); + this.sorts = sorts; + this.limit = limit; + } + @Override public boolean isFinished() { return doneCollecting && isEmitting() == false; @@ -187,29 +195,35 @@ private Page emit(boolean startEmitting) { return null; } int size = Math.min(maxPageSize, scoreDocs.length - offset); - IntVector.Builder currentSegmentBuilder = IntVector.newVectorBuilder(size); - IntVector.Builder currentDocsBuilder = IntVector.newVectorBuilder(size); + IntBlock shard = null; + IntVector segments = null; + IntVector docs = null; + Page page = null; + try ( + IntVector.Builder currentSegmentBuilder = IntVector.newVectorBuilder(size, blockFactory); + IntVector.Builder currentDocsBuilder = IntVector.newVectorBuilder(size, blockFactory) + ) { + int start = offset; + offset += size; + List leafContexts = perShardCollector.searchContext.searcher().getLeafContexts(); + for (int i = start; i < offset; i++) { + int doc = scoreDocs[i].doc; + int segment = ReaderUtil.subIndex(doc, leafContexts); + currentSegmentBuilder.appendInt(segment); + currentDocsBuilder.appendInt(doc - leafContexts.get(segment).docBase); // the offset inside the segment + } - int start = offset; - offset += size; - List leafContexts = perShardCollector.searchContext.searcher().getLeafContexts(); - for (int i = start; i < offset; i++) { - int doc = scoreDocs[i].doc; - int segment = ReaderUtil.subIndex(doc, leafContexts); - currentSegmentBuilder.appendInt(segment); - currentDocsBuilder.appendInt(doc - leafContexts.get(segment).docBase); // the offset inside the segment + shard = IntBlock.newConstantBlockWith(perShardCollector.shardIndex, size, blockFactory); + segments = currentSegmentBuilder.build(); + docs = currentDocsBuilder.build(); + page = new Page(size, new DocVector(shard.asVector(), segments, docs, null).asBlock()); + } finally { + if (page == null) { + Releasables.close(shard, segments, docs); + } } - pagesEmitted++; - return new Page( - size, - new DocVector( - IntBlock.newConstantBlockWith(perShardCollector.shardIndex, size).asVector(), - currentSegmentBuilder.build(), - currentDocsBuilder.build(), - null - ).asBlock() - ); + return page; } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java index 83f4a6895b154..6c1f088aeca7b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java @@ -29,7 +29,7 @@ import org.elasticsearch.compute.operator.NullInsertingSourceOperator; import org.elasticsearch.compute.operator.Operator; import org.elasticsearch.compute.operator.PositionMergingSourceOperator; -import org.elasticsearch.compute.operator.ResultPageSinkOperator; +import org.elasticsearch.compute.operator.TestResultPageSinkOperator; import java.util.ArrayList; import java.util.List; @@ -110,7 +110,7 @@ public final void testIgnoresNulls() { driverContext, new NullInsertingSourceOperator(new CannedSourceOperator(input.iterator()), blockFactory), List.of(simple(nonBreakingBigArrays().withCircuitBreaking()).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java index b08bda4a71d38..ea428d7d87cad 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunctionTests.java @@ -14,9 +14,9 @@ import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.Driver; import org.elasticsearch.compute.operator.DriverContext; -import org.elasticsearch.compute.operator.ResultPageSinkOperator; import org.elasticsearch.compute.operator.SequenceDoubleBlockSourceOperator; import org.elasticsearch.compute.operator.SourceOperator; +import org.elasticsearch.compute.operator.TestResultPageSinkOperator; import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; @@ -57,7 +57,7 @@ public void testOverflowSucceeds() { driverContext, new SequenceDoubleBlockSourceOperator(driverContext.blockFactory(), DoubleStream.of(Double.MAX_VALUE - 1, 2)), List.of(simple(nonBreakingBigArrays()).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { @@ -78,7 +78,7 @@ public void testSummationAccuracy() { DoubleStream.of(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7) ), List.of(simple(nonBreakingBigArrays()).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { @@ -104,7 +104,7 @@ public void testSummationAccuracy() { driverContext, new SequenceDoubleBlockSourceOperator(driverContext.blockFactory(), DoubleStream.of(values)), List.of(simple(nonBreakingBigArrays()).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { @@ -126,7 +126,7 @@ public void testSummationAccuracy() { driverContext, new SequenceDoubleBlockSourceOperator(driverContext.blockFactory(), DoubleStream.of(largeValues)), List.of(simple(nonBreakingBigArrays()).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { @@ -145,7 +145,7 @@ public void testSummationAccuracy() { driverContext, new SequenceDoubleBlockSourceOperator(driverContext.blockFactory(), DoubleStream.of(largeValues)), List.of(simple(nonBreakingBigArrays()).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneCountOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneCountOperatorTests.java index 0696d02d87af6..d6edc903607cc 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneCountOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneCountOperatorTests.java @@ -16,6 +16,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; +import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.LongBlock; @@ -24,10 +25,11 @@ import org.elasticsearch.compute.operator.Driver; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.OperatorTestCase; -import org.elasticsearch.compute.operator.PageConsumerOperator; +import org.elasticsearch.compute.operator.TestResultPageSinkOperator; import org.elasticsearch.core.IOUtils; import org.elasticsearch.index.cache.query.TrivialQueryCachingPolicy; import org.elasticsearch.index.query.SearchExecutionContext; +import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.search.internal.ContextIndexSearcher; import org.elasticsearch.search.internal.SearchContext; import org.junit.After; @@ -37,6 +39,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Supplier; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -115,25 +118,53 @@ protected String expectedDescriptionOfSimple() { // TODO tests for the other data partitioning configurations public void testSimple() { + testSimple(this::driverContext); + } + + public void testSimpleWithCranky() { + try { + testSimple(this::crankyDriverContext); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + private void testSimple(Supplier contexts) { int size = between(1_000, 20_000); int limit = randomBoolean() ? between(10, size) : Integer.MAX_VALUE; - testCount(size, limit); + testCount(contexts, size, limit); } public void testEmpty() { + testEmpty(this::driverContext); + } + + public void testEmptyWithCranky() { + try { + testEmpty(this::crankyDriverContext); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + private void testEmpty(Supplier contexts) { int limit = randomBoolean() ? between(10, 10000) : Integer.MAX_VALUE; - testCount(0, limit); + testCount(contexts, 0, limit); } - private void testCount(int size, int limit) { + private void testCount(Supplier contexts, int size, int limit) { DataPartitioning dataPartitioning = randomFrom(DataPartitioning.values()); - LuceneCountOperator.Factory factory = simple(nonBreakingBigArrays(), dataPartitioning, size, limit); + LuceneCountOperator.Factory factory = simple(contexts.get().bigArrays(), dataPartitioning, size, limit); List results = new CopyOnWriteArrayList<>(); List drivers = new ArrayList<>(); int taskConcurrency = between(1, 8); for (int i = 0; i < taskConcurrency; i++) { - DriverContext ctx = driverContext(); - drivers.add(new Driver(ctx, factory.get(ctx), List.of(), new PageConsumerOperator(results::add), () -> {})); + DriverContext ctx = contexts.get(); + drivers.add(new Driver(ctx, factory.get(ctx), List.of(), new TestResultPageSinkOperator(results::add), () -> {})); } OperatorTestCase.runDriver(drivers); assertThat(results.size(), lessThanOrEqualTo(taskConcurrency)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java index 131082859bf4c..ced1f6360bbbc 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java @@ -16,6 +16,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; +import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; @@ -24,7 +25,7 @@ import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.Operator; import org.elasticsearch.compute.operator.OperatorTestCase; -import org.elasticsearch.compute.operator.PageConsumerOperator; +import org.elasticsearch.compute.operator.TestResultPageSinkOperator; import org.elasticsearch.core.IOUtils; import org.elasticsearch.index.cache.query.TrivialQueryCachingPolicy; import org.elasticsearch.index.fielddata.FieldDataContext; @@ -35,6 +36,7 @@ import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.support.NestedScope; +import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.search.internal.ContextIndexSearcher; import org.elasticsearch.search.internal.SearchContext; import org.junit.After; @@ -46,6 +48,7 @@ import java.util.function.Function; import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.lessThan; @@ -133,21 +136,53 @@ protected String expectedDescriptionOfSimple() { public void testShardDataPartitioning() { int size = between(1_000, 20_000); int limit = between(10, size); - testSimple(size, limit); + testSimple(driverContext(), size, limit); } public void testEmpty() { - testSimple(0, between(10, 10_000)); + testSimple(driverContext(), 0, between(10, 10_000)); } - private void testSimple(int size, int limit) { - DriverContext ctx = driverContext(); - LuceneSourceOperator.Factory factory = simple(nonBreakingBigArrays(), DataPartitioning.SHARD, size, limit); + public void testWithCranky() { + try { + testSimple(crankyDriverContext(), between(1, 10_000), 100); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + public void testEmptyWithCranky() { + try { + testSimple(crankyDriverContext(), 0, between(10, 10_000)); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + public void testShardDataPartitioningWithCranky() { + int size = between(1_000, 20_000); + int limit = between(10, size); + try { + testSimple(crankyDriverContext(), size, limit); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + private void testSimple(DriverContext ctx, int size, int limit) { + LuceneSourceOperator.Factory factory = simple(ctx.bigArrays(), DataPartitioning.SHARD, size, limit); Operator.OperatorFactory readS = ValuesSourceReaderOperatorTests.factory(reader, S_FIELD); List results = new ArrayList<>(); + OperatorTestCase.runDriver( - new Driver(ctx, factory.get(ctx), List.of(readS.get(ctx)), new PageConsumerOperator(page -> results.add(page)), () -> {}) + new Driver(ctx, factory.get(ctx), List.of(readS.get(ctx)), new TestResultPageSinkOperator(results::add), () -> {}) ); OperatorTestCase.assertDriverContext(ctx); @@ -186,4 +221,9 @@ public static SearchContext mockSearchContext(IndexReader reader) { throw new UncheckedIOException(e); } } + + @Override + protected DriverContext driverContext() { + return breakingDriverContext(); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java index f0eb49c233e7a..4e604144927c0 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java @@ -15,6 +15,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; +import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; @@ -23,7 +24,7 @@ import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.Operator; import org.elasticsearch.compute.operator.OperatorTestCase; -import org.elasticsearch.compute.operator.PageConsumerOperator; +import org.elasticsearch.compute.operator.TestResultPageSinkOperator; import org.elasticsearch.core.IOUtils; import org.elasticsearch.index.fielddata.FieldDataContext; import org.elasticsearch.index.fielddata.IndexFieldData; @@ -33,6 +34,7 @@ import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.support.NestedScope; +import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortBuilder; @@ -136,23 +138,50 @@ protected String expectedDescriptionOfSimple() { // TODO tests for the other data partitioning configurations public void testShardDataPartitioning() { + testShardDataPartitioning(driverContext()); + } + + public void testShardDataPartitioningWithCranky() { + try { + testShardDataPartitioning(crankyDriverContext()); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + private void testShardDataPartitioning(DriverContext context) { int size = between(1_000, 20_000); int limit = between(10, size); - testSimple(size, limit); + testSimple(context, size, limit); } public void testEmpty() { - testSimple(0, between(10, 10_000)); + testEmpty(driverContext()); } - private void testSimple(int size, int limit) { - DriverContext ctx = driverContext(); - LuceneTopNSourceOperator.Factory factory = simple(nonBreakingBigArrays(), DataPartitioning.SHARD, size, limit); + public void testEmptyWithCranky() { + try { + testEmpty(crankyDriverContext()); + logger.info("cranky didn't break"); + } catch (CircuitBreakingException e) { + logger.info("broken", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + private void testEmpty(DriverContext context) { + testSimple(context, 0, between(10, 10_000)); + } + + private void testSimple(DriverContext ctx, int size, int limit) { + LuceneTopNSourceOperator.Factory factory = simple(ctx.bigArrays(), DataPartitioning.SHARD, size, limit); Operator.OperatorFactory readS = ValuesSourceReaderOperatorTests.factory(reader, S_FIELD); List results = new ArrayList<>(); OperatorTestCase.runDriver( - new Driver(ctx, factory.get(ctx), List.of(readS.get(ctx)), new PageConsumerOperator(page -> results.add(page)), () -> {}) + new Driver(ctx, factory.get(ctx), List.of(readS.get(ctx)), new TestResultPageSinkOperator(results::add), () -> {}) ); OperatorTestCase.assertDriverContext(ctx); @@ -171,4 +200,9 @@ private void testSimple(int size, int limit) { int pages = (int) Math.ceil((float) Math.min(size, limit) / factory.maxPageSize()); assertThat(results, hasSize(pages)); } + + @Override + protected DriverContext driverContext() { + return breakingDriverContext(); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java index 00c054ef0031a..984bc7527e59a 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java @@ -16,6 +16,7 @@ import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.MockBlockFactory; +import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.test.ESTestCase; import org.junit.After; @@ -34,7 +35,7 @@ public abstract class AnyOperatorTestCase extends ESTestCase { * The operator configured a "simple" or basic way, used for smoke testing * descriptions and {@link BigArrays} and scatter/gather. */ - protected abstract Operator.OperatorFactory simple(BigArrays bigArrays); + protected abstract Operator.OperatorFactory simple(BigArrays bigArrays); // TODO remove BigArrays - that's part of the context /** * The description of the operator produced by {@link #simple}. @@ -119,6 +120,16 @@ protected DriverContext breakingDriverContext() { // TODO move this to driverCon return new DriverContext(bigArrays, factory); } + protected final DriverContext crankyDriverContext() { + CrankyCircuitBreakerService cranky = new CrankyCircuitBreakerService(); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, cranky).withCircuitBreaking(); + CircuitBreaker breaker = bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); + breakers.add(breaker); + BlockFactory blockFactory = new MockBlockFactory(breaker, bigArrays); + blockFactories.add(blockFactory); + return new DriverContext(bigArrays, blockFactory); + } + @After public void allBreakersEmpty() throws Exception { // first check that all big arrays are released, which can affect breakers diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java index 1a2c87aff1591..9403d22f2b4c4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ForkingOperatorTestCase.java @@ -71,7 +71,7 @@ public final void testInitialFinal() { simpleWithMode(bigArrays, AggregatorMode.INITIAL).get(driverContext), simpleWithMode(bigArrays, AggregatorMode.FINAL).get(driverContext) ), - new ResultPageSinkOperator(page -> results.add(page)), + new TestResultPageSinkOperator(page -> results.add(page)), () -> {} ) ) { @@ -93,7 +93,7 @@ public final void testManyInitialFinal() { driverContext, new CannedSourceOperator(partials.iterator()), List.of(simpleWithMode(bigArrays, AggregatorMode.FINAL).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { @@ -119,7 +119,7 @@ public final void testInitialIntermediateFinal() { simpleWithMode(bigArrays, AggregatorMode.INTERMEDIATE).get(driverContext), simpleWithMode(bigArrays, AggregatorMode.FINAL).get(driverContext) ), - new ResultPageSinkOperator(page -> results.add(page)), + new TestResultPageSinkOperator(page -> results.add(page)), () -> {} ) ) { @@ -148,7 +148,7 @@ public final void testManyInitialManyPartialFinal() { driverContext, new CannedSourceOperator(intermediates.iterator()), List.of(simpleWithMode(bigArrays, AggregatorMode.FINAL).get(driverContext)), - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { @@ -255,7 +255,7 @@ List createDriversForInput(BigArrays bigArrays, List input, List

    {} ) ); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java index 50b97021c216b..fe6061ee90779 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/OperatorTestCase.java @@ -124,14 +124,11 @@ public final void testSimpleWithCranky() { DriverContext inputFactoryContext = driverContext(); List input = CannedSourceOperator.collectPages(simpleInput(inputFactoryContext.blockFactory(), between(1_000, 10_000))); - CrankyCircuitBreakerService cranky = new CrankyCircuitBreakerService(); - BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, cranky).withCircuitBreaking(); - BlockFactory blockFactory = BlockFactory.getInstance(cranky.getBreaker(CircuitBreaker.REQUEST), bigArrays); - DriverContext driverContext = new DriverContext(bigArrays, blockFactory); + DriverContext driverContext = crankyDriverContext(); boolean driverStarted = false; try { - Operator operator = simple(bigArrays).get(driverContext); + Operator operator = simple(driverContext.bigArrays()).get(driverContext); driverStarted = true; drive(operator, input.iterator(), driverContext); // Either we get lucky and cranky doesn't throw and the test completes or we don't and it throws @@ -145,7 +142,6 @@ public final void testSimpleWithCranky() { } // Note the lack of try/finally here - we're asserting that when the driver throws an exception we clear the breakers. - assertThat(bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST).getUsed(), equalTo(0L)); assertThat(inputFactoryContext.breaker().getUsed(), equalTo(0L)); } @@ -248,7 +244,7 @@ protected final List drive(List operators, Iterator input, driverContext, new CannedSourceOperator(input), operators, - new ResultPageSinkOperator(results::add), + new TestResultPageSinkOperator(results::add), () -> {} ) ) { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ResultPageSinkOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TestResultPageSinkOperator.java similarity index 85% rename from x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ResultPageSinkOperator.java rename to x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TestResultPageSinkOperator.java index 69e7a78ae5c97..aaa3a6ac8a3c8 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ResultPageSinkOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/TestResultPageSinkOperator.java @@ -17,9 +17,9 @@ * Page Consumer operator that deep copies the input page, closes it, and then passes the copy * to the underlying page consumer. */ -public class ResultPageSinkOperator extends PageConsumerOperator { +public class TestResultPageSinkOperator extends PageConsumerOperator { - public ResultPageSinkOperator(Consumer pageConsumer) { + public TestResultPageSinkOperator(Consumer pageConsumer) { super(page -> { Page copy = BlockTestUtils.deepCopyOf(page, BlockFactory.getNonBreakingInstance()); page.releaseBlocks(); From a7062881227cebf8f86e7982f053877a1a331f9e Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 26 Oct 2023 19:20:28 -0400 Subject: [PATCH 144/203] ESQL: Remove unused code (#101397) This removes a no longer used java file from ESQL. We stopped using it in #101235. --- .../compute/lucene/IdValueSource.java | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/IdValueSource.java diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/IdValueSource.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/IdValueSource.java deleted file mode 100644 index 906d6a0932806..0000000000000 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/IdValueSource.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -package org.elasticsearch.compute.lucene; - -import org.apache.lucene.index.LeafReaderContext; -import org.elasticsearch.index.fielddata.SortedBinaryDocValues; -import org.elasticsearch.search.aggregations.support.ValuesSource; - -public class IdValueSource extends ValuesSource.Bytes { - - private final IdFieldIndexFieldData indexFieldData; - - public IdValueSource(IdFieldIndexFieldData indexFieldData) { - this.indexFieldData = indexFieldData; - } - - @Override - public SortedBinaryDocValues bytesValues(LeafReaderContext leafReaderContext) { - return indexFieldData.load(leafReaderContext).getBytesValues(); - } -} From 5365daa221863c9665e157ce3c241344d9562a26 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 26 Oct 2023 21:24:43 -0400 Subject: [PATCH 145/203] ESQL: Track memory from values loaded from lucene (#101383) This adds memory tracking for values loaded from doc values and stored fields. --- .../operator/ValuesSourceReaderBenchmark.java | 2 + docs/changelog/101383.yaml | 5 + .../index/mapper/BlockDocValuesReader.java | 265 +++++++++--------- .../index/mapper/BlockLoader.java | 14 +- .../index/mapper/BlockSourceReader.java | 19 +- .../index/mapper/BlockStoredFieldsReader.java | 11 +- .../BooleanScriptBlockDocValuesReader.java | 11 +- .../DateScriptBlockDocValuesReader.java | 11 +- .../DoubleScriptBlockDocValuesReader.java | 11 +- .../index/mapper/IndexFieldMapper.java | 11 +- .../mapper/IpScriptBlockDocValuesReader.java | 11 +- .../KeywordScriptBlockDocValuesReader.java | 11 +- .../LongScriptBlockDocValuesReader.java | 11 +- .../elasticsearch/index/mapper/TestBlock.java | 13 +- .../compute/data/AbstractBlockBuilder.java | 3 +- .../org/elasticsearch/compute/data/Block.java | 9 +- .../compute/data/BlockUtils.java | 1 + .../compute/data/ConstantNullBlock.java | 7 +- .../elasticsearch/compute/data/DocBlock.java | 8 +- .../lucene/ValuesSourceReaderOperator.java | 39 +-- .../operator/OrdinalsGroupingOperator.java | 2 +- .../elasticsearch/compute/OperatorTests.java | 1 + .../AggregatorFunctionTestCase.java | 6 - .../GroupingAggregatorFunctionTestCase.java | 6 - .../lucene/LuceneSourceOperatorTests.java | 5 - .../lucene/LuceneTopNSourceOperatorTests.java | 5 - .../ValuesSourceReaderOperatorTests.java | 43 ++- .../compute/operator/AnyOperatorTestCase.java | 19 +- .../operator/CannedSourceOperator.java | 36 ++- .../compute/operator/EvalOperatorTests.java | 5 - .../compute/operator/FilterOperatorTests.java | 5 - .../HashAggregationOperatorTests.java | 6 - .../compute/operator/LimitOperatorTests.java | 5 - .../operator/MvExpandOperatorTests.java | 5 - .../operator/ProjectOperatorTests.java | 5 - .../operator/StringExtractOperatorTests.java | 5 + .../operator/topn/TopNOperatorTests.java | 5 - .../esql/qa/single_node/HeapAttackIT.java | 1 - .../esql/enrich/EnrichLookupService.java | 4 +- .../mapper/ConstantKeywordFieldMapper.java | 11 +- 40 files changed, 335 insertions(+), 318 deletions(-) create mode 100644 docs/changelog/101383.yaml diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java index 1827babe6f091..9fa876a00c35c 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java @@ -19,6 +19,7 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.NumericUtils; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; import org.elasticsearch.compute.data.DocVector; @@ -132,6 +133,7 @@ private static BlockLoader numericBlockLoader(String name, NumberFieldMapper.Num @OperationsPerInvocation(INDEX_SIZE) public void benchmark() { ValuesSourceReaderOperator op = new ValuesSourceReaderOperator( + BlockFactory.getNonBreakingInstance(), List.of(BlockReaderFactories.loaderToFactory(reader, blockLoader(name))), 0, name diff --git a/docs/changelog/101383.yaml b/docs/changelog/101383.yaml new file mode 100644 index 0000000000000..4875403acfaeb --- /dev/null +++ b/docs/changelog/101383.yaml @@ -0,0 +1,5 @@ +pr: 101383 +summary: "ESQL: Track memory from values loaded from lucene" +area: ES|QL +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java index 049a779c97503..90a295e5a25f2 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockDocValuesReader.java @@ -26,7 +26,6 @@ import org.elasticsearch.index.mapper.BlockLoader.DoubleBuilder; import org.elasticsearch.index.mapper.BlockLoader.IntBuilder; import org.elasticsearch.index.mapper.BlockLoader.LongBuilder; -import org.elasticsearch.index.mapper.BlockLoader.SingletonOrdinalsBuilder; import java.io.IOException; @@ -61,7 +60,7 @@ public BlockDocValuesReader() { /** * Reads the values of the given documents specified in the input block */ - public abstract Builder readValues(BuilderFactory factory, Docs docs) throws IOException; + public abstract BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException; /** * Reads the values of the given document into the builder @@ -189,27 +188,28 @@ private static class SingletonLongs extends BlockDocValuesReader { } @Override - public LongBuilder builder(BuilderFactory factory, int expectedCount) { + public BlockLoader.LongBuilder builder(BuilderFactory factory, int expectedCount) { return factory.longsFromDocValues(expectedCount); } @Override - public LongBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - int lastDoc = -1; - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendLong(numericDocValues.longValue()); - } else { - blockBuilder.appendNull(); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.LongBuilder builder = builder(factory, docs.count())) { + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (numericDocValues.advanceExact(doc)) { + builder.appendLong(numericDocValues.longValue()); + } else { + builder.appendNull(); + } + lastDoc = doc; } - lastDoc = doc; + return builder.build(); } - return blockBuilder; } @Override @@ -247,16 +247,17 @@ public BlockLoader.LongBuilder builder(BuilderFactory factory, int expectedCount } @Override - public BlockLoader.LongBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.LongBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); } - read(doc, blockBuilder); + return builder.build(); } - return blockBuilder; } @Override @@ -307,22 +308,23 @@ public IntBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public IntBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - int lastDoc = -1; - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendInt(Math.toIntExact(numericDocValues.longValue())); - } else { - blockBuilder.appendNull(); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.IntBuilder builder = builder(factory, docs.count())) { + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (numericDocValues.advanceExact(doc)) { + builder.appendInt(Math.toIntExact(numericDocValues.longValue())); + } else { + builder.appendNull(); + } + lastDoc = doc; } - lastDoc = doc; + return builder.build(); } - return blockBuilder; } @Override @@ -360,16 +362,17 @@ public IntBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public IntBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.IntBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); } - read(doc, blockBuilder); + return builder.build(); } - return blockBuilder; } @Override @@ -423,23 +426,24 @@ public DoubleBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public DoubleBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - int lastDoc = -1; - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.DoubleBuilder builder = builder(factory, docs.count())) { + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (docValues.advanceExact(doc)) { + builder.appendDouble(toDouble.convert(docValues.longValue())); + } else { + builder.appendNull(); + } + lastDoc = doc; + this.docID = doc; } - if (docValues.advanceExact(doc)) { - blockBuilder.appendDouble(toDouble.convert(docValues.longValue())); - } else { - blockBuilder.appendNull(); - } - lastDoc = doc; - this.docID = doc; + return builder.build(); } - return blockBuilder; } @Override @@ -480,16 +484,17 @@ public DoubleBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public DoubleBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.DoubleBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); } - read(doc, blockBuilder); + return builder.build(); } - return blockBuilder; } @Override @@ -539,21 +544,21 @@ public BytesRefBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public SingletonOrdinalsBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - SingletonOrdinalsBuilder builder = factory.singletonOrdinalsBuilder(ordinals, docs.count()); - - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < ordinals.docID()) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (ordinals.advanceExact(doc)) { - builder.appendOrd(ordinals.ordValue()); - } else { - builder.appendNull(); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.SingletonOrdinalsBuilder builder = factory.singletonOrdinalsBuilder(ordinals, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < ordinals.docID()) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (ordinals.advanceExact(doc)) { + builder.appendOrd(ordinals.ordValue()); + } else { + builder.appendNull(); + } } + return builder.build(); } - return builder; } @Override @@ -589,17 +594,17 @@ public BytesRefBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public BytesRefBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - BytesRefBuilder builder = builder(factory, docs.count()); - - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < ordinals.docID()) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BytesRefBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < ordinals.docID()) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); } - read(doc, builder); + return builder.build(); } - return builder; } @Override @@ -649,16 +654,17 @@ public BytesRefBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public BytesRefBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < docID) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.BytesRefBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); } - read(doc, blockBuilder); + return builder.build(); } - return blockBuilder; } @Override @@ -709,22 +715,23 @@ public BooleanBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public BooleanBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - int lastDoc = -1; - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < lastDoc) { - throw new IllegalStateException("docs within same block must be in order"); - } - if (numericDocValues.advanceExact(doc)) { - blockBuilder.appendBoolean(numericDocValues.longValue() != 0); - } else { - blockBuilder.appendNull(); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.BooleanBuilder builder = builder(factory, docs.count())) { + int lastDoc = -1; + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < lastDoc) { + throw new IllegalStateException("docs within same block must be in order"); + } + if (numericDocValues.advanceExact(doc)) { + builder.appendBoolean(numericDocValues.longValue() != 0); + } else { + builder.appendNull(); + } + lastDoc = doc; } - lastDoc = doc; + return builder.build(); } - return blockBuilder; } @Override @@ -762,16 +769,17 @@ public BooleanBuilder builder(BuilderFactory factory, int expectedCount) { } @Override - public BooleanBuilder readValues(BuilderFactory factory, Docs docs) throws IOException { - var blockBuilder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.BooleanBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + read(doc, builder); } - read(doc, blockBuilder); + return builder.build(); } - return blockBuilder; } @Override @@ -813,17 +821,18 @@ private static class Nulls extends BlockDocValuesReader { private int docID = -1; @Override - public Builder builder(BuilderFactory factory, int expectedCount) { + public BlockLoader.Builder builder(BuilderFactory factory, int expectedCount) { return factory.nulls(expectedCount); } @Override - public Builder readValues(BuilderFactory factory, Docs docs) throws IOException { - Builder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - builder.appendNull(); + public BlockLoader.Block readValues(BuilderFactory factory, Docs docs) throws IOException { + try (BlockLoader.Builder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + builder.appendNull(); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java index 7e973f9c32033..af53ab42d35d9 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockLoader.java @@ -12,6 +12,7 @@ import org.apache.lucene.index.SortedDocValues; import org.apache.lucene.index.SortedSetDocValues; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.core.Releasable; import java.io.IOException; @@ -124,13 +125,24 @@ interface BuilderFactory { // TODO support non-singleton ords } + /** + * Marker interface for block results. The compute engine has a fleshed + * out implementation. + */ + interface Block {} + /** * A builder for typed values. For each document you may either call * {@link #appendNull}, {@code append}, or * {@link #beginPositionEntry} followed by two or more {@code append} * calls, and then {@link #endPositionEntry}. */ - interface Builder { + interface Builder extends Releasable { + /** + * Build the actual block. + */ + Block build(); + /** * Insert a null value. */ diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java index 2b9daadda31d6..1261a3612d3cb 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockSourceReader.java @@ -156,16 +156,17 @@ public String toString() { } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) throws IOException { - BlockLoader.Builder blockBuilder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - int doc = docs.get(i); - if (doc < this.docID) { - throw new IllegalStateException("docs within same block must be in order"); - } - readValuesFromSingleDoc(doc, blockBuilder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) throws IOException { + try (BlockLoader.Builder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + int doc = docs.get(i); + if (doc < this.docID) { + throw new IllegalStateException("docs within same block must be in order"); + } + readValuesFromSingleDoc(doc, builder); + } + return builder.build(); } - return blockBuilder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java index d38d30a03b275..5984482fd9441 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BlockStoredFieldsReader.java @@ -63,12 +63,13 @@ protected BlockStoredFieldsReader(LeafStoredFieldLoader loader) { } @Override - public final BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) throws IOException { - var builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - readValuesFromSingleDoc(docs.get(i), builder); + public final BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) throws IOException { + try (BlockLoader.Builder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + readValuesFromSingleDoc(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java index 0d29bc43700e8..b59df56791fbe 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BooleanScriptBlockDocValuesReader.java @@ -37,12 +37,13 @@ public BlockLoader.BooleanBuilder builder(BlockLoader.BuilderFactory factory, in } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.BooleanBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - read(docs.get(i), builder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.BooleanBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java index 6e6cdd3d1f057..ad630a71870a4 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateScriptBlockDocValuesReader.java @@ -36,12 +36,13 @@ public BlockLoader.LongBuilder builder(BlockLoader.BuilderFactory factory, int e } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.LongBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - read(docs.get(i), builder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.LongBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java index 856321f53244d..4e317a3ed11cb 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptBlockDocValuesReader.java @@ -36,12 +36,13 @@ public BlockLoader.DoubleBuilder builder(BlockLoader.BuilderFactory factory, int } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.DoubleBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - read(docs.get(i), builder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.DoubleBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java index cb69fa4b7c50a..5f987fd96ca66 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -96,12 +96,13 @@ public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, i } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - builder.appendBytesRef(bytes); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.BytesRefBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + builder.appendBytesRef(bytes); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java index f05b9aff890af..23229a6533cdb 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpScriptBlockDocValuesReader.java @@ -36,12 +36,13 @@ public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, i } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - read(docs.get(i), builder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.BytesRefBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java index 51058b3b60bf4..6afbcae50d31f 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/KeywordScriptBlockDocValuesReader.java @@ -38,12 +38,13 @@ public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, i } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - read(docs.get(i), builder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.BytesRefBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java b/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java index 4896f7d858144..91c099cd2813b 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/LongScriptBlockDocValuesReader.java @@ -36,12 +36,13 @@ public BlockLoader.LongBuilder builder(BlockLoader.BuilderFactory factory, int e } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.LongBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - read(docs.get(i), builder); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.LongBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + read(docs.get(i), builder); + } + return builder.build(); } - return builder; } @Override diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java index 5a42d5c6890b5..298acb9519532 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java @@ -28,7 +28,8 @@ public class TestBlock BlockLoader.DoubleBuilder, BlockLoader.IntBuilder, BlockLoader.LongBuilder, - BlockLoader.SingletonOrdinalsBuilder { + BlockLoader.SingletonOrdinalsBuilder, + BlockLoader.Block { public static BlockLoader.BuilderFactory FACTORY = new BlockLoader.BuilderFactory() { @Override public BlockLoader.BooleanBuilder booleansFromDocValues(int expectedCount) { @@ -192,8 +193,18 @@ public TestBlock appendOrd(int value) { } } + @Override + public TestBlock build() { + return this; + } + private TestBlock add(Object value) { (currentPosition == null ? values : currentPosition).add(value); return this; } + + @Override + public void close() { + // TODO assert that we close the test blocks + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java index bd13a9045a28a..d6d6584e1b534 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/AbstractBlockBuilder.java @@ -140,8 +140,9 @@ protected final void ensureCapacity() { return; } int newSize = calculateNewArraySize(valuesLength); - adjustBreaker((long) (newSize - valuesLength) * elementSize()); + adjustBreaker(newSize * elementSize()); growValuesArray(newSize); + adjustBreaker(-valuesLength * elementSize()); } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index d7c3a5cb9bfab..b9506153aac5a 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -25,16 +25,9 @@ * or dense data. A Block can represent either single or multi valued data. A Block that represents * dense single-valued data can be viewed as a {@link Vector}. * - * TODO: update comment - *

    All Blocks share the same set of data retrieval methods, but actual concrete implementations - * effectively support a subset of these, throwing {@code UnsupportedOperationException} where a - * particular data retrieval method is not supported. For example, a Block of primitive longs may - * not support retrieval as an integer, {code getInt}. This greatly simplifies Block usage and - * avoids cumbersome use-site casting. - * *

    Block are immutable and can be passed between threads. */ -public interface Block extends Accountable, NamedWriteable, Releasable { +public interface Block extends Accountable, BlockLoader.Block, NamedWriteable, Releasable { /** * {@return an efficient dense single-value view of this block}. diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java index 56a4dc249388f..89b40d6e46a14 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockUtils.java @@ -161,6 +161,7 @@ public static Block[] fromList(BlockFactory blockFactory, List> lis public static Block deepCopyOf(Block block, BlockFactory blockFactory) { try (Block.Builder builder = block.elementType().newBlockBuilder(block.getPositionCount(), blockFactory)) { builder.copyFrom(block, 0, block.getPositionCount()); + builder.mvOrdering(block.mvOrdering()); return builder.build(); } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java index dba0ced86e60e..00b93d08a36ff 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java @@ -182,7 +182,12 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { @Override public Block.Builder mvOrdering(MvOrdering mvOrdering) { - throw new UnsupportedOperationException(); + /* + * This is called when copying but otherwise doesn't do + * anything because there aren't multivalue fields in a + * block containing only nulls. + */ + return this; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java index ccd740bc91ba9..ed7e317bfc4c7 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/DocBlock.java @@ -145,7 +145,13 @@ public Block.Builder appendAllValuesToCurrentPosition(Block block) { @Override public Block.Builder mvOrdering(MvOrdering mvOrdering) { - throw new UnsupportedOperationException("doc blocks only contain one value per position"); + /* + * This is called when copying but otherwise doesn't do + * anything because there aren't multivalue fields in a + * block containing doc references. Every position can + * only reference one doc. + */ + return this; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java index 8b1c4f78825ad..61c1bd9730e02 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperator.java @@ -53,7 +53,7 @@ public record ValuesSourceReaderOperatorFactory(List factories, int docChannel, String field) { + public ValuesSourceReaderOperator( + BlockFactory blockFactory, + List factories, + int docChannel, + String field + ) { this.factories = factories; this.docChannel = docChannel; this.field = field; - this.blockFactory = new ComputeBlockLoaderFactory(BlockFactory.getNonBreakingInstance()); // TODO breaking! + this.blockFactory = new ComputeBlockLoaderFactory(blockFactory); } @Override @@ -106,7 +111,7 @@ protected Page process(Page page) { private Block loadFromSingleLeaf(DocVector docVector) throws IOException { setupReader(docVector.shards().getInt(0), docVector.segments().getInt(0), docVector.docs().getInt(0)); - return ((Block.Builder) lastReader.readValues(blockFactory, new BlockLoader.Docs() { + return ((Block) lastReader.readValues(blockFactory, new BlockLoader.Docs() { private final IntVector docs = docVector.docs(); @Override @@ -118,26 +123,28 @@ public int count() { public int get(int i) { return docs.getInt(i); } - })).build(); + })); } private Block loadFromManyLeaves(DocVector docVector) throws IOException { int[] forwards = docVector.shardSegmentDocMapForwards(); int doc = docVector.docs().getInt(forwards[0]); setupReader(docVector.shards().getInt(forwards[0]), docVector.segments().getInt(forwards[0]), doc); - BlockLoader.Builder builder = lastReader.builder(blockFactory, forwards.length); - lastReader.readValuesFromSingleDoc(doc, builder); - for (int i = 1; i < forwards.length; i++) { - int shard = docVector.shards().getInt(forwards[i]); - int segment = docVector.segments().getInt(forwards[i]); - doc = docVector.docs().getInt(forwards[i]); - if (segment != lastSegment || shard != lastShard) { - setupReader(shard, segment, doc); - } + try (BlockLoader.Builder builder = lastReader.builder(blockFactory, forwards.length)) { lastReader.readValuesFromSingleDoc(doc, builder); + for (int i = 1; i < forwards.length; i++) { + int shard = docVector.shards().getInt(forwards[i]); + int segment = docVector.segments().getInt(forwards[i]); + doc = docVector.docs().getInt(forwards[i]); + if (segment != lastSegment || shard != lastShard) { + setupReader(shard, segment, doc); + } + lastReader.readValuesFromSingleDoc(doc, builder); + } + try (Block orig = ((Block.Builder) builder).build()) { + return orig.filter(docVector.shardSegmentDocMapBackwards()); + } } - // TODO maybe it's better for downstream consumers if we perform a copy here. - return ((Block.Builder) builder).build().filter(docVector.shardSegmentDocMapBackwards()); } private void setupReader(int shard, int segment, int doc) throws IOException { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java index 2c5eedb6d8bbe..07494f97cfd6d 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/OrdinalsGroupingOperator.java @@ -467,7 +467,7 @@ private static class ValuesAggregator implements Releasable { int maxPageSize, DriverContext driverContext ) { - this.extractor = new ValuesSourceReaderOperator(factories, docChannel, groupingField); + this.extractor = new ValuesSourceReaderOperator(BlockFactory.getNonBreakingInstance(), factories, docChannel, groupingField); this.aggregator = new HashAggregationOperator( aggregatorFactories, () -> BlockHash.build(List.of(new GroupSpec(channelIndex, groupingElementType)), driverContext, maxPageSize, false), diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java index 6951cdf4d56ca..bde2ae0a747e4 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java @@ -141,6 +141,7 @@ public void testQueryOperator() throws IOException { } } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101413") public void testGroupingWithOrdinals() throws Exception { DriverContext driverContext = driverContext(); BlockFactory blockFactory = driverContext.blockFactory(); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java index 6c1f088aeca7b..894b94476c08d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/AggregatorFunctionTestCase.java @@ -43,12 +43,6 @@ import static org.hamcrest.Matchers.hasSize; public abstract class AggregatorFunctionTestCase extends ForkingOperatorTestCase { - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } - protected abstract AggregatorFunctionSupplier aggregatorFunction(BigArrays bigArrays, List inputChannels); protected final int aggregatorIntermediateBlockCount() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java index ba930e943e79a..753b5878de2ae 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/GroupingAggregatorFunctionTestCase.java @@ -48,12 +48,6 @@ import static org.hamcrest.Matchers.hasSize; public abstract class GroupingAggregatorFunctionTestCase extends ForkingOperatorTestCase { - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } - protected abstract AggregatorFunctionSupplier aggregatorFunction(BigArrays bigArrays, List inputChannels); protected final int aggregatorIntermediateBlockCount() { diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java index ced1f6360bbbc..41fe1a93d9c8b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneSourceOperatorTests.java @@ -221,9 +221,4 @@ public static SearchContext mockSearchContext(IndexReader reader) { throw new UncheckedIOException(e); } } - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java index 4e604144927c0..d1b9e706750df 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/LuceneTopNSourceOperatorTests.java @@ -200,9 +200,4 @@ private void testSimple(DriverContext ctx, int size, int limit) { int pages = (int) Math.ceil((float) Math.min(size, limit) / factory.maxPageSize()); assertThat(results, hasSize(pages)); } - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java index a6e5e3bf4744d..269a478560bac 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java @@ -223,7 +223,6 @@ public void testLoadAllInOnePageShuffled() { } private void loadSimpleAndAssert(DriverContext driverContext, List input) { - List results = new ArrayList<>(); List operators = List.of( factory(reader, new NumberFieldMapper.NumberFieldType("key", NumberFieldMapper.NumberType.INTEGER)).get(driverContext), factory(reader, new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG)).get(driverContext), @@ -236,17 +235,7 @@ private void loadSimpleAndAssert(DriverContext driverContext, List input) factory(reader, new NumberFieldMapper.NumberFieldType("double", NumberFieldMapper.NumberType.DOUBLE)).get(driverContext), factory(reader, new NumberFieldMapper.NumberFieldType("mv_double", NumberFieldMapper.NumberType.DOUBLE)).get(driverContext) ); - try ( - Driver d = new Driver( - driverContext, - new CannedSourceOperator(input.iterator()), - operators, - new PageConsumerOperator(page -> results.add(page)), - () -> {} - ) - ) { - runDriver(d); - } + List results = drive(operators, input.iterator(), driverContext); assertThat(results, hasSize(input.size())); for (Page p : results) { assertThat(p.getBlockCount(), equalTo(11)); @@ -368,20 +357,24 @@ public void testValuesSourceReaderOperatorWithNulls() throws IOException { factory(reader, kwFt).get(driverContext) ), new PageConsumerOperator(page -> { - logger.debug("New page: {}", page); - IntBlock intValuesBlock = page.getBlock(1); - LongBlock longValuesBlock = page.getBlock(2); - DoubleBlock doubleValuesBlock = page.getBlock(3); - BytesRefBlock keywordValuesBlock = page.getBlock(4); + try { + logger.debug("New page: {}", page); + IntBlock intValuesBlock = page.getBlock(1); + LongBlock longValuesBlock = page.getBlock(2); + DoubleBlock doubleValuesBlock = page.getBlock(3); + BytesRefBlock keywordValuesBlock = page.getBlock(4); - for (int i = 0; i < page.getPositionCount(); i++) { - assertFalse(intValuesBlock.isNull(i)); - long j = intValuesBlock.getInt(i); - // Every 100 documents we set fields to null - boolean fieldIsEmpty = j % 100 == 0; - assertEquals(fieldIsEmpty, longValuesBlock.isNull(i)); - assertEquals(fieldIsEmpty, doubleValuesBlock.isNull(i)); - assertEquals(fieldIsEmpty, keywordValuesBlock.isNull(i)); + for (int i = 0; i < page.getPositionCount(); i++) { + assertFalse(intValuesBlock.isNull(i)); + long j = intValuesBlock.getInt(i); + // Every 100 documents we set fields to null + boolean fieldIsEmpty = j % 100 == 0; + assertEquals(fieldIsEmpty, longValuesBlock.isNull(i)); + assertEquals(fieldIsEmpty, doubleValuesBlock.isNull(i)); + assertEquals(fieldIsEmpty, keywordValuesBlock.isNull(i)); + } + } finally { + page.releaseBlocks(); } }), () -> {} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java index 984bc7527e59a..290756e81cfae 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/AnyOperatorTestCase.java @@ -101,17 +101,7 @@ protected final BigArrays nonBreakingBigArrays() { /** * A {@link DriverContext} with a nonBreakingBigArrays. */ - protected DriverContext driverContext() { // TODO make this final and return a breaking block factory - return new DriverContext(nonBreakingBigArrays(), BlockFactory.getNonBreakingInstance()); - } - - private final List breakers = new ArrayList<>(); - private final List blockFactories = new ArrayList<>(); - - /** - * A {@link DriverContext} with a breaking {@link BigArrays} and {@link BlockFactory}. - */ - protected DriverContext breakingDriverContext() { // TODO move this to driverContext once everyone supports breaking + protected DriverContext driverContext() { // TODO make this final once all operators support memory tracking BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, ByteSizeValue.ofGb(1)).withCircuitBreaking(); CircuitBreaker breaker = bigArrays.breakerService().getBreaker(CircuitBreaker.REQUEST); breakers.add(breaker); @@ -120,6 +110,13 @@ protected DriverContext breakingDriverContext() { // TODO move this to driverCon return new DriverContext(bigArrays, factory); } + protected final DriverContext nonBreakingDriverContext() { // TODO drop this once the driverContext method isn't overrideable + return new DriverContext(nonBreakingBigArrays(), BlockFactory.getNonBreakingInstance()); + } + + private final List breakers = new ArrayList<>(); + private final List blockFactories = new ArrayList<>(); + protected final DriverContext crankyDriverContext() { CrankyCircuitBreakerService cranky = new CrankyCircuitBreakerService(); BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, cranky).withCircuitBreaking(); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java index f5dd2680e0ac7..47febc09e45f5 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/CannedSourceOperator.java @@ -7,8 +7,11 @@ package org.elasticsearch.compute.operator; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.core.Releasable; +import org.elasticsearch.core.Releasables; import java.util.ArrayList; import java.util.Iterator; @@ -42,19 +45,32 @@ public static Page mergePages(List pages) { int totalPositions = pages.stream().mapToInt(Page::getPositionCount).sum(); Page first = pages.get(0); Block.Builder[] builders = new Block.Builder[first.getBlockCount()]; - for (int b = 0; b < builders.length; b++) { - builders[b] = first.getBlock(b).elementType().newBlockBuilder(totalPositions); - } - for (Page p : pages) { + try { for (int b = 0; b < builders.length; b++) { - builders[b].copyFrom(p.getBlock(b), 0, p.getPositionCount()); + builders[b] = first.getBlock(b).elementType().newBlockBuilder(totalPositions); } + for (Page p : pages) { + for (int b = 0; b < builders.length; b++) { + builders[b].copyFrom(p.getBlock(b), 0, p.getPositionCount()); + } + } + Block[] blocks = new Block[builders.length]; + Page result = null; + try { + for (int b = 0; b < blocks.length; b++) { + blocks[b] = builders[b].build(); + } + result = new Page(blocks); + } finally { + if (result == null) { + Releasables.close(blocks); + } + } + return result; + } finally { + Iterable releasePages = () -> Iterators.map(pages.iterator(), p -> p::releaseBlocks); + Releasables.closeExpectNoException(Releasables.wrap(builders), Releasables.wrap(releasePages)); } - Block[] blocks = new Block[builders.length]; - for (int b = 0; b < blocks.length; b++) { - blocks[b] = builders[b].build(); - } - return new Page(blocks); } /** diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java index 95a5647717851..e7f5db7579869 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/EvalOperatorTests.java @@ -118,9 +118,4 @@ public void testReadFromBlock() { protected ByteSizeValue smallEnoughToCircuitBreak() { return ByteSizeValue.ofBytes(between(1, 8000)); } - - @Override - protected DriverContext driverContext() { // TODO remove this when the parent uses a breaking block factory - return breakingDriverContext(); - } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java index fa4c7bea7c9cc..e16f643e1ca4d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/FilterOperatorTests.java @@ -119,9 +119,4 @@ public void testReadFromBlock() { protected ByteSizeValue smallEnoughToCircuitBreak() { return ByteSizeValue.ofBytes(between(1, 600)); } - - @Override - protected DriverContext driverContext() { // TODO remove this when the parent uses a breaking block factory - return breakingDriverContext(); - } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java index afa307c494431..b1ef784ca339c 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/HashAggregationOperatorTests.java @@ -31,12 +31,6 @@ import static org.hamcrest.Matchers.hasSize; public class HashAggregationOperatorTests extends ForkingOperatorTestCase { - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } - @Override protected SourceOperator simpleInput(BlockFactory blockFactory, int size) { long max = randomLongBetween(1, Long.MAX_VALUE / size); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java index 76f99389a697b..8c85f5927196f 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/LimitOperatorTests.java @@ -23,11 +23,6 @@ import static org.hamcrest.Matchers.sameInstance; public class LimitOperatorTests extends OperatorTestCase { - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } - @Override protected LimitOperator.Factory simple(BigArrays bigArrays) { return new LimitOperator.Factory(100); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java index a105818a2bf03..3572dc620287d 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/MvExpandOperatorTests.java @@ -257,9 +257,4 @@ protected Page createPage(int positionOffset, int length) { List results = drive(new MvExpandOperator(0, randomIntBetween(1, 1000)), input.iterator(), context); assertSimpleOutput(origInput, results); } - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java index 1acdbc4895c94..1aff6be7594aa 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ProjectOperatorTests.java @@ -28,11 +28,6 @@ import static org.mockito.Mockito.when; public class ProjectOperatorTests extends OperatorTestCase { - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } - public void testProjectionOnEmptyPage() { var page = new Page(0); var projection = new ProjectOperator(randomProjection(10)); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java index 5dff00ec930c4..55470d1dcae12 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java @@ -126,4 +126,9 @@ public void close() {} assertThat(brb.getBytesRef(idx + 1, spare).utf8ToString(), equalTo("foo4")); assertThat(brb.getBytesRef(idx + 2, spare).utf8ToString(), equalTo("foo5")); } + + @Override + protected DriverContext driverContext() { + return nonBreakingDriverContext(); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java index 049bc449069b8..6c5bab9b8f784 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/topn/TopNOperatorTests.java @@ -1359,11 +1359,6 @@ public void testCloseWithoutCompleting() { } } - @Override - protected DriverContext driverContext() { // TODO remove this when the parent uses a breaking block factory - return breakingDriverContext(); - } - @SuppressWarnings({ "unchecked", "rawtypes" }) private static void readAsRows(List>> values, Page page) { if (page.getBlockCount() == 0) { diff --git a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/HeapAttackIT.java b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/HeapAttackIT.java index eed96a007a1b9..31d0a7646e1b7 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/HeapAttackIT.java +++ b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/HeapAttackIT.java @@ -290,7 +290,6 @@ public void testFetchManyBigFields() throws IOException { fetchManyBigFields(100); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100528") public void testFetchTooManyBigFields() throws IOException { initManyBigFieldsIndex(500); assertCircuitBreaks(() -> fetchManyBigFields(500)); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index bf246b5ac02d4..98c1397d97860 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -255,7 +255,9 @@ private void doLookup( extractField instanceof Alias a ? ((NamedExpression) a.child()).name() : extractField.name(), EsqlDataTypes.isUnsupported(extractField.dataType()) ); - intermediateOperators.add(new ValuesSourceReaderOperator(sources, 0, extractField.name())); + intermediateOperators.add( + new ValuesSourceReaderOperator(BlockFactory.getNonBreakingInstance(), sources, 0, extractField.name()) + ); } // drop docs block intermediateOperators.add(droppingBlockOperator(extractFields.size() + 2, 0)); diff --git a/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java b/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java index 75202749d8dca..61db9dcd54a02 100644 --- a/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java +++ b/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java @@ -153,12 +153,13 @@ public BlockLoader.BytesRefBuilder builder(BlockLoader.BuilderFactory factory, i } @Override - public BlockLoader.Builder readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { - BlockLoader.BytesRefBuilder builder = builder(factory, docs.count()); - for (int i = 0; i < docs.count(); i++) { - builder.appendBytesRef(bytes); + public BlockLoader.Block readValues(BlockLoader.BuilderFactory factory, BlockLoader.Docs docs) { + try (BlockLoader.BytesRefBuilder builder = builder(factory, docs.count())) { + for (int i = 0; i < docs.count(); i++) { + builder.appendBytesRef(bytes); + } + return builder.build(); } - return builder; } @Override From cb6ef1736c42d0eed53a2009b169390f92ecf2ff Mon Sep 17 00:00:00 2001 From: Ievgen Degtiarenko Date: Fri, 27 Oct 2023 08:12:53 +0200 Subject: [PATCH 146/203] Enable test that was fixed before (#101370) --- .../elasticsearch/action/admin/cluster/node/tasks/TasksIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java index 4d488ede6f0de..2ca29f4b13236 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java @@ -522,7 +522,6 @@ public void testTasksCancellation() throws Exception { assertEquals(0, clusterAdmin().prepareListTasks().setActions(TEST_TASK_ACTION.name() + "*").get().getTasks().size()); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/95325") public void testTasksUnblocking() throws Exception { // Start blocking test task TestTaskPlugin.NodesRequest request = new TestTaskPlugin.NodesRequest("test"); From 64a91bc8bc54b71feaf26af146496e7db25aed6b Mon Sep 17 00:00:00 2001 From: Ievgen Degtiarenko Date: Fri, 27 Oct 2023 09:19:20 +0200 Subject: [PATCH 147/203] Log test list tasks wait for completion (#101388) Enable additional logging for testListTasksWaitForCompletion --- .../action/admin/cluster/node/tasks/TasksIT.java | 5 +++++ .../cluster/node/tasks/list/TransportListTasksAction.java | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java index 2ca29f4b13236..dee70b8fa3ca9 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java @@ -50,6 +50,7 @@ import org.elasticsearch.tasks.TaskResult; import org.elasticsearch.tasks.TaskResultsService; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.tasks.MockTaskManager; import org.elasticsearch.test.tasks.MockTaskManagerListener; import org.elasticsearch.test.transport.MockTransportService; @@ -542,6 +543,10 @@ public void testTasksUnblocking() throws Exception { ); } + @TestLogging( + reason = "https://github.com/elastic/elasticsearch/issues/97923", + value = "org.elasticsearch.action.admin.cluster.node.tasks.list.TransportListTasksAction:TRACE" + ) public void testListTasksWaitForCompletion() throws Exception { waitForCompletionTestCase( randomBoolean(), diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java index f79b0ac9c02b4..c0c9ec493de70 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java @@ -8,6 +8,8 @@ package org.elasticsearch.action.admin.cluster.node.tasks.list; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionType; import org.elasticsearch.action.FailedNodeException; @@ -40,6 +42,9 @@ import static org.elasticsearch.core.TimeValue.timeValueSeconds; public class TransportListTasksAction extends TransportTasksAction { + + private static final Logger logger = LogManager.getLogger(TransportListTasksAction.class); + public static final ActionType TYPE = new ActionType<>("cluster:monitor/tasks/lists", ListTasksResponse::new); public static long waitForCompletionTimeout(TimeValue timeout) { @@ -127,6 +132,7 @@ protected void processTasks(CancellableTask nodeTask, ListTasksRequest request, } processedTasks.add(task); } + logger.trace("Matched {} tasks of all running {}", processedTasks, taskManager.getTasks().values()); } catch (Exception e) { allMatchedTasksRemovedListener.onFailure(e); return; From ec0a314832f8532d774def4667713f66a359b185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20R=C3=BChsen?= Date: Fri, 27 Oct 2023 10:13:42 +0200 Subject: [PATCH 148/203] Add index profiling-costs (#101371) This new index is required for the cost calculator that is WIP. Co-authored-by: Elastic Machine --- .../common/logging/HeaderWarningTests.java | 2 + .../index-template/profiling-costs.json | 60 +++++++++++++++++++ .../profiling/ProfilingIndexManager.java | 1 + .../ProfilingIndexTemplateRegistry.java | 8 +++ 4 files changed, 71 insertions(+) create mode 100644 x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-costs.json diff --git a/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java b/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java index bbb14fe6e9133..598cc8213e91f 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java @@ -310,6 +310,7 @@ public void testAddComplexWarning() { + ".ml-stats,.monitoring-beats-mb,.monitoring-ent-search-mb,.monitoring-es-mb,.monitoring-kibana-mb," + ".monitoring-logstash-mb,.profiling-ilm-lock,.slm-history,.watch-history-16,behavioral_analytics-events-default," + "ilm-history,logs,metrics,profiling-events,profiling-executables,profiling-metrics,profiling-returnpads-private," + + "profiling-costs" + "profiling-sq-executables,profiling-sq-leafframes,profiling-stackframes,profiling-stacktraces," + "profiling-symbols,synthetics] with patterns (.deprecation-indexing-template => [.logs-deprecation.*]," + ".fleet-file-data => [.fleet-file-data-*-*],.fleet-files => [.fleet-files-*-*],.ml-anomalies- => [.ml-anomalies-*]," @@ -322,6 +323,7 @@ public void testAddComplexWarning() { + "logs => [logs-*-*],metrics => [metrics-*-*],profiling-events => [profiling-events*],profiling-executables => " + "[profiling-executables*],profiling-metrics => [profiling-metrics*],profiling-returnpads-private => " + "[.profiling-returnpads-private*],profiling-sq-executables => [.profiling-sq-executables*]," + + "profiling-costs => [.profiling-costs*]," + "profiling-sq-leafframes => [.profiling-sq-leafframes*],profiling-stackframes => [profiling-stackframes*]," + "profiling-stacktraces => [profiling-stacktraces*],profiling-symbols => [.profiling-symbols*],synthetics => " + "[synthetics-*-*]); this template [global] may be ignored in favor of a composable template at index creation time" diff --git a/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-costs.json b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-costs.json new file mode 100644 index 0000000000000..7f54b012f8803 --- /dev/null +++ b/x-pack/plugin/core/template-resources/src/main/resources/profiling/index-template/profiling-costs.json @@ -0,0 +1,60 @@ +{ + "index_patterns": [ + ".profiling-costs*" + ], + "template": { + "settings": { + "index": { + "number_of_replicas": 0, + "auto_expand_replicas": "0-1", + "refresh_interval": "30s", + "hidden": true + } + }, + "mappings": { + "_source": { + "mode": "synthetic" + }, + "_meta": { + "index-template-version": ${xpack.profiling.template.version}, + "index-version": ${xpack.profiling.index.costs.version} + }, + "dynamic": false, + "properties": { + "ecs.version": { + "type": "keyword", + "index": true + }, + "@timestamp": { // creation date + "type": "date", + "index": true + }, + "provider": { + "type": "keyword", + "index": true + }, + "region": { + "type": "keyword", + "index": true + }, + "instance_type": { + "type": "keyword", + "index": true + }, + "co2_factor": { + "type": "double", + "index": false + }, + "cost_factor": { + "type": "double", + "index": false + } + } + } + }, + "priority": 100, + "_meta": { + "description": "Index template for .profiling-costs" + }, + "version": ${xpack.profiling.template.version} +} diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexManager.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexManager.java index 746159c23dda0..00a57faa85401 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexManager.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexManager.java @@ -42,6 +42,7 @@ class ProfilingIndexManager extends AbstractProfilingPersistenceManager { // For testing public static final List PROFILING_INDICES = List.of( + ProfilingIndex.regular("profiling-costs", ProfilingIndexTemplateRegistry.PROFILING_COSTS_VERSION, OnVersionBump.KEEP_OLD), ProfilingIndex.regular( "profiling-returnpads-private", ProfilingIndexTemplateRegistry.PROFILING_RETURNPADS_PRIVATE_VERSION, diff --git a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java index 36dc280fc1a74..571fe6bd803fc 100644 --- a/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java +++ b/x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/ProfilingIndexTemplateRegistry.java @@ -55,6 +55,7 @@ public class ProfilingIndexTemplateRegistry extends IndexTemplateRegistry { public static final int PROFILING_RETURNPADS_PRIVATE_VERSION = 1; public static final int PROFILING_SQ_EXECUTABLES_VERSION = 1; public static final int PROFILING_SQ_LEAFFRAMES_VERSION = 1; + public static final int PROFILING_COSTS_VERSION = 1; public static final String PROFILING_TEMPLATE_VERSION_VARIABLE = "xpack.profiling.template.version"; @@ -232,6 +233,13 @@ protected Map getComponentTemplateConfigs() { PROFILING_TEMPLATE_VERSION_VARIABLE ), // templates for regular indices + new IndexTemplateConfig( + "profiling-costs", + "/profiling/index-template/profiling-costs.json", + INDEX_TEMPLATE_VERSION, + PROFILING_TEMPLATE_VERSION_VARIABLE, + indexVersion("costs", PROFILING_COSTS_VERSION) + ), new IndexTemplateConfig( "profiling-returnpads-private", "/profiling/index-template/profiling-returnpads-private.json", From abf30d2bfc6ccc6f3929dc9c21375ce50299fb3c Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 27 Oct 2023 10:49:40 +0200 Subject: [PATCH 149/203] Reenable testWarningHeadersOnFailedConversions (#101329) Reenable RestEsqlIT testWarningHeadersOnFailedConversions --- .../org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java index 4d9a5a259ed03..3693f0b0c2bb9 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEsqlTestCase.java @@ -242,13 +242,12 @@ public void testCSVNoHeaderMode() throws IOException { assertEquals("keyword0,0\r\n", actual); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/98719") public void testWarningHeadersOnFailedConversions() throws IOException { int count = randomFrom(10, 40, 60); bulkLoadTestData(count); Request request = prepareRequest(); - var query = fromIndex() + " | eval asInt = to_int(case(integer % 2 == 0, to_str(integer), keyword))"; + var query = fromIndex() + " | eval asInt = to_int(case(integer % 2 == 0, to_str(integer), keyword)) | limit 1000"; var mediaType = attachBody(new RequestObjectBuilder().query(query).build(), request); RequestOptions.Builder options = request.getOptions().toBuilder(); From acdb46869a5f23cfd295de1c8ada7b7ac1770283 Mon Sep 17 00:00:00 2001 From: David Turner Date: Fri, 27 Oct 2023 09:54:52 +0100 Subject: [PATCH 150/203] Introduce MockTransportService#getInstance (#101391) It's a pretty long line of code to get a `TransportService` instance from an `InternalTestCluster` and cast it to `MockTransportService` so that you can adjust its behaviour. This commit introduces a utility method to make it easier. --- .../datastreams/DataStreamIT.java | 24 +++---- .../DataStreamLifecycleServiceIT.java | 18 +++--- ...ansportClusterStateActionDisruptionIT.java | 7 +-- .../diskusage/IndexDiskUsageAnalyzerIT.java | 38 ++++++----- .../action/search/PointInTimeIT.java | 3 +- ...tReplicationActionRetryOnClosedNodeIT.java | 6 +- .../cluster/ClusterInfoServiceIT.java | 7 +-- .../cluster/PrevalidateNodeRemovalIT.java | 29 ++++----- .../coordination/VotingConfigurationIT.java | 5 +- .../discovery/ClusterDisruptionIT.java | 20 +++--- .../discovery/DiscoveryDisruptionIT.java | 23 +++---- .../gateway/ReplicaShardAllocatorIT.java | 16 +---- .../ReplicaShardAllocatorSyncIdIT.java | 8 +-- .../index/seqno/GlobalCheckpointSyncIT.java | 37 ++++------- .../index/seqno/RetentionLeaseIT.java | 5 +- .../index/store/CorruptedFileIT.java | 33 +++++----- .../index/store/ExceptionRetryIT.java | 23 +++---- .../indices/recovery/IndexRecoveryIT.java | 33 +++------- .../state/CloseWhileRelocatingShardsIT.java | 9 +-- .../indices/state/ReopenWhileClosingIT.java | 5 +- .../store/IndicesStoreIntegrationIT.java | 23 +++---- .../elasticsearch/recovery/RelocationIT.java | 2 +- .../recovery/TruncatedRecoveryIT.java | 29 ++++----- .../SearchServiceCleanupOnLostMasterIT.java | 8 +-- .../search/fieldcaps/FieldCapabilitiesIT.java | 25 +++----- .../DedicatedClusterSnapshotRestoreIT.java | 12 ++-- .../AbstractIndexRecoveryIntegTestCase.java | 35 +++-------- .../test/transport/MockTransportService.java | 11 ++++ .../DownsampleTransportFailureIT.java | 16 +---- ...tiallyCachedShardAllocationIntegTests.java | 19 +++--- .../SnapshotBasedIndexRecoveryIT.java | 63 ++++++------------- 31 files changed, 209 insertions(+), 383 deletions(-) diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java index cf5e23c272985..90c76d630f0d0 100644 --- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java +++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java @@ -90,7 +90,6 @@ import org.elasticsearch.search.fetch.subphase.FieldAndFormat; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.ObjectPath; import org.elasticsearch.xcontent.XContentType; @@ -2157,11 +2156,11 @@ public void testWriteLoadAndAvgShardSizeIsStoredInABestEffort() throws Exception for (String nodeId : failingIndicesStatsNodeIds) { String nodeName = clusterStateBeforeRollover.nodes().resolveNode(nodeId).getName(); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, nodeName); - transportService.addRequestHandlingBehavior( - IndicesStatsAction.NAME + "[n]", - (handler, request, channel, task) -> channel.sendResponse(new RuntimeException("Unable to get stats")) - ); + MockTransportService.getInstance(nodeName) + .addRequestHandlingBehavior( + IndicesStatsAction.NAME + "[n]", + (handler, request, channel, task) -> channel.sendResponse(new RuntimeException("Unable to get stats")) + ); } logger.info( @@ -2223,14 +2222,11 @@ public void testNoShardSizeIsForecastedWhenAllShardStatRequestsFail() throws Exc .currentNodeId(); final String nodeName = clusterStateBeforeRollover.nodes().resolveNode(assignedShardNodeId).getName(); - final MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeName - ); - transportService.addRequestHandlingBehavior( - IndicesStatsAction.NAME + "[n]", - (handler, request, channel, task) -> channel.sendResponse(new RuntimeException("Unable to get stats")) - ); + MockTransportService.getInstance(nodeName) + .addRequestHandlingBehavior( + IndicesStatsAction.NAME + "[n]", + (handler, request, channel, task) -> channel.sendResponse(new RuntimeException("Unable to get stats")) + ); assertAcked(indicesAdmin().rolloverIndex(new RolloverRequest(dataStreamName, null)).actionGet()); diff --git a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java index 80b1b89054304..e3128fd1b904b 100644 --- a/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java +++ b/modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceIT.java @@ -52,7 +52,6 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentType; import org.junit.After; @@ -288,16 +287,13 @@ public void testAutomaticForceMerge() throws Exception { for (DiscoveryNode node : internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()) .state() .getNodes()) { - final MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - node.getName() - ); - transportService.addRequestHandlingBehavior(ForceMergeAction.NAME + "[n]", (handler, request, channel, task) -> { - String index = ((IndicesRequest) request).indices()[0]; - forceMergedIndices.add(index); - logger.info("Force merging {}", index); - handler.messageReceived(request, channel, task); - }); + MockTransportService.getInstance(node.getName()) + .addRequestHandlingBehavior(ForceMergeAction.NAME + "[n]", (handler, request, channel, task) -> { + String index = ((IndicesRequest) request).indices()[0]; + forceMergedIndices.add(index); + logger.info("Force merging {}", index); + handler.messageReceived(request, channel, task); + }); } CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java index b7dadaaff3d01..8750389480071 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java @@ -193,12 +193,9 @@ public void runRepeatedlyWhileChangingMaster(Runnable runnable) throws Exception assertingThread.start(); updatingThread.start(); - final MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - masterName - ); + final var masterTransportService = MockTransportService.getInstance(masterName); - for (MockTransportService mockTransportService : mockTransportServices) { + for (final var mockTransportService : mockTransportServices) { if (masterTransportService != mockTransportService) { masterTransportService.addFailToSendNoConnectRule(mockTransportService); mockTransportService.addFailToSendNoConnectRule(masterTransportService); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/diskusage/IndexDiskUsageAnalyzerIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/diskusage/IndexDiskUsageAnalyzerIT.java index 09b72a3c50f3b..94c08bd7e8162 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/diskusage/IndexDiskUsageAnalyzerIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/diskusage/IndexDiskUsageAnalyzerIT.java @@ -26,7 +26,6 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; import org.junit.Before; @@ -249,23 +248,23 @@ public void testFailingTargetShards() throws Exception { final AtomicInteger successfulShards = new AtomicInteger(); try { for (String node : internalCluster().getNodeNames()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.addRequestHandlingBehavior(AnalyzeIndexDiskUsageAction.NAME + "[s]", (handler, request, channel, task) -> { - AnalyzeDiskUsageShardRequest shardRequest = (AnalyzeDiskUsageShardRequest) request; - IndicesService indicesService = internalCluster().getInstance(IndicesService.class, node); - logger.info("--> handling shard request {} on node {}", shardRequest.shardId(), node); - ShardId shardId = shardRequest.shardId(); - if (failingShards.contains(shardId)) { - IndexShard indexShard = indicesService.getShardOrNull(shardId); - assertNotNull("No shard found for shard " + shardId, indexShard); - logger.info("--> failing shard {} on node {}", shardRequest.shardId(), node); - indexShard.close("test", randomBoolean()); - failedShards.incrementAndGet(); - } else { - successfulShards.incrementAndGet(); - } - handler.messageReceived(request, channel, task); - }); + MockTransportService.getInstance(node) + .addRequestHandlingBehavior(AnalyzeIndexDiskUsageAction.NAME + "[s]", (handler, request, channel, task) -> { + AnalyzeDiskUsageShardRequest shardRequest = (AnalyzeDiskUsageShardRequest) request; + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, node); + logger.info("--> handling shard request {} on node {}", shardRequest.shardId(), node); + ShardId shardId = shardRequest.shardId(); + if (failingShards.contains(shardId)) { + IndexShard indexShard = indicesService.getShardOrNull(shardId); + assertNotNull("No shard found for shard " + shardId, indexShard); + logger.info("--> failing shard {} on node {}", shardRequest.shardId(), node); + indexShard.close("test", randomBoolean()); + failedShards.incrementAndGet(); + } else { + successfulShards.incrementAndGet(); + } + handler.messageReceived(request, channel, task); + }); } AnalyzeIndexDiskUsageResponse resp = client().execute( AnalyzeIndexDiskUsageAction.INSTANCE, @@ -279,8 +278,7 @@ public void testFailingTargetShards() throws Exception { assertThat(resp.getShardFailures(), arrayWithSize(failedShards.get())); } finally { for (String node : internalCluster().getNodeNames()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.clearAllRules(); + MockTransportService.getInstance(node).clearAllRules(); } } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java index e931cd0da822d..bb7658f5011e3 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/search/PointInTimeIT.java @@ -37,7 +37,6 @@ import org.elasticsearch.tasks.TaskInfo; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import java.util.Collection; import java.util.HashSet; @@ -449,7 +448,7 @@ public void testOpenPITConcurrentShardRequests() throws Exception { .build() ) ); - var transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, dataNode.getName()); + final var transportService = MockTransportService.getInstance(dataNode.getName()); try { CountDownLatch sentLatch = new CountDownLatch(maxConcurrentRequests); CountDownLatch readyLatch = new CountDownLatch(1); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java index 73dcce55aa2a0..05a39c02808ba 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java @@ -205,13 +205,9 @@ public void testRetryOnStoppedTransportService() throws Exception { assertTrue(primaryTestPlugin.actionRunningLatch.await(10, TimeUnit.SECONDS)); - MockTransportService primaryTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - primary - ); // we pause node after TransportService has moved to stopped, but before closing connections, since if connections are closed // we would not hit the transport service closed case. - primaryTransportService.addOnStopListener(() -> { + MockTransportService.getInstance(primary).addOnStopListener(() -> { primaryTestPlugin.actionWaitLatch.countDown(); safeAwait(doneLatch); }); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/ClusterInfoServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/ClusterInfoServiceIT.java index 3846711764ec9..48ba897ebb76c 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/ClusterInfoServiceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/ClusterInfoServiceIT.java @@ -240,10 +240,7 @@ public void testClusterInfoServiceInformationClearOnError() { ); } - MockTransportService mockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - internalTestCluster.getMasterName() - ); + final var masterTransportService = MockTransportService.getInstance(internalTestCluster.getMasterName()); final AtomicBoolean timeout = new AtomicBoolean(false); final Set blockedActions = newHashSet( @@ -254,7 +251,7 @@ public void testClusterInfoServiceInformationClearOnError() { ); // drop all outgoing stats requests to force a timeout. for (DiscoveryNode node : internalTestCluster.clusterService().state().getNodes()) { - mockTransportService.addSendBehavior( + masterTransportService.addSendBehavior( internalTestCluster.getInstance(TransportService.class, node.getName()), (connection, requestId, action, request, options) -> { if (blockedActions.contains(action)) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/PrevalidateNodeRemovalIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/PrevalidateNodeRemovalIT.java index f620cd4697715..11b3027c23550 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/PrevalidateNodeRemovalIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/PrevalidateNodeRemovalIT.java @@ -28,7 +28,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.transport.ConnectTransportException; -import org.elasticsearch.transport.TransportService; import java.util.Arrays; import java.util.Collection; @@ -131,16 +130,15 @@ public void testNodeRemovalFromRedClusterWithLocalShardCopy() throws Exception { // its ACTION_SHARD_EXISTS requests since after a relocation, the source first waits // until the shard exists somewhere else, then it removes it locally. final CountDownLatch shardActiveRequestSent = new CountDownLatch(1); - MockTransportService node1transport = (MockTransportService) internalCluster().getInstance(TransportService.class, node1); - TransportService node2transport = internalCluster().getInstance(TransportService.class, node2); - node1transport.addSendBehavior(node2transport, (connection, requestId, action, request, options) -> { - if (action.equals(IndicesStore.ACTION_SHARD_EXISTS)) { - shardActiveRequestSent.countDown(); - logger.info("prevent shard active request from being sent"); - throw new ConnectTransportException(connection.getNode(), "DISCONNECT: simulated"); - } - connection.sendRequest(requestId, action, request, options); - }); + MockTransportService.getInstance(node1) + .addSendBehavior(MockTransportService.getInstance(node2), (connection, requestId, action, request, options) -> { + if (action.equals(IndicesStore.ACTION_SHARD_EXISTS)) { + shardActiveRequestSent.countDown(); + logger.info("prevent shard active request from being sent"); + throw new ConnectTransportException(connection.getNode(), "DISCONNECT: simulated"); + } + connection.sendRequest(requestId, action, request, options); + }); logger.info("--> move shard from {} to {}, and wait for relocation to finish", node1, node2); updateIndexSettings(Settings.builder().put("index.routing.allocation.require._name", node2), indexName); shardActiveRequestSent.await(); @@ -179,13 +177,10 @@ public void testNodeRemovalFromRedClusterWithTimeout() throws Exception { // make it red! internalCluster().stopNode(node1); ensureRed(indexName); - MockTransportService node2TransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node2); - node2TransportService.addRequestHandlingBehavior( - TransportPrevalidateShardPathAction.ACTION_NAME + "[n]", - (handler, request, channel, task) -> { + MockTransportService.getInstance(node2) + .addRequestHandlingBehavior(TransportPrevalidateShardPathAction.ACTION_NAME + "[n]", (handler, request, channel, task) -> { logger.info("drop the check shards request"); - } - ); + }); PrevalidateNodeRemovalRequest req = PrevalidateNodeRemovalRequest.builder() .setNames(node2) .build() diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/VotingConfigurationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/VotingConfigurationIT.java index 33e67520b82b6..dee6ac3859b15 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/VotingConfigurationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/coordination/VotingConfigurationIT.java @@ -92,10 +92,7 @@ public void testElectsNodeNotInVotingConfiguration() throws Exception { if (sender.equals(excludedNodeName)) { continue; } - final MockTransportService senderTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - sender - ); + final var senderTransportService = MockTransportService.getInstance(sender); for (final String receiver : nodeNames) { senderTransportService.addSendBehavior( internalCluster().getInstance(TransportService.class, receiver), diff --git a/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java index ce9ec8b5fc75c..6f387128fb581 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java @@ -42,7 +42,6 @@ import org.elasticsearch.test.disruption.ServiceDisruptionScheme; import org.elasticsearch.test.junit.annotations.TestIssueLogging; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentType; import java.util.ArrayList; @@ -573,17 +572,14 @@ protected boolean blockingAllowed() { } }); - final MockTransportService dataTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - dataNode - ); - dataTransportService.addRequestHandlingBehavior(FollowersChecker.FOLLOWER_CHECK_ACTION_NAME, (handler, request, channel, task) -> { - if (removedNode.isDone() == false) { - channel.sendResponse(new ElasticsearchException("simulated check failure")); - } else { - handler.messageReceived(request, channel, task); - } - }); + MockTransportService.getInstance(dataNode) + .addRequestHandlingBehavior(FollowersChecker.FOLLOWER_CHECK_ACTION_NAME, (handler, request, channel, task) -> { + if (removedNode.isDone() == false) { + channel.sendResponse(new ElasticsearchException("simulated check failure")); + } else { + handler.messageReceived(request, channel, task); + } + }); removedNode.actionGet(10, TimeUnit.SECONDS); ensureStableCluster(2); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java index 1413ac453da1b..71c6ef956c4d4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/discovery/DiscoveryDisruptionIT.java @@ -57,19 +57,13 @@ public void testClusterJoinDespiteOfPublishingIssues() throws Exception { ); logger.info("blocking requests from non master [{}] to master [{}]", nonMasterNode, masterNode); - MockTransportService nonMasterTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nonMasterNode - ); + final var nonMasterTransportService = MockTransportService.getInstance(nonMasterNode); nonMasterTransportService.addFailToSendNoConnectRule(masterTranspotService); assertNoMaster(nonMasterNode); logger.info("blocking cluster state publishing from master [{}] to non master [{}]", masterNode, nonMasterNode); - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - masterNode - ); + final var masterTransportService = MockTransportService.getInstance(masterNode); TransportService localTransportService = internalCluster().getInstance( TransportService.class, discoveryNodes.getLocalNode().getName() @@ -188,10 +182,7 @@ public void testNodeNotReachableFromMaster() throws Exception { } logger.info("blocking request from master [{}] to [{}]", masterNode, nonMasterNode); - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - masterNode - ); + final var masterTransportService = MockTransportService.getInstance(masterNode); if (randomBoolean()) { masterTransportService.addUnresponsiveRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); } else { @@ -232,13 +223,13 @@ public void testJoinWaitsForClusterApplier() { safeAwait(barrier); // drop the victim from the cluster with a network disruption - final var masterTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, masterName); + final var masterTransportService = MockTransportService.getInstance(masterName); masterTransportService.addFailToSendNoConnectRule(internalCluster().getInstance(TransportService.class, victimName)); logger.info("--> waiting for victim's departure"); ensureStableCluster(2, masterName); // verify that the victim sends no joins while the applier is blocked - final var victimTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, victimName); + final var victimTransportService = MockTransportService.getInstance(victimName); victimTransportService.addSendBehavior((connection, requestId, action, request, options) -> { assertNotEquals(action, JoinHelper.JOIN_ACTION_NAME); connection.sendRequest(requestId, action, request, options); @@ -283,13 +274,13 @@ public void testJoinWaitsForCircuitBreaker() throws InterruptedException { } // drop the victim from the cluster with a network disruption - final var masterTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, masterName); + final var masterTransportService = MockTransportService.getInstance(masterName); masterTransportService.addFailToSendNoConnectRule(internalCluster().getInstance(TransportService.class, victimName)); logger.info("--> waiting for victim's departure"); ensureStableCluster(2, masterName); // verify that the victim sends no joins while the circuit breaker is breaking - final var victimTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, victimName); + final var victimTransportService = MockTransportService.getInstance(victimName); victimTransportService.addSendBehavior((connection, requestId, action, request, options) -> { assertNotEquals(action, JoinHelper.JOIN_ACTION_NAME); connection.sendRequest(requestId, action, request, options); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorIT.java b/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorIT.java index e22359c30265e..8cbce0cc098ed 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorIT.java @@ -34,7 +34,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import java.util.Arrays; import java.util.Collection; @@ -108,10 +107,7 @@ public void testPreferCopyCanPerformNoopRecovery() throws Exception { } CountDownLatch blockRecovery = new CountDownLatch(1); CountDownLatch recoveryStarted = new CountDownLatch(1); - MockTransportService transportServiceOnPrimary = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeWithPrimary - ); + final var transportServiceOnPrimary = MockTransportService.getInstance(nodeWithPrimary); transportServiceOnPrimary.addSendBehavior((connection, requestId, action, request, options) -> { if (PeerRecoveryTargetService.Actions.FILES_INFO.equals(action)) { recoveryStarted.countDown(); @@ -169,10 +165,7 @@ public void testRecentPrimaryInformation() throws Exception { } CountDownLatch blockRecovery = new CountDownLatch(1); CountDownLatch recoveryStarted = new CountDownLatch(1); - MockTransportService transportServiceOnPrimary = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeWithPrimary - ); + final var transportServiceOnPrimary = MockTransportService.getInstance(nodeWithPrimary); transportServiceOnPrimary.addSendBehavior((connection, requestId, action, request, options) -> { if (PeerRecoveryTargetService.Actions.FILES_INFO.equals(action)) { recoveryStarted.countDown(); @@ -349,10 +342,7 @@ public void testDoNotCancelRecoveryForBrokenNode() throws Exception { ); indicesAdmin().prepareFlush(indexName).get(); String brokenNode = internalCluster().startDataOnlyNode(); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeWithPrimary - ); + final var transportService = MockTransportService.getInstance(nodeWithPrimary); CountDownLatch newNodeStarted = new CountDownLatch(1); transportService.addSendBehavior((connection, requestId, action, request, options) -> { if (action.equals(PeerRecoveryTargetService.Actions.TRANSLOG_OPS)) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorSyncIdIT.java b/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorSyncIdIT.java index 29e9d98852a4d..313d1e686e1fd 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorSyncIdIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/gateway/ReplicaShardAllocatorSyncIdIT.java @@ -35,7 +35,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.junit.Before; import java.io.IOException; @@ -187,10 +186,7 @@ public void testPreferCopyCanPerformNoopRecovery() throws Exception { }); CountDownLatch blockRecovery = new CountDownLatch(1); CountDownLatch recoveryStarted = new CountDownLatch(1); - MockTransportService transportServiceOnPrimary = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeWithPrimary - ); + final var transportServiceOnPrimary = MockTransportService.getInstance(nodeWithPrimary); transportServiceOnPrimary.addSendBehavior((connection, requestId, action, request, options) -> { if (PeerRecoveryTargetService.Actions.FILES_INFO.equals(action)) { recoveryStarted.countDown(); @@ -279,7 +275,7 @@ public void testSimulateRecoverySourceOnOldNode() throws Exception { syncFlush(indexName); } internalCluster().startDataOnlyNode(); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, source); + final var transportService = MockTransportService.getInstance(source); Semaphore failRecovery = new Semaphore(1); transportService.addSendBehavior((connection, requestId, action, request, options) -> { if (action.equals(PeerRecoveryTargetService.Actions.CLEAN_FILES)) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncIT.java index a528116031ab3..d1122004ccce2 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/GlobalCheckpointSyncIT.java @@ -23,7 +23,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentType; import java.util.ArrayList; @@ -95,21 +94,17 @@ public void testBackgroundGlobalCheckpointSync() throws Exception { if (node == other) { continue; } - final MockTransportService senderTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - node.getName() - ); - final MockTransportService receiverTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - other.getName() - ); - senderTransportService.addSendBehavior(receiverTransportService, (connection, requestId, action, request, options) -> { - if ("indices:admin/seq_no/global_checkpoint_sync[r]".equals(action)) { - throw new IllegalStateException("blocking indices:admin/seq_no/global_checkpoint_sync[r]"); - } else { - connection.sendRequest(requestId, action, request, options); - } - }); + MockTransportService.getInstance(node.getName()) + .addSendBehavior( + MockTransportService.getInstance(other.getName()), + (connection, requestId, action, request, options) -> { + if ("indices:admin/seq_no/global_checkpoint_sync[r]".equals(action)) { + throw new IllegalStateException("blocking indices:admin/seq_no/global_checkpoint_sync[r]"); + } else { + connection.sendRequest(requestId, action, request, options); + } + } + ); } } }, client -> { @@ -120,15 +115,7 @@ public void testBackgroundGlobalCheckpointSync() throws Exception { if (node == other) { continue; } - final MockTransportService senderTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - node.getName() - ); - final MockTransportService receiverTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - other.getName() - ); - senderTransportService.clearOutboundRules(receiverTransportService); + MockTransportService.getInstance(node.getName()).clearOutboundRules(MockTransportService.getInstance(other.getName())); } } }); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java index c4ba69e3bcaec..61ea5f8c54f69 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java @@ -352,10 +352,7 @@ public void testRetentionLeasesSyncOnRecovery() throws Exception { Settings.builder().put(INDICES_RECOVERY_RETRY_DELAY_NETWORK_SETTING.getKey(), TimeValue.timeValueMillis(100)) ); final Semaphore recoveriesToDisrupt = new Semaphore(scaledRandomIntBetween(0, 4)); - final MockTransportService primaryTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - primaryShardNodeName - ); + final var primaryTransportService = MockTransportService.getInstance(primaryShardNodeName); primaryTransportService.addSendBehavior((connection, requestId, action, request, options) -> { if (action.equals(PeerRecoveryTargetService.Actions.FINALIZE) && recoveriesToDisrupt.tryAcquire()) { if (randomBoolean()) { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java index 9219b732ddb02..8de218f8a29c8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/store/CorruptedFileIT.java @@ -350,23 +350,20 @@ public void testCorruptionOnNetworkLayerFinalizingRecovery() throws InterruptedE final AtomicBoolean corrupt = new AtomicBoolean(true); final CountDownLatch hasCorrupted = new CountDownLatch(1); for (var dataNode : dataNodes) { - MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( - TransportService.class, - dataNode.getName() - )); - mockTransportService.addSendBehavior( - internalCluster().getInstance(TransportService.class, unluckyNode.getName()), - (connection, requestId, action, request, options) -> { - if (corrupt.get() && action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { - RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; - byte[] array = BytesRef.deepCopyOf(req.content().toBytesRef()).bytes; - int i = randomIntBetween(0, req.content().length() - 1); - array[i] = (byte) ~array[i]; // flip one byte in the content - hasCorrupted.countDown(); + MockTransportService.getInstance(dataNode.getName()) + .addSendBehavior( + internalCluster().getInstance(TransportService.class, unluckyNode.getName()), + (connection, requestId, action, request, options) -> { + if (corrupt.get() && action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { + RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; + byte[] array = BytesRef.deepCopyOf(req.content().toBytesRef()).bytes; + int i = randomIntBetween(0, req.content().length() - 1); + array[i] = (byte) ~array[i]; // flip one byte in the content + hasCorrupted.countDown(); + } + connection.sendRequest(requestId, action, request, options); } - connection.sendRequest(requestId, action, request, options); - } - ); + ); } updateIndexSettings( @@ -413,8 +410,8 @@ public void testCorruptionOnNetworkLayer() throws InterruptedException { // we have to flush at least once here since we don't corrupt the translog assertHitCount(prepareSearch().setSize(0), numDocs); - var source = (MockTransportService) internalCluster().getInstance(TransportService.class, primariesNode.getName()); - var target = internalCluster().getInstance(TransportService.class, unluckyNode.getName()); + final var source = MockTransportService.getInstance(primariesNode.getName()); + final var target = MockTransportService.getInstance(unluckyNode.getName()); final boolean truncate = randomBoolean(); source.addSendBehavior(target, (connection, requestId, action, request, options) -> { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java index 2ea5be52cb51d..19efcd9e3f31f 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/store/ExceptionRetryIT.java @@ -74,20 +74,17 @@ public void testRetryDueToExceptionOnNetworkLayer() throws ExecutionException, I logger.info("unlucky node: {}", unluckyNode.getNode()); // create a transport service that throws a ConnectTransportException for one bulk request and therefore triggers a retry. for (NodeStats dataNode : nodeStats.getNodes()) { - MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( - TransportService.class, - dataNode.getNode().getName() - )); - mockTransportService.addSendBehavior( - internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), - (connection, requestId, action, request, options) -> { - connection.sendRequest(requestId, action, request, options); - if (action.equals(TransportShardBulkAction.ACTION_NAME) && exceptionThrown.compareAndSet(false, true)) { - logger.debug("Throw ConnectTransportException"); - throw new ConnectTransportException(connection.getNode(), action); + MockTransportService.getInstance(dataNode.getNode().getName()) + .addSendBehavior( + internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), + (connection, requestId, action, request, options) -> { + connection.sendRequest(requestId, action, request, options); + if (action.equals(TransportShardBulkAction.ACTION_NAME) && exceptionThrown.compareAndSet(false, true)) { + logger.debug("Throw ConnectTransportException"); + throw new ConnectTransportException(connection.getNode(), action); + } } - } - ); + ); } BulkRequestBuilder bulkBuilder = client.prepareBulk(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index e0a0d1f5254b2..fc07643b08ef8 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -486,7 +486,7 @@ public void testCancelNewShardRecoveryAndUsesExistingShardCopy() throws Exceptio // hold peer recovery on phase 2 after nodeB down CountDownLatch phase1ReadyBlocked = new CountDownLatch(1); CountDownLatch allowToCompletePhase1Latch = new CountDownLatch(1); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, nodeA); + final var transportService = MockTransportService.getInstance(nodeA); transportService.addSendBehavior((connection, requestId, action, request, options) -> { if (PeerRecoveryTargetService.Actions.CLEAN_FILES.equals(action)) { phase1ReadyBlocked.countDown(); @@ -1046,11 +1046,7 @@ public void testDoNotInfinitelyWaitForMapping() { } Semaphore recoveryBlocked = new Semaphore(1); for (DiscoveryNode node : clusterService().state().nodes()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - node.getName() - ); - transportService.addSendBehavior((connection, requestId, action, request, options) -> { + MockTransportService.getInstance(node.getName()).addSendBehavior((connection, requestId, action, request, options) -> { if (action.equals(PeerRecoverySourceService.Actions.START_RECOVERY)) { if (recoveryBlocked.tryAcquire()) { PluginsService pluginService = internalCluster().getInstance(PluginsService.class, node.getName()); @@ -1088,11 +1084,10 @@ public void testOngoingRecoveryAndMasterFailOver() throws Exception { indicesAdmin().prepareCreate(indexName) .setSettings(indexSettings(1, 0).put("index.routing.allocation.include._name", nodeWithPrimary)) ); - MockTransportService transport = (MockTransportService) internalCluster().getInstance(TransportService.class, nodeWithPrimary); CountDownLatch phase1ReadyBlocked = new CountDownLatch(1); CountDownLatch allowToCompletePhase1Latch = new CountDownLatch(1); Semaphore blockRecovery = new Semaphore(1); - transport.addSendBehavior((connection, requestId, action, request, options) -> { + MockTransportService.getInstance(nodeWithPrimary).addSendBehavior((connection, requestId, action, request, options) -> { if (PeerRecoveryTargetService.Actions.CLEAN_FILES.equals(action) && blockRecovery.tryAcquire()) { phase1ReadyBlocked.countDown(); safeAwait(allowToCompletePhase1Latch); @@ -1159,8 +1154,7 @@ public void testRecoverLocallyUpToGlobalCheckpoint() throws Exception { // first try because the local recovery happens once and its stats is reset when the recovery fails. SetOnce localRecoveredOps = new SetOnce<>(); for (String node : nodes) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.addSendBehavior((connection, requestId, action, request, options) -> { + MockTransportService.getInstance(node).addSendBehavior((connection, requestId, action, request, options) -> { if (action.equals(PeerRecoverySourceService.Actions.START_RECOVERY)) { final RecoveryState recoveryState = internalCluster().getInstance(IndicesService.class, failingNode) .getShardOrNull(new ShardId(resolveIndex(indexName), 0)) @@ -1223,8 +1217,7 @@ public void testRecoverLocallyUpToGlobalCheckpoint() throws Exception { } } for (String node : nodes) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.clearAllRules(); + MockTransportService.getInstance(node).clearAllRules(); } } @@ -1669,10 +1662,7 @@ public void testPeerRecoveryTrimsLocalTranslog() throws Exception { ClusterState clusterState = clusterAdmin().prepareState().get().getState(); DiscoveryNode nodeWithOldPrimary = clusterState.nodes() .get(clusterState.routingTable().index(indexName).shard(0).primaryShard().currentNodeId()); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeWithOldPrimary.getName() - ); + final var transportService = MockTransportService.getInstance(nodeWithOldPrimary.getName()); CountDownLatch readyToRestartNode = new CountDownLatch(1); AtomicBoolean stopped = new AtomicBoolean(); transportService.addSendBehavior((connection, requestId, action, request, options) -> { @@ -1745,14 +1735,10 @@ public void testReservesBytesDuringPeerRecoveryPhaseOne() throws Exception { ClusterState clusterState = clusterAdmin().prepareState().get().getState(); DiscoveryNode nodeWithPrimary = clusterState.nodes() .get(clusterState.routingTable().index(indexName).shard(0).primaryShard().currentNodeId()); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - nodeWithPrimary.getName() - ); final AtomicBoolean fileInfoIntercepted = new AtomicBoolean(); final AtomicBoolean fileChunkIntercepted = new AtomicBoolean(); - transportService.addSendBehavior((connection, requestId, action, request, options) -> { + MockTransportService.getInstance(nodeWithPrimary.getName()).addSendBehavior((connection, requestId, action, request, options) -> { if (action.equals(PeerRecoveryTargetService.Actions.FILES_INFO)) { if (fileInfoIntercepted.compareAndSet(false, true)) { final NodeIndicesStats nodeIndicesStats = clusterAdmin().prepareNodesStats(connection.getNode().getId()) @@ -1825,10 +1811,7 @@ public void testWaitForClusterStateToBeAppliedOnSourceNode() throws Exception { final long initialClusterStateVersion = clusterService().state().version(); try (var recoveryClusterStateDelayListeners = new RecoveryClusterStateDelayListeners(initialClusterStateVersion)) { - final var primaryNodeTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - primaryNode - ); + final var primaryNodeTransportService = MockTransportService.getInstance(primaryNode); primaryNodeTransportService.addRequestHandlingBehavior( Coordinator.COMMIT_STATE_ACTION_NAME, (handler, request, channel, task) -> { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java index 5fa512eafc45f..53d3e62109536 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/CloseWhileRelocatingShardsIT.java @@ -31,7 +31,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.test.transport.StubbableTransport; -import org.elasticsearch.transport.TransportService; import java.util.ArrayList; import java.util.Collection; @@ -177,15 +176,11 @@ public void testCloseWhileRelocatingShards() throws Exception { connection.sendRequest(requestId, action, request, options); }; - final MockTransportService targetTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - targetNode - ); + final var targetTransportService = MockTransportService.getInstance(targetNode); for (DiscoveryNode node : state.getNodes()) { if (node.canContainData() && node.getName().equals(targetNode) == false) { - final TransportService sourceTransportService = internalCluster().getInstance(TransportService.class, node.getName()); - targetTransportService.addSendBehavior(sourceTransportService, sendBehavior); + targetTransportService.addSendBehavior(MockTransportService.getInstance(node.getName()), sendBehavior); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/ReopenWhileClosingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/ReopenWhileClosingIT.java index 9c12c00ae76cd..3c16e0f2624ed 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/state/ReopenWhileClosingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/state/ReopenWhileClosingIT.java @@ -127,10 +127,7 @@ private void createIndexWithDocs(final String indexName, final Collection release = new ListenableFuture<>(); for (DiscoveryNode node : internalCluster().clusterService().state().getNodes()) { mockTransportService.addSendBehavior( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java index be441c5439fbc..ca749eeaef545 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java @@ -42,7 +42,6 @@ import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.transport.ConnectTransportException; import org.elasticsearch.transport.TransportMessageListener; -import org.elasticsearch.transport.TransportService; import java.io.IOException; import java.nio.file.Files; @@ -155,11 +154,10 @@ public static BlockClusterStateProcessing relocateAndBlockCompletion( ) throws InterruptedException { BlockClusterStateProcessing disruption = new BlockClusterStateProcessing(nodeTo, random()); internalCluster().setDisruptionScheme(disruption); - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, nodeTo); CountDownLatch beginRelocationLatch = new CountDownLatch(1); CountDownLatch receivedShardExistsRequestLatch = new CountDownLatch(1); // use a tracer on the target node to track relocation start and end - transportService.addMessageListener(new TransportMessageListener() { + MockTransportService.getInstance(nodeTo).addMessageListener(new TransportMessageListener() { @Override public void onRequestReceived(long requestId, String action) { if (action.equals(PeerRecoveryTargetService.Actions.FILES_INFO)) { @@ -213,17 +211,16 @@ public void testShardCleanupIfShardDeletionAfterRelocationFailedAndIndexDeleted( // add a transport delegate that will prevent the shard active request to succeed the first time after relocation has finished. // node_1 will then wait for the next cluster state change before it tries a next attempt to delete the shard. - MockTransportService transportServiceNode_1 = (MockTransportService) internalCluster().getInstance(TransportService.class, node_1); - TransportService transportServiceNode_2 = internalCluster().getInstance(TransportService.class, node_2); final CountDownLatch shardActiveRequestSent = new CountDownLatch(1); - transportServiceNode_1.addSendBehavior(transportServiceNode_2, (connection, requestId, action, request, options) -> { - if (action.equals("internal:index/shard/exists") && shardActiveRequestSent.getCount() > 0) { - shardActiveRequestSent.countDown(); - logger.info("prevent shard active request from being sent"); - throw new ConnectTransportException(connection.getNode(), "DISCONNECT: simulated"); - } - connection.sendRequest(requestId, action, request, options); - }); + MockTransportService.getInstance(node_1) + .addSendBehavior(MockTransportService.getInstance(node_2), (connection, requestId, action, request, options) -> { + if (action.equals("internal:index/shard/exists") && shardActiveRequestSent.getCount() > 0) { + shardActiveRequestSent.countDown(); + logger.info("prevent shard active request from being sent"); + throw new ConnectTransportException(connection.getNode(), "DISCONNECT: simulated"); + } + connection.sendRequest(requestId, action, request, options); + }); logger.info("--> move shard from {} to {}, and wait for relocation to finish", node_1, node_2); internalCluster().client().admin().cluster().prepareReroute().add(new MoveAllocationCommand("test", 0, node_1, node_2)).get(); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java index ea392e14104fa..9e04413bfb014 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/RelocationIT.java @@ -404,7 +404,7 @@ public void testCancellationCleansTempFiles() throws Exception { logger.info("--> blocking recoveries from primary (allowed failures: [{}])", allowedFailures); CountDownLatch corruptionCount = new CountDownLatch(allowedFailures); ClusterService clusterService = internalCluster().getInstance(ClusterService.class, p_node); - MockTransportService mockTransportService = (MockTransportService) internalCluster().getInstance(TransportService.class, p_node); + final var mockTransportService = MockTransportService.getInstance(p_node); for (DiscoveryNode node : clusterService.state().nodes()) { if (node.equals(clusterService.localNode()) == false) { mockTransportService.addSendBehavior( diff --git a/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java index 74b0b5a1ef864..6281df7fc6646 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java @@ -104,24 +104,21 @@ public void testCancelRecoveryAndResume() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final AtomicBoolean truncate = new AtomicBoolean(true); for (NodeStats dataNode : dataNodeStats) { - MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( - TransportService.class, - dataNode.getNode().getName() - )); - mockTransportService.addSendBehavior( - internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), - (connection, requestId, action, request, options) -> { - if (action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { - RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; - logger.info("file chunk [{}] lastChunk: {}", req, req.lastChunk()); - if ((req.name().endsWith("cfs") || req.name().endsWith("fdt")) && req.lastChunk() && truncate.get()) { - latch.countDown(); - throw new RuntimeException("Caused some truncated files for fun and profit"); + MockTransportService.getInstance(dataNode.getNode().getName()) + .addSendBehavior( + internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), + (connection, requestId, action, request, options) -> { + if (action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { + RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; + logger.info("file chunk [{}] lastChunk: {}", req, req.lastChunk()); + if ((req.name().endsWith("cfs") || req.name().endsWith("fdt")) && req.lastChunk() && truncate.get()) { + latch.countDown(); + throw new RuntimeException("Caused some truncated files for fun and profit"); + } } + connection.sendRequest(requestId, action, request, options); } - connection.sendRequest(requestId, action, request, options); - } - ); + ); } logger.info("--> bumping replicas to 1"); // diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java index e84afecb96f3f..000dccdee34c6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/SearchServiceCleanupOnLostMasterIT.java @@ -16,7 +16,6 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.hamcrest.Matchers; import java.util.Collection; @@ -48,11 +47,8 @@ public void testMasterRestart() throws Exception { public void testDroppedOutNode() throws Exception { testLostMaster((master, dataNode) -> { - final MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - master - ); - final TransportService dataTransportService = internalCluster().getInstance(TransportService.class, dataNode); + final var masterTransportService = MockTransportService.getInstance(master); + final var dataTransportService = MockTransportService.getInstance(dataNode); masterTransportService.addFailToSendNoConnectRule(dataTransportService, FollowersChecker.FOLLOWER_CHECK_ACTION_NAME); assertBusy(() -> { diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fieldcaps/FieldCapabilitiesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fieldcaps/FieldCapabilitiesIT.java index a40938094d2b1..474d4ebc12843 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fieldcaps/FieldCapabilitiesIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fieldcaps/FieldCapabilitiesIT.java @@ -58,7 +58,6 @@ import org.elasticsearch.test.MockLogAppender; import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.ObjectParser; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; @@ -469,17 +468,14 @@ public void testTargetNodeFails() throws Exception { try { final AtomicBoolean failedRequest = new AtomicBoolean(); for (String node : internalCluster().getNodeNames()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.addRequestHandlingBehavior( - TransportFieldCapabilitiesAction.ACTION_NODE_NAME, - (handler, request, channel, task) -> { + MockTransportService.getInstance(node) + .addRequestHandlingBehavior(TransportFieldCapabilitiesAction.ACTION_NODE_NAME, (handler, request, channel, task) -> { if (failedRequest.compareAndSet(false, true)) { channel.sendResponse(new CircuitBreakingException("Simulated", CircuitBreaker.Durability.TRANSIENT)); } else { handler.messageReceived(request, channel, task); } - } - ); + }); } FieldCapabilitiesRequest request = new FieldCapabilitiesRequest(); request.indices("log-index-*"); @@ -495,8 +491,7 @@ public void testTargetNodeFails() throws Exception { assertThat(response.getField("field1"), hasKey("keyword")); } finally { for (String node : internalCluster().getNodeNames()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.clearAllRules(); + MockTransportService.getInstance(node).clearAllRules(); } } } @@ -571,16 +566,13 @@ public void testRelocation() throws Exception { try { final AtomicBoolean relocated = new AtomicBoolean(); for (String node : internalCluster().getNodeNames()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.addRequestHandlingBehavior( - TransportFieldCapabilitiesAction.ACTION_NODE_NAME, - (handler, request, channel, task) -> { + MockTransportService.getInstance(node) + .addRequestHandlingBehavior(TransportFieldCapabilitiesAction.ACTION_NODE_NAME, (handler, request, channel, task) -> { if (relocated.compareAndSet(false, true)) { moveOrCloseShardsOnNodes(node); } handler.messageReceived(request, channel, task); - } - ); + }); } FieldCapabilitiesRequest request = new FieldCapabilitiesRequest(); request.indices("log-index-*"); @@ -595,8 +587,7 @@ public void testRelocation() throws Exception { assertThat(response.getField("field1"), hasKey("long")); } finally { for (String node : internalCluster().getNodeNames()) { - MockTransportService transportService = (MockTransportService) internalCluster().getInstance(TransportService.class, node); - transportService.clearAllRules(); + MockTransportService.getInstance(node).clearAllRules(); } } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 9753a9138ff07..e188c11125c42 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -1094,14 +1094,12 @@ public void testSnapshotDeleteRelocatingPrimaryIndex() throws Exception { // Drop all file chunk requests so that below relocation takes forever and we're guaranteed to run the snapshot in parallel to it for (String nodeName : dataNodes) { - ((MockTransportService) internalCluster().getInstance(TransportService.class, nodeName)).addSendBehavior( - (connection, requestId, action, request, options) -> { - if (PeerRecoveryTargetService.Actions.FILE_CHUNK.equals(action)) { - return; - } - connection.sendRequest(requestId, action, request, options); + MockTransportService.getInstance(nodeName).addSendBehavior((connection, requestId, action, request, options) -> { + if (PeerRecoveryTargetService.Actions.FILE_CHUNK.equals(action)) { + return; } - ); + connection.sendRequest(requestId, action, request, options); + }); } logger.info("--> start relocations"); diff --git a/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java index 5bcec997c26c3..6fc6b349fc989 100644 --- a/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/indices/recovery/AbstractIndexRecoveryIntegTestCase.java @@ -147,14 +147,8 @@ protected void checkTransientErrorsDuringRecoveryAreRetried(String recoveryActio createSnapshotThatCanBeUsedDuringRecovery(indexName); } - MockTransportService blueTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - blueNodeName - ); - MockTransportService redTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - redNodeName - ); + final var blueTransportService = MockTransportService.getInstance(blueNodeName); + final var redTransportService = MockTransportService.getInstance(redNodeName); final AtomicBoolean recoveryStarted = new AtomicBoolean(false); final AtomicBoolean finalizeReceived = new AtomicBoolean(false); @@ -251,14 +245,8 @@ public void checkDisconnectsWhileRecovering(String recoveryActionToBlock) throws createSnapshotThatCanBeUsedDuringRecovery(indexName); } - MockTransportService blueMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - blueNodeName - ); - MockTransportService redMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - redNodeName - ); + final var blueMockTransportService = MockTransportService.getInstance(blueNodeName); + final var redMockTransportService = MockTransportService.getInstance(redNodeName); TransportService redTransportService = internalCluster().getInstance(TransportService.class, redNodeName); TransportService blueTransportService = internalCluster().getInstance(TransportService.class, blueNodeName); final CountDownLatch requestFailed = new CountDownLatch(1); @@ -348,18 +336,9 @@ public void checkDisconnectsDuringRecovery(boolean useSnapshotBasedRecoveries) t createSnapshotThatCanBeUsedDuringRecovery(indexName); } - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - masterNodeName - ); - MockTransportService blueMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - blueNodeName - ); - MockTransportService redMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - redNodeName - ); + final var masterTransportService = MockTransportService.getInstance(masterNodeName); + final var blueMockTransportService = MockTransportService.getInstance(blueNodeName); + final var redMockTransportService = MockTransportService.getInstance(redNodeName); redMockTransportService.addSendBehavior(blueMockTransportService, new StubbableTransport.SendRequestBehavior() { private final AtomicInteger count = new AtomicInteger(); diff --git a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java index 3a9b918e654e5..e0cd47c48515b 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java +++ b/test/framework/src/main/java/org/elasticsearch/test/transport/MockTransportService.java @@ -39,6 +39,7 @@ import org.elasticsearch.search.SearchModule; import org.elasticsearch.tasks.TaskManager; import org.elasticsearch.telemetry.tracing.Tracer; +import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.tasks.MockTaskManager; import org.elasticsearch.threadpool.ThreadPool; @@ -74,6 +75,8 @@ import java.util.function.Function; import java.util.function.Supplier; +import static org.junit.Assert.assertNotNull; + /** * A mock delegate service that allows to simulate different network topology failures. * Internally it maps TransportAddress objects to rules that inject failures. @@ -188,6 +191,14 @@ public static MockTransportService createNewService( ); } + public static MockTransportService getInstance(String nodeName) { + assertNotNull("nodeName must not be null", nodeName); + return ESTestCase.asInstanceOf( + MockTransportService.class, + ESIntegTestCase.internalCluster().getInstance(TransportService.class, nodeName) + ); + } + private final Transport original; /** diff --git a/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java b/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java index 26740d7c52a2c..59a0cd34a1db0 100644 --- a/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java +++ b/x-pack/plugin/downsample/src/internalClusterTest/java/org/elasticsearch/xpack/downsample/DownsampleTransportFailureIT.java @@ -32,7 +32,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.TransportService; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentFactory; import org.elasticsearch.xcontent.XContentType; @@ -106,19 +105,8 @@ public Client masterClient() { return client(this.cluster.getMasterName()); } - public MockTransportService masterMockTransportService() { - return (MockTransportService) internalCluster().getInstance(TransportService.class, internalCluster().getMasterName()); - } - - public MockTransportService coordinatorMockTransportService() { - assert this.coordinator != null; - return (MockTransportService) internalCluster().getInstance(TransportService.class, this.coordinator); - } - public List allMockTransportServices() { - return Arrays.stream(cluster.getNodeNames()) - .map(nodeName -> (MockTransportService) internalCluster().getInstance(TransportService.class, nodeName)) - .collect(Collectors.toList()); + return Arrays.stream(cluster.getNodeNames()).map(MockTransportService::getInstance).toList(); } public String coordinatorName() { @@ -301,7 +289,7 @@ public void testNoDisruption() { public void testDownsampleActionExceptionDisruption() { // GIVEN - final MockTransportService coordinator = testCluster.coordinatorMockTransportService(); + final MockTransportService coordinator = MockTransportService.getInstance(testCluster.coordinator); final DownsampleAction.Request downsampleRequest = new DownsampleAction.Request( SOURCE_INDEX_NAME, TARGET_INDEX_NAME, diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/PartiallyCachedShardAllocationIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/PartiallyCachedShardAllocationIntegTests.java index eafe0f5d819e3..2824aa22496a1 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/PartiallyCachedShardAllocationIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/shared/PartiallyCachedShardAllocationIntegTests.java @@ -271,18 +271,15 @@ public void testPartialSearchableSnapshotDelaysAllocationUntilNodeCacheStatesKno ); // Unblock the other new node, but maybe inject a few errors - final MockTransportService transportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - newNodes.get(0) - ); final Semaphore failurePermits = new Semaphore(between(0, 2)); - transportService.addRequestHandlingBehavior(FrozenCacheInfoNodeAction.NAME, (handler, request, channel, task) -> { - if (failurePermits.tryAcquire()) { - channel.sendResponse(new ElasticsearchException("simulated")); - } else { - handler.messageReceived(request, channel, task); - } - }); + MockTransportService.getInstance(newNodes.get(0)) + .addRequestHandlingBehavior(FrozenCacheInfoNodeAction.NAME, (handler, request, channel, task) -> { + if (failurePermits.tryAcquire()) { + channel.sendResponse(new ElasticsearchException("simulated")); + } else { + handler.messageReceived(request, channel, task); + } + }); cacheInfoBlockGetter.apply(newNodes.get(0)).onResponse(null); final RestoreSnapshotResponse restoreSnapshotResponse = responseFuture.actionGet(10, TimeUnit.SECONDS); diff --git a/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java b/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java index 3fd9a3a2e9c4d..4c9d8d1ab29e5 100644 --- a/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java +++ b/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java @@ -317,19 +317,11 @@ public void testPeerRecoveryUsesSnapshots() throws Exception { String targetNode = internalCluster().startDataOnlyNode(); - MockTransportService sourceMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - sourceNode - ); - MockTransportService targetMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - targetNode - ); - - sourceMockTransportService.addSendBehavior(targetMockTransportService, (connection, requestId, action, request, options) -> { - assertNotEquals(PeerRecoveryTargetService.Actions.FILE_CHUNK, action); - connection.sendRequest(requestId, action, request, options); - }); + MockTransportService.getInstance(sourceNode) + .addSendBehavior(MockTransportService.getInstance(targetNode), (connection, requestId, action, request, options) -> { + assertNotEquals(PeerRecoveryTargetService.Actions.FILE_CHUNK, action); + connection.sendRequest(requestId, action, request, options); + }); updateIndexSettings(Settings.builder().put("index.routing.allocation.require._name", targetNode), indexName); @@ -597,23 +589,19 @@ public void testRecoveryIsCancelledAfterDeletingTheIndex() throws Exception { targetNode = internalCluster().startDataOnlyNode(); } - MockTransportService targetMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - targetNode - ); - CountDownLatch recoverSnapshotFileRequestReceived = new CountDownLatch(1); CountDownLatch respondToRecoverSnapshotFile = new CountDownLatch(1); AtomicInteger numberOfRecoverSnapshotFileRequestsReceived = new AtomicInteger(); - targetMockTransportService.addRequestHandlingBehavior( - PeerRecoveryTargetService.Actions.RESTORE_FILE_FROM_SNAPSHOT, - (handler, request, channel, task) -> { - assertThat(numberOfRecoverSnapshotFileRequestsReceived.incrementAndGet(), is(equalTo(1))); - recoverSnapshotFileRequestReceived.countDown(); - respondToRecoverSnapshotFile.await(); - handler.messageReceived(request, channel, task); - } - ); + MockTransportService.getInstance(targetNode) + .addRequestHandlingBehavior( + PeerRecoveryTargetService.Actions.RESTORE_FILE_FROM_SNAPSHOT, + (handler, request, channel, task) -> { + assertThat(numberOfRecoverSnapshotFileRequestsReceived.incrementAndGet(), is(equalTo(1))); + recoverSnapshotFileRequestReceived.countDown(); + respondToRecoverSnapshotFile.await(); + handler.messageReceived(request, channel, task); + } + ); if (seqNoRecovery) { ClusterState clusterState = clusterAdmin().prepareState().get().getState(); @@ -1257,10 +1245,7 @@ public void testRecoveryRetryKeepsTheGrantedSnapshotFileDownloadPermit() throws recoverySnapshotFileRequests, awaitForRecoverSnapshotFileRequestReceived, respondToRecoverSnapshotFile) -> { - MockTransportService sourceMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - sourceNode - ); + final var sourceMockTransportService = MockTransportService.getInstance(sourceNode); CountDownLatch startRecoveryRetryReceived = new CountDownLatch(1); AtomicBoolean delayRecoveryExceptionSent = new AtomicBoolean(); @@ -1363,15 +1348,8 @@ public void testNodeDisconnectsDoNotOverAccountRecoveredBytes() throws Exception .findFirst() .orElseThrow(); - MockTransportService sourceMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - replicaNodeName - ); - - MockTransportService targetMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - newReplicaNodeName - ); + final var sourceMockTransportService = MockTransportService.getInstance(replicaNodeName); + final var targetMockTransportService = MockTransportService.getInstance(newReplicaNodeName); final CountDownLatch firstDownloadStartLatch = new CountDownLatch(1); final CountDownLatch blockSnapshotFileDownload = new CountDownLatch(1); @@ -1479,10 +1457,7 @@ private void executeRecoveryWithSnapshotFileDownloadThrottled(SnapshotBasedRecov String sourceNode = dataNodes.get(0); String targetNode = dataNodes.get(1); - MockTransportService targetMockTransportService = (MockTransportService) internalCluster().getInstance( - TransportService.class, - targetNode - ); + final var targetMockTransportService = MockTransportService.getInstance(targetNode); List recoverySnapshotFileRequests = Collections.synchronizedList(new ArrayList<>()); CountDownLatch recoverSnapshotFileRequestReceived = new CountDownLatch(1); From 99868197c0a8862ff54f515f5508788141d4e6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Fri, 27 Oct 2023 10:56:08 +0200 Subject: [PATCH 151/203] Mute testGeoIpDatabasesDownload tests (#101420) Related to https://github.com/elastic/elasticsearch/issues/85818 and https://github.com/elastic/elasticsearch/issues/100361 Bug issue: https://github.com/elastic/elasticsearch/issues/92888 --- .../java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java b/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java index 6d360cda59b09..9a739132e5808 100644 --- a/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java +++ b/modules/ingest-geoip/src/internalClusterTest/java/org/elasticsearch/ingest/geoip/GeoIpDownloaderIT.java @@ -216,6 +216,7 @@ public void testInvalidTimestamp() throws Exception { }); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/92888") public void testUpdatedTimestamp() throws Exception { assumeTrue("only test with fixture to have stable results", getEndpoint() != null); testGeoIpDatabasesDownload(); @@ -227,6 +228,7 @@ public void testUpdatedTimestamp() throws Exception { testGeoIpDatabasesDownload(); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/92888") public void testGeoIpDatabasesDownload() throws Exception { putGeoIpPipeline(); updateClusterSettings(Settings.builder().put(GeoIpDownloaderTaskExecutor.ENABLED_SETTING.getKey(), true)); From 0e6efeb81497f2663576d8c5597602365ef2335c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Fri, 27 Oct 2023 11:11:12 +0200 Subject: [PATCH 152/203] Added retries to readiness probes (#101378) --- .../readiness/MockReadinessService.java | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java b/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java index 6b58a6ce36117..0eb7a48622083 100644 --- a/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java +++ b/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java @@ -29,6 +29,10 @@ public class MockReadinessService extends ReadinessService { */ public static class TestPlugin extends Plugin {} + private static final int RETRIES = 3; + + private static final int RETRY_DELAY_IN_MILLIS = 10; + private static final String METHOD_NOT_MOCKED = "This method has not been mocked"; private static class MockServerSocketChannel extends ServerSocketChannel { @@ -90,20 +94,30 @@ public MockReadinessService(ClusterService clusterService, Environment environme super(clusterService, environment, MockServerSocketChannel::openMock); } - static void tcpReadinessProbeTrue(ReadinessService readinessService) { + private static boolean socketIsOpen(ReadinessService readinessService) { ServerSocketChannel mockedSocket = readinessService.serverChannel(); - if (mockedSocket == null) { - throw new AssertionError("Mocked socket not created for this node"); - } - if (mockedSocket.isOpen() == false) { - throw new AssertionError("Readiness socket should be open"); + return mockedSocket != null && mockedSocket.isOpen(); + } + + static void tcpReadinessProbeTrue(ReadinessService readinessService) throws InterruptedException { + for (int i = 1; i <= RETRIES; ++i) { + if (socketIsOpen(readinessService)) { + return; + } + Thread.sleep(RETRY_DELAY_IN_MILLIS * i); } + + throw new AssertionError("Readiness socket should be open"); } - static void tcpReadinessProbeFalse(ReadinessService readinessService) { - ServerSocketChannel mockedSocket = readinessService.serverChannel(); - if (mockedSocket != null && mockedSocket.isOpen()) { - throw new AssertionError("Readiness socket should be closed"); + static void tcpReadinessProbeFalse(ReadinessService readinessService) throws InterruptedException { + for (int i = 0; i < RETRIES; ++i) { + if (socketIsOpen(readinessService) == false) { + return; + } + Thread.sleep(RETRY_DELAY_IN_MILLIS * i); } + + throw new AssertionError("Readiness socket should be closed"); } } From 5e42714c3b73ecc183f78f99e00652204e1d0d6d Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Fri, 27 Oct 2023 12:03:03 +0200 Subject: [PATCH 153/203] ESQL: Fix planning of MV_EXPAND with foldable expressions (#101385) --- docs/changelog/101385.yaml | 6 +++ .../src/main/resources/mv_expand.csv-spec | 8 ++++ .../xpack/esql/analysis/Analyzer.java | 21 ++++++++++ .../xpack/esql/io/stream/PlanNamedTypes.java | 6 ++- .../esql/optimizer/PhysicalPlanOptimizer.java | 4 ++ .../xpack/esql/parser/LogicalPlanBuilder.java | 3 +- .../xpack/esql/plan/logical/MvExpand.java | 39 ++++++++++++++++--- .../esql/plan/physical/MvExpandExec.java | 27 ++++++++++--- .../esql/planner/LocalExecutionPlanner.java | 18 ++++++++- .../xpack/esql/planner/Mapper.java | 2 +- .../esql/io/stream/PlanNamedTypesTests.java | 2 +- .../optimizer/LogicalPlanOptimizerTests.java | 25 ++++++++++++ 12 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 docs/changelog/101385.yaml diff --git a/docs/changelog/101385.yaml b/docs/changelog/101385.yaml new file mode 100644 index 0000000000000..406ed804cbbcc --- /dev/null +++ b/docs/changelog/101385.yaml @@ -0,0 +1,6 @@ +pr: 101385 +summary: "ESQL: Fix planning of MV_EXPAND with foldable expressions" +area: ES|QL +type: bug +issues: + - 101118 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec index dd1abf59f8348..345ce501e68f4 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec @@ -225,3 +225,11 @@ emp_no:integer | job_positions:keyword 10004 |Head Human Resources 10004 |Reporting Analyst ; + + +expandFoldable +row a = "foobar", b = ["foo", "bar"], c = 12 | mv_expand b | where b LIKE "fo*"; + +a:keyword | b:keyword | c:integer +foobar | foo | 12 +; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java index 4db49c4d76c89..297be5c6aec48 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java @@ -19,6 +19,7 @@ import org.elasticsearch.xpack.esql.plan.logical.EsqlUnresolvedRelation; import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.Keep; +import org.elasticsearch.xpack.esql.plan.logical.MvExpand; import org.elasticsearch.xpack.esql.plan.logical.Rename; import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; @@ -324,9 +325,29 @@ protected LogicalPlan doRule(LogicalPlan plan) { return resolveEnrich(p, childrenOutput); } + if (plan instanceof MvExpand p) { + return resolveMvExpand(p, childrenOutput); + } + return plan.transformExpressionsUp(UnresolvedAttribute.class, ua -> maybeResolveAttribute(ua, childrenOutput)); } + private LogicalPlan resolveMvExpand(MvExpand p, List childrenOutput) { + if (p.target() instanceof UnresolvedAttribute ua) { + Attribute resolved = maybeResolveAttribute(ua, childrenOutput); + if (resolved == ua) { + return p; + } + return new MvExpand( + p.source(), + p.child(), + resolved, + new ReferenceAttribute(resolved.source(), resolved.name(), resolved.dataType(), null, resolved.nullable(), null, false) + ); + } + return p; + } + private Attribute maybeResolveAttribute(UnresolvedAttribute ua, List childrenOutput) { if (ua.customMessage()) { return ua; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java index 93dc2fb094f54..20ec1ac410f64 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java @@ -585,13 +585,14 @@ static void writeLimitExec(PlanStreamOutput out, LimitExec limitExec) throws IOE } static MvExpandExec readMvExpandExec(PlanStreamInput in) throws IOException { - return new MvExpandExec(in.readSource(), in.readPhysicalPlanNode(), in.readNamedExpression()); + return new MvExpandExec(in.readSource(), in.readPhysicalPlanNode(), in.readNamedExpression(), in.readAttribute()); } static void writeMvExpandExec(PlanStreamOutput out, MvExpandExec mvExpandExec) throws IOException { out.writeNoSource(); out.writePhysicalPlanNode(mvExpandExec.child()); out.writeNamedExpression(mvExpandExec.target()); + out.writeAttribute(mvExpandExec.expanded()); } static OrderExec readOrderExec(PlanStreamInput in) throws IOException { @@ -780,13 +781,14 @@ static void writeLimit(PlanStreamOutput out, Limit limit) throws IOException { } static MvExpand readMvExpand(PlanStreamInput in) throws IOException { - return new MvExpand(in.readSource(), in.readLogicalPlanNode(), in.readNamedExpression()); + return new MvExpand(in.readSource(), in.readLogicalPlanNode(), in.readNamedExpression(), in.readAttribute()); } static void writeMvExpand(PlanStreamOutput out, MvExpand mvExpand) throws IOException { out.writeNoSource(); out.writeLogicalPlanNode(mvExpand.child()); out.writeNamedExpression(mvExpand.target()); + out.writeAttribute(mvExpand.expanded()); } static OrderBy readOrderBy(PlanStreamInput in) throws IOException { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizer.java index 3f05fa90ac8ab..a8d00920bef79 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizer.java @@ -12,6 +12,7 @@ import org.elasticsearch.xpack.esql.plan.physical.EnrichExec; import org.elasticsearch.xpack.esql.plan.physical.ExchangeExec; import org.elasticsearch.xpack.esql.plan.physical.FragmentExec; +import org.elasticsearch.xpack.esql.plan.physical.MvExpandExec; import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan; import org.elasticsearch.xpack.esql.plan.physical.ProjectExec; import org.elasticsearch.xpack.esql.plan.physical.RegexExtractExec; @@ -115,6 +116,9 @@ public PhysicalPlan apply(PhysicalPlan plan) { if (p instanceof RegexExtractExec ree) { attributes.removeAll(ree.extractedFields()); } + if (p instanceof MvExpandExec mvee) { + attributes.remove(mvee.expanded()); + } if (p instanceof EnrichExec ee) { for (NamedExpression enrichField : ee.enrichFields()) { // TODO: why is this different then the remove above? diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java index 3b1ab30f0bd80..49caf0e4618bd 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java @@ -150,7 +150,8 @@ public PlanFactory visitDissectCommand(EsqlBaseParser.DissectCommandContext ctx) @Override public PlanFactory visitMvExpandCommand(EsqlBaseParser.MvExpandCommandContext ctx) { String identifier = visitSourceIdentifier(ctx.sourceIdentifier()); - return child -> new MvExpand(source(ctx), child, new UnresolvedAttribute(source(ctx), identifier)); + Source src = source(ctx); + return child -> new MvExpand(src, child, new UnresolvedAttribute(src, identifier), new UnresolvedAttribute(src, identifier)); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/MvExpand.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/MvExpand.java index 6f7830a12c708..17f669b5d30b3 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/MvExpand.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/MvExpand.java @@ -7,26 +7,50 @@ package org.elasticsearch.xpack.esql.plan.logical; +import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.expression.NamedExpression; import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.ql.plan.logical.UnaryPlan; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; public class MvExpand extends UnaryPlan { private final NamedExpression target; + private final Attribute expanded; - public MvExpand(Source source, LogicalPlan child, NamedExpression target) { + private final List output; + + public MvExpand(Source source, LogicalPlan child, NamedExpression target, Attribute expanded) { super(source, child); this.target = target; + this.expanded = expanded; + this.output = calculateOutput(child.output(), target, expanded); + } + + public static List calculateOutput(List input, NamedExpression target, Attribute expanded) { + List result = new ArrayList<>(); + for (Attribute attribute : input) { + if (attribute.name().equals(target.name())) { + result.add(expanded); + } else { + result.add(attribute); + } + } + return result; } public NamedExpression target() { return target; } + public Attribute expanded() { + return expanded; + } + @Override public boolean expressionsResolved() { return target.resolved(); @@ -34,17 +58,22 @@ public boolean expressionsResolved() { @Override public UnaryPlan replaceChild(LogicalPlan newChild) { - return new MvExpand(source(), newChild, target); + return new MvExpand(source(), newChild, target, expanded); + } + + @Override + public List output() { + return output; } @Override protected NodeInfo info() { - return NodeInfo.create(this, MvExpand::new, child(), target); + return NodeInfo.create(this, MvExpand::new, child(), target, expanded); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), target); + return Objects.hash(super.hashCode(), target, expanded); } @Override @@ -52,6 +81,6 @@ public boolean equals(Object obj) { if (false == super.equals(obj)) { return false; } - return Objects.equals(target, ((MvExpand) obj).target); + return Objects.equals(target, ((MvExpand) obj).target) && Objects.equals(expanded, ((MvExpand) obj).expanded); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/MvExpandExec.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/MvExpandExec.java index 4bbd4b8aae2e3..816b6261c0f3d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/MvExpandExec.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/MvExpandExec.java @@ -6,38 +6,55 @@ */ package org.elasticsearch.xpack.esql.plan.physical; +import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.expression.NamedExpression; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; +import java.util.List; import java.util.Objects; +import static org.elasticsearch.xpack.esql.plan.logical.MvExpand.calculateOutput; + public class MvExpandExec extends UnaryExec { private final NamedExpression target; + private final Attribute expanded; + private final List output; - public MvExpandExec(Source source, PhysicalPlan child, NamedExpression target) { + public MvExpandExec(Source source, PhysicalPlan child, NamedExpression target, Attribute expanded) { super(source, child); this.target = target; + this.expanded = expanded; + this.output = calculateOutput(child.output(), target, expanded); } @Override protected NodeInfo info() { - return NodeInfo.create(this, MvExpandExec::new, child(), target); + return NodeInfo.create(this, MvExpandExec::new, child(), target, expanded); } @Override public MvExpandExec replaceChild(PhysicalPlan newChild) { - return new MvExpandExec(source(), newChild, target); + return new MvExpandExec(source(), newChild, target, expanded); } public NamedExpression target() { return target; } + public Attribute expanded() { + return expanded; + } + + @Override + public List output() { + return output; + } + @Override public int hashCode() { - return Objects.hash(target, child()); + return Objects.hash(target, child(), expanded); } @Override @@ -51,6 +68,6 @@ public boolean equals(Object obj) { MvExpandExec other = (MvExpandExec) obj; - return Objects.equals(target, other.target) && Objects.equals(child(), other.child()); + return Objects.equals(target, other.target) && Objects.equals(child(), other.child()) && Objects.equals(expanded, other.expanded); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java index 72a1a62707438..c2f7fba9a5206 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java @@ -612,8 +612,24 @@ private PhysicalOperation planLimit(LimitExec limit, LocalExecutionPlannerContex private PhysicalOperation planMvExpand(MvExpandExec mvExpandExec, LocalExecutionPlannerContext context) { PhysicalOperation source = plan(mvExpandExec.child(), context); + List childOutput = mvExpandExec.child().output(); int blockSize = 5000;// TODO estimate row size and use context.pageSize() - return source.with(new MvExpandOperator.Factory(source.layout.get(mvExpandExec.target().id()).channel(), blockSize), source.layout); + + Layout.Builder layout = new Layout.Builder(); + List inverse = source.layout.inverse(); + var expandedName = mvExpandExec.expanded().name(); + for (int index = 0; index < inverse.size(); index++) { + if (childOutput.get(index).name().equals(expandedName)) { + layout.append(mvExpandExec.expanded()); + } else { + layout.append(inverse.get(index)); + } + } + + return source.with( + new MvExpandOperator.Factory(source.layout.get(mvExpandExec.target().id()).channel(), blockSize), + layout.build() + ); } /** diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/Mapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/Mapper.java index eb50a1ceb4071..3eea84b0bd1f9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/Mapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/Mapper.java @@ -151,7 +151,7 @@ private PhysicalPlan map(UnaryPlan p, PhysicalPlan child) { } if (p instanceof MvExpand mvExpand) { - return new MvExpandExec(mvExpand.source(), map(mvExpand.child()), mvExpand.target()); + return new MvExpandExec(mvExpand.source(), map(mvExpand.child()), mvExpand.target(), mvExpand.expanded()); } // diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java index b3da177b1c39c..85612427a1867 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java @@ -488,7 +488,7 @@ public void testEsqlProject() throws IOException { public void testMvExpand() throws IOException { var esRelation = new EsRelation(Source.EMPTY, randomEsIndex(), List.of(randomFieldAttribute()), randomBoolean()); - var orig = new MvExpand(Source.EMPTY, esRelation, randomFieldAttribute()); + var orig = new MvExpand(Source.EMPTY, esRelation, randomFieldAttribute(), randomFieldAttribute()); BytesStreamOutput bso = new BytesStreamOutput(); PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry); PlanNamedTypes.writeMvExpand(out, orig); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 17429d3a6b1c3..f63026c28279a 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -45,6 +45,7 @@ import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.Grok; import org.elasticsearch.xpack.esql.plan.logical.MvExpand; +import org.elasticsearch.xpack.esql.plan.logical.Row; import org.elasticsearch.xpack.esql.plan.logical.TopN; import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject; import org.elasticsearch.xpack.esql.plan.logical.local.LocalRelation; @@ -2447,6 +2448,30 @@ public void testEliminateDuplicateAggsNonCount() { var source = as(agg.child(), EsRelation.class); } + /** + * Expected + * Limit[2[INTEGER]] + * \_Filter[a{r}#6 > 2[INTEGER]] + * \_MvExpand[a{r}#2,a{r}#6] + * \_Row[[[1, 2, 3][INTEGER] AS a]] + */ + public void testMvExpandFoldable() { + LogicalPlan plan = optimizedPlan(""" + row a = [1, 2, 3] + | mv_expand a + | where a > 2 + | limit 2"""); + + var limit = as(plan, Limit.class); + var filter = as(limit.child(), Filter.class); + var expand = as(filter.child(), MvExpand.class); + assertThat(filter.condition(), instanceOf(GreaterThan.class)); + var filterProp = ((GreaterThan) filter.condition()).left(); + assertTrue(expand.expanded().semanticEquals(filterProp)); + assertFalse(expand.target().semanticEquals(filterProp)); + var row = as(expand.child(), Row.class); + } + private T aliased(Expression exp, Class clazz) { var alias = as(exp, Alias.class); return as(alias.child(), clazz); From d3e9bf02f83ea32607c6ff7ab2fce14fc97d0f34 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Fri, 27 Oct 2023 06:47:13 -0400 Subject: [PATCH 154/203] Updating percolate query docs to account for custom similarity limitation (#101386) --- docs/reference/query-dsl/percolate-query.asciidoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/reference/query-dsl/percolate-query.asciidoc b/docs/reference/query-dsl/percolate-query.asciidoc index 24b951a46ed9d..c5a3ebb782edd 100644 --- a/docs/reference/query-dsl/percolate-query.asciidoc +++ b/docs/reference/query-dsl/percolate-query.asciidoc @@ -685,3 +685,6 @@ a different index configuration, like the number of primary shards. ===== Allow expensive queries Percolate queries will not be executed if <> is set to false. + +===== Using custom similarities +Percolate queries will not respect any configured <>. They always use the default Lucene similarity. From a9285418892ed60617a6bc90db485bc3ad27131d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Fri, 27 Oct 2023 12:51:31 +0200 Subject: [PATCH 155/203] Move NodeShutdown/ReadinessService interactions IT (#101369) --- .../readiness/MockReadinessService.java | 4 +- .../xpack/shutdown/NodeShutdownIT.java | 52 +-------- .../shutdown/NodeShutdownReadinessIT.java | 104 ++++++++++++++++++ 3 files changed, 107 insertions(+), 53 deletions(-) create mode 100644 x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownReadinessIT.java diff --git a/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java b/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java index 0eb7a48622083..e5841071a787b 100644 --- a/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java +++ b/test/framework/src/main/java/org/elasticsearch/readiness/MockReadinessService.java @@ -99,7 +99,7 @@ private static boolean socketIsOpen(ReadinessService readinessService) { return mockedSocket != null && mockedSocket.isOpen(); } - static void tcpReadinessProbeTrue(ReadinessService readinessService) throws InterruptedException { + public static void tcpReadinessProbeTrue(ReadinessService readinessService) throws InterruptedException { for (int i = 1; i <= RETRIES; ++i) { if (socketIsOpen(readinessService)) { return; @@ -110,7 +110,7 @@ static void tcpReadinessProbeTrue(ReadinessService readinessService) throws Inte throw new AssertionError("Readiness socket should be open"); } - static void tcpReadinessProbeFalse(ReadinessService readinessService) throws InterruptedException { + public static void tcpReadinessProbeFalse(ReadinessService readinessService) throws InterruptedException { for (int i = 0; i < RETRIES; ++i) { if (socketIsOpen(readinessService) == false) { return; diff --git a/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java b/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java index dca0b33e17a59..c004eaf58939b 100644 --- a/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java +++ b/x-pack/plugin/shutdown/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownIT.java @@ -15,7 +15,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.Nullable; -import org.elasticsearch.test.readiness.ReadinessClientProbe; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.xcontent.ObjectPath; import org.elasticsearch.xcontent.XContentBuilder; @@ -38,7 +37,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class NodeShutdownIT extends ESRestTestCase implements ReadinessClientProbe { +public class NodeShutdownIT extends ESRestTestCase { public void testRestartCRUD() throws Exception { checkCRUD(randomFrom("restart", "RESTART"), randomPositiveTimeValue(), null, null); @@ -98,55 +97,6 @@ public void checkCRUD( } } - @SuppressWarnings("unchecked") - public void testShutdownReadinessService() throws Exception { - // Get a node from the cluster and find its readiness port - Request getNodes = new Request("GET", "_nodes"); - Map nodesResponse = responseAsMap(client().performRequest(getNodes)); - Map nodesObject = (Map) nodesResponse.get("nodes"); - - String nodeId = nodesObject.keySet().iterator().next(); - Map nodeObject = (Map) nodesObject.get(nodeId); - Map httpObject = (Map) nodeObject.get("http"); - String publishAddress = (String) httpObject.get("publish_address"); - - String readinessPorts = this.getTestReadinessPorts(); - String restPorts = this.getTestRestCluster(); - - String[] restAddresses = restPorts.split(","); - int nodeIndex = 0; - for (String restAddress : restAddresses) { - // skip ipv6 if any - if (restAddress.startsWith("[")) { - continue; - } - if (restAddress.equals(publishAddress)) { - break; - } - nodeIndex++; - } - - String[] readinessAddresses = readinessPorts.split(","); - String readinessAddress = readinessAddresses[nodeIndex]; - - String portStr = readinessAddress.substring(readinessAddress.lastIndexOf(':') + 1); - Integer port = Integer.parseInt(portStr); - - // Once we have the right port, check to see if it's ready, has to be for a properly started cluster - tcpReadinessProbeTrue(port); - - // Mark the node for shutdown and check that it's not ready - checkCRUD(nodeId, randomFrom("restart", "RESTART"), "1ms", null, false, null); - tcpReadinessProbeFalse(port); - - // Delete the shutdown request and verify that the node is ready again - Request deleteRequest = new Request("DELETE", "_nodes/" + nodeId + "/shutdown"); - assertOK(client().performRequest(deleteRequest)); - assertNoShuttingDownNodes(nodeId); - - tcpReadinessProbeTrue(port); - } - public void testPutShutdownIsIdempotentForRestart() throws Exception { checkPutShutdownIdempotency("RESTART"); } diff --git a/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownReadinessIT.java b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownReadinessIT.java new file mode 100644 index 0000000000000..87eaf4d37ae00 --- /dev/null +++ b/x-pack/plugin/shutdown/src/internalClusterTest/java/org/elasticsearch/xpack/shutdown/NodeShutdownReadinessIT.java @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.shutdown; + +import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.cluster.metadata.SingleNodeShutdownMetadata; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.readiness.MockReadinessService; +import org.elasticsearch.readiness.ReadinessService; +import org.elasticsearch.test.ESIntegTestCase; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.elasticsearch.readiness.MockReadinessService.tcpReadinessProbeFalse; +import static org.elasticsearch.readiness.MockReadinessService.tcpReadinessProbeTrue; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.empty; + +@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) +public class NodeShutdownReadinessIT extends ESIntegTestCase { + + @Override + protected Collection> getMockPlugins() { + final List> plugins = new ArrayList<>(super.getMockPlugins()); + plugins.add(MockReadinessService.TestPlugin.class); + return Collections.unmodifiableList(plugins); + } + + @Override + protected Collection> nodePlugins() { + return List.of(ShutdownPlugin.class); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { + Settings.Builder settings = Settings.builder() + .put(super.nodeSettings(nodeOrdinal, otherSettings)) + .put(Settings.builder().put(ReadinessService.PORT.getKey(), 0).build()); + return settings.build(); + } + + private void putNodeShutdown(String nodeId, SingleNodeShutdownMetadata.Type type, TimeValue allocationDelay) { + assertAcked( + client().execute( + PutShutdownNodeAction.INSTANCE, + new PutShutdownNodeAction.Request(nodeId, type, this.getTestName(), allocationDelay, null, null) + ) + ); + } + + private void deleteNodeShutdown(String nodeId) { + assertAcked(client().execute(DeleteShutdownNodeAction.INSTANCE, new DeleteShutdownNodeAction.Request(nodeId))); + } + + private String getNodeId(String nodeName) { + NodesInfoResponse nodes = clusterAdmin().prepareNodesInfo().clear().get(); + return nodes.getNodes() + .stream() + .map(NodeInfo::getNode) + .filter(node -> node.getName().equals(nodeName)) + .map(DiscoveryNode::getId) + .findFirst() + .orElseThrow(); + } + + private void assertNoShuttingDownNodes(String nodeId) throws ExecutionException, InterruptedException { + var response = client().execute(GetShutdownStatusAction.INSTANCE, new GetShutdownStatusAction.Request(nodeId)).get(); + assertThat(response.getShutdownStatuses(), empty()); + } + + public void testShutdownReadinessService() throws Exception { + + final String nodeName = internalCluster().startMasterOnlyNode(); + final String nodeId = getNodeId(nodeName); + + final var readinessService = internalCluster().getInstance(ReadinessService.class, nodeName); + + // Once we have the right port, check to see if it's ready, has to be for a properly started cluster + tcpReadinessProbeTrue(readinessService); + + // Mark the node for shutdown and check that it's not ready + putNodeShutdown(nodeId, SingleNodeShutdownMetadata.Type.RESTART, TimeValue.timeValueMinutes(1)); + tcpReadinessProbeFalse(readinessService); + + // Delete the shutdown request and verify that the node is ready again + deleteNodeShutdown(nodeId); + assertNoShuttingDownNodes(nodeId); + + tcpReadinessProbeTrue(readinessService); + } +} From 2e3fec241a47120cd18a3732d46e9175bed92574 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 27 Oct 2023 07:43:16 -0400 Subject: [PATCH 156/203] ESQL: Deprecate builder methods on Blocks (#101411) We have a factory, let's use it! --- .../compute/data/BooleanBlock.java | 22 +++++++++++++++++-- .../compute/data/BytesRefBlock.java | 22 +++++++++++++++++-- .../compute/data/DoubleBlock.java | 22 +++++++++++++++++-- .../elasticsearch/compute/data/IntBlock.java | 22 +++++++++++++++++-- .../elasticsearch/compute/data/LongBlock.java | 22 +++++++++++++++++-- .../org/elasticsearch/compute/data/Block.java | 7 ++++++ .../compute/data/X-Block.java.st | 22 +++++++++++++++++-- 7 files changed, 127 insertions(+), 12 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java index c518b3d603319..81e2031b9af34 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java @@ -166,22 +166,40 @@ static int hash(BooleanBlock block) { return result; } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newBooleanBlockBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newBlockBuilder(int estimatedSize) { return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a builder. + * @deprecated use {@link BlockFactory#newBooleanBlockBuilder} + */ + @Deprecated static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newBooleanBlockBuilder(estimatedSize); } - /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a constant block built by the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstantBooleanBlockWith} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static BooleanBlock newConstantBlockWith(boolean value, int positions) { return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a constant block. + * @deprecated use {@link BlockFactory#newConstantBooleanBlockWith} + */ + @Deprecated static BooleanBlock newConstantBlockWith(boolean value, int positions, BlockFactory blockFactory) { return blockFactory.newConstantBooleanBlockWith(value, positions); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java index 6da60fbfe011d..c456486fbed6b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java @@ -171,22 +171,40 @@ static int hash(BytesRefBlock block) { return result; } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newBytesRefBlockBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newBlockBuilder(int estimatedSize) { return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a builder. + * @deprecated use {@link BlockFactory#newBytesRefBlockBuilder} + */ + @Deprecated static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newBytesRefBlockBuilder(estimatedSize); } - /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a constant block built by the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstantBytesRefBlockWith} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static BytesRefBlock newConstantBlockWith(BytesRef value, int positions) { return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a constant block. + * @deprecated use {@link BlockFactory#newConstantBytesRefBlockWith} + */ + @Deprecated static BytesRefBlock newConstantBlockWith(BytesRef value, int positions, BlockFactory blockFactory) { return blockFactory.newConstantBytesRefBlockWith(value, positions); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java index 9d42e5dd3f284..3ee4bf64a8d54 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java @@ -167,22 +167,40 @@ static int hash(DoubleBlock block) { return result; } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newDoubleBlockBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newBlockBuilder(int estimatedSize) { return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a builder. + * @deprecated use {@link BlockFactory#newDoubleBlockBuilder} + */ + @Deprecated static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newDoubleBlockBuilder(estimatedSize); } - /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a constant block built by the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstantDoubleBlockWith} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static DoubleBlock newConstantBlockWith(double value, int positions) { return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a constant block. + * @deprecated use {@link BlockFactory#newConstantDoubleBlockWith} + */ + @Deprecated static DoubleBlock newConstantBlockWith(double value, int positions, BlockFactory blockFactory) { return blockFactory.newConstantDoubleBlockWith(value, positions); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java index d4305e23dd4a1..248e68a9961fe 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java @@ -166,22 +166,40 @@ static int hash(IntBlock block) { return result; } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newIntBlockBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newBlockBuilder(int estimatedSize) { return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a builder. + * @deprecated use {@link BlockFactory#newIntBlockBuilder} + */ + @Deprecated static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newIntBlockBuilder(estimatedSize); } - /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a constant block built by the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstantIntBlockWith} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static IntBlock newConstantBlockWith(int value, int positions) { return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a constant block. + * @deprecated use {@link BlockFactory#newConstantIntBlockWith} + */ + @Deprecated static IntBlock newConstantBlockWith(int value, int positions, BlockFactory blockFactory) { return blockFactory.newConstantIntBlockWith(value, positions); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java index 9def8475161ff..2edd840742b91 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java @@ -167,22 +167,40 @@ static int hash(LongBlock block) { return result; } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newLongBlockBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newBlockBuilder(int estimatedSize) { return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a builder. + * @deprecated use {@link BlockFactory#newLongBlockBuilder} + */ + @Deprecated static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newLongBlockBuilder(estimatedSize); } - /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a constant block built by the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstantLongBlockWith} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static LongBlock newConstantBlockWith(long value, int positions) { return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a constant block. + * @deprecated use {@link BlockFactory#newConstantLongBlockWith} + */ + @Deprecated static LongBlock newConstantBlockWith(long value, int positions, BlockFactory blockFactory) { return blockFactory.newConstantLongBlockWith(value, positions); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java index b9506153aac5a..8dca74109b2cc 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/Block.java @@ -140,12 +140,19 @@ default boolean mvSortedAscending() { /** * {@return a constant null block with the given number of positions, using the non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstantNullBlock} */ // Eventually, this should use the GLOBAL breaking instance + @Deprecated static Block constantNullBlock(int positions) { return constantNullBlock(positions, BlockFactory.getNonBreakingInstance()); } + /** + * {@return a constant null block with the given number of positions}. + * @deprecated use {@link BlockFactory#newConstantNullBlock} + */ + @Deprecated static Block constantNullBlock(int positions, BlockFactory blockFactory) { return blockFactory.newConstantNullBlock(positions); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st index 7f03e22332ccf..ae8a1f162ea25 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st @@ -203,22 +203,40 @@ $endif$ return result; } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#new$Type$BlockBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newBlockBuilder(int estimatedSize) { return newBlockBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a builder. + * @deprecated use {@link BlockFactory#new$Type$BlockBuilder} + */ + @Deprecated static Builder newBlockBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.new$Type$BlockBuilder(estimatedSize); } - /** Returns a block using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a constant block built by the {@link BlockFactory#getNonBreakingInstance non-breaking block factory}. + * @deprecated use {@link BlockFactory#newConstant$Type$BlockWith} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static $Type$Block newConstantBlockWith($type$ value, int positions) { return newConstantBlockWith(value, positions, BlockFactory.getNonBreakingInstance()); } + /** + * Returns a constant block. + * @deprecated use {@link BlockFactory#newConstant$Type$BlockWith} + */ + @Deprecated static $Type$Block newConstantBlockWith($type$ value, int positions, BlockFactory blockFactory) { return blockFactory.newConstant$Type$BlockWith(value, positions); } From 348383b5feea92130babe9439692a1056adc5384 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 27 Oct 2023 07:43:22 -0400 Subject: [PATCH 157/203] ESQL: Enable block tracking in extract tests (#101400) This enables memory tracking in the tests for `grok` and `dissect`. They are already tracking their memory. --- .../operator/ColumnExtractOperatorTests.java | 5 ++ .../operator/StringExtractOperatorTests.java | 61 +++++++++++-------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java index 7680fd410a709..78d0485593197 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java @@ -91,4 +91,9 @@ protected void assertSimpleOutput(List input, List results) { protected ByteSizeValue smallEnoughToCircuitBreak() { return ByteSizeValue.ofBytes(between(1, 32)); } + + @Override + protected DriverContext driverContext() { + return breakingDriverContext(); + } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java index 55470d1dcae12..bc77f83cd9111 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java @@ -99,32 +99,41 @@ public Block.Ref eval(Page page) { public void close() {} }, new FirstWord("test"), driverContext()); - BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(1); - builder.beginPositionEntry(); - builder.appendBytesRef(new BytesRef("foo1 bar1")); - builder.appendBytesRef(new BytesRef("foo2 bar2")); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendBytesRef(new BytesRef("foo3 bar3")); - builder.appendBytesRef(new BytesRef("foo4 bar4")); - builder.appendBytesRef(new BytesRef("foo5 bar5")); - builder.endPositionEntry(); - Page page = new Page(builder.build()); - - Page result = operator.process(page); - Block resultBlock = result.getBlock(1); - assertThat(resultBlock.getPositionCount(), equalTo(2)); - assertThat(resultBlock.getValueCount(0), equalTo(2)); - assertThat(resultBlock.getValueCount(1), equalTo(3)); - BytesRefBlock brb = (BytesRefBlock) resultBlock; - BytesRef spare = new BytesRef(""); - int idx = brb.getFirstValueIndex(0); - assertThat(brb.getBytesRef(idx, spare).utf8ToString(), equalTo("foo1")); - assertThat(brb.getBytesRef(idx + 1, spare).utf8ToString(), equalTo("foo2")); - idx = brb.getFirstValueIndex(1); - assertThat(brb.getBytesRef(idx, spare).utf8ToString(), equalTo("foo3")); - assertThat(brb.getBytesRef(idx + 1, spare).utf8ToString(), equalTo("foo4")); - assertThat(brb.getBytesRef(idx + 2, spare).utf8ToString(), equalTo("foo5")); + Page result = null; + try (BytesRefBlock.Builder builder = BytesRefBlock.newBlockBuilder(1)) { + builder.beginPositionEntry(); + builder.appendBytesRef(new BytesRef("foo1 bar1")); + builder.appendBytesRef(new BytesRef("foo2 bar2")); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendBytesRef(new BytesRef("foo3 bar3")); + builder.appendBytesRef(new BytesRef("foo4 bar4")); + builder.appendBytesRef(new BytesRef("foo5 bar5")); + builder.endPositionEntry(); + result = operator.process(new Page(builder.build())); + } + try { + Block resultBlock = result.getBlock(1); + assertThat(resultBlock.getPositionCount(), equalTo(2)); + assertThat(resultBlock.getValueCount(0), equalTo(2)); + assertThat(resultBlock.getValueCount(1), equalTo(3)); + BytesRefBlock brb = (BytesRefBlock) resultBlock; + BytesRef spare = new BytesRef(""); + int idx = brb.getFirstValueIndex(0); + assertThat(brb.getBytesRef(idx, spare).utf8ToString(), equalTo("foo1")); + assertThat(brb.getBytesRef(idx + 1, spare).utf8ToString(), equalTo("foo2")); + idx = brb.getFirstValueIndex(1); + assertThat(brb.getBytesRef(idx, spare).utf8ToString(), equalTo("foo3")); + assertThat(brb.getBytesRef(idx + 1, spare).utf8ToString(), equalTo("foo4")); + assertThat(brb.getBytesRef(idx + 2, spare).utf8ToString(), equalTo("foo5")); + } finally { + result.releaseBlocks(); + } + } + + @Override + protected DriverContext driverContext() { + return breakingDriverContext(); } @Override From 5e22c27c897908e1cf8d4a6db91c7c73fcecccd6 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 27 Oct 2023 07:45:13 -0400 Subject: [PATCH 158/203] ESQL: Fix build Two PRs pass in the night. Now we can't build. This fixes it. --- .../compute/operator/ColumnExtractOperatorTests.java | 5 ----- .../compute/operator/StringExtractOperatorTests.java | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java index 78d0485593197..7680fd410a709 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java @@ -91,9 +91,4 @@ protected void assertSimpleOutput(List input, List results) { protected ByteSizeValue smallEnoughToCircuitBreak() { return ByteSizeValue.ofBytes(between(1, 32)); } - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java index bc77f83cd9111..c55fbeb29a25e 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/StringExtractOperatorTests.java @@ -130,14 +130,4 @@ public void close() {} result.releaseBlocks(); } } - - @Override - protected DriverContext driverContext() { - return breakingDriverContext(); - } - - @Override - protected DriverContext driverContext() { - return nonBreakingDriverContext(); - } } From dc763c046ba8e73bb264f453dd6600c8231b6007 Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Fri, 27 Oct 2023 14:07:31 +0200 Subject: [PATCH 159/203] ESQL: Fix eval of functions on foldable literals (#101438) --- docs/changelog/101438.yaml | 6 ++++++ .../src/main/resources/eval.csv-spec | 9 +++++++++ .../src/main/resources/mv_expand.csv-spec | 19 +++++++++++++++++++ .../xpack/esql/evaluator/EvalMapper.java | 4 +++- 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101438.yaml diff --git a/docs/changelog/101438.yaml b/docs/changelog/101438.yaml new file mode 100644 index 0000000000000..8189ee96b6576 --- /dev/null +++ b/docs/changelog/101438.yaml @@ -0,0 +1,6 @@ +pr: 101438 +summary: "ESQL: Fix eval of functions on foldable literals" +area: ES|QL +type: bug +issues: + - 101425 diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/eval.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/eval.csv-spec index a22a0e6529df7..b29c8024950f9 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/eval.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/eval.csv-spec @@ -206,3 +206,12 @@ row a = [1.2], b = [2.4, 7.9] | eval c = round(a), d = round(b), e = round([1.2] a:double | b:double | c:double | d: double | e:double | f:double | g:double | h:double 1.2 | [2.4, 7.9] | 1.0 | null | 1.0 | null | 1.1 | null ; + + +evalSplitFoldable +from employees | sort emp_no | eval foldable = "foo,bar" | eval folded_mv = split(foldable, ",") | keep emp_no, foldable, folded_mv | limit 2; + +emp_no:integer | foldable:keyword | folded_mv:keyword +10001 | "foo,bar" | [foo, bar] +10002 | "foo,bar" | [foo, bar] +; diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec index 345ce501e68f4..7553cea0e26d5 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mv_expand.csv-spec @@ -233,3 +233,22 @@ row a = "foobar", b = ["foo", "bar"], c = 12 | mv_expand b | where b LIKE "fo*"; a:keyword | b:keyword | c:integer foobar | foo | 12 ; + +expandEvalFoldable +from employees | sort emp_no | limit 2 | eval foldable = "foo,bar" | eval generate_mv = split(foldable,",") | mv_expand generate_mv | keep emp_no, first_name, generate_mv | sort emp_no asc, generate_mv desc; + +emp_no:integer | first_name:keyword | generate_mv:keyword +10001 | Georgi | foo +10001 | Georgi | bar +10002 | Bezalel | foo +10002 | Bezalel | bar +; + + +expandEvalFoldableWhere +from employees | sort emp_no | limit 2 | eval foldable = "foo,bar" | eval generate_mv = split(foldable,",") | mv_expand generate_mv | keep emp_no, first_name, generate_mv | where generate_mv LIKE "fo*"; + +emp_no:integer | first_name:keyword | generate_mv:keyword +10001 | Georgi | foo +10002 | Bezalel | foo +; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java index 845cccdff78bf..b9ef94d587556 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/evaluator/EvalMapper.java @@ -230,7 +230,9 @@ private static Block block(Literal lit, BlockFactory blockFactory, int positions return Block.constantNullBlock(positions, blockFactory); } var wrapper = BlockUtils.wrapperFor(blockFactory, ElementType.fromJava(multiValue.get(0).getClass()), positions); - wrapper.accept(multiValue); + for (int i = 0; i < positions; i++) { + wrapper.accept(multiValue); + } return wrapper.builder().build(); } return BlockUtils.constantBlock(blockFactory, value, positions); From fd0c7f57a8b5b94bc9850a9fe4bbc5900fb1d8fe Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Fri, 27 Oct 2023 08:27:55 -0400 Subject: [PATCH 160/203] Remove outdated note from get search application docs (#101451) --- .../apis/get-search-application.asciidoc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/reference/search-application/apis/get-search-application.asciidoc b/docs/reference/search-application/apis/get-search-application.asciidoc index 53e6df0262db8..f0c107011eb40 100644 --- a/docs/reference/search-application/apis/get-search-application.asciidoc +++ b/docs/reference/search-application/apis/get-search-application.asciidoc @@ -120,10 +120,3 @@ A sample response: } ---- // TESTRESPONSE[s/"updated_at_millis": 1682105622204/"updated_at_millis": $body.$_path/] - -[NOTE] -==== -The indices associated with a search application are not returned with the GET response. -To view the indices, use the <> API. -The alias name will match the search application name, for example `my-app` in the example above. -==== From 928671666054ea7c99154bd710644b7dc197b80d Mon Sep 17 00:00:00 2001 From: David Roberts Date: Fri, 27 Oct 2023 13:52:03 +0100 Subject: [PATCH 161/203] [ML] Include ML processor limits in `_ml/info` response (#101392) The _ml/info response now includes two extra fields in its `limits`: 1. `max_single_ml_node_processors` 2. `total_ml_processors` These fields are _only_ included if they can be accurately calculated. If autoscaling is enabled and the ML nodes are not at their maximum size then these fields _cannot_ currently be accurately calculated. (This could potentially be improved in the future with additional settings set by the control plane.) --- docs/changelog/101392.yaml | 5 + .../ml/common/apis/get-ml-info.asciidoc | 6 +- .../ml/action/TransportMlInfoAction.java | 40 ++++ .../xpack/ml/utils/MlProcessors.java | 27 +++ .../ml/action/TransportMlInfoActionTests.java | 37 ++++ .../xpack/ml/utils/MlProcessorsTests.java | 189 ++++++++++++++++++ 6 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101392.yaml create mode 100644 x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportMlInfoActionTests.java diff --git a/docs/changelog/101392.yaml b/docs/changelog/101392.yaml new file mode 100644 index 0000000000000..af79917245726 --- /dev/null +++ b/docs/changelog/101392.yaml @@ -0,0 +1,5 @@ +pr: 101392 +summary: Include ML processor limits in `_ml/info` response +area: Machine Learning +type: enhancement +issues: [] diff --git a/docs/reference/ml/common/apis/get-ml-info.asciidoc b/docs/reference/ml/common/apis/get-ml-info.asciidoc index 48f74314560e6..104375bd641c8 100644 --- a/docs/reference/ml/common/apis/get-ml-info.asciidoc +++ b/docs/reference/ml/common/apis/get-ml-info.asciidoc @@ -120,7 +120,9 @@ This is a possible response: }, "limits" : { "effective_max_model_memory_limit": "28961mb", - "total_ml_memory": "86883mb" + "total_ml_memory": "86883mb", + "total_ml_processors": 16, + "max_single_ml_node_processors": 16 } } ---- @@ -129,3 +131,5 @@ This is a possible response: // TESTRESPONSE[s/"build_hash": "99a07c016d5a73"/"build_hash": "$body.native_code.build_hash"/] // TESTRESPONSE[s/"effective_max_model_memory_limit": "28961mb"/"effective_max_model_memory_limit": "$body.limits.effective_max_model_memory_limit"/] // TESTRESPONSE[s/"total_ml_memory": "86883mb"/"total_ml_memory": "$body.limits.total_ml_memory"/] +// TESTRESPONSE[s/"total_ml_processors": 16/"total_ml_processors": $body.limits.total_ml_processors/] +// TESTRESPONSE[s/"max_single_ml_node_processors": 16/"max_single_ml_node_processors": $body.limits.max_single_ml_node_processors/] diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlInfoAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlInfoAction.java index 6a67f942c0f19..cd7d4258855eb 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlInfoAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportMlInfoAction.java @@ -11,11 +11,13 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; +import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.unit.Processors; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.tasks.Task; import org.elasticsearch.transport.TransportService; @@ -26,14 +28,20 @@ import org.elasticsearch.xpack.core.ml.job.config.AnalysisLimits; import org.elasticsearch.xpack.core.ml.job.config.CategorizationAnalyzerConfig; import org.elasticsearch.xpack.core.ml.job.config.Job; +import org.elasticsearch.xpack.ml.MachineLearning; +import org.elasticsearch.xpack.ml.job.NodeLoadDetector; import org.elasticsearch.xpack.ml.process.MlControllerHolder; +import org.elasticsearch.xpack.ml.utils.MlProcessors; import org.elasticsearch.xpack.ml.utils.NativeMemoryCalculator; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.OptionalLong; import java.util.concurrent.TimeoutException; public class TransportMlInfoAction extends HandledTransportAction { @@ -137,6 +145,38 @@ private Map limits() { limits.put("effective_max_model_memory_limit", effectiveMaxModelMemoryLimit.getStringRep()); } limits.put("total_ml_memory", NativeMemoryCalculator.calculateTotalMlMemory(clusterSettings, nodes).getStringRep()); + + // Add processor information _if_ known with certainty. It won't be known with certainty if autoscaling is enabled. + // If we can scale up in terms of memory, assume we can also scale up in terms of processors. + List mlNodes = nodes.stream().filter(MachineLearning::isMlNode).toList(); + if (areMlNodesBiggestSize(clusterSettings.get(MachineLearning.MAX_ML_NODE_SIZE), mlNodes)) { + Processors singleNodeProcessors = MlProcessors.getMaxMlNodeProcessors( + nodes, + clusterSettings.get(MachineLearning.ALLOCATED_PROCESSORS_SCALE) + ); + if (singleNodeProcessors.count() > 0) { + limits.put("max_single_ml_node_processors", singleNodeProcessors.roundDown()); + } + Processors totalMlProcessors = MlProcessors.getTotalMlNodeProcessors( + nodes, + clusterSettings.get(MachineLearning.ALLOCATED_PROCESSORS_SCALE) + ); + if (totalMlProcessors.count() > 0) { + int potentialExtraProcessors = Math.max(0, clusterSettings.get(MachineLearning.MAX_LAZY_ML_NODES) - mlNodes.size()) + * singleNodeProcessors.roundDown(); + limits.put("total_ml_processors", totalMlProcessors.roundDown() + potentialExtraProcessors); + } + } return limits; } + + static boolean areMlNodesBiggestSize(ByteSizeValue maxMLNodeSize, Collection mlNodes) { + if (maxMLNodeSize.getBytes() == 0) { + return true; + } + + OptionalLong smallestMLNode = mlNodes.stream().map(NodeLoadDetector::getNodeSize).flatMapToLong(OptionalLong::stream).min(); + + return smallestMLNode.isPresent() && smallestMLNode.getAsLong() >= maxMLNodeSize.getBytes(); + } } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/MlProcessors.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/MlProcessors.java index 1c45c6da2bcc7..1769a7946ce80 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/MlProcessors.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/utils/MlProcessors.java @@ -8,6 +8,8 @@ package org.elasticsearch.xpack.ml.utils; import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.unit.Processors; import org.elasticsearch.xpack.ml.MachineLearning; @@ -44,4 +46,29 @@ public static Processors get(DiscoveryNode node, Integer allocatedProcessorScale return Processors.ZERO; } } + + public static Processors getMaxMlNodeProcessors(DiscoveryNodes nodes, Integer allocatedProcessorScale) { + Processors answer = Processors.ZERO; + for (DiscoveryNode node : nodes) { + if (node.getRoles().contains(DiscoveryNodeRole.ML_ROLE)) { + Processors nodeProcessors = get(node, allocatedProcessorScale); + if (answer.compareTo(nodeProcessors) < 0) { + answer = nodeProcessors; + } + } + } + return answer; + } + + public static Processors getTotalMlNodeProcessors(DiscoveryNodes nodes, Integer allocatedProcessorScale) { + int total = 0; + for (DiscoveryNode node : nodes) { + if (node.getRoles().contains(DiscoveryNodeRole.ML_ROLE)) { + Processors nodeProcessors = get(node, allocatedProcessorScale); + // Round down before summing, because ML only uses whole processors + total += nodeProcessors.roundDown(); + } + } + return Processors.of((double) total); + } } diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportMlInfoActionTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportMlInfoActionTests.java new file mode 100644 index 0000000000000..24f2c48d5cde0 --- /dev/null +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/action/TransportMlInfoActionTests.java @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ml.action; + +import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.node.DiscoveryNodeUtils; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.ml.MachineLearning; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static org.hamcrest.Matchers.is; + +public class TransportMlInfoActionTests extends ESTestCase { + + public void testAreMlNodesBiggestSize() { + boolean expectedResult = randomBoolean(); + long mlNodeSize = randomLongBetween(10000000L, 10000000000L); + long biggestSize = expectedResult ? mlNodeSize : mlNodeSize * randomLongBetween(2, 5); + int numMlNodes = randomIntBetween(2, 4); + var nodes = Stream.generate( + () -> DiscoveryNodeUtils.builder("node") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.MACHINE_MEMORY_NODE_ATTR, Long.toString(mlNodeSize))) + .build() + ).limit(numMlNodes).toList(); + assertThat(TransportMlInfoAction.areMlNodesBiggestSize(ByteSizeValue.ofBytes(biggestSize), nodes), is(expectedResult)); + } +} diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/MlProcessorsTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/MlProcessorsTests.java index 2ff3196dc87e9..b1b213e2c3f15 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/MlProcessorsTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/MlProcessorsTests.java @@ -7,11 +7,14 @@ package org.elasticsearch.xpack.ml.utils; +import org.elasticsearch.cluster.node.DiscoveryNodeRole; import org.elasticsearch.cluster.node.DiscoveryNodeUtils; +import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.ml.MachineLearning; import java.util.Map; +import java.util.Set; import static org.hamcrest.Matchers.equalTo; @@ -34,4 +37,190 @@ public void testGetWithNull() { var processor = MlProcessors.get(node, null); assertThat(processor.count(), equalTo(8.0)); } + + public void testGetMaxMlNodeProcessors() { + var nodes = DiscoveryNodes.builder() + .add( + DiscoveryNodeUtils.builder("n1") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "8.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n2") + .roles(Set.of(DiscoveryNodeRole.DATA_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "9.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n3") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "7.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n4") + .roles(Set.of(DiscoveryNodeRole.MASTER_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.0")) + .build() + ) + .build(); + var processor = MlProcessors.getMaxMlNodeProcessors(nodes, 1); + assertThat(processor.count(), equalTo(8.0)); + } + + public void testGetMaxMlNodeProcessorsWithScale() { + var nodes = DiscoveryNodes.builder() + .add( + DiscoveryNodeUtils.builder("n1") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "8.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n2") + .roles(Set.of(DiscoveryNodeRole.DATA_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "9.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n3") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "12.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n4") + .roles(Set.of(DiscoveryNodeRole.MASTER_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "10.0")) + .build() + ) + .build(); + var processor = MlProcessors.getMaxMlNodeProcessors(nodes, 2); + assertThat(processor.count(), equalTo(6.0)); + } + + public void testGetMaxMlNodeProcessorsWithNull() { + var nodes = DiscoveryNodes.builder() + .add( + DiscoveryNodeUtils.builder("n1") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n2") + .roles(Set.of(DiscoveryNodeRole.DATA_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "9.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n3") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "7.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n4") + .roles(Set.of(DiscoveryNodeRole.MASTER_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.0")) + .build() + ) + .build(); + var processor = MlProcessors.getMaxMlNodeProcessors(nodes, null); + assertThat(processor.count(), equalTo(7.0)); + } + + public void testGetTotalMlNodeProcessors() { + var nodes = DiscoveryNodes.builder() + .add( + DiscoveryNodeUtils.builder("n1") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "8.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n2") + .roles(Set.of(DiscoveryNodeRole.DATA_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "9.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n3") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "7.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n4") + .roles(Set.of(DiscoveryNodeRole.MASTER_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.0")) + .build() + ) + .build(); + var processor = MlProcessors.getTotalMlNodeProcessors(nodes, 1); + assertThat(processor.count(), equalTo(15.0)); + } + + public void testGetTotalMlNodeProcessorsWithScale() { + var nodes = DiscoveryNodes.builder() + .add( + DiscoveryNodeUtils.builder("n1") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "8.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n2") + .roles(Set.of(DiscoveryNodeRole.DATA_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "9.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n3") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "7.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n4") + .roles(Set.of(DiscoveryNodeRole.MASTER_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.0")) + .build() + ) + .build(); + var processor = MlProcessors.getTotalMlNodeProcessors(nodes, 2); + assertThat(processor.count(), equalTo(7.0)); + } + + public void testGetTotalMlNodeProcessorsWithNull() { + var nodes = DiscoveryNodes.builder() + .add( + DiscoveryNodeUtils.builder("n1") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.5")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n2") + .roles(Set.of(DiscoveryNodeRole.DATA_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "9.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n3") + .roles(Set.of(DiscoveryNodeRole.ML_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "7.0")) + .build() + ) + .add( + DiscoveryNodeUtils.builder("n4") + .roles(Set.of(DiscoveryNodeRole.MASTER_ROLE)) + .attributes(Map.of(MachineLearning.ALLOCATED_PROCESSORS_NODE_ATTR, "6.0")) + .build() + ) + .build(); + var processor = MlProcessors.getTotalMlNodeProcessors(nodes, null); + assertThat(processor.count(), equalTo(13.0)); + } } From f35cf032e86792fd0710f4033694e5ccb9a3820e Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Fri, 27 Oct 2023 08:02:41 -0600 Subject: [PATCH 162/203] Make the ShardsAvailabilityIndicator pluggable (#101394) * Make the ShardsAvailabilityIndicator pluggable This commit makes the `ShardsAvailabilityIndicator` pluggable. It currently provides the existing `ShardsAvailabilityHealthIndicatorService` (without moving it) for the `shards_availability` output in the health report API. This will allow us in the future to provide a separate `shards_availability` plugin to override the way that health is reported for shards. --- .../health-shards-availability/build.gradle | 25 +++++++++++ .../plugin/ShardsAvailabilityPlugin.java | 41 +++++++++++++++++++ qa/smoke-test-multinode/build.gradle | 1 + ...okeTestMultiNodeClientYamlTestSuiteIT.java | 1 + rest-api-spec/build.gradle | 1 + .../test/rest/ClientYamlTestSuiteIT.java | 1 + .../elasticsearch/node/NodeConstruction.java | 8 ++-- .../org/elasticsearch/plugins/Plugin.java | 6 +++ x-pack/plugin/core/build.gradle | 1 + ...ierShardAvailabilityHealthIndicatorIT.java | 3 +- .../build.gradle | 1 + ...CoreWithSecurityClientYamlTestSuiteIT.java | 1 + 12 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 modules/health-shards-availability/build.gradle create mode 100644 modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java diff --git a/modules/health-shards-availability/build.gradle b/modules/health-shards-availability/build.gradle new file mode 100644 index 0000000000000..6c7cf5a19c8ac --- /dev/null +++ b/modules/health-shards-availability/build.gradle @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +apply plugin: 'elasticsearch.internal-yaml-rest-test' +apply plugin: 'elasticsearch.yaml-rest-compat-test' +apply plugin: 'elasticsearch.internal-cluster-test' + +esplugin { + description 'Health report API extension providing the shards_availability output' + classname 'org.elasticsearch.health.plugin.ShardsAvailabilityPlugin' +} + +restResources { + restApi { + include '_common', 'indices', 'index', 'cluster', 'nodes', 'get', 'ingest' + } +} + +tasks.named("yamlRestTestV7CompatTransform").configure {task -> + task.addAllowedWarningRegex("setting \\[ecs\\] is deprecated as ECS format is the default and only option") +} diff --git a/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java b/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java new file mode 100644 index 0000000000000..3ec59ad8a9b49 --- /dev/null +++ b/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.health.plugin; + +import org.apache.lucene.util.SetOnce; +import org.elasticsearch.cluster.routing.allocation.ShardsAvailabilityHealthIndicatorService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.health.HealthIndicatorService; +import org.elasticsearch.plugin.Inject; +import org.elasticsearch.plugins.HealthPlugin; +import org.elasticsearch.plugins.Plugin; + +import java.util.Collection; +import java.util.Set; + +public class ShardsAvailabilityPlugin extends Plugin implements HealthPlugin { + + private final SetOnce shardHealthService = new SetOnce<>(); + + @Inject + public ShardsAvailabilityPlugin(Settings settings) {} + + @Override + public Collection createComponents(PluginServices services) { + this.shardHealthService.set( + new ShardsAvailabilityHealthIndicatorService(services.clusterService(), services.allocationService(), services.systemIndices()) + ); + return Set.of(this.shardHealthService.get()); + } + + @Override + public Collection getHealthIndicatorServices() { + return Set.of(this.shardHealthService.get()); + } +} diff --git a/qa/smoke-test-multinode/build.gradle b/qa/smoke-test-multinode/build.gradle index 7e352c0f8adb2..f5beef38319e5 100644 --- a/qa/smoke-test-multinode/build.gradle +++ b/qa/smoke-test-multinode/build.gradle @@ -18,6 +18,7 @@ dependencies { clusterModules project(":modules:ingest-common") clusterModules project(":modules:reindex") clusterModules project(":modules:analysis-common") + clusterModules project(":modules:health-shards-availability") } tasks.named("yamlRestTest").configure { diff --git a/qa/smoke-test-multinode/src/yamlRestTest/java/org/elasticsearch/smoketest/SmokeTestMultiNodeClientYamlTestSuiteIT.java b/qa/smoke-test-multinode/src/yamlRestTest/java/org/elasticsearch/smoketest/SmokeTestMultiNodeClientYamlTestSuiteIT.java index ff3af31e4afd6..9afb533b037b4 100644 --- a/qa/smoke-test-multinode/src/yamlRestTest/java/org/elasticsearch/smoketest/SmokeTestMultiNodeClientYamlTestSuiteIT.java +++ b/qa/smoke-test-multinode/src/yamlRestTest/java/org/elasticsearch/smoketest/SmokeTestMultiNodeClientYamlTestSuiteIT.java @@ -29,6 +29,7 @@ public class SmokeTestMultiNodeClientYamlTestSuiteIT extends ESClientYamlSuiteTe .module("ingest-common") .module("reindex") .module("analysis-common") + .module("health-shards-availability") // The first node does not have the ingest role so we're sure ingest requests are forwarded: .node(0, n -> n.setting("node.roles", "[master,data,ml,remote_cluster_client,transform]")) .feature(FeatureFlag.TIME_SERIES_MODE) diff --git a/rest-api-spec/build.gradle b/rest-api-spec/build.gradle index cf40c786db17b..e484b98d3188e 100644 --- a/rest-api-spec/build.gradle +++ b/rest-api-spec/build.gradle @@ -37,6 +37,7 @@ dependencies { clusterModules project(":modules:rest-root") clusterModules project(":modules:reindex") clusterModules project(':modules:analysis-common') + clusterModules project(':modules:health-shards-availability') } tasks.named("yamlRestTestV7CompatTransform").configure { task -> diff --git a/rest-api-spec/src/yamlRestTest/java/org/elasticsearch/test/rest/ClientYamlTestSuiteIT.java b/rest-api-spec/src/yamlRestTest/java/org/elasticsearch/test/rest/ClientYamlTestSuiteIT.java index 36110174b9e83..465f17eca5532 100644 --- a/rest-api-spec/src/yamlRestTest/java/org/elasticsearch/test/rest/ClientYamlTestSuiteIT.java +++ b/rest-api-spec/src/yamlRestTest/java/org/elasticsearch/test/rest/ClientYamlTestSuiteIT.java @@ -31,6 +31,7 @@ public class ClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { .module("rest-root") .module("reindex") .module("analysis-common") + .module("health-shards-availability") .feature(FeatureFlag.TIME_SERIES_MODE) .build(); diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index adcb9d29861c0..1e547a405c651 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -55,7 +55,6 @@ import org.elasticsearch.cluster.routing.RerouteService; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.routing.allocation.DiskThresholdMonitor; -import org.elasticsearch.cluster.routing.allocation.ShardsAvailabilityHealthIndicatorService; import org.elasticsearch.cluster.routing.allocation.WriteLoadForecaster; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.TransportVersionsFixupListener; @@ -755,7 +754,8 @@ record PluginServiceInstances( Supplier repositoriesServiceSupplier, TelemetryProvider telemetryProvider, AllocationService allocationService, - IndicesService indicesService + IndicesService indicesService, + SystemIndices systemIndices ) implements Plugin.PluginServices {} PluginServiceInstances pluginServices = new PluginServiceInstances( client, @@ -771,7 +771,8 @@ record PluginServiceInstances( repositoriesServiceReference::get, telemetryProvider, clusterModule.getAllocationService(), - indicesService + indicesService, + systemIndices ); Collection pluginComponents = pluginsService.flatMap(p -> p.createComponents(pluginServices)).toList(); @@ -1268,7 +1269,6 @@ private HealthService createHealthService( var serverHealthIndicatorServices = Stream.of( new StableMasterHealthIndicatorService(coordinationDiagnosticsService, clusterService), new RepositoryIntegrityHealthIndicatorService(clusterService), - new ShardsAvailabilityHealthIndicatorService(clusterService, clusterModule.getAllocationService(), systemIndices), new DiskHealthIndicatorService(clusterService), new ShardsCapacityHealthIndicatorService(clusterService) ); diff --git a/server/src/main/java/org/elasticsearch/plugins/Plugin.java b/server/src/main/java/org/elasticsearch/plugins/Plugin.java index b0e1946a4e5dd..5530f8bf9f307 100644 --- a/server/src/main/java/org/elasticsearch/plugins/Plugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/Plugin.java @@ -24,6 +24,7 @@ import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettingProvider; import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.script.ScriptService; import org.elasticsearch.telemetry.TelemetryProvider; @@ -137,6 +138,11 @@ public interface PluginServices { * A service to manage indices in the cluster */ IndicesService indicesService(); + + /** + * The system indices for the cluster + */ + SystemIndices systemIndices(); } /** diff --git a/x-pack/plugin/core/build.gradle b/x-pack/plugin/core/build.gradle index 6f9c6b93ac373..f023837247345 100644 --- a/x-pack/plugin/core/build.gradle +++ b/x-pack/plugin/core/build.gradle @@ -58,6 +58,7 @@ dependencies { testImplementation project(path: ':modules:lang-mustache') testImplementation project(path: ':modules:analysis-common') testImplementation project(path: ':modules:rest-root') + testImplementation project(path: ':modules:health-shards-availability') testImplementation project(":client:rest-high-level") // Needed for Fips140ProviderVerificationTests testCompileOnly('org.bouncycastle:bc-fips:1.0.2') diff --git a/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/xpack/cluster/routing/allocation/DataTierShardAvailabilityHealthIndicatorIT.java b/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/xpack/cluster/routing/allocation/DataTierShardAvailabilityHealthIndicatorIT.java index 00bcd66120352..7737a5b42dfae 100644 --- a/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/xpack/cluster/routing/allocation/DataTierShardAvailabilityHealthIndicatorIT.java +++ b/x-pack/plugin/core/src/internalClusterTest/java/org/elasticsearch/xpack/cluster/routing/allocation/DataTierShardAvailabilityHealthIndicatorIT.java @@ -25,6 +25,7 @@ import org.elasticsearch.health.GetHealthAction; import org.elasticsearch.health.HealthIndicatorResult; import org.elasticsearch.health.HealthStatus; +import org.elasticsearch.health.plugin.ShardsAvailabilityPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; @@ -49,7 +50,7 @@ public class DataTierShardAvailabilityHealthIndicatorIT extends ESIntegTestCase @Override protected Collection> nodePlugins() { - return List.of(LocalStateCompositeXPackPlugin.class); + return List.of(LocalStateCompositeXPackPlugin.class, ShardsAvailabilityPlugin.class); } /** diff --git a/x-pack/qa/core-rest-tests-with-security/build.gradle b/x-pack/qa/core-rest-tests-with-security/build.gradle index 778af00403ad0..dda8d6a249bc4 100644 --- a/x-pack/qa/core-rest-tests-with-security/build.gradle +++ b/x-pack/qa/core-rest-tests-with-security/build.gradle @@ -8,6 +8,7 @@ dependencies { clusterModules project(':modules:ingest-common') clusterModules project(':modules:reindex') clusterModules project(':modules:analysis-common') + clusterModules project(':modules:health-shards-availability') clusterModules project(xpackModule('stack')) clusterModules project(xpackModule('ilm')) clusterModules project(xpackModule('mapper-constant-keyword')) diff --git a/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java b/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java index 10d5dc23eed09..a0f5ba84fd355 100644 --- a/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java +++ b/x-pack/qa/core-rest-tests-with-security/src/yamlRestTest/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java @@ -39,6 +39,7 @@ public class CoreWithSecurityClientYamlTestSuiteIT extends ESClientYamlSuiteTest .module("reindex") .module("wildcard") .module("analysis-common") + .module("health-shards-availability") .setting("xpack.security.enabled", "true") .setting("xpack.watcher.enabled", "false") .setting("xpack.ml.enabled", "false") From a16b05662e8eddf913e30c1e558c6cc9a0363be7 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Fri, 27 Oct 2023 10:03:32 -0400 Subject: [PATCH 163/203] ESQL: Fix test (#101448) We have to release a block. Close #101413 --- .../elasticsearch/compute/OperatorTests.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java index bde2ae0a747e4..b45f597553e1b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/OperatorTests.java @@ -141,7 +141,6 @@ public void testQueryOperator() throws IOException { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101413") public void testGroupingWithOrdinals() throws Exception { DriverContext driverContext = driverContext(); BlockFactory blockFactory = driverContext.blockFactory(); @@ -173,19 +172,23 @@ protected Page process(Page page) { int positionCount = docVector.getPositionCount(); IntVector shards = docVector.shards(); if (randomBoolean()) { - IntVector.Builder builder = IntVector.newVectorBuilder(positionCount); - for (int i = 0; i < positionCount; i++) { - builder.appendInt(shards.getInt(i)); + try (IntVector.Builder builder = IntVector.newVectorBuilder(positionCount)) { + for (int i = 0; i < positionCount; i++) { + builder.appendInt(shards.getInt(i)); + } + shards.close(); + shards = builder.build(); } - shards = builder.build(); } IntVector segments = docVector.segments(); if (randomBoolean()) { - IntVector.Builder builder = IntVector.newVectorBuilder(positionCount); - for (int i = 0; i < positionCount; i++) { - builder.appendInt(segments.getInt(i)); + try (IntVector.Builder builder = IntVector.newVectorBuilder(positionCount)) { + for (int i = 0; i < positionCount; i++) { + builder.appendInt(segments.getInt(i)); + } + segments.close(); + segments = builder.build(); } - segments = builder.build(); } IntVector docs = docVector.docs(); if (randomBoolean()) { @@ -194,6 +197,7 @@ protected Page process(Page page) { ids.add(docs.getInt(i)); } Collections.shuffle(ids, random()); + docs.close(); docs = blockFactory.newIntArrayVector(ids.stream().mapToInt(n -> n).toArray(), positionCount); } Block[] blocks = new Block[page.getBlockCount()]; From 4bda17506addaaa0acaafc276a6aa0eae403bf52 Mon Sep 17 00:00:00 2001 From: Michael Peterson Date: Fri, 27 Oct 2023 10:24:18 -0400 Subject: [PATCH 164/203] Log field-caps Exceptions at WARN level even when partial results are returned (#101083) Used the existing dedup logic for logging exceptions We log exceptions in the merge method so logging only occurs on the primary coordinator --- .../org/elasticsearch/ExceptionsHelper.java | 19 ++++++++++++++++ .../TransportFieldCapabilitiesAction.java | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/ExceptionsHelper.java b/server/src/main/java/org/elasticsearch/ExceptionsHelper.java index 7e22e1797b527..6c0836c277444 100644 --- a/server/src/main/java/org/elasticsearch/ExceptionsHelper.java +++ b/server/src/main/java/org/elasticsearch/ExceptionsHelper.java @@ -278,6 +278,25 @@ public static ShardOperationFailedException[] groupBy(ShardOperationFailedExcept return uniqueFailures.toArray(new ShardOperationFailedException[0]); } + /** + * Utility method useful for determine whether to log an Exception or perhaps + * avoid logging a stacktrace if the caller/logger is not interested in these + * types of node/shard issues. + * + * @param t Throwable to inspect + * @return true if the Throwable is an instance of an Exception that indicates + * that either a Node or shard is unavailable/disconnected. + */ + public static boolean isNodeOrShardUnavailableTypeException(Throwable t) { + return (t instanceof org.elasticsearch.action.NoShardAvailableActionException + || t instanceof org.elasticsearch.action.UnavailableShardsException + || t instanceof org.elasticsearch.node.NodeClosedException + || t instanceof org.elasticsearch.transport.NodeDisconnectedException + || t instanceof org.elasticsearch.discovery.MasterNotDiscoveredException + || t instanceof org.elasticsearch.transport.NodeNotConnectedException + || t instanceof org.elasticsearch.cluster.block.ClusterBlockException); + } + private static class GroupBy { final String reason; final String index; diff --git a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index c9a44c14106ee..d9837f94b0996 100644 --- a/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -325,9 +325,31 @@ private static FieldCapabilitiesResponse merge( } else { collectResponseMap(responseMapBuilder, responseMap); } + + // The merge method is only called on the primary coordinator for cross-cluster field caps, so we + // log relevant "5xx" errors that occurred in this 2xx response to ensure they are only logged once. + // These failures have already been deduplicated, before this method was called. + for (FieldCapabilitiesFailure failure : failures) { + if (shouldLogException(failure.getException())) { + LOGGER.warn( + "Field caps partial-results Exception for indices " + Arrays.toString(failure.getIndices()), + failure.getException() + ); + } + } return new FieldCapabilitiesResponse(indices, Collections.unmodifiableMap(responseMap), failures); } + private static boolean shouldLogException(Exception e) { + // ConnectTransportExceptions are thrown when a cluster marked with skip_unavailable=false are not available for searching + // (Clusters marked with skip_unavailable=false return a different error that is considered a 4xx error.) + // In such a case, the field-caps endpoint returns a 200 (unless all clusters failed). + // To keep the logs from being too noisy, we choose not to log the ConnectTransportException here. + return e instanceof org.elasticsearch.transport.ConnectTransportException == false + && ExceptionsHelper.status(e).getStatus() >= 500 + && ExceptionsHelper.isNodeOrShardUnavailableTypeException(e) == false; + } + private static void collectResponseMapIncludingUnmapped( String[] indices, Map> responseMapBuilder, From d0ed335f5a5ef234ae69f5b1143b02bab118521f Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 27 Oct 2023 07:30:03 -0700 Subject: [PATCH 165/203] Implement all block types for ConstantNullBlock (#101414) "class org.elasticsearch.compute.data.ConstantNullBlock cannot be cast to class org.elasticsearch.compute.data.BytesRefBlock." We have encountered this error several times. This error happens when all elements of a BlockBuilder are null, resulting in the creation of a ConstantNullBlock. Subsequently, consumers must check areAllValuesNull() before casting it to the expected block type. I think that this pattern is prone to errors, as it requires careful handling and can easily lead to mistakes. This pull request implements all block types to the ConstantNullBlock, making it castable to any block type. This change should simplify the handling of such scenarios and reduce the likelihood of errors. --- .../compute/data/BooleanBlock.java | 2 +- .../compute/data/BooleanVector.java | 3 +- .../compute/data/BytesRefBlock.java | 2 +- .../compute/data/BytesRefVector.java | 2 +- .../compute/data/DoubleBlock.java | 2 +- .../compute/data/DoubleVector.java | 3 +- .../elasticsearch/compute/data/IntBlock.java | 2 +- .../elasticsearch/compute/data/IntVector.java | 2 +- .../elasticsearch/compute/data/LongBlock.java | 2 +- .../compute/data/LongVector.java | 2 +- .../compute/data/ConstantNullBlock.java | 39 ++++++++- .../compute/data/ConstantNullVector.java | 86 +++++++++++++++++++ .../compute/data/X-Block.java.st | 2 +- .../compute/data/X-Vector.java.st | 10 ++- .../compute/data/BasicBlockTests.java | 8 ++ .../operator/ColumnExtractOperatorTests.java | 23 ++++- 16 files changed, 170 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullVector.java diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java index 81e2031b9af34..352ee783d8614 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanBlock.java @@ -18,7 +18,7 @@ * Block that stores boolean values. * This class is generated. Do not edit it. */ -public sealed interface BooleanBlock extends Block permits BooleanArrayBlock, BooleanVectorBlock { +public sealed interface BooleanBlock extends Block permits BooleanArrayBlock, BooleanVectorBlock, ConstantNullBlock { /** * Retrieves the boolean value stored at the given value index. diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java index 9a4cc64d760ef..001dcf9aa6f8b 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java @@ -16,7 +16,8 @@ * Vector that stores boolean values. * This class is generated. Do not edit it. */ -public sealed interface BooleanVector extends Vector permits ConstantBooleanVector, BooleanArrayVector, BooleanBigArrayVector { +public sealed interface BooleanVector extends Vector permits ConstantBooleanVector, BooleanArrayVector, BooleanBigArrayVector, + ConstantNullVector { boolean getBoolean(int position); @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java index c456486fbed6b..50611f3e15130 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefBlock.java @@ -19,7 +19,7 @@ * Block that stores BytesRef values. * This class is generated. Do not edit it. */ -public sealed interface BytesRefBlock extends Block permits BytesRefArrayBlock, BytesRefVectorBlock { +public sealed interface BytesRefBlock extends Block permits BytesRefArrayBlock, BytesRefVectorBlock, ConstantNullBlock { BytesRef NULL_VALUE = new BytesRef(); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java index 82a18f5d5b79e..9271b5cc17079 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java @@ -17,7 +17,7 @@ * Vector that stores BytesRef values. * This class is generated. Do not edit it. */ -public sealed interface BytesRefVector extends Vector permits ConstantBytesRefVector, BytesRefArrayVector { +public sealed interface BytesRefVector extends Vector permits ConstantBytesRefVector, BytesRefArrayVector, ConstantNullVector { BytesRef getBytesRef(int position, BytesRef dest); @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java index 3ee4bf64a8d54..31d0000d28515 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleBlock.java @@ -18,7 +18,7 @@ * Block that stores double values. * This class is generated. Do not edit it. */ -public sealed interface DoubleBlock extends Block permits DoubleArrayBlock, DoubleVectorBlock { +public sealed interface DoubleBlock extends Block permits DoubleArrayBlock, DoubleVectorBlock, ConstantNullBlock { /** * Retrieves the double value stored at the given value index. diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java index 545d17004333a..396cd89583810 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java @@ -16,7 +16,8 @@ * Vector that stores double values. * This class is generated. Do not edit it. */ -public sealed interface DoubleVector extends Vector permits ConstantDoubleVector, DoubleArrayVector, DoubleBigArrayVector { +public sealed interface DoubleVector extends Vector permits ConstantDoubleVector, DoubleArrayVector, DoubleBigArrayVector, + ConstantNullVector { double getDouble(int position); @Override diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java index 248e68a9961fe..3909d2b6761be 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntBlock.java @@ -18,7 +18,7 @@ * Block that stores int values. * This class is generated. Do not edit it. */ -public sealed interface IntBlock extends Block permits IntArrayBlock, IntVectorBlock { +public sealed interface IntBlock extends Block permits IntArrayBlock, IntVectorBlock, ConstantNullBlock { /** * Retrieves the int value stored at the given value index. diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java index 6c3b46c3228e6..bc88f85855b4a 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java @@ -16,7 +16,7 @@ * Vector that stores int values. * This class is generated. Do not edit it. */ -public sealed interface IntVector extends Vector permits ConstantIntVector, IntArrayVector, IntBigArrayVector { +public sealed interface IntVector extends Vector permits ConstantIntVector, IntArrayVector, IntBigArrayVector, ConstantNullVector { int getInt(int position); diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java index 2edd840742b91..41ac8f7237f64 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongBlock.java @@ -18,7 +18,7 @@ * Block that stores long values. * This class is generated. Do not edit it. */ -public sealed interface LongBlock extends Block permits LongArrayBlock, LongVectorBlock { +public sealed interface LongBlock extends Block permits LongArrayBlock, LongVectorBlock, ConstantNullBlock { /** * Retrieves the long value stored at the given value index. diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java index 44e81139adccf..3c7d83ab72e66 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java @@ -16,7 +16,7 @@ * Vector that stores long values. * This class is generated. Do not edit it. */ -public sealed interface LongVector extends Vector permits ConstantLongVector, LongArrayVector, LongBigArrayVector { +public sealed interface LongVector extends Vector permits ConstantLongVector, LongArrayVector, LongBigArrayVector, ConstantNullVector { long getLong(int position); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java index 00b93d08a36ff..9437bdd35e21f 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullBlock.java @@ -7,6 +7,7 @@ package org.elasticsearch.compute.data; +import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; @@ -18,7 +19,7 @@ /** * Block implementation representing a constant null value. */ -public final class ConstantNullBlock extends AbstractBlock { +public final class ConstantNullBlock extends AbstractBlock implements BooleanBlock, IntBlock, LongBlock, DoubleBlock, BytesRefBlock { private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ConstantNullBlock.class); @@ -32,7 +33,7 @@ public final class ConstantNullBlock extends AbstractBlock { } @Override - public Vector asVector() { + public ConstantNullVector asVector() { return null; } @@ -67,8 +68,8 @@ public ElementType elementType() { } @Override - public Block filter(int... positions) { - return blockFactory.newConstantNullBlock(positions.length); + public ConstantNullBlock filter(int... positions) { + return (ConstantNullBlock) blockFactory.newConstantNullBlock(positions.length); } public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( @@ -204,4 +205,34 @@ public void close() { closed = true; } } + + @Override + public boolean getBoolean(int valueIndex) { + assert false : "null block"; + throw new UnsupportedOperationException("null block"); + } + + @Override + public BytesRef getBytesRef(int valueIndex, BytesRef dest) { + assert false : "null block"; + throw new UnsupportedOperationException("null block"); + } + + @Override + public double getDouble(int valueIndex) { + assert false : "null block"; + throw new UnsupportedOperationException("null block"); + } + + @Override + public int getInt(int valueIndex) { + assert false : "null block"; + throw new UnsupportedOperationException("null block"); + } + + @Override + public long getLong(int valueIndex) { + assert false : "null block"; + throw new UnsupportedOperationException("null block"); + } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullVector.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullVector.java new file mode 100644 index 0000000000000..ebe1aeda24412 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/ConstantNullVector.java @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.data; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * This vector is never instantiated. This class serves as a type holder for {@link ConstantNullBlock#asVector()}. + */ +public final class ConstantNullVector extends AbstractVector implements BooleanVector, IntVector, LongVector, DoubleVector, BytesRefVector { + + private ConstantNullVector(int positionCount, BlockFactory blockFactory) { + super(positionCount, blockFactory); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public ConstantNullBlock asBlock() { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public ConstantNullVector filter(int... positions) { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public boolean getBoolean(int position) { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public BytesRef getBytesRef(int position, BytesRef dest) { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public double getDouble(int position) { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public int getInt(int position) { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public long getLong(int position) { + assert false : "null vector"; + throw new UnsupportedOperationException("null vector"); + } + + @Override + public ElementType elementType() { + return ElementType.NULL; + } + + @Override + public boolean isConstant() { + return true; + } + + @Override + public long ramBytesUsed() { + return 0; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st index ae8a1f162ea25..2ff537016459c 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Block.java.st @@ -22,7 +22,7 @@ import java.io.IOException; * Block that stores $type$ values. * This class is generated. Do not edit it. */ -public sealed interface $Type$Block extends Block permits $Type$ArrayBlock, $Type$VectorBlock { +public sealed interface $Type$Block extends Block permits $Type$ArrayBlock, $Type$VectorBlock, ConstantNullBlock { $if(BytesRef)$ BytesRef NULL_VALUE = new BytesRef(); diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st index 90fd30f8b7e64..ac9f1638eacac 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st @@ -20,13 +20,15 @@ import java.io.IOException; * This class is generated. Do not edit it. */ $if(BytesRef)$ -public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector { +public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, ConstantNullVector { $elseif(boolean)$ -public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, $Type$BigArrayVector { +public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, $Type$BigArrayVector, + ConstantNullVector { $elseif(double)$ -public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, $Type$BigArrayVector { +public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, $Type$BigArrayVector, + ConstantNullVector { $else$ -public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, $Type$BigArrayVector { +public sealed interface $Type$Vector extends Vector permits Constant$Type$Vector, $Type$ArrayVector, $Type$BigArrayVector, ConstantNullVector { $endif$ $if(BytesRef)$ diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java index ef8d33a0148b3..0a36617f35b18 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/BasicBlockTests.java @@ -31,6 +31,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -577,6 +578,13 @@ public void testConstantNullBlock() { assertThat(breaker.getUsed(), is(0L)); int positionCount = randomIntBetween(1, 16 * 1024); Block block = Block.constantNullBlock(positionCount, blockFactory); + assertTrue(block.areAllValuesNull()); + assertThat(block, instanceOf(BooleanBlock.class)); + assertThat(block, instanceOf(IntBlock.class)); + assertThat(block, instanceOf(LongBlock.class)); + assertThat(block, instanceOf(DoubleBlock.class)); + assertThat(block, instanceOf(BytesRefBlock.class)); + assertNull(block.asVector()); if (randomBoolean()) { Block orig = block; block = (new ConstantNullBlock.Builder(blockFactory)).copyFrom(block, 0, block.getPositionCount()).build(); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java index 7680fd410a709..6906e5f3adda8 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/ColumnExtractOperatorTests.java @@ -22,6 +22,8 @@ import java.util.stream.Collectors; import java.util.stream.LongStream; +import static org.hamcrest.Matchers.equalTo; + public class ColumnExtractOperatorTests extends OperatorTestCase { @Override @@ -53,7 +55,13 @@ protected Operator.OperatorFactory simple(BigArrays bigArrays) { dvrCtx -> new EvalOperator.ExpressionEvaluator() { @Override public Block.Ref eval(Page page) { - return new Block.Ref(page.getBlock(0), page); + BytesRefBlock input = page.getBlock(0); + for (int i = 0; i < input.getPositionCount(); i++) { + if (input.getBytesRef(i, new BytesRef()).utf8ToString().startsWith("no_")) { + return Block.Ref.floating(Block.constantNullBlock(input.getPositionCount(), input.blockFactory())); + } + } + return new Block.Ref(input, page); } @Override @@ -91,4 +99,17 @@ protected void assertSimpleOutput(List input, List results) { protected ByteSizeValue smallEnoughToCircuitBreak() { return ByteSizeValue.ofBytes(between(1, 32)); } + + public void testAllNullValues() { + DriverContext driverContext = driverContext(); + BytesRef scratch = new BytesRef(); + Block input1 = BytesRefBlock.newBlockBuilder(1, driverContext.blockFactory()).appendBytesRef(new BytesRef("can_match")).build(); + Block input2 = BytesRefBlock.newBlockBuilder(1, driverContext.blockFactory()).appendBytesRef(new BytesRef("no_match")).build(); + List inputPages = List.of(new Page(input1), new Page(input2)); + List outputPages = drive(simple(driverContext.bigArrays()).get(driverContext), inputPages.iterator(), driverContext); + BytesRefBlock output1 = outputPages.get(0).getBlock(1); + BytesRefBlock output2 = outputPages.get(1).getBlock(1); + assertThat(output1.getBytesRef(0, scratch), equalTo(new BytesRef("can_match"))); + assertTrue(output2.areAllValuesNull()); + } } From 5e6bf3ceb1c6b1f9df7e169a57fb648d655aa320 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 27 Oct 2023 07:31:11 -0700 Subject: [PATCH 166/203] Add pragma for enrich workers (#101009) This pull request introduces a query pragma for configuring the maximum number of workers for enrich lookup. Increasing the number of workers can reduce latency but will also increase the load on the cluster. Similar to other pragmas, this feature is only available in snapshot builds and will be used to evaluate the performance of ESQL enrich. --- .../xpack/esql/EsqlSecurityIT.java | 3 +++ .../xpack/esql/qa/single_node/RestEsqlIT.java | 9 ++++++++- .../planner/EsPhysicalOperationProviders.java | 8 ++++---- .../esql/planner/LocalExecutionPlanner.java | 20 ++++++++----------- .../xpack/esql/plugin/QueryPragmas.java | 10 ++++++++++ 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java b/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java index 0cd9570927635..10c77a05af49b 100644 --- a/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java +++ b/x-pack/plugin/esql/qa/security/src/javaRestTest/java/org/elasticsearch/xpack/esql/EsqlSecurityIT.java @@ -264,6 +264,9 @@ private Response runESQLCommand(String user, String command) throws IOException if (randomBoolean()) { settings.put("data_partitioning", randomFrom("shard", "segment", "doc")); } + if (randomBoolean()) { + settings.put("enrich_max_workers", between(1, 5)); + } pragmas = settings.build(); } XContentBuilder query = JsonXContent.contentBuilder(); diff --git a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEsqlIT.java b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEsqlIT.java index efb7192bbc3e8..10e63a563efc7 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEsqlIT.java +++ b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEsqlIT.java @@ -54,7 +54,14 @@ public void testBasicEsql() throws IOException { public void testInvalidPragma() throws IOException { assumeTrue("pragma only enabled on snapshot builds", Build.current().isSnapshot()); - RequestObjectBuilder builder = new RequestObjectBuilder().query("row a = 1, b = 2"); + createIndex("test-index"); + for (int i = 0; i < 10; i++) { + Request request = new Request("POST", "/test-index/_doc/"); + request.addParameter("refresh", "true"); + request.setJsonEntity("{\"f\":" + i + "}"); + assertOK(client().performRequest(request)); + } + RequestObjectBuilder builder = new RequestObjectBuilder().query("from test-index | limit 1 | keep f"); builder.pragmas(Settings.builder().put("data_partitioning", "invalid-option").build()); builder.build(); ResponseException re = expectThrows(ResponseException.class, () -> runEsql(builder)); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java index 2cb739d6f068e..f73ab716cb534 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java @@ -136,8 +136,8 @@ public final PhysicalOperation sourcePhysicalOperation(EsQueryExec esQueryExec, luceneFactory = new LuceneTopNSourceOperator.Factory( searchContexts, querySupplier, - context.dataPartitioning(), - context.taskConcurrency(), + context.queryPragmas().dataPartitioning(), + context.queryPragmas().taskConcurrency(), context.pageSize(rowEstimatedSize), limit, fieldSorts @@ -146,8 +146,8 @@ public final PhysicalOperation sourcePhysicalOperation(EsQueryExec esQueryExec, luceneFactory = new LuceneSourceOperator.Factory( searchContexts, querySupplier, - context.dataPartitioning(), - context.taskConcurrency(), + context.queryPragmas().dataPartitioning(), + context.queryPragmas().taskConcurrency(), context.pageSize(rowEstimatedSize), limit ); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java index c2f7fba9a5206..963b92c048382 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java @@ -16,7 +16,6 @@ import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.Page; -import org.elasticsearch.compute.lucene.DataPartitioning; import org.elasticsearch.compute.lucene.LuceneCountOperator; import org.elasticsearch.compute.lucene.LuceneOperator; import org.elasticsearch.compute.operator.ColumnExtractOperator; @@ -76,6 +75,7 @@ import org.elasticsearch.xpack.esql.plan.physical.RowExec; import org.elasticsearch.xpack.esql.plan.physical.ShowExec; import org.elasticsearch.xpack.esql.plan.physical.TopNExec; +import org.elasticsearch.xpack.esql.plugin.QueryPragmas; import org.elasticsearch.xpack.esql.session.EsqlConfiguration; import org.elasticsearch.xpack.ql.expression.Alias; import org.elasticsearch.xpack.ql.expression.Attribute; @@ -151,9 +151,7 @@ public LocalExecutionPlan plan(PhysicalPlan node) { var context = new LocalExecutionPlannerContext( new ArrayList<>(), new Holder<>(DriverParallelism.SINGLE), - configuration.pragmas().taskConcurrency(), - configuration.pragmas().dataPartitioning(), - configuration.pragmas().pageSize(), + configuration.pragmas(), bigArrays, blockFactory ); @@ -256,8 +254,8 @@ private PhysicalOperation planEsStats(EsStatsQueryExec statsQuery, LocalExecutio final LuceneOperator.Factory luceneFactory = new LuceneCountOperator.Factory( esProvider.searchContexts(), querySupplier, - context.dataPartitioning(), - context.taskConcurrency(), + context.queryPragmas.dataPartitioning(), + context.queryPragmas.taskConcurrency(), limit ); @@ -529,7 +527,7 @@ private PhysicalOperation planEnrich(EnrichExec enrich, LocalExecutionPlannerCon new EnrichLookupOperator.Factory( sessionId, parentTask, - 1, // TODO: Add a concurrent setting for enrich - also support unordered mode + context.queryPragmas().enrichMaxWorkers(), source.layout.get(enrich.matchField().id()).channel(), enrichLookupService, enrichIndex, @@ -731,9 +729,7 @@ enum Type { public record LocalExecutionPlannerContext( List driverFactories, Holder driverParallelism, - int taskConcurrency, - DataPartitioning dataPartitioning, - int configuredPageSize, + QueryPragmas queryPragmas, BigArrays bigArrays, BlockFactory blockFactory ) { @@ -752,8 +748,8 @@ int pageSize(Integer estimatedRowSize) { if (estimatedRowSize == 0) { throw new IllegalStateException("estimated row size can't be 0"); } - if (configuredPageSize != 0) { - return configuredPageSize; + if (queryPragmas.pageSize() != 0) { + return queryPragmas.pageSize(); } return Math.max(SourceOperator.MIN_TARGET_PAGE_SIZE, SourceOperator.TARGET_PAGE_SIZE / estimatedRowSize); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/QueryPragmas.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/QueryPragmas.java index 602e04ff08f6c..65a07c98af29a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/QueryPragmas.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/QueryPragmas.java @@ -28,6 +28,8 @@ public final class QueryPragmas implements Writeable { public static final Setting EXCHANGE_BUFFER_SIZE = Setting.intSetting("exchange_buffer_size", 10); public static final Setting EXCHANGE_CONCURRENT_CLIENTS = Setting.intSetting("exchange_concurrent_clients", 3); + public static final Setting ENRICH_MAX_WORKERS = Setting.intSetting("enrich_max_workers", 1); + private static final Setting TASK_CONCURRENCY = Setting.intSetting( "task_concurrency", ThreadPool.searchOrGetThreadPoolSize(EsExecutors.allocatedProcessors(Settings.EMPTY)) @@ -104,6 +106,14 @@ public TimeValue statusInterval() { return STATUS_INTERVAL.get(settings); } + /** + * Returns the maximum number of workers for enrich lookup. A higher number of workers reduces latency but increases cluster load. + * Defaults to 1. + */ + public int enrichMaxWorkers() { + return ENRICH_MAX_WORKERS.get(settings); + } + public boolean isEmpty() { return settings.isEmpty(); } From d53c0cbf82a69675c561ca3a6b8825385408999b Mon Sep 17 00:00:00 2001 From: Luigi Dell'Aquila Date: Fri, 27 Oct 2023 16:35:44 +0200 Subject: [PATCH 167/203] ESQL: annotate trigonometric functions and auto_bucket() (SHOW FUNCTIONS) (#101460) --- .../esql/functions/signature/auto_bucket.svg | 2 +- .../esql/functions/types/auto_bucket.asciidoc | 2 +- .../src/main/resources/show.csv-spec | 44 +++++++++---------- .../expression/function/scalar/math/Abs.java | 2 + .../expression/function/scalar/math/Acos.java | 2 + .../expression/function/scalar/math/Asin.java | 2 + .../expression/function/scalar/math/Atan.java | 2 + .../function/scalar/math/Atan2.java | 2 + .../function/scalar/math/AutoBucket.java | 11 ++++- .../expression/function/scalar/math/Cos.java | 2 + .../expression/function/scalar/math/Cosh.java | 2 + .../expression/function/scalar/math/Sinh.java | 2 + .../expression/function/scalar/math/Tan.java | 2 + .../expression/function/scalar/math/Tanh.java | 2 + .../function/AbstractFunctionTestCase.java | 3 ++ .../function/scalar/math/AutoBucketTests.java | 37 +++++++++++++++- 16 files changed, 92 insertions(+), 27 deletions(-) diff --git a/docs/reference/esql/functions/signature/auto_bucket.svg b/docs/reference/esql/functions/signature/auto_bucket.svg index 8343661db064e..7da9a053825f1 100644 --- a/docs/reference/esql/functions/signature/auto_bucket.svg +++ b/docs/reference/esql/functions/signature/auto_bucket.svg @@ -1 +1 @@ -AUTO_BUCKET(arg1,arg2,arg3,arg4) \ No newline at end of file +AUTO_BUCKET(field,buckets,from,to) \ No newline at end of file diff --git a/docs/reference/esql/functions/types/auto_bucket.asciidoc b/docs/reference/esql/functions/types/auto_bucket.asciidoc index d2f134b99fbb0..e0ede29e40df1 100644 --- a/docs/reference/esql/functions/types/auto_bucket.asciidoc +++ b/docs/reference/esql/functions/types/auto_bucket.asciidoc @@ -1,5 +1,5 @@ [%header.monospaced.styled,format=dsv,separator=|] |=== -arg1 | arg2 | arg3 | arg4 | result +field | buckets | from | to | result |=== diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec index 5c045ea7efc48..117bb9646bc5d 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec @@ -9,20 +9,20 @@ showFunctions show functions; name:keyword | synopsis:keyword | argNames:keyword | argTypes:keyword | argDescriptions:keyword |returnType:keyword | description:keyword | optionalArgs:boolean | variadic:boolean -abs |"? abs(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false -acos |"? acos(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false -asin |"? asin(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false -atan |"? atan(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false -atan2 |"? atan2(y:integer|long|double|unsigned_long, x:integer|long|double|unsigned_long)" |[y, x] |["integer|long|double|unsigned_long", "integer|long|double|unsigned_long"] |["", ""] |? | "" | [false, false] | false -auto_bucket |? auto_bucket(arg1:?, arg2:?, arg3:?, arg4:?) |[arg1, arg2, arg3, arg4] |[?, ?, ?, ?] |["", "", "", ""] |? | "" | [false, false, false, false] | false +abs |"integer|long|double|unsigned_long abs(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |"integer|long|double|unsigned_long" | "" | false | false +acos |"double acos(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |double | "" | false | false +asin |"double asin(n:integer|long|double|unsigned_long)"|n |"integer|long|double|unsigned_long" | "" |double | "" | false | false +atan |"double atan(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |double | "" | false | false +atan2 |"double atan2(y:integer|long|double|unsigned_long, x:integer|long|double|unsigned_long)" |[y, x] |["integer|long|double|unsigned_long", "integer|long|double|unsigned_long"] |["", ""] |double | "" | [false, false] | false +auto_bucket |"double|date auto_bucket(field:integer|long|double|date, buckets:integer, from:integer|long|double|date, to:integer|long|double|date)" |[field, buckets, from, to] |["integer|long|double|date", "integer", "integer|long|double|date", "integer|long|double|date"] |["", "", "", ""] | "double|date" | "" | [false, false, false, false] | false avg |? avg(arg1:?) |arg1 |? | "" |? | "" | false | false case |? case(arg1:?, arg2...:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | true ceil |"? ceil(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false cidr_match |? cidr_match(arg1:?, arg2...:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | true coalesce |? coalesce(arg1:?, arg2...:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | true concat |? concat(arg1:?, arg2...:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | true -cos |"? cos(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false -cosh |"? cosh(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false +cos |"double cos(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |double | "" | false | false +cosh |"double cosh(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |double | "" | false | false count |? count(arg1:?) |arg1 |? | "" |? | "" | false | false count_distinct |? count_distinct(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false date_extract |? date_extract(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false @@ -62,14 +62,14 @@ right |"? right(string:keyword, length:integer)" |[string round |? round(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false rtrim |? rtrim(arg1:?) |arg1 |? | "" |? | "" | false | false sin |"double sin(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" |An angle, in radians |double |Returns the trigonometric sine of an angle | false | false -sinh |"? sinh(n:integer|long|double|unsigned_long)"|n |"integer|long|double|unsigned_long" | "" |? | "" | false | false +sinh |"double sinh(n:integer|long|double|unsigned_long)"|n |"integer|long|double|unsigned_long" | "" |double | "" | false | false split |? split(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false sqrt |"? sqrt(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false starts_with |? starts_with(arg1:?, arg2:?) |[arg1, arg2] |[?, ?] |["", ""] |? | "" | [false, false] | false substring |? substring(arg1:?, arg2:?, arg3:?) |[arg1, arg2, arg3] |[?, ?, ?] |["", "", ""] |? | "" | [false, false, false]| false sum |? sum(arg1:?) |arg1 |? | "" |? | "" | false | false -tan |"? tan(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false -tanh |"? tanh(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |? | "" | false | false +tan |"double tan(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |double | "" | false | false +tanh |"double tanh(n:integer|long|double|unsigned_long)" |n |"integer|long|double|unsigned_long" | "" |double | "" | false | false tau |? tau() | null | null | null |? | "" | null | false to_bool |? to_bool(arg1:?) |arg1 |? | "" |? | "" | false | false to_boolean |? to_boolean(arg1:?) |arg1 |? | "" |? | "" | false | false @@ -98,20 +98,20 @@ showFunctionsSynopsis show functions | keep synopsis; synopsis:keyword -"? abs(n:integer|long|double|unsigned_long)" -"? acos(n:integer|long|double|unsigned_long)" -"? asin(n:integer|long|double|unsigned_long)" -"? atan(n:integer|long|double|unsigned_long)" -"? atan2(y:integer|long|double|unsigned_long, x:integer|long|double|unsigned_long)" -? auto_bucket(arg1:?, arg2:?, arg3:?, arg4:?) +"integer|long|double|unsigned_long abs(n:integer|long|double|unsigned_long)" +"double acos(n:integer|long|double|unsigned_long)" +"double asin(n:integer|long|double|unsigned_long)" +"double atan(n:integer|long|double|unsigned_long)" +"double atan2(y:integer|long|double|unsigned_long, x:integer|long|double|unsigned_long)" +"double|date auto_bucket(field:integer|long|double|date, buckets:integer, from:integer|long|double|date, to:integer|long|double|date)" ? avg(arg1:?) ? case(arg1:?, arg2...:?) "? ceil(n:integer|long|double|unsigned_long)" ? cidr_match(arg1:?, arg2...:?) ? coalesce(arg1:?, arg2...:?) ? concat(arg1:?, arg2...:?) -"? cos(n:integer|long|double|unsigned_long)" -"? cosh(n:integer|long|double|unsigned_long)" +"double cos(n:integer|long|double|unsigned_long)" +"double cosh(n:integer|long|double|unsigned_long)" ? count(arg1:?) ? count_distinct(arg1:?, arg2:?) ? date_extract(arg1:?, arg2:?) @@ -151,14 +151,14 @@ synopsis:keyword ? round(arg1:?, arg2:?) ? rtrim(arg1:?) "double sin(n:integer|long|double|unsigned_long)" -"? sinh(n:integer|long|double|unsigned_long)" +"double sinh(n:integer|long|double|unsigned_long)" ? split(arg1:?, arg2:?) "? sqrt(n:integer|long|double|unsigned_long)" ? starts_with(arg1:?, arg2:?) ? substring(arg1:?, arg2:?, arg3:?) ? sum(arg1:?) -"? tan(n:integer|long|double|unsigned_long)" -"? tanh(n:integer|long|double|unsigned_long)" +"double tan(n:integer|long|double|unsigned_long)" +"double tanh(n:integer|long|double|unsigned_long)" ? tau() ? to_bool(arg1:?) ? to_boolean(arg1:?) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java index f686630838313..90766a95e9cc0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Abs.java @@ -11,6 +11,7 @@ import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.ql.expression.Expression; @@ -22,6 +23,7 @@ import java.util.function.Function; public class Abs extends UnaryScalarFunction implements EvaluatorMapper { + @FunctionInfo(returnType = { "integer", "long", "double", "unsigned_long" }) public Abs(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java index 05c3414aee188..5df73102a5ee6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Acos.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Inverse cosine trigonometric function. */ public class Acos extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Acos(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java index 66e6f2654742f..66d35d8e8bb2c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Asin.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Inverse cosine trigonometric function. */ public class Asin extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Asin(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java index 99f8e35974d2d..f730b3358a7f1 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Inverse cosine trigonometric function. */ public class Atan extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Atan(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java index 204bdbcdfa183..31fdea6e0d00c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2.java @@ -10,6 +10,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.Expressions; @@ -33,6 +34,7 @@ public class Atan2 extends ScalarFunction implements EvaluatorMapper { private final Expression y; private final Expression x; + @FunctionInfo(returnType = "double") public Atan2( Source source, @Param(name = "y", type = { "integer", "long", "double", "unsigned_long" }) Expression y, diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucket.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucket.java index 3aaca5f53e8d0..33115352d9e54 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucket.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucket.java @@ -14,6 +14,8 @@ import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.expression.function.scalar.date.DateTrunc; import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Div; import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Mul; @@ -81,7 +83,14 @@ public class AutoBucket extends ScalarFunction implements EvaluatorMapper { private final Expression from; private final Expression to; - public AutoBucket(Source source, Expression field, Expression buckets, Expression from, Expression to) { + @FunctionInfo(returnType = { "double", "date" }) + public AutoBucket( + Source source, + @Param(name = "field", type = { "integer", "long", "double", "date" }) Expression field, + @Param(name = "buckets", type = { "integer" }) Expression buckets, + @Param(name = "from", type = { "integer", "long", "double", "date" }) Expression from, + @Param(name = "to", type = { "integer", "long", "double", "date" }) Expression to + ) { super(source, List.of(field, buckets, from, to)); this.field = field; this.buckets = buckets; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java index 7b193752dc97a..dab1fa65d05dc 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cos.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Cosine trigonometric function. */ public class Cos extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Cos(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java index b6df7e1260624..cc315c3e9569c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Cosh.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Cosine hyperbolic function. */ public class Cosh extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Cosh(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java index e55e607f9a09a..07228dd1743dd 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Sinh.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Sine hyperbolic function. */ public class Sinh extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Sinh(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java index 2fee1f3e41850..7980c2dd94cb2 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tan.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Tangent trigonometric function. */ public class Tan extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Tan(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java index af48285192ccf..4d0af5b6a8de9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tanh.java @@ -9,6 +9,7 @@ import org.elasticsearch.compute.ann.Evaluator; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -20,6 +21,7 @@ * Tangent hyperbolic function. */ public class Tanh extends AbstractTrigonometricFunction { + @FunctionInfo(returnType = "double") public Tanh(Source source, @Param(name = "n", type = { "integer", "long", "double", "unsigned_long" }) Expression n) { super(source, n); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 8079fb5bb70f7..094ecc9bfe569 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -522,6 +522,9 @@ public static void testFunctionInfo() { continue; // TODO remove this eventually, so that all the functions will have to provide signature info } Set signatureTypes = typesFromSignature.get(i); + if (signatureTypes.isEmpty()) { + continue; + } assertEquals(annotationTypes, signatureTypes); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucketTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucketTests.java index 79089864daa4a..043bf083b580a 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucketTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AutoBucketTests.java @@ -46,7 +46,36 @@ public static Iterable parameters() { args, "DateTruncEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding[DAY_OF_MONTH in Z][fixed to midnight]]", DataTypes.DATETIME, - resultsMatcher(args) + dateResultsMatcher(args) + ); + }), new TestCaseSupplier("Autobucket Single long", () -> { + List args = List.of(new TestCaseSupplier.TypedData(100L, DataTypes.LONG, "arg")); + return new TestCaseSupplier.TestCase( + args, + "MulDoublesEvaluator[lhs=FloorDoubleEvaluator[" + + "val=DivDoublesEvaluator[lhs=CastLongToDoubleEvaluator[v=Attribute[channel=0]], " + + "rhs=LiteralsEvaluator[lit=50.0]]], rhs=LiteralsEvaluator[lit=50.0]]", + DataTypes.DOUBLE, + numericResultsMatcher(args, 100.0) + ); + }), new TestCaseSupplier("Autobucket Single int", () -> { + List args = List.of(new TestCaseSupplier.TypedData(100, DataTypes.INTEGER, "arg")); + return new TestCaseSupplier.TestCase( + args, + "MulDoublesEvaluator[lhs=FloorDoubleEvaluator[" + + "val=DivDoublesEvaluator[lhs=CastIntToDoubleEvaluator[v=Attribute[channel=0]], " + + "rhs=LiteralsEvaluator[lit=50.0]]], rhs=LiteralsEvaluator[lit=50.0]]", + DataTypes.DOUBLE, + numericResultsMatcher(args, 100.0) + ); + }), new TestCaseSupplier("Autobucket Single double", () -> { + List args = List.of(new TestCaseSupplier.TypedData(100.0, DataTypes.DOUBLE, "arg")); + return new TestCaseSupplier.TestCase( + args, + "MulDoublesEvaluator[lhs=FloorDoubleEvaluator[val=DivDoublesEvaluator[lhs=Attribute[channel=0], " + + "rhs=LiteralsEvaluator[lit=50.0]]], rhs=LiteralsEvaluator[lit=50.0]]", + DataTypes.DOUBLE, + numericResultsMatcher(args, 100.0) ); }))); } @@ -72,11 +101,15 @@ protected DataType expectedType(List argTypes) { return argTypes.get(0); } - private static Matcher resultsMatcher(List typedData) { + private static Matcher dateResultsMatcher(List typedData) { long millis = ((Number) typedData.get(0).data()).longValue(); return equalTo(Rounding.builder(Rounding.DateTimeUnit.DAY_OF_MONTH).build().prepareForUnknown().round(millis)); } + private static Matcher numericResultsMatcher(List typedData, Object value) { + return equalTo(value); + } + @Override protected List argSpec() { DataType[] numerics = numerics(); From 9f9d11ed3d84841cc8c8c1b1b2f31b97614e97df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lorenzo=20Dematt=C3=A9?= Date: Fri, 27 Oct 2023 17:28:52 +0200 Subject: [PATCH 168/203] Mute testCrankyEvaluateBlockWithoutNullsFloating as it is failing reproducibly (see #100820) (#101471) Mutes #100820 --- .../xpack/esql/expression/function/AbstractFunctionTestCase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 094ecc9bfe569..a4c5638ae815f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -297,6 +297,7 @@ public final void testCrankyEvaluateBlockWithoutNulls() { * input pattern contained only a single value. *

    */ + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/100820") public final void testCrankyEvaluateBlockWithoutNullsFloating() { assumeTrue("sometimes the cranky breaker silences warnings, just skip these cases", testCase.getExpectedWarnings() == null); try { From 3a806b4b583c68c0fd6fe27e0fd967ae9aa3c634 Mon Sep 17 00:00:00 2001 From: Artem Prigoda Date: Fri, 27 Oct 2023 17:56:22 +0200 Subject: [PATCH 169/203] Add methods for adding generation listeners with primary term (#100899) Provide a facility to supply the primary term for the engine for a more precise comparison of generations. Deprecate existing `addSegmentGenerationListener` and `waitForSegmentGeneration` methods Prerequisite for #99752 --- docs/changelog/100899.yaml | 5 +++++ .../java/org/elasticsearch/index/engine/Engine.java | 12 ++++++++++++ .../org/elasticsearch/index/shard/IndexShard.java | 13 ++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/100899.yaml diff --git a/docs/changelog/100899.yaml b/docs/changelog/100899.yaml new file mode 100644 index 0000000000000..988546bb22cbe --- /dev/null +++ b/docs/changelog/100899.yaml @@ -0,0 +1,5 @@ +pr: 100899 +summary: Add methods for adding generation listeners with primary term +area: Store +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/index/engine/Engine.java b/server/src/main/java/org/elasticsearch/index/engine/Engine.java index 7f896c352d958..85ede932f231a 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -111,6 +111,7 @@ public abstract class Engine implements Closeable { public static final String SEARCH_SOURCE = "search"; // TODO: Make source of search enum? public static final String CAN_MATCH_SEARCH_SOURCE = "can_match"; protected static final String DOC_STATS_SOURCE = "doc_stats"; + public static final long UNKNOWN_PRIMARY_TERM = -1L; protected final ShardId shardId; protected final Logger logger; @@ -2114,8 +2115,19 @@ public final EngineConfig getEngineConfig() { /** * Allows registering a listener for when the index shard is on a segment generation >= minGeneration. + * + * @deprecated use {@link #addPrimaryTermAndGenerationListener(long, long, ActionListener)} instead. */ + @Deprecated public void addSegmentGenerationListener(long minGeneration, ActionListener listener) { + addPrimaryTermAndGenerationListener(UNKNOWN_PRIMARY_TERM, minGeneration, listener); + } + + /** + * Allows registering a listener for when the index shard is on a primary term >= minPrimaryTerm + * and a segment generation >= minGeneration. + */ + public void addPrimaryTermAndGenerationListener(long minPrimaryTerm, long minGeneration, ActionListener listener) { throw new UnsupportedOperationException(); } diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 376724d978768..f4812f280f917 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -4173,7 +4173,18 @@ public String toString() { return "IndexShard(shardRouting=" + shardRouting + ")"; } + /** + * @deprecated use {@link #waitForPrimaryTermAndGeneration(long, long, ActionListener)} instead. + */ + @Deprecated public void waitForSegmentGeneration(long segmentGeneration, ActionListener listener) { - getEngine().addSegmentGenerationListener(segmentGeneration, listener); + waitForPrimaryTermAndGeneration(getOperationPrimaryTerm(), segmentGeneration, listener); + } + + /** + * Registers a listener for an event when the shard advances to the provided primary term and segment generation + */ + public void waitForPrimaryTermAndGeneration(long primaryTerm, long segmentGeneration, ActionListener listener) { + getEngine().addPrimaryTermAndGenerationListener(primaryTerm, segmentGeneration, listener); } } From 6571d396cc5d4e6a46a58da8a981caa00cb2fa4f Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 27 Oct 2023 18:15:58 +0200 Subject: [PATCH 170/203] Optimize `MurmurHash3` (#101202) - Makes Murmur3Hasher.update allocation free by not allocating a byte[] - Adds digestHash methods that don't return a newly allocated byte[]. The method takes an optional and re-usable Hash128 instance. - Hash128 adds a getBytes method that takes an optional target byte[] to avoid allocations --- docs/changelog/101202.yaml | 5 + .../common/hash/Murmur3Hasher.java | 91 ++++++++++--------- .../common/hash/MurmurHash3.java | 15 ++- .../common/hashing/Murmur3HasherTests.java | 17 +++- 4 files changed, 77 insertions(+), 51 deletions(-) create mode 100644 docs/changelog/101202.yaml diff --git a/docs/changelog/101202.yaml b/docs/changelog/101202.yaml new file mode 100644 index 0000000000000..565338a2dbb6e --- /dev/null +++ b/docs/changelog/101202.yaml @@ -0,0 +1,5 @@ +pr: 101202 +summary: Optimize `MurmurHash3` +area: "Ingest Node" +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/common/hash/Murmur3Hasher.java b/server/src/main/java/org/elasticsearch/common/hash/Murmur3Hasher.java index 0d4d6fd4f61f2..b85e3107ba09d 100644 --- a/server/src/main/java/org/elasticsearch/common/hash/Murmur3Hasher.java +++ b/server/src/main/java/org/elasticsearch/common/hash/Murmur3Hasher.java @@ -8,9 +8,6 @@ package org.elasticsearch.common.hash; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.Numbers; - /** * Wraps {@link MurmurHash3} to provide an interface similar to {@link java.security.MessageDigest} that * allows hashing of byte arrays passed through multiple calls to {@link #update(byte[])}. Like @@ -35,38 +32,45 @@ public Murmur3Hasher(long seed) { /** * Supplies some or all of the bytes to be hashed. Multiple calls to this method may - * be made to sequentially supply the bytes for hashing. Once all bytes have been supplied, the - * {@link #digest()} method should be called to complete the hash calculation. + * be made to sequentially supply the bytes for hashing. Once all bytes have been supplied, either the + * {@link #digestHash} method (preferred) or the {@link #digest()} method should be called to complete the hash calculation. */ public void update(byte[] inputBytes) { - int totalLength = remainderLength + inputBytes.length; - if (totalLength >= 16) { - // hash as many bytes as available in integer multiples of 16 - int numBytesToHash = totalLength & 0xFFFFFFF0; - byte[] bytesToHash; + update(inputBytes, 0, inputBytes.length); + } + + private void update(byte[] inputBytes, int offset, int length) { + if (remainderLength + length >= remainder.length) { if (remainderLength > 0) { - bytesToHash = new byte[numBytesToHash]; - System.arraycopy(remainder, 0, bytesToHash, 0, remainderLength); - System.arraycopy(inputBytes, 0, bytesToHash, remainderLength, numBytesToHash - remainderLength); - } else { - bytesToHash = inputBytes; - } + // fill rest of remainder from inputBytes and hash remainder + int bytesToCopyFromInputToRemainder = remainder.length - remainderLength; + System.arraycopy(inputBytes, offset, remainder, remainderLength, bytesToCopyFromInputToRemainder); + offset = bytesToCopyFromInputToRemainder; + length = length - bytesToCopyFromInputToRemainder; - MurmurHash3.IntermediateResult result = MurmurHash3.intermediateHash(bytesToHash, 0, numBytesToHash, h1, h2); - h1 = result.h1; - h2 = result.h2; - this.length += numBytesToHash; + MurmurHash3.IntermediateResult result = MurmurHash3.intermediateHash(remainder, 0, remainder.length, h1, h2); + h1 = result.h1; + h2 = result.h2; + remainderLength = 0; + this.length += remainder.length; + } + // hash as many bytes as available in integer multiples of 16 as intermediateHash can only process multiples of 16 + int numBytesToHash = length & 0xFFFFFFF0; + if (numBytesToHash > 0) { + MurmurHash3.IntermediateResult result = MurmurHash3.intermediateHash(inputBytes, offset, numBytesToHash, h1, h2); + h1 = result.h1; + h2 = result.h2; + this.length += numBytesToHash; + } // save the remaining bytes, if any - if (totalLength > numBytesToHash) { - System.arraycopy(inputBytes, numBytesToHash - remainderLength, remainder, 0, totalLength - numBytesToHash); - remainderLength = totalLength - numBytesToHash; - } else { - remainderLength = 0; + if (length > numBytesToHash) { + this.remainderLength = length - numBytesToHash; + System.arraycopy(inputBytes, offset + numBytesToHash, remainder, 0, remainderLength); } } else { - System.arraycopy(inputBytes, 0, remainder, remainderLength, inputBytes.length); - remainderLength += inputBytes.length; + System.arraycopy(inputBytes, 0, remainder, remainderLength, length); + remainderLength += length; } } @@ -81,29 +85,30 @@ public void reset() { } /** - * Completes the hash of all bytes previously passed to {@link #update(byte[])}. + * Completes the hash of all bytes previously passed to {@link #update}. */ public byte[] digest() { - length += remainderLength; - MurmurHash3.Hash128 h = MurmurHash3.finalizeHash(new MurmurHash3.Hash128(), remainder, 0, length, h1, h2); - byte[] hash = new byte[16]; - System.arraycopy(Numbers.longToBytes(h.h1), 0, hash, 0, 8); - System.arraycopy(Numbers.longToBytes(h.h2), 0, hash, 8, 8); - return hash; + return digestHash().getBytes(); } - public static String getAlgorithm() { - return METHOD; + /** + * Completes the hash of all bytes previously passed to {@link #update}. + */ + public MurmurHash3.Hash128 digestHash() { + return digestHash(new MurmurHash3.Hash128()); } /** - * Converts the 128-bit byte array returned by {@link #digest()} to a - * {@link org.elasticsearch.common.hash.MurmurHash3.Hash128} + * Completes the hash of all bytes previously passed to {@link #update}. + * Allows passing in a re-usable {@link org.elasticsearch.common.hash.MurmurHash3.Hash128} instance to avoid allocations. */ - public static MurmurHash3.Hash128 toHash128(byte[] doubleLongBytes) { - MurmurHash3.Hash128 hash128 = new MurmurHash3.Hash128(); - hash128.h1 = Numbers.bytesToLong(new BytesRef(doubleLongBytes, 0, 8)); - hash128.h2 = Numbers.bytesToLong(new BytesRef(doubleLongBytes, 8, 8)); - return hash128; + public MurmurHash3.Hash128 digestHash(MurmurHash3.Hash128 hash) { + length += remainderLength; + MurmurHash3.finalizeHash(hash, remainder, 0, length, h1, h2); + return hash; + } + + public static String getAlgorithm() { + return METHOD; } } diff --git a/server/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java b/server/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java index 903b7a080a6ca..6d6fdbc45ec99 100644 --- a/server/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java +++ b/server/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java @@ -8,7 +8,6 @@ package org.elasticsearch.common.hash; -import org.elasticsearch.common.Numbers; import org.elasticsearch.common.util.ByteUtils; import java.math.BigInteger; @@ -29,6 +28,17 @@ public static class Hash128 { /** higher 64 bits part **/ public long h2; + public byte[] getBytes() { + byte[] hash = new byte[16]; + getBytes(hash, 0); + return hash; + } + + public void getBytes(byte[] bytes, int offset) { + ByteUtils.writeLongBE(h1, bytes, offset); + ByteUtils.writeLongBE(h2, bytes, offset + 8); + } + @Override public boolean equals(Object other) { if (this == other) { @@ -49,8 +59,7 @@ public int hashCode() { @Override public String toString() { byte[] longBytes = new byte[17]; - System.arraycopy(Numbers.longToBytes(h1), 0, longBytes, 1, 8); - System.arraycopy(Numbers.longToBytes(h2), 0, longBytes, 9, 8); + getBytes(longBytes, 1); BigInteger bi = new BigInteger(longBytes); return "0x" + bi.toString(16); } diff --git a/server/src/test/java/org/elasticsearch/common/hashing/Murmur3HasherTests.java b/server/src/test/java/org/elasticsearch/common/hashing/Murmur3HasherTests.java index 8574f8debb8c0..fdebec676192c 100644 --- a/server/src/test/java/org/elasticsearch/common/hashing/Murmur3HasherTests.java +++ b/server/src/test/java/org/elasticsearch/common/hashing/Murmur3HasherTests.java @@ -8,14 +8,13 @@ package org.elasticsearch.common.hashing; +import org.elasticsearch.common.Numbers; import org.elasticsearch.common.hash.Murmur3Hasher; import org.elasticsearch.common.hash.MurmurHash3; import org.elasticsearch.test.ESTestCase; import java.nio.charset.StandardCharsets; -import static org.hamcrest.Matchers.equalTo; - public class Murmur3HasherTests extends ESTestCase { public void testKnownValues() { @@ -37,13 +36,21 @@ private static void assertHash(long lower, long upper, String inputString, long byte[] bytes = inputString.getBytes(StandardCharsets.UTF_8); Murmur3Hasher mh = new Murmur3Hasher(seed); mh.update(bytes); - MurmurHash3.Hash128 actual = Murmur3Hasher.toHash128(mh.digest()); + MurmurHash3.Hash128 actual = mh.digestHash(); assertHash(expected, actual); } private static void assertHash(MurmurHash3.Hash128 expected, MurmurHash3.Hash128 actual) { assertEquals(expected.h1, actual.h1); assertEquals(expected.h2, actual.h2); + assertEquals(expected, toHash128(expected.getBytes())); + } + + public static MurmurHash3.Hash128 toHash128(byte[] doubleLongBytes) { + MurmurHash3.Hash128 hash128 = new MurmurHash3.Hash128(); + hash128.h1 = Numbers.bytesToLong(doubleLongBytes, 0); + hash128.h2 = Numbers.bytesToLong(doubleLongBytes, 8); + return hash128; } public void testSingleVsSequentialMurmur3() { @@ -85,7 +92,7 @@ public void testSingleVsSequentialMurmur3() { mh.update(splitBytes[k]); } } - MurmurHash3.Hash128 sequentialHash = Murmur3Hasher.toHash128(mh.digest()); - assertThat(singleHash, equalTo(sequentialHash)); + MurmurHash3.Hash128 sequentialHash = mh.digestHash(); + assertHash(singleHash, sequentialHash); } } From debe882c0f0dbb0fceab2351a0a223657ce7bb97 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Fri, 27 Oct 2023 18:32:19 +0200 Subject: [PATCH 171/203] ESQL: Remove the swapped-args check for date_xxx() (#101362) This removes the check and helper error message for some of the `date_xxx()` functions whose args have been swapped for consistency. Close #99562 --- docs/changelog/101362.yaml | 6 ++++++ .../scalar/date/BinaryDateTimeFunction.java | 11 ---------- .../function/scalar/date/DateExtract.java | 21 ++----------------- .../function/scalar/date/DateFormat.java | 11 +--------- .../function/scalar/date/DateTrunc.java | 17 ++------------- .../xpack/esql/analysis/AnalyzerTests.java | 21 ------------------- 6 files changed, 11 insertions(+), 76 deletions(-) create mode 100644 docs/changelog/101362.yaml diff --git a/docs/changelog/101362.yaml b/docs/changelog/101362.yaml new file mode 100644 index 0000000000000..e1d763cd416fa --- /dev/null +++ b/docs/changelog/101362.yaml @@ -0,0 +1,6 @@ +pr: 101362 +summary: "ESQL: Remove the swapped-args check for date_xxx()" +area: ES|QL +type: enhancement +issues: + - 99562 diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/BinaryDateTimeFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/BinaryDateTimeFunction.java index 455c9d162dc8a..c7c923e8e912a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/BinaryDateTimeFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/BinaryDateTimeFunction.java @@ -17,9 +17,6 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Objects; -import java.util.function.Predicate; - -import static org.elasticsearch.common.logging.LoggerMessageFormat.format; public abstract class BinaryDateTimeFunction extends BinaryScalarFunction { @@ -69,12 +66,4 @@ public boolean equals(Object o) { BinaryDateTimeFunction that = (BinaryDateTimeFunction) o; return zoneId().equals(that.zoneId()); } - - // TODO: drop check once 8.11 is released - static TypeResolution argumentTypesAreSwapped(DataType left, DataType right, Predicate rightTest, String source) { - if (DataTypes.isDateTime(left) && rightTest.test(right)) { - return new TypeResolution(format(null, "function definition has been updated, please swap arguments in [{}]", source)); - } - return TypeResolution.TYPE_RESOLVED; - } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java index af1a536787398..96d78474bbb9e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtract.java @@ -31,7 +31,6 @@ import java.util.Locale; import java.util.function.Function; -import static org.elasticsearch.xpack.esql.expression.function.scalar.date.BinaryDateTimeFunction.argumentTypesAreSwapped; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isDate; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isStringAndExact; @@ -109,25 +108,9 @@ protected TypeResolution resolveType() { if (childrenResolved() == false) { return new TypeResolution("Unresolved children"); } - TypeResolution resolution = argumentTypesAreSwapped( - children().get(0).dataType(), - children().get(1).dataType(), - DataTypes::isString, - sourceText() + return isStringAndExact(children().get(0), sourceText(), TypeResolutions.ParamOrdinal.FIRST).and( + isDate(children().get(1), sourceText(), TypeResolutions.ParamOrdinal.SECOND) ); - if (resolution.unresolved()) { - return resolution; - } - resolution = isStringAndExact(children().get(0), sourceText(), TypeResolutions.ParamOrdinal.FIRST); - if (resolution.unresolved()) { - return resolution; - } - resolution = isDate(children().get(1), sourceText(), TypeResolutions.ParamOrdinal.SECOND); - if (resolution.unresolved()) { - return resolution; - } - - return TypeResolution.TYPE_RESOLVED; } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormat.java index 0ec2aae8306db..e7ae1f8d4aeca 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormat.java @@ -28,7 +28,6 @@ import java.util.Locale; import java.util.function.Function; -import static org.elasticsearch.xpack.esql.expression.function.scalar.date.BinaryDateTimeFunction.argumentTypesAreSwapped; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.ParamOrdinal.FIRST; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.ParamOrdinal.SECOND; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isDate; @@ -57,15 +56,7 @@ protected TypeResolution resolveType() { return new TypeResolution("Unresolved children"); } - TypeResolution resolution; - if (format != null) { - resolution = argumentTypesAreSwapped(format.dataType(), field.dataType(), DataTypes::isString, sourceText()); - if (resolution.unresolved()) { - return resolution; - } - } - - resolution = isDate(field, sourceText(), format == null ? FIRST : SECOND); + TypeResolution resolution = isDate(field, sourceText(), format == null ? FIRST : SECOND); if (resolution.unresolved()) { return resolution; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java index 4ef2504bd7fc8..0c70c9065dfc4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTrunc.java @@ -42,22 +42,9 @@ protected TypeResolution resolveType() { return new TypeResolution("Unresolved children"); } - TypeResolution resolution = argumentTypesAreSwapped( - left().dataType(), - right().dataType(), - EsqlDataTypes::isTemporalAmount, - sourceText() + return isDate(timestampField(), sourceText(), FIRST).and( + isType(interval(), EsqlDataTypes::isTemporalAmount, sourceText(), SECOND, "dateperiod", "timeduration") ); - if (resolution.unresolved()) { - return resolution; - } - - resolution = isDate(timestampField(), sourceText(), FIRST); - if (resolution.unresolved()) { - return resolution; - } - - return isType(interval(), EsqlDataTypes::isTemporalAmount, sourceText(), SECOND, "dateperiod", "timeduration"); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java index 0349a1874415b..41e4b906f7e64 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java @@ -1031,27 +1031,6 @@ public void testDateTruncWithNumericInterval() { """, "second argument of [date_trunc(1, date)] must be [dateperiod or timeduration], found value [1] type [integer]"); } - public void testDateExtractWithSwappedArguments() { - verifyUnsupported(""" - from test - | eval date_extract(date, "year") - """, "function definition has been updated, please swap arguments in [date_extract(date, \"year\")]"); - } - - public void testDateFormatWithSwappedArguments() { - verifyUnsupported(""" - from test - | eval date_format(date, "yyyy-MM-dd") - """, "function definition has been updated, please swap arguments in [date_format(date, \"yyyy-MM-dd\")]"); - } - - public void testDateTruncWithSwappedArguments() { - verifyUnsupported(""" - from test - | eval date_trunc(date, 1 month) - """, "function definition has been updated, please swap arguments in [date_trunc(date, 1 month)]"); - } - public void testDateTruncWithDateInterval() { verifyUnsupported(""" from test From 2b058d6741e715b4b86d30883748625b7508db18 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 27 Oct 2023 12:02:17 -0600 Subject: [PATCH 172/203] Make IPAddress writeable (#101093) IPAddress may be returned from scripts and thus needs to be writeable. This commit makes it writeable as a generic writeable. closes #101082 --- docs/changelog/101093.yaml | 6 +++++ .../org/elasticsearch/TransportVersions.java | 1 + .../elasticsearch/script/field/IPAddress.java | 25 ++++++++++++++++++- .../script/field/IPAddressTests.java | 18 +++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101093.yaml diff --git a/docs/changelog/101093.yaml b/docs/changelog/101093.yaml new file mode 100644 index 0000000000000..99765170dd257 --- /dev/null +++ b/docs/changelog/101093.yaml @@ -0,0 +1,6 @@ +pr: 101093 +summary: Make IPAddress writeable +area: Infra/Scripting +type: bug +issues: + - 101082 diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 60c14740658bb..f969ce312835c 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -146,6 +146,7 @@ static TransportVersion def(int id) { public static final TransportVersion TOO_MANY_SCROLL_CONTEXTS_EXCEPTION_ADDED = def(8_521_00_0); public static final TransportVersion UNCONTENDED_REGISTER_ANALYSIS_ADDED = def(8_522_00_0); public static final TransportVersion TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED = def(8_523_00_0); + public static final TransportVersion IP_ADDRESS_WRITEABLE = def(8_524_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/script/field/IPAddress.java b/server/src/main/java/org/elasticsearch/script/field/IPAddress.java index 53f0f118a09b0..ed49928d6afa6 100644 --- a/server/src/main/java/org/elasticsearch/script/field/IPAddress.java +++ b/server/src/main/java/org/elasticsearch/script/field/IPAddress.java @@ -8,6 +8,11 @@ package org.elasticsearch.script.field; +import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; +import org.elasticsearch.common.io.stream.GenericNamedWriteable; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.xcontent.ToXContentObject; import org.elasticsearch.xcontent.XContentBuilder; @@ -20,7 +25,8 @@ /** * IP address for use in scripting. */ -public class IPAddress implements ToXContentObject { +public class IPAddress implements ToXContentObject, GenericNamedWriteable { + static final String NAMED_WRITEABLE_NAME = "IPAddress"; protected final InetAddress address; IPAddress(InetAddress address) { @@ -31,6 +37,14 @@ public IPAddress(String address) { this.address = InetAddresses.forString(address); } + public IPAddress(StreamInput input) throws IOException { + this(input.readString()); + } + + public void writeTo(StreamOutput output) throws IOException { + output.writeString(toString()); + } + public boolean isV4() { return address instanceof Inet4Address; } @@ -49,4 +63,13 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder.value(this.toString()); } + @Override + public String getWriteableName() { + return NAMED_WRITEABLE_NAME; + } + + @Override + public TransportVersion getMinimalSupportedVersion() { + return TransportVersions.IP_ADDRESS_WRITEABLE; + } } diff --git a/server/src/test/java/org/elasticsearch/script/field/IPAddressTests.java b/server/src/test/java/org/elasticsearch/script/field/IPAddressTests.java index f6e6660151311..63e5f9f072e98 100644 --- a/server/src/test/java/org/elasticsearch/script/field/IPAddressTests.java +++ b/server/src/test/java/org/elasticsearch/script/field/IPAddressTests.java @@ -8,8 +8,16 @@ package org.elasticsearch.script.field; +import org.elasticsearch.common.io.stream.GenericNamedWriteable; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.test.ESTestCase; +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; + public class IPAddressTests extends ESTestCase { public void testToString() { @@ -30,4 +38,14 @@ public void testV6() { assertFalse(addr4.isV4()); assertTrue(addr4.isV6()); } + + public void testWriteable() throws IOException { + var registry = new NamedWriteableRegistry( + List.of(new Entry(GenericNamedWriteable.class, IPAddress.NAMED_WRITEABLE_NAME, IPAddress::new)) + ); + var original = new IPAddress("192.168.1.1"); + var generic = copyNamedWriteable(original, registry, GenericNamedWriteable.class); + var copied = asInstanceOf(IPAddress.class, generic); + assertThat(copied.toString(), equalTo(original.toString())); + } } From a1a89e75f944ae1f8a872e9c2d2cc551e01aaf1c Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Fri, 27 Oct 2023 15:39:15 -0400 Subject: [PATCH 173/203] Increase timeout for QueryRulesIndexServiceTests (#101478) * Increase timeout for QueryRulesIndexServiceTests * Rename variable --- .../rules/QueryRulesIndexServiceTests.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/QueryRulesIndexServiceTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/QueryRulesIndexServiceTests.java index 2c18a866d684a..9ce62ee8d4c16 100644 --- a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/QueryRulesIndexServiceTests.java +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/QueryRulesIndexServiceTests.java @@ -41,6 +41,8 @@ public class QueryRulesIndexServiceTests extends ESSingleNodeTestCase { + private static final int REQUEST_TIMEOUT_SECONDS = 10; + private QueryRulesIndexService queryRulesIndexService; @Before @@ -212,11 +214,11 @@ public void onFailure(Exception e) { latch.countDown(); } }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue("Timeout waiting for put request", latch.await(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)); if (exc.get() != null) { throw exc.get(); } - assertNotNull(resp.get()); + assertNotNull("Received null response from put request", resp.get()); return resp.get(); } @@ -237,11 +239,11 @@ public void onFailure(Exception e) { latch.countDown(); } }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue("Timeout waiting for get request", latch.await(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)); if (exc.get() != null) { throw exc.get(); } - assertNotNull(resp.get()); + assertNotNull("Received null response from get request", resp.get()); return resp.get(); } @@ -262,11 +264,11 @@ public void onFailure(Exception e) { latch.countDown(); } }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue("Timeout waiting for delete request", latch.await(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)); if (exc.get() != null) { throw exc.get(); } - assertNotNull(resp.get()); + assertNotNull("Received null response from delete request", resp.get()); return resp.get(); } @@ -287,11 +289,11 @@ public void onFailure(Exception e) { latch.countDown(); } }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue("Timeout waiting for list request", latch.await(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)); if (exc.get() != null) { throw exc.get(); } - assertNotNull(resp.get()); + assertNotNull("Received null response from list request", resp.get()); return resp.get(); } From adea1cde964cfa947b778cb88cc87c3e30795f95 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Fri, 27 Oct 2023 15:39:27 -0400 Subject: [PATCH 174/203] Fix CanMatchPreFilterSearchPhaseTests#testInvalidSortShards() test & SearchCancellationIT NPE (#101444) closes https://github.com/elastic/elasticsearch/issues/101416 Related to: https://github.com/elastic/elasticsearch/issues/99929 --- .../action/search/CanMatchPreFilterSearchPhaseTests.java | 2 +- .../test/AbstractSearchCancellationTestCase.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/action/search/CanMatchPreFilterSearchPhaseTests.java b/server/src/test/java/org/elasticsearch/action/search/CanMatchPreFilterSearchPhaseTests.java index 3af46aea79319..7166664181eb1 100644 --- a/server/src/test/java/org/elasticsearch/action/search/CanMatchPreFilterSearchPhaseTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/CanMatchPreFilterSearchPhaseTests.java @@ -442,7 +442,7 @@ public void sendCanMatch( shardsIter, timeProvider, null, - true, + shardsIter.size() > shardToSkip.size(), EMPTY_CONTEXT_PROVIDER, ActionTestUtils.assertNoFailureListener(iter -> { result.set(iter); diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java index 4325ba46f5e86..c4eb63f440905 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractSearchCancellationTestCase.java @@ -281,7 +281,9 @@ public void onIndexModule(IndexModule indexModule) { indexModule.addSearchOperationListener(new SearchOperationListener() { @Override public void onNewReaderContext(ReaderContext c) { - runOnNewReaderContext.get().accept(c); + if (runOnNewReaderContext.get() != null) { + runOnNewReaderContext.get().accept(c); + } } }); } From b3db1814a592a5a5acd61e87ee173896d8891a7e Mon Sep 17 00:00:00 2001 From: Artem Prigoda Date: Fri, 27 Oct 2023 21:58:05 +0200 Subject: [PATCH 175/203] Pass index shard's primary term to Engine#addSegmentGenerationListener (#99752) So the search engine can check that generation listeners are called on the same primary term for the search shard as well as the index shard. --- docs/changelog/99752.yaml | 5 +++ .../org/elasticsearch/TransportVersions.java | 1 + .../refresh/TransportShardRefreshAction.java | 1 + ...ansportUnpromotableShardRefreshAction.java | 6 ++- .../UnpromotableShardRefreshRequest.java | 22 ++++++++++- .../action/get/TransportGetAction.java | 5 ++- .../get/TransportGetFromTranslogAction.java | 33 +++++++++++++--- .../get/TransportShardMultiGetAction.java | 5 ++- ...ansportShardMultiGetFomTranslogAction.java | 38 +++++++++++++------ .../support/replication/PostWriteRefresh.java | 1 + .../elasticsearch/index/engine/Engine.java | 4 +- .../index/engine/InternalEngine.java | 3 +- ...romTranslogResponseSerializationTests.java | 27 +++++++++---- ...romTranslogResponseSerializationTests.java | 27 +++++++++---- .../TransportWriteActionTests.java | 4 +- 15 files changed, 142 insertions(+), 40 deletions(-) create mode 100644 docs/changelog/99752.yaml diff --git a/docs/changelog/99752.yaml b/docs/changelog/99752.yaml new file mode 100644 index 0000000000000..c137a563bea39 --- /dev/null +++ b/docs/changelog/99752.yaml @@ -0,0 +1,5 @@ +pr: 99752 +summary: Pass shard's primary term to Engine#addSegmentGenerationListener +area: Store +type: enhancement +issues: [] diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index f969ce312835c..ac3d693df52f6 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -147,6 +147,7 @@ static TransportVersion def(int id) { public static final TransportVersion UNCONTENDED_REGISTER_ANALYSIS_ADDED = def(8_522_00_0); public static final TransportVersion TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED = def(8_523_00_0); public static final TransportVersion IP_ADDRESS_WRITEABLE = def(8_524_00_0); + public static final TransportVersion PRIMARY_TERM_ADDED = def(8_525_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java index f5ebbcd4cc3c9..f4c72d7e37f3d 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java @@ -126,6 +126,7 @@ public void onPrimaryOperationComplete( } else { UnpromotableShardRefreshRequest unpromotableReplicaRequest = new UnpromotableShardRefreshRequest( indexShardRoutingTable, + replicaRequest.primaryRefreshResult.primaryTerm(), replicaRequest.primaryRefreshResult.generation(), false ); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportUnpromotableShardRefreshAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportUnpromotableShardRefreshAction.java index aab4e1f52b1fc..64091af1266f8 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportUnpromotableShardRefreshAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportUnpromotableShardRefreshAction.java @@ -60,7 +60,11 @@ protected void unpromotableShardOperation( ) { ActionListener.run(responseListener, listener -> { IndexShard shard = indicesService.indexServiceSafe(request.shardId().getIndex()).getShard(request.shardId().id()); - shard.waitForSegmentGeneration(request.getSegmentGeneration(), listener.map(l -> ActionResponse.Empty.INSTANCE)); + shard.waitForPrimaryTermAndGeneration( + request.getPrimaryTerm(), + request.getSegmentGeneration(), + listener.map(l -> ActionResponse.Empty.INSTANCE) + ); }); } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/UnpromotableShardRefreshRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/UnpromotableShardRefreshRequest.java index 3e514b230bd54..2e4492725dd8f 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/UnpromotableShardRefreshRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/UnpromotableShardRefreshRequest.java @@ -8,9 +8,11 @@ package org.elasticsearch.action.admin.indices.refresh; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.broadcast.unpromotable.BroadcastUnpromotableRequest; import org.elasticsearch.cluster.routing.IndexShardRoutingTable; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.index.engine.Engine; @@ -21,20 +23,26 @@ public class UnpromotableShardRefreshRequest extends BroadcastUnpromotableRequest { + private final long primaryTerm; private final long segmentGeneration; public UnpromotableShardRefreshRequest( IndexShardRoutingTable indexShardRoutingTable, + long primaryTerm, long segmentGeneration, boolean failShardOnError ) { super(indexShardRoutingTable, failShardOnError); + this.primaryTerm = primaryTerm; this.segmentGeneration = segmentGeneration; } public UnpromotableShardRefreshRequest(StreamInput in) throws IOException { super(in); segmentGeneration = in.readVLong(); + primaryTerm = in.getTransportVersion().onOrAfter(TransportVersions.PRIMARY_TERM_ADDED) + ? in.readVLong() + : Engine.UNKNOWN_PRIMARY_TERM; } @Override @@ -50,14 +58,26 @@ public ActionRequestValidationException validate() { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeVLong(segmentGeneration); + if (out.getTransportVersion().onOrAfter(TransportVersions.PRIMARY_TERM_ADDED)) { + out.writeVLong(primaryTerm); + } } public long getSegmentGeneration() { return segmentGeneration; } + public long getPrimaryTerm() { + return primaryTerm; + } + @Override public String toString() { - return "UnpromotableShardRefreshRequest{" + "shardId=" + shardId() + ", segmentGeneration=" + segmentGeneration + '}'; + return Strings.format( + "UnpromotableShardRefreshRequest{shardId=%s, primaryTerm=%d, segmentGeneration=%d}", + shardId(), + primaryTerm, + segmentGeneration + ); } } diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java index 4b70373979919..9f2fe8ae5aa8c 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.index.IndexService; +import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.get.GetResult; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; @@ -213,7 +214,9 @@ private void handleGetOnUnpromotableShard(GetRequest request, IndexShard indexSh ActionRunnable.supply(l, () -> shardOperation(request, shardId)).run(); } else { assert r.segmentGeneration() > -1L; - indexShard.waitForSegmentGeneration( + assert r.primaryTerm() > Engine.UNKNOWN_PRIMARY_TERM; + indexShard.waitForPrimaryTermAndGeneration( + r.primaryTerm(), r.segmentGeneration(), listener.delegateFailureAndWrap((ll, aLong) -> super.asyncShardOperation(request, shardId, ll)) ); diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java index 553da3d845b54..1b180874b433d 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportGetFromTranslogAction.java @@ -10,6 +10,7 @@ import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; @@ -18,6 +19,7 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -81,7 +83,7 @@ protected void doExecute(Task task, Request request, ActionListener li } segmentGeneration = ((InternalEngine) engine).getLastUnsafeSegmentGenerationForGets(); } - return new Response(result, segmentGeneration); + return new Response(result, indexShard.getOperationPrimaryTerm(), segmentGeneration); }); } @@ -140,23 +142,31 @@ public IndicesOptions indicesOptions() { public static class Response extends ActionResponse { @Nullable private final GetResult getResult; + private final long primaryTerm; private final long segmentGeneration; - public Response(GetResult getResult, long segmentGeneration) { + public Response(GetResult getResult, long primaryTerm, long segmentGeneration) { this.getResult = getResult; this.segmentGeneration = segmentGeneration; + this.primaryTerm = primaryTerm; } public Response(StreamInput in) throws IOException { super(in); segmentGeneration = in.readZLong(); getResult = in.readOptionalWriteable(GetResult::new); + primaryTerm = in.getTransportVersion().onOrAfter(TransportVersions.PRIMARY_TERM_ADDED) + ? in.readVLong() + : Engine.UNKNOWN_PRIMARY_TERM; } @Override public void writeTo(StreamOutput out) throws IOException { out.writeZLong(segmentGeneration); out.writeOptionalWriteable(getResult); + if (out.getTransportVersion().onOrAfter(TransportVersions.PRIMARY_TERM_ADDED)) { + out.writeVLong(primaryTerm); + } } @Nullable @@ -173,22 +183,33 @@ public long segmentGeneration() { return segmentGeneration; } + public long primaryTerm() { + return primaryTerm; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof Response == false) return false; - Response other = (Response) o; - return segmentGeneration == other.segmentGeneration && Objects.equals(getResult, other.getResult); + Response response = (Response) o; + return segmentGeneration == response.segmentGeneration + && Objects.equals(getResult, response.getResult) + && primaryTerm == response.primaryTerm; } @Override public int hashCode() { - return Objects.hash(segmentGeneration, getResult); + return Objects.hash(segmentGeneration, getResult, primaryTerm); } @Override public String toString() { - return "Response{" + "getResult=" + getResult + ", segmentGeneration=" + segmentGeneration + "}"; + return Strings.format( + "Response{getResult=%s, primaryTerm=%d, segmentGeneration=%d}", + getResult, + primaryTerm, + segmentGeneration + ); } } } diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java index 83d5e6a89d138..9986c4019c81b 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetAction.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.index.IndexService; +import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.get.GetResult; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; @@ -203,7 +204,9 @@ private void handleMultiGetOnUnpromotableShard( ActionRunnable.supply(l, () -> handleLocalGets(request, r.multiGetShardResponse(), shardId)).run(); } else { assert r.segmentGeneration() > -1L; - indexShard.waitForSegmentGeneration( + assert r.primaryTerm() > Engine.UNKNOWN_PRIMARY_TERM; + indexShard.waitForPrimaryTermAndGeneration( + r.primaryTerm(), r.segmentGeneration(), listener.delegateFailureAndWrap( (ll, aLong) -> getExecutor(request, shardId).execute( diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java index 032f002a675c4..5058990efd966 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportShardMultiGetFomTranslogAction.java @@ -9,6 +9,7 @@ package org.elasticsearch.action.get; import org.apache.lucene.store.AlreadyClosedException; +import org.elasticsearch.TransportVersions; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; @@ -16,6 +17,7 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.TransportActions; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.index.IndexService; @@ -102,7 +104,7 @@ protected void doExecute(Task task, Request request, ActionListener li } segmentGeneration = ((InternalEngine) engine).getLastUnsafeSegmentGenerationForGets(); } - return new Response(multiGetShardResponse, segmentGeneration); + return new Response(multiGetShardResponse, indexShard.getOperationPrimaryTerm(), segmentGeneration); }); } @@ -164,9 +166,11 @@ public int hashCode() { public static class Response extends ActionResponse { private final MultiGetShardResponse multiGetShardResponse; + private final long primaryTerm; private final long segmentGeneration; - public Response(MultiGetShardResponse response, long segmentGeneration) { + public Response(MultiGetShardResponse response, long primaryTerm, long segmentGeneration) { + this.primaryTerm = primaryTerm; this.segmentGeneration = segmentGeneration; this.multiGetShardResponse = response; } @@ -175,43 +179,55 @@ public Response(StreamInput in) throws IOException { super(in); segmentGeneration = in.readZLong(); multiGetShardResponse = new MultiGetShardResponse(in); + primaryTerm = in.getTransportVersion().onOrAfter(TransportVersions.PRIMARY_TERM_ADDED) + ? in.readVLong() + : Engine.UNKNOWN_PRIMARY_TERM; } @Override public void writeTo(StreamOutput out) throws IOException { out.writeZLong(segmentGeneration); multiGetShardResponse.writeTo(out); + if (out.getTransportVersion().onOrAfter(TransportVersions.PRIMARY_TERM_ADDED)) { + out.writeVLong(primaryTerm); + } } public long segmentGeneration() { return segmentGeneration; } + public long primaryTerm() { + return primaryTerm; + } + public MultiGetShardResponse multiGetShardResponse() { return multiGetShardResponse; } @Override public String toString() { - return "ShardMultiGetFomTranslogResponse{" - + "multiGetShardResponse=" - + multiGetShardResponse - + ", segmentGeneration=" - + segmentGeneration - + "}"; + return Strings.format( + "ShardMultiGetFomTranslogResponse{multiGetShardResponse=%s, primaryTerm=%d, segmentGeneration=%d}", + multiGetShardResponse, + primaryTerm, + segmentGeneration + ); } @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof Response == false) return false; - Response other = (Response) o; - return segmentGeneration == other.segmentGeneration && Objects.equals(multiGetShardResponse, other.multiGetShardResponse); + Response response = (Response) o; + return segmentGeneration == response.segmentGeneration + && Objects.equals(multiGetShardResponse, response.multiGetShardResponse) + && primaryTerm == response.primaryTerm; } @Override public int hashCode() { - return Objects.hash(segmentGeneration, multiGetShardResponse); + return Objects.hash(segmentGeneration, multiGetShardResponse, primaryTerm); } } } diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/PostWriteRefresh.java b/server/src/main/java/org/elasticsearch/action/support/replication/PostWriteRefresh.java index 6fd7ef88a8e86..9f6d4ed27cf6c 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/PostWriteRefresh.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/PostWriteRefresh.java @@ -146,6 +146,7 @@ private void sendUnpromotableRequests( ) { UnpromotableShardRefreshRequest unpromotableReplicaRequest = new UnpromotableShardRefreshRequest( indexShard.getReplicationGroup().getRoutingTable(), + indexShard.getOperationPrimaryTerm(), generation, true ); diff --git a/server/src/main/java/org/elasticsearch/index/engine/Engine.java b/server/src/main/java/org/elasticsearch/index/engine/Engine.java index 85ede932f231a..a496429cc3e2b 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -2141,13 +2141,13 @@ public void addFlushListener(Translog.Location location, ActionListener li * refreshed is true if a refresh happened. If refreshed, generation * contains the generation of the index commit that the reader has opened upon refresh. */ - public record RefreshResult(boolean refreshed, long generation) { + public record RefreshResult(boolean refreshed, long primaryTerm, long generation) { public static final long UNKNOWN_GENERATION = -1L; public static final RefreshResult NO_REFRESH = new RefreshResult(false); public RefreshResult(boolean refreshed) { - this(refreshed, UNKNOWN_GENERATION); + this(refreshed, UNKNOWN_PRIMARY_TERM, UNKNOWN_GENERATION); } } diff --git a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index e9651a7f63867..9419cd7e6ab5f 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -2075,7 +2075,8 @@ protected final RefreshResult refresh(String source, SearcherScope scope, boolea // for a long time: maybePruneDeletes(); mergeScheduler.refreshConfig(); - return new RefreshResult(refreshed, segmentGeneration); + long primaryTerm = config().getPrimaryTermSupplier().getAsLong(); + return new RefreshResult(refreshed, primaryTerm, segmentGeneration); } @Override diff --git a/server/src/test/java/org/elasticsearch/action/get/GetFromTranslogResponseSerializationTests.java b/server/src/test/java/org/elasticsearch/action/get/GetFromTranslogResponseSerializationTests.java index 4c1d3c14f4956..1a6cc70ba08ad 100644 --- a/server/src/test/java/org/elasticsearch/action/get/GetFromTranslogResponseSerializationTests.java +++ b/server/src/test/java/org/elasticsearch/action/get/GetFromTranslogResponseSerializationTests.java @@ -24,26 +24,39 @@ protected Writeable.Reader instanceRead @Override protected TransportGetFromTranslogAction.Response createTestInstance() { - return new TransportGetFromTranslogAction.Response(randomGetResult(), randomSegmentGeneration()); + return new TransportGetFromTranslogAction.Response(randomGetResult(), randomPrimaryTerm(), randomSegmentGeneration()); } @Override protected TransportGetFromTranslogAction.Response mutateInstance(TransportGetFromTranslogAction.Response instance) throws IOException { - return randomBoolean() - ? new TransportGetFromTranslogAction.Response( - instance.getResult(), - randomValueOtherThan(instance.segmentGeneration(), this::randomSegmentGeneration) - ) - : new TransportGetFromTranslogAction.Response( + return switch (randomInt(2)) { + case 0 -> new TransportGetFromTranslogAction.Response( randomValueOtherThan(instance.getResult(), this::randomGetResult), + instance.primaryTerm(), + instance.segmentGeneration() + ); + case 1 -> new TransportGetFromTranslogAction.Response( + instance.getResult(), + randomValueOtherThan(instance.primaryTerm(), this::randomPrimaryTerm), instance.segmentGeneration() ); + case 2 -> new TransportGetFromTranslogAction.Response( + instance.getResult(), + instance.primaryTerm(), + randomValueOtherThan(instance.segmentGeneration(), this::randomSegmentGeneration) + ); + default -> randomValueOtherThan(instance, this::createTestInstance); + }; } private long randomSegmentGeneration() { return randomBoolean() ? -1L : randomNonNegativeLong(); } + private long randomPrimaryTerm() { + return randomNonNegativeLong(); + } + private GetResult randomGetResult() { return randomBoolean() ? null : GetResultTests.randomGetResult(randomFrom(XContentType.values())).v1(); } diff --git a/server/src/test/java/org/elasticsearch/action/get/ShardMultiGetFromTranslogResponseSerializationTests.java b/server/src/test/java/org/elasticsearch/action/get/ShardMultiGetFromTranslogResponseSerializationTests.java index c3f87ee9ce26d..402566394f61b 100644 --- a/server/src/test/java/org/elasticsearch/action/get/ShardMultiGetFromTranslogResponseSerializationTests.java +++ b/server/src/test/java/org/elasticsearch/action/get/ShardMultiGetFromTranslogResponseSerializationTests.java @@ -28,26 +28,39 @@ protected Writeable.Reader instanceReader() { @Override protected Response createTestInstance() { - return new Response(randomMultiGetShardResponse(), randomSegmentGeneration()); + return new Response(randomMultiGetShardResponse(), randomPrimaryTerm(), randomSegmentGeneration()); } @Override protected Response mutateInstance(Response instance) throws IOException { - return randomBoolean() - ? new Response( - instance.multiGetShardResponse(), - randomValueOtherThan(instance.segmentGeneration(), this::randomSegmentGeneration) - ) - : new Response( + return switch (randomInt(2)) { + case 0 -> new Response( randomValueOtherThan(instance.multiGetShardResponse(), this::randomMultiGetShardResponse), + instance.primaryTerm(), + instance.segmentGeneration() + ); + case 1 -> new Response( + instance.multiGetShardResponse(), + randomValueOtherThan(instance.primaryTerm(), this::randomPrimaryTerm), instance.segmentGeneration() ); + case 2 -> new Response( + instance.multiGetShardResponse(), + instance.primaryTerm(), + randomValueOtherThan(instance.segmentGeneration(), this::randomSegmentGeneration) + ); + default -> randomValueOtherThan(instance, this::createTestInstance); + }; } private long randomSegmentGeneration() { return randomBoolean() ? -1L : randomNonNegativeLong(); } + private long randomPrimaryTerm() { + return randomNonNegativeLong(); + } + private GetResponse randomGetResponse() { return randomBoolean() ? null : new GetResponse(GetResultTests.randomGetResult(randomFrom(XContentType.values())).v1()); } diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java index 4f926b14a1904..25d9f9fec884b 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java @@ -103,7 +103,7 @@ public void initCommonMocks() { indexShard = mock(IndexShard.class); location = mock(Translog.Location.class); clusterService = createClusterService(threadPool); - when(indexShard.refresh(any())).thenReturn(new Engine.RefreshResult(true, 1)); + when(indexShard.refresh(any())).thenReturn(new Engine.RefreshResult(true, randomNonNegativeLong(), 1)); ReplicationGroup replicationGroup = mock(ReplicationGroup.class); when(indexShard.getReplicationGroup()).thenReturn(replicationGroup); when(replicationGroup.getReplicationTargets()).thenReturn(Collections.emptyList()); @@ -199,7 +199,7 @@ public void testReplicaImmediateRefresh() throws Exception { verify(indexShard).externalRefresh(eq(PostWriteRefresh.FORCED_REFRESH_AFTER_INDEX), refreshListener.capture()); verify(indexShard, never()).addRefreshListener(any(), any()); // Fire the listener manually - refreshListener.getValue().onResponse(new Engine.RefreshResult(randomBoolean(), randomNonNegativeLong())); + refreshListener.getValue().onResponse(new Engine.RefreshResult(randomBoolean(), randomNonNegativeLong(), randomNonNegativeLong())); assertNotNull(listener.response); assertNull(listener.failure); } From 74da80771ac16af9d2436590819f421f59d26bc4 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Fri, 27 Oct 2023 22:59:17 +0300 Subject: [PATCH 176/203] ESQL: adds Enrich implicit match_fields to field_caps call (#101456) * Take into account the Enrich implicit match_field when resolving field names of the source index --- docs/changelog/101456.yaml | 6 + docs/reference/esql/metadata-fields.asciidoc | 8 +- .../esql/processing-commands/enrich.asciidoc | 20 +- .../esql/qa/single_node/RestEnrichIT.java | 12 ++ .../esql/qa/rest/RestEnrichTestCase.java | 173 ++++++++++++++++++ ...s.csv-spec => blog-IT_tests_only.csv-spec} | 0 ...s.csv-spec => docs-IT_tests_only.csv-spec} | 0 ...csv-spec => enrich-IT_tests_only.csv-spec} | 0 ...v-spec => metadata-IT_tests_only.csv-spec} | 0 .../xpack/esql/analysis/Analyzer.java | 6 +- .../xpack/esql/session/EsqlSession.java | 35 +++- .../elasticsearch/xpack/esql/CsvTests.java | 5 +- .../xpack/esql/analysis/AnalyzerTests.java | 2 +- 13 files changed, 237 insertions(+), 30 deletions(-) create mode 100644 docs/changelog/101456.yaml create mode 100644 x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEnrichIT.java create mode 100644 x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEnrichTestCase.java rename x-pack/plugin/esql/qa/testFixtures/src/main/resources/{blog-ignoreCsvTests.csv-spec => blog-IT_tests_only.csv-spec} (100%) rename x-pack/plugin/esql/qa/testFixtures/src/main/resources/{docs-ignoreCsvTests.csv-spec => docs-IT_tests_only.csv-spec} (100%) rename x-pack/plugin/esql/qa/testFixtures/src/main/resources/{enrich-ignoreCsvTests.csv-spec => enrich-IT_tests_only.csv-spec} (100%) rename x-pack/plugin/esql/qa/testFixtures/src/main/resources/{metadata-ignoreCsvTests.csv-spec => metadata-IT_tests_only.csv-spec} (100%) diff --git a/docs/changelog/101456.yaml b/docs/changelog/101456.yaml new file mode 100644 index 0000000000000..db55dfbde1c64 --- /dev/null +++ b/docs/changelog/101456.yaml @@ -0,0 +1,6 @@ +pr: 101456 +summary: "ESQL: adds Enrich implicit `match_fields` to `field_caps` call" +area: ES|QL +type: bug +issues: + - 101328 diff --git a/docs/reference/esql/metadata-fields.asciidoc b/docs/reference/esql/metadata-fields.asciidoc index c034d4d0dd2b3..eb08ee085de38 100644 --- a/docs/reference/esql/metadata-fields.asciidoc +++ b/docs/reference/esql/metadata-fields.asciidoc @@ -34,11 +34,11 @@ like the other index fields: [source.merge.styled,esql] ---- -include::{esql-specs}/metadata-ignoreCsvTests.csv-spec[tag=multipleIndices] +include::{esql-specs}/metadata-IT_tests_only.csv-spec[tag=multipleIndices] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/metadata-ignoreCsvTests.csv-spec[tag=multipleIndices-result] +include::{esql-specs}/metadata-IT_tests_only.csv-spec[tag=multipleIndices-result] |=== Also, similar to the index fields, once an aggregation is performed, a @@ -47,9 +47,9 @@ used as grouping field: [source.merge.styled,esql] ---- -include::{esql-specs}/metadata-ignoreCsvTests.csv-spec[tag=metaIndexInAggs] +include::{esql-specs}/metadata-IT_tests_only.csv-spec[tag=metaIndexInAggs] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/metadata-ignoreCsvTests.csv-spec[tag=metaIndexInAggs-result] +include::{esql-specs}/metadata-IT_tests_only.csv-spec[tag=metaIndexInAggs-result] |=== diff --git a/docs/reference/esql/processing-commands/enrich.asciidoc b/docs/reference/esql/processing-commands/enrich.asciidoc index 1e489119d4ca3..df402f3b1bd50 100644 --- a/docs/reference/esql/processing-commands/enrich.asciidoc +++ b/docs/reference/esql/processing-commands/enrich.asciidoc @@ -49,15 +49,15 @@ column for each enrich field defined in the policy. The match is performed using the `match_field` defined in the <> and requires that the input table has a column with the same name (`language_code` in this example). `ENRICH` will look for records in the -<> based on the match field value. +<> based on the match field value. [source.merge.styled,esql] ---- -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich-result] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich-result] |=== To use a column with a different name than the `match_field` defined in the @@ -65,35 +65,35 @@ policy as the match field, use `ON `: [source.merge.styled,esql] ---- -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_on] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich_on] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_on-result] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich_on-result] |=== By default, each of the enrich fields defined in the policy is added as a -column. To explicitly select the enrich fields that are added, use +column. To explicitly select the enrich fields that are added, use `WITH , ...`: [source.merge.styled,esql] ---- -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_with] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich_with] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_with-result] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich_with-result] |=== You can rename the columns that are added using `WITH new_name=`: [source.merge.styled,esql] ---- -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_rename] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich_rename] ---- [%header.monospaced.styled,format=dsv,separator=|] |=== -include::{esql-specs}/docs-ignoreCsvTests.csv-spec[tag=enrich_rename-result] +include::{esql-specs}/docs-IT_tests_only.csv-spec[tag=enrich_rename-result] |=== In case of name collisions, the newly created columns will override existing diff --git a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEnrichIT.java b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEnrichIT.java new file mode 100644 index 0000000000000..b2222f4f2e78e --- /dev/null +++ b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/RestEnrichIT.java @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.qa.single_node; + +import org.elasticsearch.xpack.esql.qa.rest.RestEnrichTestCase; + +public class RestEnrichIT extends RestEnrichTestCase {} diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEnrichTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEnrichTestCase.java new file mode 100644 index 0000000000000..f409fc6e69dee --- /dev/null +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/RestEnrichTestCase.java @@ -0,0 +1,173 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.qa.rest; + +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.test.rest.ESRestTestCase; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.test.MapMatcher.assertMap; +import static org.elasticsearch.test.MapMatcher.matchesMap; +import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.runEsql; +import static org.hamcrest.Matchers.containsString; + +public class RestEnrichTestCase extends ESRestTestCase { + + private static final String sourceIndexName = "countries"; + private static final String testIndexName = "test"; + private static final String policyName = "countries"; + + @Before + @After + public void assertRequestBreakerEmpty() throws Exception { + EsqlSpecTestCase.assertRequestBreakerEmpty(); + } + + @Before + public void loadTestData() throws IOException { + Request request = new Request("PUT", "/" + testIndexName); + request.setJsonEntity(""" + { + "mappings": { + "properties": { + "geo.dest": { + "type": "keyword" + }, + "number": { + "type": "long" + } + } + } + }"""); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + request = new Request("POST", "/" + testIndexName + "/_bulk"); + request.addParameter("refresh", "true"); + request.setJsonEntity(""" + { "index": {"_id": 1} } + { "geo.dest": "US", "number": 1000 } + { "index": {"_id": 2} } + { "geo.dest": "US", "number": 1000 } + { "index": {"_id": 3} } + { "geo.dest": "CN", "number": 5000 } + """); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + request = new Request("PUT", "/" + sourceIndexName); + request.setJsonEntity(""" + { + "mappings": { + "properties": { + "geo.dest": { + "type": "keyword" + }, + "country_name": { + "type": "keyword" + } + } + } + }"""); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + request = new Request("POST", "/" + sourceIndexName + "/_bulk"); + request.addParameter("refresh", "true"); + request.setJsonEntity(""" + { "index" : {}} + { "geo.dest": "US", "country_name": "United States of America" } + { "index" : {}} + { "geo.dest": "IN", "country_name": "India" } + { "index" : {}} + { "geo.dest": "CN", "country_name": "China" } + """); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + request = new Request("PUT", "/_enrich/policy/" + policyName); + request.setJsonEntity(""" + { + "match": { + "indices": "countries", + "match_field": "geo.dest", + "enrich_fields": ["country_name"] + } + } + """); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + + request = new Request("PUT", "/_enrich/policy/" + policyName + "/_execute"); + assertEquals(200, client().performRequest(request).getStatusLine().getStatusCode()); + } + + @After + public void wipeTestData() throws IOException { + try { + var response = client().performRequest(new Request("DELETE", "/" + testIndexName)); + assertEquals(200, response.getStatusLine().getStatusCode()); + response = client().performRequest(new Request("DELETE", "/" + sourceIndexName)); + assertEquals(200, response.getStatusLine().getStatusCode()); + response = client().performRequest(new Request("DELETE", "/_enrich/policy/" + policyName)); + assertEquals(200, response.getStatusLine().getStatusCode()); + } catch (ResponseException re) { + assertEquals(404, re.getResponse().getStatusLine().getStatusCode()); + } + } + + public void testNonExistentEnrichPolicy() throws IOException { + ResponseException re = expectThrows( + ResponseException.class, + () -> runEsql(new RestEsqlTestCase.RequestObjectBuilder().query("from test | enrich countris").build()) + ); + assertThat( + EntityUtils.toString(re.getResponse().getEntity()), + containsString("unresolved enrich policy [countris], did you mean [countries]?") + ); + } + + public void testNonExistentEnrichPolicy_KeepField() throws IOException { + ResponseException re = expectThrows( + ResponseException.class, + () -> runEsql(new RestEsqlTestCase.RequestObjectBuilder().query("from test | enrich countris | keep number").build()) + ); + assertThat( + EntityUtils.toString(re.getResponse().getEntity()), + containsString("unresolved enrich policy [countris], did you mean [countries]?") + ); + } + + public void testMatchField_ImplicitFieldsList() throws IOException { + Map result = runEsql( + new RestEsqlTestCase.RequestObjectBuilder().query("from test | enrich countries | keep number").build() + ); + var columns = List.of(Map.of("name", "number", "type", "long")); + var values = List.of(List.of(1000), List.of(1000), List.of(5000)); + + assertMap(result, matchesMap().entry("columns", columns).entry("values", values)); + } + + public void testMatchField_ImplicitFieldsList_WithStats() throws IOException { + Map result = runEsql( + new RestEsqlTestCase.RequestObjectBuilder().query("from test | enrich countries | stats s = sum(number) by country_name") + .build() + ); + var columns = List.of(Map.of("name", "s", "type", "long"), Map.of("name", "country_name", "type", "keyword")); + var values = List.of(List.of(2000, "United States of America"), List.of(5000, "China")); + + assertMap(result, matchesMap().entry("columns", columns).entry("values", values)); + } + + @Override + protected boolean preserveClusterUponCompletion() { + return true; + } +} diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/blog-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/blog-IT_tests_only.csv-spec similarity index 100% rename from x-pack/plugin/esql/qa/testFixtures/src/main/resources/blog-ignoreCsvTests.csv-spec rename to x-pack/plugin/esql/qa/testFixtures/src/main/resources/blog-IT_tests_only.csv-spec diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs-IT_tests_only.csv-spec similarity index 100% rename from x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs-ignoreCsvTests.csv-spec rename to x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs-IT_tests_only.csv-spec diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/enrich-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/enrich-IT_tests_only.csv-spec similarity index 100% rename from x-pack/plugin/esql/qa/testFixtures/src/main/resources/enrich-ignoreCsvTests.csv-spec rename to x-pack/plugin/esql/qa/testFixtures/src/main/resources/enrich-IT_tests_only.csv-spec diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-IT_tests_only.csv-spec similarity index 100% rename from x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-ignoreCsvTests.csv-spec rename to x-pack/plugin/esql/qa/testFixtures/src/main/resources/metadata-IT_tests_only.csv-spec diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java index 297be5c6aec48..557da9639a086 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java @@ -522,11 +522,7 @@ private LogicalPlan resolveEnrich(Enrich enrich, List childrenOutput) } if (resolved.resolved() && resolved.dataType() != KEYWORD) { resolved = ua.withUnresolvedMessage( - "Unsupported type [" - + resolved.dataType() - + "] for enrich matching field [" - + ua.name() - + "]; only KEYWORD allowed" + "Unsupported type [" + resolved.dataType() + "] for enrich matching field [" + ua.name() + "]; only KEYWORD allowed" ); } return new Enrich(enrich.source(), enrich.child(), enrich.policyName(), resolved, enrich.policy(), enrich.enrichFields()); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java index 0004f085eec56..d1a073f64fe81 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java @@ -54,7 +54,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -151,22 +150,33 @@ private void preAnalyze(LogicalPlan parsed, BiFunction policyNames = new HashSet<>(preAnalysis.policyNames); EnrichResolution resolution = new EnrichResolution(ConcurrentCollections.newConcurrentSet(), enrichPolicyResolver.allPolicyNames()); - AtomicReference resolvedIndex = new AtomicReference<>(); + ActionListener groupedListener = listener.delegateFailureAndWrap((l, unused) -> { assert resolution.resolvedPolicies().size() == policyNames.size() : resolution.resolvedPolicies().size() + " != " + policyNames.size(); - assert resolvedIndex.get() != null : "index wasn't resolved"; - l.onResponse(action.apply(resolvedIndex.get(), resolution)); + + // first we need the match_fields names from enrich policies and THEN, with an updated list of fields, we call field_caps API + var matchFields = resolution.resolvedPolicies() + .stream() + .filter(p -> p.index().isValid()) // only if the policy by the specified name was found; later the Verifier will be + // triggered + .map(p -> p.policy().getMatchField()) + .collect(Collectors.toSet()); + + preAnalyzeIndices( + parsed, + ActionListener.wrap(indexResolution -> l.onResponse(action.apply(indexResolution, resolution)), listener::onFailure), + matchFields + ); }); try (RefCountingListener refs = new RefCountingListener(groupedListener)) { - preAnalyzeIndices(parsed, refs.acquire(resolvedIndex::set)); for (String policyName : policyNames) { enrichPolicyResolver.resolvePolicy(policyName, refs.acquire(resolution.resolvedPolicies()::add)); } } } - private void preAnalyzeIndices(LogicalPlan parsed, ActionListener listener) { + private void preAnalyzeIndices(LogicalPlan parsed, ActionListener listener, Set enrichPolicyMatchFields) { PreAnalyzer.PreAnalysis preAnalysis = new PreAnalyzer().preAnalyze(parsed); // TODO we plan to support joins in the future when possible, but for now we'll just fail early if we see one if (preAnalysis.indices.size() > 1) { @@ -176,6 +186,11 @@ private void preAnalyzeIndices(LogicalPlan parsed, ActionListener fieldNames(LogicalPlan parsed) { if (fieldNames.isEmpty()) { return IndexResolver.ALL_FIELDS; } else { - fieldNames.addAll( - fieldNames.stream().filter(name -> name.endsWith(WILDCARD) == false).map(name -> name + ".*").collect(Collectors.toSet()) - ); + fieldNames.addAll(subfields(fieldNames)); return fieldNames; } } @@ -269,6 +282,10 @@ private static boolean matchByName(Attribute attr, String other, boolean skipIfP return isPattern ? Regex.simpleMatch(attr.qualifiedName(), other) : attr.qualifiedName().equals(other); } + private static Set subfields(Set names) { + return names.stream().filter(name -> name.endsWith(WILDCARD) == false).map(name -> name + ".*").collect(Collectors.toSet()); + } + public void optimizedPlan(LogicalPlan logicalPlan, ActionListener listener) { analyzedPlan(logicalPlan, map(listener, p -> { var plan = logicalPlanOptimizer.optimize(p); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index be5f2e8315aa6..13581710f7c53 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -142,6 +142,7 @@ public class CsvTests extends ESTestCase { private static final Logger LOGGER = LogManager.getLogger(CsvTests.class); + private static final String IGNORED_CSV_FILE_NAMES_PATTERN = "-IT_tests_only"; private final String fileName; private final String groupName; @@ -160,7 +161,9 @@ public class CsvTests extends ESTestCase { @ParametersFactory(argumentFormatting = "%2$s.%3$s") public static List readScriptSpec() throws Exception { - List urls = classpathResources("/*.csv-spec").stream().filter(x -> x.toString().contains("-ignoreCsvTests") == false).toList(); + List urls = classpathResources("/*.csv-spec").stream() + .filter(x -> x.toString().contains(IGNORED_CSV_FILE_NAMES_PATTERN) == false) + .toList(); assertTrue("Not enough specs found " + urls, urls.size() > 0); return SpecReader.readScriptSpec(urls, specParser()); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java index 41e4b906f7e64..6e75eea75f655 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java @@ -1274,7 +1274,7 @@ public void testEnrichWrongMatchFieldType() { """)); assertThat( e.getMessage(), - containsString("Unsupported type [INTEGER] for enrich matching field [languages]; only KEYWORD allowed") + containsString("Unsupported type [INTEGER] for enrich matching field [languages]; only KEYWORD allowed") ); } From 5cb3dae344ab8d6af9198fe06b63048af3ff370e Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 27 Oct 2023 21:55:59 -0700 Subject: [PATCH 177/203] Remove null block check in aggs (#101479) We no longer need these checks because a null block can be cast to any block type. Relates #101414 --- .../compute/gen/AggregatorImplementer.java | 4 +--- .../gen/GroupingAggregatorImplementer.java | 12 +----------- .../CountDistinctBooleanAggregatorFunction.java | 6 +----- ...DistinctBooleanGroupingAggregatorFunction.java | 15 +-------------- .../CountDistinctBytesRefAggregatorFunction.java | 6 +----- ...istinctBytesRefGroupingAggregatorFunction.java | 15 +-------------- .../CountDistinctDoubleAggregatorFunction.java | 6 +----- ...tDistinctDoubleGroupingAggregatorFunction.java | 15 +-------------- .../CountDistinctIntAggregatorFunction.java | 6 +----- ...ountDistinctIntGroupingAggregatorFunction.java | 15 +-------------- .../CountDistinctLongAggregatorFunction.java | 6 +----- ...untDistinctLongGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/MaxDoubleAggregatorFunction.java | 6 +----- .../MaxDoubleGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/MaxIntAggregatorFunction.java | 6 +----- .../MaxIntGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/MaxLongAggregatorFunction.java | 6 +----- .../MaxLongGroupingAggregatorFunction.java | 15 +-------------- ...AbsoluteDeviationDoubleAggregatorFunction.java | 6 +----- ...DeviationDoubleGroupingAggregatorFunction.java | 15 +-------------- ...ianAbsoluteDeviationIntAggregatorFunction.java | 6 +----- ...uteDeviationIntGroupingAggregatorFunction.java | 15 +-------------- ...anAbsoluteDeviationLongAggregatorFunction.java | 6 +----- ...teDeviationLongGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/MinDoubleAggregatorFunction.java | 6 +----- .../MinDoubleGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/MinIntAggregatorFunction.java | 6 +----- .../MinIntGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/MinLongAggregatorFunction.java | 6 +----- .../MinLongGroupingAggregatorFunction.java | 15 +-------------- .../PercentileDoubleAggregatorFunction.java | 6 +----- ...ercentileDoubleGroupingAggregatorFunction.java | 15 +-------------- .../PercentileIntAggregatorFunction.java | 6 +----- .../PercentileIntGroupingAggregatorFunction.java | 15 +-------------- .../PercentileLongAggregatorFunction.java | 6 +----- .../PercentileLongGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/SumDoubleAggregatorFunction.java | 6 +----- .../SumDoubleGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/SumIntAggregatorFunction.java | 6 +----- .../SumIntGroupingAggregatorFunction.java | 15 +-------------- .../aggregation/SumLongAggregatorFunction.java | 6 +----- .../SumLongGroupingAggregatorFunction.java | 15 +-------------- .../CountGroupingAggregatorFunction.java | 10 ---------- 43 files changed, 42 insertions(+), 404 deletions(-) diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/AggregatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/AggregatorImplementer.java index 633af256257ad..8c5646ab56011 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/AggregatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/AggregatorImplementer.java @@ -293,9 +293,7 @@ private MethodSpec intermediateBlockCount() { private MethodSpec addRawInput() { MethodSpec.Builder builder = MethodSpec.methodBuilder("addRawInput"); builder.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(PAGE, "page"); - builder.addStatement("$T uncastBlock = page.getBlock(channels.get(0))", BLOCK); - builder.beginControlFlow("if (uncastBlock.areAllValuesNull())").addStatement("return").endControlFlow(); - builder.addStatement("$T block = ($T) uncastBlock", valueBlockType(init, combine), valueBlockType(init, combine)); + builder.addStatement("$T block = page.getBlock(channels.get(0))", valueBlockType(init, combine)); builder.addStatement("$T vector = block.asVector()", valueVectorType(init, combine)); builder.beginControlFlow("if (vector != null)").addStatement("addRawVector(vector)"); builder.nextControlFlow("else").addStatement("addRawBlock(block)").endControlFlow(); diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/GroupingAggregatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/GroupingAggregatorImplementer.java index e357f057c13f2..b84ab64f4f54c 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/GroupingAggregatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/GroupingAggregatorImplementer.java @@ -36,7 +36,6 @@ import static org.elasticsearch.compute.gen.Methods.findMethod; import static org.elasticsearch.compute.gen.Methods.findRequiredMethod; import static org.elasticsearch.compute.gen.Methods.vectorAccessorName; -import static org.elasticsearch.compute.gen.Types.BLOCK; import static org.elasticsearch.compute.gen.Types.BLOCK_ARRAY; import static org.elasticsearch.compute.gen.Types.BYTES_REF; import static org.elasticsearch.compute.gen.Types.DRIVER_CONTEXT; @@ -250,16 +249,7 @@ private MethodSpec prepareProcessPage() { builder.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).returns(GROUPING_AGGREGATOR_FUNCTION_ADD_INPUT); builder.addParameter(SEEN_GROUP_IDS, "seenGroupIds").addParameter(PAGE, "page"); - builder.addStatement("$T uncastValuesBlock = page.getBlock(channels.get(0))", BLOCK); - - builder.beginControlFlow("if (uncastValuesBlock.areAllValuesNull())"); - { - builder.addStatement("state.enableGroupIdTracking(seenGroupIds)"); - builder.addStatement("return $L", addInput(b -> {})); - } - builder.endControlFlow(); - - builder.addStatement("$T valuesBlock = ($T) uncastValuesBlock", valueBlockType(init, combine), valueBlockType(init, combine)); + builder.addStatement("$T valuesBlock = page.getBlock(channels.get(0))", valueBlockType(init, combine)); builder.addStatement("$T valuesVector = valuesBlock.asVector()", valueVectorType(init, combine)); builder.beginControlFlow("if (valuesVector == null)"); { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunction.java index a081166ed8bf5..f0344ec6b56ba 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanAggregatorFunction.java @@ -54,11 +54,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - BooleanBlock block = (BooleanBlock) uncastBlock; + BooleanBlock block = page.getBlock(channels.get(0)); BooleanVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunction.java index 820cb889d2ec6..adc09f8bba828 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBooleanGroupingAggregatorFunction.java @@ -62,20 +62,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - BooleanBlock valuesBlock = (BooleanBlock) uncastValuesBlock; + BooleanBlock valuesBlock = page.getBlock(channels.get(0)); BooleanVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunction.java index f63144ea3c72f..15d3290cdb18f 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefAggregatorFunction.java @@ -61,11 +61,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - BytesRefBlock block = (BytesRefBlock) uncastBlock; + BytesRefBlock block = page.getBlock(channels.get(0)); BytesRefVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunction.java index 36bcb6d145c05..46cfbcc99a373 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctBytesRefGroupingAggregatorFunction.java @@ -65,20 +65,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - BytesRefBlock valuesBlock = (BytesRefBlock) uncastValuesBlock; + BytesRefBlock valuesBlock = page.getBlock(channels.get(0)); BytesRefVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunction.java index 4e7893d74ca78..77e47b543bd26 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleAggregatorFunction.java @@ -63,11 +63,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - DoubleBlock block = (DoubleBlock) uncastBlock; + DoubleBlock block = page.getBlock(channels.get(0)); DoubleVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunction.java index 54c57c2138505..84ed6d2f06329 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctDoubleGroupingAggregatorFunction.java @@ -67,20 +67,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - DoubleBlock valuesBlock = (DoubleBlock) uncastValuesBlock; + DoubleBlock valuesBlock = page.getBlock(channels.get(0)); DoubleVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunction.java index 091379cd2145f..1f6f91d4d6adc 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntAggregatorFunction.java @@ -63,11 +63,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - IntBlock block = (IntBlock) uncastBlock; + IntBlock block = page.getBlock(channels.get(0)); IntVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunction.java index acaff7b8e96d7..b5dc7e43467a8 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctIntGroupingAggregatorFunction.java @@ -65,20 +65,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - IntBlock valuesBlock = (IntBlock) uncastValuesBlock; + IntBlock valuesBlock = page.getBlock(channels.get(0)); IntVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunction.java index 5a85b4132b4ef..cf51b3dcff5a1 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongAggregatorFunction.java @@ -63,11 +63,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - LongBlock block = (LongBlock) uncastBlock; + LongBlock block = page.getBlock(channels.get(0)); LongVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunction.java index 06165026a5d16..6c69845dbf107 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/CountDistinctLongGroupingAggregatorFunction.java @@ -67,20 +67,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - LongBlock valuesBlock = (LongBlock) uncastValuesBlock; + LongBlock valuesBlock = page.getBlock(channels.get(0)); LongVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunction.java index e5dfb17bdf819..c613623230e7f 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - DoubleBlock block = (DoubleBlock) uncastBlock; + DoubleBlock block = page.getBlock(channels.get(0)); DoubleVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunction.java index b761bb66b8edb..86ada78737512 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxDoubleGroupingAggregatorFunction.java @@ -59,20 +59,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - DoubleBlock valuesBlock = (DoubleBlock) uncastValuesBlock; + DoubleBlock valuesBlock = page.getBlock(channels.get(0)); DoubleVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunction.java index 1f1f0981ec4cd..9307522e515a8 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - IntBlock block = (IntBlock) uncastBlock; + IntBlock block = page.getBlock(channels.get(0)); IntVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunction.java index a7ef8d5573fdc..d0809b2a6853c 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxIntGroupingAggregatorFunction.java @@ -57,20 +57,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - IntBlock valuesBlock = (IntBlock) uncastValuesBlock; + IntBlock valuesBlock = page.getBlock(channels.get(0)); IntVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunction.java index 0e527fcefce79..6902539b8b2f0 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - LongBlock block = (LongBlock) uncastBlock; + LongBlock block = page.getBlock(channels.get(0)); LongVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunction.java index d224ecd2d293e..5b28fa01ef0ce 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MaxLongGroupingAggregatorFunction.java @@ -59,20 +59,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - LongBlock valuesBlock = (LongBlock) uncastValuesBlock; + LongBlock valuesBlock = page.getBlock(channels.get(0)); LongVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunction.java index 075ee7660adce..d7b84492c4cbc 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - DoubleBlock block = (DoubleBlock) uncastBlock; + DoubleBlock block = page.getBlock(channels.get(0)); DoubleVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunction.java index 5707f9942d0f8..08bbde35e592e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationDoubleGroupingAggregatorFunction.java @@ -63,20 +63,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - DoubleBlock valuesBlock = (DoubleBlock) uncastValuesBlock; + DoubleBlock valuesBlock = page.getBlock(channels.get(0)); DoubleVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunction.java index 3927456023ffa..211f0f622b728 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - IntBlock block = (IntBlock) uncastBlock; + IntBlock block = page.getBlock(channels.get(0)); IntVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunction.java index b8f08efffd7d1..cad7e172e67bb 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationIntGroupingAggregatorFunction.java @@ -61,20 +61,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - IntBlock valuesBlock = (IntBlock) uncastValuesBlock; + IntBlock valuesBlock = page.getBlock(channels.get(0)); IntVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunction.java index 3123d102a1a72..2311ce3c18315 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - LongBlock block = (LongBlock) uncastBlock; + LongBlock block = page.getBlock(channels.get(0)); LongVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunction.java index 74ee25c27c86a..dae97d17db711 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MedianAbsoluteDeviationLongGroupingAggregatorFunction.java @@ -63,20 +63,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - LongBlock valuesBlock = (LongBlock) uncastValuesBlock; + LongBlock valuesBlock = page.getBlock(channels.get(0)); LongVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunction.java index d40f2a0622974..afcace2069ebf 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - DoubleBlock block = (DoubleBlock) uncastBlock; + DoubleBlock block = page.getBlock(channels.get(0)); DoubleVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunction.java index d7c2c59eaed2c..4d0e78a26865d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinDoubleGroupingAggregatorFunction.java @@ -59,20 +59,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - DoubleBlock valuesBlock = (DoubleBlock) uncastValuesBlock; + DoubleBlock valuesBlock = page.getBlock(channels.get(0)); DoubleVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntAggregatorFunction.java index 5b28da79797b6..e8deaf9cf07fd 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - IntBlock block = (IntBlock) uncastBlock; + IntBlock block = page.getBlock(channels.get(0)); IntVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunction.java index bf0c4c5a5e02c..97b5eafa9e72e 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinIntGroupingAggregatorFunction.java @@ -57,20 +57,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - IntBlock valuesBlock = (IntBlock) uncastValuesBlock; + IntBlock valuesBlock = page.getBlock(channels.get(0)); IntVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongAggregatorFunction.java index 2b6bf88e0880d..61f41c9693725 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - LongBlock block = (LongBlock) uncastBlock; + LongBlock block = page.getBlock(channels.get(0)); LongVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunction.java index d7baa88d6da26..7e68a4b933841 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/MinLongGroupingAggregatorFunction.java @@ -59,20 +59,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - LongBlock valuesBlock = (LongBlock) uncastValuesBlock; + LongBlock valuesBlock = page.getBlock(channels.get(0)); LongVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunction.java index 2e1c36a78cd67..0757cb91b2747 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleAggregatorFunction.java @@ -59,11 +59,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - DoubleBlock block = (DoubleBlock) uncastBlock; + DoubleBlock block = page.getBlock(channels.get(0)); DoubleVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunction.java index 63613daa47ad1..232287356174c 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileDoubleGroupingAggregatorFunction.java @@ -67,20 +67,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - DoubleBlock valuesBlock = (DoubleBlock) uncastValuesBlock; + DoubleBlock valuesBlock = page.getBlock(channels.get(0)); DoubleVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunction.java index 936ffaba2e0ae..142772592a9a8 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntAggregatorFunction.java @@ -59,11 +59,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - IntBlock block = (IntBlock) uncastBlock; + IntBlock block = page.getBlock(channels.get(0)); IntVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunction.java index 38911d7a02f05..1b4e49fa1e040 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileIntGroupingAggregatorFunction.java @@ -65,20 +65,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - IntBlock valuesBlock = (IntBlock) uncastValuesBlock; + IntBlock valuesBlock = page.getBlock(channels.get(0)); IntVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunction.java index 9ca64b4ee8137..d360f14453ce5 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongAggregatorFunction.java @@ -59,11 +59,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - LongBlock block = (LongBlock) uncastBlock; + LongBlock block = page.getBlock(channels.get(0)); LongVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunction.java index efb512e84f1f5..1382aa8b27331 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/PercentileLongGroupingAggregatorFunction.java @@ -67,20 +67,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - LongBlock valuesBlock = (LongBlock) uncastValuesBlock; + LongBlock valuesBlock = page.getBlock(channels.get(0)); LongVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunction.java index e9f9c0e1c15de..48c50b026f198 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleAggregatorFunction.java @@ -57,11 +57,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - DoubleBlock block = (DoubleBlock) uncastBlock; + DoubleBlock block = page.getBlock(channels.get(0)); DoubleVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunction.java index 6094c7115159f..6e2207ca069cd 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumDoubleGroupingAggregatorFunction.java @@ -65,20 +65,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - DoubleBlock valuesBlock = (DoubleBlock) uncastValuesBlock; + DoubleBlock valuesBlock = page.getBlock(channels.get(0)); DoubleVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntAggregatorFunction.java index a2656c7197643..f834c932b0a56 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntAggregatorFunction.java @@ -58,11 +58,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - IntBlock block = (IntBlock) uncastBlock; + IntBlock block = page.getBlock(channels.get(0)); IntVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunction.java index d0c9ecb6ac84a..373a7af4b3d67 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumIntGroupingAggregatorFunction.java @@ -59,20 +59,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - IntBlock valuesBlock = (IntBlock) uncastValuesBlock; + IntBlock valuesBlock = page.getBlock(channels.get(0)); IntVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongAggregatorFunction.java index 1cf9fdddea876..058fdcbe507b4 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongAggregatorFunction.java @@ -56,11 +56,7 @@ public int intermediateBlockCount() { @Override public void addRawInput(Page page) { - Block uncastBlock = page.getBlock(channels.get(0)); - if (uncastBlock.areAllValuesNull()) { - return; - } - LongBlock block = (LongBlock) uncastBlock; + LongBlock block = page.getBlock(channels.get(0)); LongVector vector = block.asVector(); if (vector != null) { addRawVector(vector); diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunction.java index 9df3cd2d76cae..46e993b49c666 100644 --- a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/SumLongGroupingAggregatorFunction.java @@ -59,20 +59,7 @@ public int intermediateBlockCount() { @Override public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { - Block uncastValuesBlock = page.getBlock(channels.get(0)); - if (uncastValuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new GroupingAggregatorFunction.AddInput() { - @Override - public void add(int positionOffset, IntBlock groupIds) { - } - - @Override - public void add(int positionOffset, IntVector groupIds) { - } - }; - } - LongBlock valuesBlock = (LongBlock) uncastValuesBlock; + LongBlock valuesBlock = page.getBlock(channels.get(0)); LongVector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunction.java index 8108ab996936b..995dc5e15740f 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunction.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/CountGroupingAggregatorFunction.java @@ -61,16 +61,6 @@ public int intermediateBlockCount() { public AddInput prepareProcessPage(SeenGroupIds seenGroupIds, Page page) { Block valuesBlock = page.getBlock(blockIndex()); if (countAll == false) { - if (valuesBlock.areAllValuesNull()) { - state.enableGroupIdTracking(seenGroupIds); - return new AddInput() { // TODO return null meaning "don't collect me" and skip those - @Override - public void add(int positionOffset, IntBlock groupIds) {} - - @Override - public void add(int positionOffset, IntVector groupIds) {} - }; - } Vector valuesVector = valuesBlock.asVector(); if (valuesVector == null) { if (valuesBlock.mayHaveNulls()) { From 6fdf9e2b7764c21960a95898a99d7b252fe31d36 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Fri, 27 Oct 2023 22:00:10 -0700 Subject: [PATCH 178/203] Remove null block check in evaluator (#101484) We no longer need these checks because a null block can be cast to any block type. Relates #101414 --- .../compute/gen/EvaluatorImplementer.java | 13 +------------ .../operator/comparison/EqualsBoolsEvaluator.java | 6 ------ .../operator/comparison/EqualsDoublesEvaluator.java | 6 ------ .../operator/comparison/EqualsIntsEvaluator.java | 6 ------ .../comparison/EqualsKeywordsEvaluator.java | 6 ------ .../operator/comparison/EqualsLongsEvaluator.java | 6 ------ .../comparison/GreaterThanDoublesEvaluator.java | 6 ------ .../comparison/GreaterThanIntsEvaluator.java | 6 ------ .../comparison/GreaterThanKeywordsEvaluator.java | 6 ------ .../comparison/GreaterThanLongsEvaluator.java | 6 ------ .../GreaterThanOrEqualDoublesEvaluator.java | 6 ------ .../comparison/GreaterThanOrEqualIntsEvaluator.java | 6 ------ .../GreaterThanOrEqualKeywordsEvaluator.java | 6 ------ .../GreaterThanOrEqualLongsEvaluator.java | 6 ------ .../comparison/LessThanDoublesEvaluator.java | 6 ------ .../operator/comparison/LessThanIntsEvaluator.java | 6 ------ .../comparison/LessThanKeywordsEvaluator.java | 6 ------ .../operator/comparison/LessThanLongsEvaluator.java | 6 ------ .../comparison/LessThanOrEqualDoublesEvaluator.java | 6 ------ .../comparison/LessThanOrEqualIntsEvaluator.java | 6 ------ .../LessThanOrEqualKeywordsEvaluator.java | 6 ------ .../comparison/LessThanOrEqualLongsEvaluator.java | 6 ------ .../comparison/NotEqualsBoolsEvaluator.java | 6 ------ .../comparison/NotEqualsDoublesEvaluator.java | 6 ------ .../operator/comparison/NotEqualsIntsEvaluator.java | 6 ------ .../comparison/NotEqualsKeywordsEvaluator.java | 6 ------ .../comparison/NotEqualsLongsEvaluator.java | 6 ------ .../predicate/operator/logical/NotEvaluator.java | 3 --- .../operator/regex/RegexMatchEvaluator.java | 3 --- .../conditional/GreatestBooleanEvaluator.java | 6 +----- .../conditional/GreatestBytesRefEvaluator.java | 6 +----- .../scalar/conditional/GreatestDoubleEvaluator.java | 6 +----- .../scalar/conditional/GreatestIntEvaluator.java | 6 +----- .../scalar/conditional/GreatestLongEvaluator.java | 6 +----- .../scalar/conditional/LeastBooleanEvaluator.java | 6 +----- .../scalar/conditional/LeastBytesRefEvaluator.java | 6 +----- .../scalar/conditional/LeastDoubleEvaluator.java | 6 +----- .../scalar/conditional/LeastIntEvaluator.java | 6 +----- .../scalar/conditional/LeastLongEvaluator.java | 6 +----- .../scalar/date/DateExtractConstantEvaluator.java | 3 --- .../function/scalar/date/DateExtractEvaluator.java | 6 ------ .../scalar/date/DateFormatConstantEvaluator.java | 3 --- .../function/scalar/date/DateFormatEvaluator.java | 6 ------ .../scalar/date/DateParseConstantEvaluator.java | 3 --- .../function/scalar/date/DateParseEvaluator.java | 6 ------ .../function/scalar/date/DateTruncEvaluator.java | 3 --- .../function/scalar/ip/CIDRMatchEvaluator.java | 9 +-------- .../function/scalar/math/AbsDoubleEvaluator.java | 3 --- .../function/scalar/math/AbsIntEvaluator.java | 3 --- .../function/scalar/math/AbsLongEvaluator.java | 3 --- .../function/scalar/math/AcosEvaluator.java | 3 --- .../function/scalar/math/AsinEvaluator.java | 3 --- .../function/scalar/math/Atan2Evaluator.java | 6 ------ .../function/scalar/math/AtanEvaluator.java | 3 --- .../scalar/math/CastIntToDoubleEvaluator.java | 3 --- .../scalar/math/CastIntToLongEvaluator.java | 3 --- .../scalar/math/CastIntToUnsignedLongEvaluator.java | 3 --- .../scalar/math/CastLongToDoubleEvaluator.java | 3 --- .../math/CastLongToUnsignedLongEvaluator.java | 3 --- .../math/CastUnsignedLongToDoubleEvaluator.java | 3 --- .../function/scalar/math/CeilDoubleEvaluator.java | 3 --- .../function/scalar/math/CosEvaluator.java | 3 --- .../function/scalar/math/CoshEvaluator.java | 3 --- .../function/scalar/math/FloorDoubleEvaluator.java | 3 --- .../function/scalar/math/IsFiniteEvaluator.java | 3 --- .../function/scalar/math/IsInfiniteEvaluator.java | 3 --- .../function/scalar/math/IsNaNEvaluator.java | 3 --- .../function/scalar/math/Log10DoubleEvaluator.java | 3 --- .../function/scalar/math/Log10IntEvaluator.java | 3 --- .../function/scalar/math/Log10LongEvaluator.java | 3 --- .../scalar/math/Log10UnsignedLongEvaluator.java | 3 --- .../function/scalar/math/PowDoubleEvaluator.java | 6 ------ .../function/scalar/math/PowIntEvaluator.java | 6 ------ .../function/scalar/math/PowLongEvaluator.java | 6 ------ .../function/scalar/math/RoundDoubleEvaluator.java | 6 ------ .../scalar/math/RoundDoubleNoDecimalsEvaluator.java | 3 --- .../function/scalar/math/RoundIntEvaluator.java | 6 ------ .../function/scalar/math/RoundLongEvaluator.java | 6 ------ .../scalar/math/RoundUnsignedLongEvaluator.java | 6 ------ .../function/scalar/math/SinEvaluator.java | 3 --- .../function/scalar/math/SinhEvaluator.java | 3 --- .../function/scalar/math/SqrtDoubleEvaluator.java | 3 --- .../function/scalar/math/SqrtIntEvaluator.java | 3 --- .../function/scalar/math/SqrtLongEvaluator.java | 3 --- .../scalar/math/SqrtUnsignedLongEvaluator.java | 3 --- .../function/scalar/math/TanEvaluator.java | 3 --- .../function/scalar/math/TanhEvaluator.java | 3 --- .../function/scalar/string/ConcatEvaluator.java | 6 +----- .../function/scalar/string/EndsWithEvaluator.java | 6 ------ .../function/scalar/string/LTrimEvaluator.java | 3 --- .../function/scalar/string/LeftEvaluator.java | 6 ------ .../function/scalar/string/LengthEvaluator.java | 3 --- .../function/scalar/string/RTrimEvaluator.java | 3 --- .../scalar/string/ReplaceConstantEvaluator.java | 6 ------ .../function/scalar/string/ReplaceEvaluator.java | 9 --------- .../function/scalar/string/RightEvaluator.java | 6 ------ .../scalar/string/SplitSingleByteEvaluator.java | 3 --- .../scalar/string/SplitVariableEvaluator.java | 6 ------ .../function/scalar/string/StartsWithEvaluator.java | 6 ------ .../function/scalar/string/SubstringEvaluator.java | 9 --------- .../scalar/string/SubstringNoLengthEvaluator.java | 6 ------ .../function/scalar/string/TrimEvaluator.java | 3 --- .../operator/arithmetic/AddDatetimesEvaluator.java | 3 --- .../operator/arithmetic/AddDoublesEvaluator.java | 6 ------ .../operator/arithmetic/AddIntsEvaluator.java | 6 ------ .../operator/arithmetic/AddLongsEvaluator.java | 6 ------ .../arithmetic/AddUnsignedLongsEvaluator.java | 6 ------ .../operator/arithmetic/DivDoublesEvaluator.java | 6 ------ .../operator/arithmetic/DivIntsEvaluator.java | 6 ------ .../operator/arithmetic/DivLongsEvaluator.java | 6 ------ .../arithmetic/DivUnsignedLongsEvaluator.java | 6 ------ .../operator/arithmetic/ModDoublesEvaluator.java | 6 ------ .../operator/arithmetic/ModIntsEvaluator.java | 6 ------ .../operator/arithmetic/ModLongsEvaluator.java | 6 ------ .../arithmetic/ModUnsignedLongsEvaluator.java | 6 ------ .../operator/arithmetic/MulDoublesEvaluator.java | 6 ------ .../operator/arithmetic/MulIntsEvaluator.java | 6 ------ .../operator/arithmetic/MulLongsEvaluator.java | 6 ------ .../arithmetic/MulUnsignedLongsEvaluator.java | 6 ------ .../operator/arithmetic/NegDoublesEvaluator.java | 3 --- .../operator/arithmetic/NegIntsEvaluator.java | 3 --- .../operator/arithmetic/NegLongsEvaluator.java | 3 --- .../operator/arithmetic/SubDatetimesEvaluator.java | 3 --- .../operator/arithmetic/SubDoublesEvaluator.java | 6 ------ .../operator/arithmetic/SubIntsEvaluator.java | 6 ------ .../operator/arithmetic/SubLongsEvaluator.java | 6 ------ .../arithmetic/SubUnsignedLongsEvaluator.java | 6 ------ .../function/scalar/conditional/Case.java | 3 --- .../scalar/convert/AbstractConvertFunction.java | 3 --- .../multivalue/AbstractMultivalueFunction.java | 6 ------ .../function/scalar/multivalue/MvConcat.java | 3 --- 131 files changed, 13 insertions(+), 636 deletions(-) diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java index dac45aa11143b..d7ba99ccbc8e9 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/EvaluatorImplementer.java @@ -439,11 +439,6 @@ public String factoryInvocation(MethodSpec.Builder factoryMethodBuilder) { public void evalToBlock(MethodSpec.Builder builder) { TypeName blockType = blockType(type); builder.beginControlFlow("try (Block.Ref $LRef = $L.eval(page))", name, name); - builder.beginControlFlow("if ($LRef.block().areAllValuesNull())", name); - builder.addStatement( - "return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory()))" - ); - builder.endControlFlow(); builder.addStatement("$T $LBlock = ($T) $LRef.block()", blockType, name, blockType, name); } @@ -570,13 +565,7 @@ public void evalToBlock(MethodSpec.Builder builder) { builder.beginControlFlow("for (int i = 0; i < $LBlocks.length; i++)", name); { builder.addStatement("$LRefs[i] = $L[i].eval(page)", name, name); - builder.addStatement("Block block = $LRefs[i].block()", name); - builder.beginControlFlow("if (block.areAllValuesNull())"); - builder.addStatement( - "return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory()))" - ); - builder.endControlFlow(); - builder.addStatement("$LBlocks[i] = ($T) block", name, blockType); + builder.addStatement("$LBlocks[i] = ($T) $LRefs[i].block()", name, blockType, name); } builder.endControlFlow(); } diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java index 623e9baa79d94..66527e8aa3ab6 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsBoolsEvaluator.java @@ -35,14 +35,8 @@ public EqualsBoolsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BooleanBlock lhsBlock = (BooleanBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BooleanBlock rhsBlock = (BooleanBlock) rhsRef.block(); BooleanVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java index 2531ac7451507..f63d0bdb95dae 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsDoublesEvaluator.java @@ -37,14 +37,8 @@ public EqualsDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java index 77f6f7f20d0da..ab8fbcb20879b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsIntsEvaluator.java @@ -37,14 +37,8 @@ public EqualsIntsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java index 786041453b967..7368f3ff23213 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsKeywordsEvaluator.java @@ -38,14 +38,8 @@ public EqualsKeywordsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock lhsBlock = (BytesRefBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock rhsBlock = (BytesRefBlock) rhsRef.block(); BytesRefVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java index 5ecd0fe796afa..85bf4296e63a8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/EqualsLongsEvaluator.java @@ -37,14 +37,8 @@ public EqualsLongsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java index 29c883fcf8d14..2b69d9b41dde9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanDoublesEvaluator.java @@ -37,14 +37,8 @@ public GreaterThanDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java index cf6936af15646..9912428489fd1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanIntsEvaluator.java @@ -37,14 +37,8 @@ public GreaterThanIntsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java index ed08299b14fce..3139138565526 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanKeywordsEvaluator.java @@ -38,14 +38,8 @@ public GreaterThanKeywordsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock lhsBlock = (BytesRefBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock rhsBlock = (BytesRefBlock) rhsRef.block(); BytesRefVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java index 966460cf09dd2..281c62932d4d4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanLongsEvaluator.java @@ -37,14 +37,8 @@ public GreaterThanLongsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java index 55596509977a4..700b2f8371aff 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualDoublesEvaluator.java @@ -37,14 +37,8 @@ public GreaterThanOrEqualDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java index e10bb4c959d2e..894c7b743d043 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualIntsEvaluator.java @@ -37,14 +37,8 @@ public GreaterThanOrEqualIntsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java index 2258853b3c9ca..da52726494428 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualKeywordsEvaluator.java @@ -38,14 +38,8 @@ public GreaterThanOrEqualKeywordsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock lhsBlock = (BytesRefBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock rhsBlock = (BytesRefBlock) rhsRef.block(); BytesRefVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java index 0018f225a6d42..aecf1e92fb6f0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/GreaterThanOrEqualLongsEvaluator.java @@ -37,14 +37,8 @@ public GreaterThanOrEqualLongsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java index f56c037143925..4c0ea5e5b0ea7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanDoublesEvaluator.java @@ -37,14 +37,8 @@ public LessThanDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java index 765c283d3e0e4..7fbb2fc4ecf05 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanIntsEvaluator.java @@ -37,14 +37,8 @@ public LessThanIntsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java index ceaedec1afe49..774571f139076 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanKeywordsEvaluator.java @@ -38,14 +38,8 @@ public LessThanKeywordsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock lhsBlock = (BytesRefBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock rhsBlock = (BytesRefBlock) rhsRef.block(); BytesRefVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java index ee468abc02729..261240496bb51 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanLongsEvaluator.java @@ -37,14 +37,8 @@ public LessThanLongsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java index 924d66a789e4c..51295be798fd9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualDoublesEvaluator.java @@ -37,14 +37,8 @@ public LessThanOrEqualDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java index 7fbc1c5c866fc..c4f29438a6057 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualIntsEvaluator.java @@ -37,14 +37,8 @@ public LessThanOrEqualIntsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java index 8b06dd0695caf..7e46ceba0152a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualKeywordsEvaluator.java @@ -38,14 +38,8 @@ public LessThanOrEqualKeywordsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock lhsBlock = (BytesRefBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock rhsBlock = (BytesRefBlock) rhsRef.block(); BytesRefVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java index 2d24260f3e734..3a8187b345ead 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/LessThanOrEqualLongsEvaluator.java @@ -37,14 +37,8 @@ public LessThanOrEqualLongsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java index 8319b0d18e96e..aea1620c63820 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsBoolsEvaluator.java @@ -35,14 +35,8 @@ public NotEqualsBoolsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BooleanBlock lhsBlock = (BooleanBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BooleanBlock rhsBlock = (BooleanBlock) rhsRef.block(); BooleanVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java index dac4f67f25641..d340c88223eec 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsDoublesEvaluator.java @@ -37,14 +37,8 @@ public NotEqualsDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java index 4f7c404e9fcea..7492140ea6e61 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsIntsEvaluator.java @@ -37,14 +37,8 @@ public NotEqualsIntsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java index 161151cd67d03..cac586f498e20 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsKeywordsEvaluator.java @@ -38,14 +38,8 @@ public NotEqualsKeywordsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock lhsBlock = (BytesRefBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock rhsBlock = (BytesRefBlock) rhsRef.block(); BytesRefVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java index f70fa2b7bf1b2..bad6c3cd2c552 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/comparison/NotEqualsLongsEvaluator.java @@ -37,14 +37,8 @@ public NotEqualsLongsEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java index 0f0474ff4f810..2a3fe4053cf3e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/logical/NotEvaluator.java @@ -31,9 +31,6 @@ public NotEvaluator(EvalOperator.ExpressionEvaluator v, DriverContext driverCont @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BooleanBlock vBlock = (BooleanBlock) vRef.block(); BooleanVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java index 5850a6ebfdcc4..89be94c494550 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/evaluator/predicate/operator/regex/RegexMatchEvaluator.java @@ -39,9 +39,6 @@ public RegexMatchEvaluator(EvalOperator.ExpressionEvaluator input, CharacterRunA @Override public Block.Ref eval(Page page) { try (Block.Ref inputRef = input.eval(page)) { - if (inputRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock inputBlock = (BytesRefBlock) inputRef.block(); BytesRefVector inputVector = inputBlock.asVector(); if (inputVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java index 97a776f2a98be..7a46806c829dd 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBooleanEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { BooleanBlock[] valuesBlocks = new BooleanBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (BooleanBlock) block; + valuesBlocks[i] = (BooleanBlock) valuesRefs[i].block(); } BooleanVector[] valuesVectors = new BooleanVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java index 926eef7d351df..c612f5573efc6 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestBytesRefEvaluator.java @@ -39,11 +39,7 @@ public Block.Ref eval(Page page) { BytesRefBlock[] valuesBlocks = new BytesRefBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (BytesRefBlock) block; + valuesBlocks[i] = (BytesRefBlock) valuesRefs[i].block(); } BytesRefVector[] valuesVectors = new BytesRefVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java index 8453f7352473c..0963543e58030 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestDoubleEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { DoubleBlock[] valuesBlocks = new DoubleBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (DoubleBlock) block; + valuesBlocks[i] = (DoubleBlock) valuesRefs[i].block(); } DoubleVector[] valuesVectors = new DoubleVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java index af29574388fcc..15e2205ad9a97 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestIntEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { IntBlock[] valuesBlocks = new IntBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (IntBlock) block; + valuesBlocks[i] = (IntBlock) valuesRefs[i].block(); } IntVector[] valuesVectors = new IntVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java index 116a6dbdaa987..4f123e90edc07 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestLongEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { LongBlock[] valuesBlocks = new LongBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (LongBlock) block; + valuesBlocks[i] = (LongBlock) valuesRefs[i].block(); } LongVector[] valuesVectors = new LongVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java index c839ac1bb8169..599eb46f34b2e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBooleanEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { BooleanBlock[] valuesBlocks = new BooleanBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (BooleanBlock) block; + valuesBlocks[i] = (BooleanBlock) valuesRefs[i].block(); } BooleanVector[] valuesVectors = new BooleanVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java index b2a5d9a629f1e..f689674d2c255 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastBytesRefEvaluator.java @@ -39,11 +39,7 @@ public Block.Ref eval(Page page) { BytesRefBlock[] valuesBlocks = new BytesRefBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (BytesRefBlock) block; + valuesBlocks[i] = (BytesRefBlock) valuesRefs[i].block(); } BytesRefVector[] valuesVectors = new BytesRefVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java index 39fdda5c489c9..5103337411881 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastDoubleEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { DoubleBlock[] valuesBlocks = new DoubleBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (DoubleBlock) block; + valuesBlocks[i] = (DoubleBlock) valuesRefs[i].block(); } DoubleVector[] valuesVectors = new DoubleVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java index 4add29c1e545d..8ed7ca24acb04 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastIntEvaluator.java @@ -37,11 +37,7 @@ public Block.Ref eval(Page page) { IntBlock[] valuesBlocks = new IntBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (IntBlock) block; + valuesBlocks[i] = (IntBlock) valuesRefs[i].block(); } IntVector[] valuesVectors = new IntVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java index 8eced2f7a246b..28d8a8adc7942 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastLongEvaluator.java @@ -38,11 +38,7 @@ public Block.Ref eval(Page page) { LongBlock[] valuesBlocks = new LongBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (LongBlock) block; + valuesBlocks[i] = (LongBlock) valuesRefs[i].block(); } LongVector[] valuesVectors = new LongVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java index f261dd0b93028..c8c2d615d3b2d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractConstantEvaluator.java @@ -40,9 +40,6 @@ public DateExtractConstantEvaluator(EvalOperator.ExpressionEvaluator value, @Override public Block.Ref eval(Page page) { try (Block.Ref valueRef = value.eval(page)) { - if (valueRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valueBlock = (LongBlock) valueRef.block(); LongVector valueVector = valueBlock.asVector(); if (valueVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java index 7160b2910c10f..12f755e0b7a8a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractEvaluator.java @@ -48,14 +48,8 @@ public DateExtractEvaluator(Source source, EvalOperator.ExpressionEvaluator valu @Override public Block.Ref eval(Page page) { try (Block.Ref valueRef = value.eval(page)) { - if (valueRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valueBlock = (LongBlock) valueRef.block(); try (Block.Ref chronoFieldRef = chronoField.eval(page)) { - if (chronoFieldRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock chronoFieldBlock = (BytesRefBlock) chronoFieldRef.block(); LongVector valueVector = valueBlock.asVector(); if (valueVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java index 6a00964b65ac1..35a3d45be821d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatConstantEvaluator.java @@ -38,9 +38,6 @@ public DateFormatConstantEvaluator(EvalOperator.ExpressionEvaluator val, DateFor @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java index 02680217f4b30..95547c978af01 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatEvaluator.java @@ -42,14 +42,8 @@ public DateFormatEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); try (Block.Ref formatterRef = formatter.eval(page)) { - if (formatterRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock formatterBlock = (BytesRefBlock) formatterRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java index d8cd727237724..7fbd0cf387768 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseConstantEvaluator.java @@ -44,9 +44,6 @@ public DateParseConstantEvaluator(Source source, EvalOperator.ExpressionEvaluato @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock valBlock = (BytesRefBlock) valRef.block(); BytesRefVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java index 47875d579ac4d..2a64bc27d79ba 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateParseEvaluator.java @@ -47,14 +47,8 @@ public DateParseEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock valBlock = (BytesRefBlock) valRef.block(); try (Block.Ref formatterRef = formatter.eval(page)) { - if (formatterRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock formatterBlock = (BytesRefBlock) formatterRef.block(); BytesRefVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java index 81e67a3ac4fae..732b957d91c42 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateTruncEvaluator.java @@ -36,9 +36,6 @@ public DateTruncEvaluator(EvalOperator.ExpressionEvaluator fieldVal, Rounding.Pr @Override public Block.Ref eval(Page page) { try (Block.Ref fieldValRef = fieldVal.eval(page)) { - if (fieldValRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock fieldValBlock = (LongBlock) fieldValRef.block(); LongVector fieldValVector = fieldValBlock.asVector(); if (fieldValVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java index 1d46b98a87785..8b1b88b4b39f0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatchEvaluator.java @@ -40,20 +40,13 @@ public CIDRMatchEvaluator(EvalOperator.ExpressionEvaluator ip, @Override public Block.Ref eval(Page page) { try (Block.Ref ipRef = ip.eval(page)) { - if (ipRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock ipBlock = (BytesRefBlock) ipRef.block(); Block.Ref[] cidrsRefs = new Block.Ref[cidrs.length]; try (Releasable cidrsRelease = Releasables.wrap(cidrsRefs)) { BytesRefBlock[] cidrsBlocks = new BytesRefBlock[cidrs.length]; for (int i = 0; i < cidrsBlocks.length; i++) { cidrsRefs[i] = cidrs[i].eval(page); - Block block = cidrsRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - cidrsBlocks[i] = (BytesRefBlock) block; + cidrsBlocks[i] = (BytesRefBlock) cidrsRefs[i].block(); } BytesRefVector ipVector = ipBlock.asVector(); if (ipVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java index 3fcd3c321a442..0c475df2c1d1b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsDoubleEvaluator.java @@ -32,9 +32,6 @@ public AbsDoubleEvaluator(EvalOperator.ExpressionEvaluator fieldVal, @Override public Block.Ref eval(Page page) { try (Block.Ref fieldValRef = fieldVal.eval(page)) { - if (fieldValRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock fieldValBlock = (DoubleBlock) fieldValRef.block(); DoubleVector fieldValVector = fieldValBlock.asVector(); if (fieldValVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java index 84ba198252bf3..789113f780677 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsIntEvaluator.java @@ -31,9 +31,6 @@ public AbsIntEvaluator(EvalOperator.ExpressionEvaluator fieldVal, DriverContext @Override public Block.Ref eval(Page page) { try (Block.Ref fieldValRef = fieldVal.eval(page)) { - if (fieldValRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock fieldValBlock = (IntBlock) fieldValRef.block(); IntVector fieldValVector = fieldValBlock.asVector(); if (fieldValVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java index 1657ce63149e4..6c8a5597dec0a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsLongEvaluator.java @@ -31,9 +31,6 @@ public AbsLongEvaluator(EvalOperator.ExpressionEvaluator fieldVal, DriverContext @Override public Block.Ref eval(Page page) { try (Block.Ref fieldValRef = fieldVal.eval(page)) { - if (fieldValRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock fieldValBlock = (LongBlock) fieldValRef.block(); LongVector fieldValVector = fieldValBlock.asVector(); if (fieldValVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java index 8a57d6a6e5210..56ffd31806ebb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosEvaluator.java @@ -38,9 +38,6 @@ public AcosEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java index 695f51c01f9df..466bffac10fd4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinEvaluator.java @@ -38,9 +38,6 @@ public AsinEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java index 38cdbf3490dca..2cceab1bbad2f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Atan2Evaluator.java @@ -35,14 +35,8 @@ public Atan2Evaluator(EvalOperator.ExpressionEvaluator y, EvalOperator.Expressio @Override public Block.Ref eval(Page page) { try (Block.Ref yRef = y.eval(page)) { - if (yRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock yBlock = (DoubleBlock) yRef.block(); try (Block.Ref xRef = x.eval(page)) { - if (xRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock xBlock = (DoubleBlock) xRef.block(); DoubleVector yVector = yBlock.asVector(); if (yVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java index ec96778cabedd..11a628ce473b8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanEvaluator.java @@ -31,9 +31,6 @@ public AtanEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driverC @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java index ce5b21cdacfb0..2df190f80fbdf 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToDoubleEvaluator.java @@ -33,9 +33,6 @@ public CastIntToDoubleEvaluator(EvalOperator.ExpressionEvaluator v, DriverContex @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock vBlock = (IntBlock) vRef.block(); IntVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java index e9d6ae8a19b4f..b45b02ec5cc7e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToLongEvaluator.java @@ -33,9 +33,6 @@ public CastIntToLongEvaluator(EvalOperator.ExpressionEvaluator v, DriverContext @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock vBlock = (IntBlock) vRef.block(); IntVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java index bc74c15cffb19..2e9705cae6dd5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastIntToUnsignedLongEvaluator.java @@ -34,9 +34,6 @@ public CastIntToUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator v, @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock vBlock = (IntBlock) vRef.block(); IntVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java index a0321cb889e8b..f182dbd9da4ff 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToDoubleEvaluator.java @@ -34,9 +34,6 @@ public CastLongToDoubleEvaluator(EvalOperator.ExpressionEvaluator v, @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock vBlock = (LongBlock) vRef.block(); LongVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java index a03da9a18f9e0..0fdeea21abd3b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastLongToUnsignedLongEvaluator.java @@ -32,9 +32,6 @@ public CastLongToUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator v, @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock vBlock = (LongBlock) vRef.block(); LongVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java index e655630ed9af5..ee97cd124ed17 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CastUnsignedLongToDoubleEvaluator.java @@ -34,9 +34,6 @@ public CastUnsignedLongToDoubleEvaluator(EvalOperator.ExpressionEvaluator v, @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock vBlock = (LongBlock) vRef.block(); LongVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java index d9c54514f3713..cf0ef72b1fbda 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilDoubleEvaluator.java @@ -31,9 +31,6 @@ public CeilDoubleEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext d @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java index 8da902073ec6b..f6201661fcaea 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosEvaluator.java @@ -31,9 +31,6 @@ public CosEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driverCo @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java index fe5e8cd09aba6..35115b4098dba 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshEvaluator.java @@ -38,9 +38,6 @@ public CoshEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java index 3e0b2683a8022..db33a7247d045 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorDoubleEvaluator.java @@ -31,9 +31,6 @@ public FloorDoubleEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java index 0ef188805bddf..4886ef5ab2a79 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsFiniteEvaluator.java @@ -33,9 +33,6 @@ public IsFiniteEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext dri @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java index 730cd3b8c5312..f59d1fff1cbc3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsInfiniteEvaluator.java @@ -33,9 +33,6 @@ public IsInfiniteEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext d @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java index a9f88911c5ec2..67d4b56a4155a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/IsNaNEvaluator.java @@ -33,9 +33,6 @@ public IsNaNEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driver @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java index f2c3fe7de297f..b65d16c496050 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java @@ -38,9 +38,6 @@ public Log10DoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java index 1bb8af1eb53cb..5153df6cdef2a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java @@ -39,9 +39,6 @@ public Log10IntEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock valBlock = (IntBlock) valRef.block(); IntVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java index a1348ce3874a1..3b8f3e5319a3b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java @@ -39,9 +39,6 @@ public Log10LongEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java index 7f281103e1bbe..baa1198f009ff 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java @@ -39,9 +39,6 @@ public Log10UnsignedLongEvaluator(Source source, EvalOperator.ExpressionEvaluato @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java index 2dfbe1e174d71..271b036f9dfdb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowDoubleEvaluator.java @@ -41,14 +41,8 @@ public PowDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator base, @Override public Block.Ref eval(Page page) { try (Block.Ref baseRef = base.eval(page)) { - if (baseRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock baseBlock = (DoubleBlock) baseRef.block(); try (Block.Ref exponentRef = exponent.eval(page)) { - if (exponentRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock exponentBlock = (DoubleBlock) exponentRef.block(); DoubleVector baseVector = baseBlock.asVector(); if (baseVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java index 4800264ecf9b0..e6e36d9057286 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowIntEvaluator.java @@ -42,14 +42,8 @@ public PowIntEvaluator(Source source, EvalOperator.ExpressionEvaluator base, @Override public Block.Ref eval(Page page) { try (Block.Ref baseRef = base.eval(page)) { - if (baseRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock baseBlock = (DoubleBlock) baseRef.block(); try (Block.Ref exponentRef = exponent.eval(page)) { - if (exponentRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock exponentBlock = (DoubleBlock) exponentRef.block(); DoubleVector baseVector = baseBlock.asVector(); if (baseVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java index 241299faad478..f634f4305e623 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/PowLongEvaluator.java @@ -42,14 +42,8 @@ public PowLongEvaluator(Source source, EvalOperator.ExpressionEvaluator base, @Override public Block.Ref eval(Page page) { try (Block.Ref baseRef = base.eval(page)) { - if (baseRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock baseBlock = (DoubleBlock) baseRef.block(); try (Block.Ref exponentRef = exponent.eval(page)) { - if (exponentRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock exponentBlock = (DoubleBlock) exponentRef.block(); DoubleVector baseVector = baseBlock.asVector(); if (baseVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java index 91ee8f68e74ea..e9d602ff73cf4 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleEvaluator.java @@ -37,14 +37,8 @@ public RoundDoubleEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); try (Block.Ref decimalsRef = decimals.eval(page)) { - if (decimalsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock decimalsBlock = (LongBlock) decimalsRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java index c2cd4ac9d2a54..0d94d317ea748 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundDoubleNoDecimalsEvaluator.java @@ -32,9 +32,6 @@ public RoundDoubleNoDecimalsEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java index 431b7717b397e..737055262ac5c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundIntEvaluator.java @@ -37,14 +37,8 @@ public RoundIntEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock valBlock = (IntBlock) valRef.block(); try (Block.Ref decimalsRef = decimals.eval(page)) { - if (decimalsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock decimalsBlock = (LongBlock) decimalsRef.block(); IntVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java index 1a5bc14eabc7a..79311138e3333 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundLongEvaluator.java @@ -35,14 +35,8 @@ public RoundLongEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); try (Block.Ref decimalsRef = decimals.eval(page)) { - if (decimalsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock decimalsBlock = (LongBlock) decimalsRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java index beb7ec29fd955..d3487fb3215fb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundUnsignedLongEvaluator.java @@ -35,14 +35,8 @@ public RoundUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); try (Block.Ref decimalsRef = decimals.eval(page)) { - if (decimalsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock decimalsBlock = (LongBlock) decimalsRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java index c6613c8f1c94f..b01800662c1a0 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinEvaluator.java @@ -31,9 +31,6 @@ public SinEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driverCo @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java index 1b77cde7b87d3..751b32dbe839d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhEvaluator.java @@ -38,9 +38,6 @@ public SinhEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java index de925d7a36d54..4f13a19c62084 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtDoubleEvaluator.java @@ -38,9 +38,6 @@ public SqrtDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java index 6bd0dae184a96..db6f173ae839a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtIntEvaluator.java @@ -39,9 +39,6 @@ public SqrtIntEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock valBlock = (IntBlock) valRef.block(); IntVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java index d6239d01d0f56..3ff2dce06dad7 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtLongEvaluator.java @@ -39,9 +39,6 @@ public SqrtLongEvaluator(Source source, EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java index 876a751fc6bf9..372b0f794dad8 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtUnsignedLongEvaluator.java @@ -34,9 +34,6 @@ public SqrtUnsignedLongEvaluator(EvalOperator.ExpressionEvaluator val, @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock valBlock = (LongBlock) valRef.block(); LongVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java index ea08945abb54f..57628ab0f1e47 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanEvaluator.java @@ -31,9 +31,6 @@ public TanEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driverCo @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java index 6d024c6a4f02a..89e5406455ec5 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhEvaluator.java @@ -31,9 +31,6 @@ public TanhEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driverC @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock valBlock = (DoubleBlock) valRef.block(); DoubleVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java index b3fd79aec7d30..70b9f19715879 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ConcatEvaluator.java @@ -44,11 +44,7 @@ public Block.Ref eval(Page page) { BytesRefBlock[] valuesBlocks = new BytesRefBlock[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { valuesRefs[i] = values[i].eval(page); - Block block = valuesRefs[i].block(); - if (block.areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } - valuesBlocks[i] = (BytesRefBlock) block; + valuesBlocks[i] = (BytesRefBlock) valuesRefs[i].block(); } BytesRefVector[] valuesVectors = new BytesRefVector[values.length]; for (int i = 0; i < valuesBlocks.length; i++) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java index 9cc2bdc4c6739..a701cd81a5a4f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithEvaluator.java @@ -38,14 +38,8 @@ public EndsWithEvaluator(EvalOperator.ExpressionEvaluator str, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref suffixRef = suffix.eval(page)) { - if (suffixRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock suffixBlock = (BytesRefBlock) suffixRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java index ff661b3a388be..27b2a10d4dc54 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LTrimEvaluator.java @@ -32,9 +32,6 @@ public LTrimEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driver @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock valBlock = (BytesRefBlock) valRef.block(); BytesRefVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java index 7d972b7cd74bc..3514f812ecfe1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LeftEvaluator.java @@ -47,14 +47,8 @@ public LeftEvaluator(BytesRef out, UnicodeUtil.UTF8CodePoint cp, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref lengthRef = length.eval(page)) { - if (lengthRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lengthBlock = (IntBlock) lengthRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java index 9e63d5f4e80a5..b91a2389c60fe 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/LengthEvaluator.java @@ -34,9 +34,6 @@ public LengthEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext drive @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock valBlock = (BytesRefBlock) valRef.block(); BytesRefVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java index 9ea3b811c4d0d..0cac1ac9380b3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RTrimEvaluator.java @@ -32,9 +32,6 @@ public RTrimEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driver @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock valBlock = (BytesRefBlock) valRef.block(); BytesRefVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java index 111b15249edaf..3baf32446ab4d 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceConstantEvaluator.java @@ -46,14 +46,8 @@ public ReplaceConstantEvaluator(Source source, EvalOperator.ExpressionEvaluator @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref newStrRef = newStr.eval(page)) { - if (newStrRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock newStrBlock = (BytesRefBlock) newStrRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java index a26963ddea64b..b2738d52b2383 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceEvaluator.java @@ -46,19 +46,10 @@ public ReplaceEvaluator(Source source, EvalOperator.ExpressionEvaluator str, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref regexRef = regex.eval(page)) { - if (regexRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock regexBlock = (BytesRefBlock) regexRef.block(); try (Block.Ref newStrRef = newStr.eval(page)) { - if (newStrRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock newStrBlock = (BytesRefBlock) newStrRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java index 460ae54f0d39b..e8b70f1ad416c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/RightEvaluator.java @@ -47,14 +47,8 @@ public RightEvaluator(BytesRef out, UnicodeUtil.UTF8CodePoint cp, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref lengthRef = length.eval(page)) { - if (lengthRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lengthBlock = (IntBlock) lengthRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java index 1bc04d7b0b68a..5a76c991b7d01 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitSingleByteEvaluator.java @@ -40,9 +40,6 @@ public SplitSingleByteEvaluator(EvalOperator.ExpressionEvaluator str, byte delim @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java index 64323851dab09..a81d1485a7c57 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SplitVariableEvaluator.java @@ -40,14 +40,8 @@ public SplitVariableEvaluator(EvalOperator.ExpressionEvaluator str, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref delimRef = delim.eval(page)) { - if (delimRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock delimBlock = (BytesRefBlock) delimRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java index 4e9bd21ca7a97..752cc4b8ad946 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithEvaluator.java @@ -38,14 +38,8 @@ public StartsWithEvaluator(EvalOperator.ExpressionEvaluator str, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref prefixRef = prefix.eval(page)) { - if (prefixRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock prefixBlock = (BytesRefBlock) prefixRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java index a95d09d65fb4e..9079f0ea84f55 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringEvaluator.java @@ -42,19 +42,10 @@ public SubstringEvaluator(EvalOperator.ExpressionEvaluator str, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref startRef = start.eval(page)) { - if (startRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock startBlock = (IntBlock) startRef.block(); try (Block.Ref lengthRef = length.eval(page)) { - if (lengthRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lengthBlock = (IntBlock) lengthRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java index 9667d1b2fbb3f..115162175e720 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/SubstringNoLengthEvaluator.java @@ -38,14 +38,8 @@ public SubstringNoLengthEvaluator(EvalOperator.ExpressionEvaluator str, @Override public Block.Ref eval(Page page) { try (Block.Ref strRef = str.eval(page)) { - if (strRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock strBlock = (BytesRefBlock) strRef.block(); try (Block.Ref startRef = start.eval(page)) { - if (startRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock startBlock = (IntBlock) startRef.block(); BytesRefVector strVector = strBlock.asVector(); if (strVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java index bb28283cc0d99..939715c718ef9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/TrimEvaluator.java @@ -32,9 +32,6 @@ public TrimEvaluator(EvalOperator.ExpressionEvaluator val, DriverContext driverC @Override public Block.Ref eval(Page page) { try (Block.Ref valRef = val.eval(page)) { - if (valRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } BytesRefBlock valBlock = (BytesRefBlock) valRef.block(); BytesRefVector valVector = valBlock.asVector(); if (valVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java index ee40b5ff778c9..40e4513629b2f 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDatetimesEvaluator.java @@ -43,9 +43,6 @@ public AddDatetimesEvaluator(Source source, EvalOperator.ExpressionEvaluator dat @Override public Block.Ref eval(Page page) { try (Block.Ref datetimeRef = datetime.eval(page)) { - if (datetimeRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock datetimeBlock = (LongBlock) datetimeRef.block(); LongVector datetimeVector = datetimeBlock.asVector(); if (datetimeVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java index 9b2101b0134c7..7f86fde38ec9b 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddDoublesEvaluator.java @@ -35,14 +35,8 @@ public AddDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java index f57e6cf4f841f..96c34aa79df9c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddIntsEvaluator.java @@ -41,14 +41,8 @@ public AddIntsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java index cb0144ee07b13..9158d6f92373c 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddLongsEvaluator.java @@ -41,14 +41,8 @@ public AddLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java index 4aecd8dbce899..6e85e253856d2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddUnsignedLongsEvaluator.java @@ -41,14 +41,8 @@ public AddUnsignedLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java index 7abda14d7e8a3..8bbce4faf25d1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivDoublesEvaluator.java @@ -35,14 +35,8 @@ public DivDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java index 5f31c9a485dfa..87933d3ef26c2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivIntsEvaluator.java @@ -41,14 +41,8 @@ public DivIntsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java index dd933110f2393..f23af932b61f3 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivLongsEvaluator.java @@ -41,14 +41,8 @@ public DivLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java index 0152e6b2178f2..09de6b0c37527 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/DivUnsignedLongsEvaluator.java @@ -41,14 +41,8 @@ public DivUnsignedLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java index 62d7bb54e6244..96888b6a6fcd1 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModDoublesEvaluator.java @@ -35,14 +35,8 @@ public ModDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java index 61bfebaf15491..047beb241ed70 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModIntsEvaluator.java @@ -41,14 +41,8 @@ public ModIntsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java index c0671d0532512..706ccee418465 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModLongsEvaluator.java @@ -41,14 +41,8 @@ public ModLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java index e40a5e2bb52b6..ede1fbce65458 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/ModUnsignedLongsEvaluator.java @@ -41,14 +41,8 @@ public ModUnsignedLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java index f7e5e797251a5..df9105b9a5b16 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulDoublesEvaluator.java @@ -35,14 +35,8 @@ public MulDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java index 16304eaddc596..f57fa9aa71612 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulIntsEvaluator.java @@ -41,14 +41,8 @@ public MulIntsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java index 45bd89539e093..ce045a7be9d17 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulLongsEvaluator.java @@ -41,14 +41,8 @@ public MulLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java index bf5d1a73556ba..cd02f11c4a4fb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/MulUnsignedLongsEvaluator.java @@ -41,14 +41,8 @@ public MulUnsignedLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java index 943aca14ed97b..dbe3e02afeb0a 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegDoublesEvaluator.java @@ -31,9 +31,6 @@ public NegDoublesEvaluator(EvalOperator.ExpressionEvaluator v, DriverContext dri @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock vBlock = (DoubleBlock) vRef.block(); DoubleVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java index dde3d44582ee3..908b0197d347e 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegIntsEvaluator.java @@ -38,9 +38,6 @@ public NegIntsEvaluator(Source source, EvalOperator.ExpressionEvaluator v, @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock vBlock = (IntBlock) vRef.block(); IntVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java index 0490080520c85..6a773e6eceba9 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/NegLongsEvaluator.java @@ -38,9 +38,6 @@ public NegLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator v, @Override public Block.Ref eval(Page page) { try (Block.Ref vRef = v.eval(page)) { - if (vRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock vBlock = (LongBlock) vRef.block(); LongVector vVector = vBlock.asVector(); if (vVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java index 574c0d48f8f89..b72bed55d0afb 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDatetimesEvaluator.java @@ -43,9 +43,6 @@ public SubDatetimesEvaluator(Source source, EvalOperator.ExpressionEvaluator dat @Override public Block.Ref eval(Page page) { try (Block.Ref datetimeRef = datetime.eval(page)) { - if (datetimeRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock datetimeBlock = (LongBlock) datetimeRef.block(); LongVector datetimeVector = datetimeBlock.asVector(); if (datetimeVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java index dc766ab59a1d4..79b064b105c68 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubDoublesEvaluator.java @@ -35,14 +35,8 @@ public SubDoublesEvaluator(EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock lhsBlock = (DoubleBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } DoubleBlock rhsBlock = (DoubleBlock) rhsRef.block(); DoubleVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java index 14e6f00bc51d4..4cf1cf68abff2 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubIntsEvaluator.java @@ -41,14 +41,8 @@ public SubIntsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock lhsBlock = (IntBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } IntBlock rhsBlock = (IntBlock) rhsRef.block(); IntVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java index 40447fe38f4f6..65ec31066e9db 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubLongsEvaluator.java @@ -41,14 +41,8 @@ public SubLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator lhs, @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java index 8415baf744587..9879dcadc1239 100644 --- a/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/SubUnsignedLongsEvaluator.java @@ -41,14 +41,8 @@ public SubUnsignedLongsEvaluator(Source source, EvalOperator.ExpressionEvaluator @Override public Block.Ref eval(Page page) { try (Block.Ref lhsRef = lhs.eval(page)) { - if (lhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock lhsBlock = (LongBlock) lhsRef.block(); try (Block.Ref rhsRef = rhs.eval(page)) { - if (rhsRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } LongBlock rhsBlock = (LongBlock) rhsRef.block(); LongVector lhsVector = lhsBlock.asVector(); if (lhsVector == null) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java index 5db081c0dc529..2117828be6533 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Case.java @@ -233,9 +233,6 @@ public Block.Ref eval(Page page) { try (Releasable ignored = limited::releaseBlocks) { for (ConditionEvaluator condition : conditions) { try (Block.Ref conditionRef = condition.condition.eval(limited)) { - if (conditionRef.block().areAllValuesNull()) { - continue; - } BooleanBlock b = (BooleanBlock) conditionRef.block(); if (b.isNull(0)) { continue; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java index b8913c6d8a85d..c717fafa877a9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java @@ -110,9 +110,6 @@ protected AbstractEvaluator(DriverContext driverContext, EvalOperator.Expression public Block.Ref eval(Page page) { try (Block.Ref ref = fieldEvaluator.eval(page)) { - if (ref.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), driverContext.blockFactory())); - } Vector vector = ref.block().asVector(); return Block.Ref.floating(vector == null ? evalBlock(ref.block()) : evalVector(vector)); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java index ce2582c3d641b..196137336bee5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/AbstractMultivalueFunction.java @@ -78,9 +78,6 @@ protected Block.Ref evalSingleValuedNotNullable(Block.Ref fieldRef) { @Override public final Block.Ref eval(Page page) { Block.Ref ref = field.eval(page); - if (ref.block().areAllValuesNull()) { - return ref; - } if (ref.block().mayHaveMultivaluedFields() == false) { if (ref.block().mayHaveNulls()) { return evalSingleValuedNullable(ref); @@ -122,9 +119,6 @@ protected Block.Ref evalSingleValuedNullable(Block.Ref fieldRef) { @Override public Block.Ref eval(Page page) { Block.Ref ref = field.eval(page); - if (ref.block().areAllValuesNull()) { - return ref; - } return ref.block().mayHaveMultivaluedFields() ? evalNullable(ref) : evalSingleValuedNullable(ref); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java index 0cb97e28b0053..a187bb41ee235 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvConcat.java @@ -122,9 +122,6 @@ private static class Evaluator implements ExpressionEvaluator { @Override public final Block.Ref eval(Page page) { try (Block.Ref fieldRef = field.eval(page); Block.Ref delimRef = delim.eval(page)) { - if (fieldRef.block().areAllValuesNull() || delimRef.block().areAllValuesNull()) { - return Block.Ref.floating(Block.constantNullBlock(page.getPositionCount(), context.blockFactory())); - } BytesRefBlock fieldVal = (BytesRefBlock) fieldRef.block(); BytesRefBlock delimVal = (BytesRefBlock) delimRef.block(); From 93b620ecf006482a0f2dee01c868b8f676638ba1 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Sat, 28 Oct 2023 16:00:58 -0400 Subject: [PATCH 179/203] ESQL: Deprecate static vector methods (#101454) This deprecates the static methods for building vectors - we have a factory now we're prefer folks use. --- .../org/elasticsearch/compute/data/BooleanVector.java | 10 +++++++++- .../elasticsearch/compute/data/BytesRefVector.java | 8 +++++++- .../org/elasticsearch/compute/data/DoubleVector.java | 10 +++++++++- .../org/elasticsearch/compute/data/IntVector.java | 10 +++++++++- .../org/elasticsearch/compute/data/LongVector.java | 10 +++++++++- .../org/elasticsearch/compute/data/X-Vector.java.st | 11 ++++++++++- 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java index 001dcf9aa6f8b..ec4ab8f7def1c 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BooleanVector.java @@ -101,8 +101,12 @@ default void writeTo(StreamOutput out) throws IOException { } } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance nonbreaking block factory}. + * @deprecated use {@link BlockFactory#newBooleanVectorBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } @@ -110,7 +114,9 @@ static Builder newVectorBuilder(int estimatedSize) { /** * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newBooleanVectorBuilder} */ + @Deprecated static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newBooleanVectorBuilder(estimatedSize); } @@ -118,7 +124,9 @@ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { /** * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newBooleanVectorFixedBuilder} */ + @Deprecated static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { return blockFactory.newBooleanVectorFixedBuilder(size); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java index 9271b5cc17079..b7011666b981d 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/BytesRefVector.java @@ -101,15 +101,21 @@ default void writeTo(StreamOutput out) throws IOException { } } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance nonbreaking block factory}. + * @deprecated use {@link BlockFactory#newBytesRefVectorBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } /** * Creates a builder that grows as needed. + * @deprecated use {@link BlockFactory#newBytesRefVectorBuilder} */ + @Deprecated static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newBytesRefVectorBuilder(estimatedSize); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java index 396cd89583810..acabd0deb17f6 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/DoubleVector.java @@ -102,8 +102,12 @@ default void writeTo(StreamOutput out) throws IOException { } } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance nonbreaking block factory}. + * @deprecated use {@link BlockFactory#newDoubleVectorBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } @@ -111,7 +115,9 @@ static Builder newVectorBuilder(int estimatedSize) { /** * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newDoubleVectorBuilder} */ + @Deprecated static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newDoubleVectorBuilder(estimatedSize); } @@ -119,7 +125,9 @@ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { /** * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newDoubleVectorFixedBuilder} */ + @Deprecated static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { return blockFactory.newDoubleVectorFixedBuilder(size); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java index bc88f85855b4a..645288565c431 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/IntVector.java @@ -101,8 +101,12 @@ default void writeTo(StreamOutput out) throws IOException { } } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance nonbreaking block factory}. + * @deprecated use {@link BlockFactory#newIntVectorBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } @@ -110,7 +114,9 @@ static Builder newVectorBuilder(int estimatedSize) { /** * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newIntVectorBuilder} */ + @Deprecated static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newIntVectorBuilder(estimatedSize); } @@ -118,7 +124,9 @@ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { /** * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newIntVectorFixedBuilder} */ + @Deprecated static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { return blockFactory.newIntVectorFixedBuilder(size); } diff --git a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java index 3c7d83ab72e66..a312d7aeab0cc 100644 --- a/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java +++ b/x-pack/plugin/esql/compute/src/main/generated-src/org/elasticsearch/compute/data/LongVector.java @@ -102,8 +102,12 @@ default void writeTo(StreamOutput out) throws IOException { } } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance nonbreaking block factory}. + * @deprecated use {@link BlockFactory#newLongVectorBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } @@ -111,7 +115,9 @@ static Builder newVectorBuilder(int estimatedSize) { /** * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newLongVectorBuilder} */ + @Deprecated static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.newLongVectorBuilder(estimatedSize); } @@ -119,7 +125,9 @@ static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { /** * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#newLongVectorFixedBuilder} */ + @Deprecated static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { return blockFactory.newLongVectorFixedBuilder(size); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st index ac9f1638eacac..6ec41ccdc6ab9 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/X-Vector.java.st @@ -143,8 +143,12 @@ $endif$ } } - /** Returns a builder using the {@link BlockFactory#getNonBreakingInstance block factory}. */ + /** + * Returns a builder using the {@link BlockFactory#getNonBreakingInstance nonbreaking block factory}. + * @deprecated use {@link BlockFactory#new$Type$VectorBuilder} + */ // Eventually, we want to remove this entirely, always passing an explicit BlockFactory + @Deprecated static Builder newVectorBuilder(int estimatedSize) { return newVectorBuilder(estimatedSize, BlockFactory.getNonBreakingInstance()); } @@ -152,13 +156,16 @@ $endif$ $if(BytesRef)$ /** * Creates a builder that grows as needed. + * @deprecated use {@link BlockFactory#new$Type$VectorBuilder} */ $else$ /** * Creates a builder that grows as needed. Prefer {@link #newVectorFixedBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#new$Type$VectorBuilder} */ $endif$ + @Deprecated static Builder newVectorBuilder(int estimatedSize, BlockFactory blockFactory) { return blockFactory.new$Type$VectorBuilder(estimatedSize); } @@ -168,7 +175,9 @@ $else$ /** * Creates a builder that never grows. Prefer this over {@link #newVectorBuilder} * if you know the size up front because it's faster. + * @deprecated use {@link BlockFactory#new$Type$VectorFixedBuilder} */ + @Deprecated static FixedBuilder newVectorFixedBuilder(int size, BlockFactory blockFactory) { return blockFactory.new$Type$VectorFixedBuilder(size); } From bd22bc901c7b77b5d7a4fa38c806b0fcaf8fb0dc Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Sat, 28 Oct 2023 17:12:51 -0400 Subject: [PATCH 180/203] ESQL: More tracking in BlockHash impls (#101488) This adds memory tracking to more parts of the `BlockHash` implementations. It also reworks the tests so they assert based on callbacks which is more like how they are used in production. --- docs/changelog/101488.yaml | 5 + .../blockhash/BooleanBlockHash.java | 34 +- .../blockhash/BytesRefBlockHash.java | 6 +- .../blockhash/BytesRefLongBlockHash.java | 39 +- .../blockhash/DoubleBlockHash.java | 6 +- .../aggregation/blockhash/IntBlockHash.java | 6 +- .../aggregation/blockhash/LongBlockHash.java | 6 +- .../blockhash/LongLongBlockHash.java | 53 +- .../blockhash/PackedValuesBlockHash.java | 79 +- .../compute/data/BlockFactory.java | 4 +- .../blockhash/BlockHashRandomizedTests.java | 39 +- .../aggregation/blockhash/BlockHashTests.java | 1779 ++++++++--------- .../compute/data/MockBlockFactory.java | 3 +- 13 files changed, 1053 insertions(+), 1006 deletions(-) create mode 100644 docs/changelog/101488.yaml diff --git a/docs/changelog/101488.yaml b/docs/changelog/101488.yaml new file mode 100644 index 0000000000000..1db48a63f8542 --- /dev/null +++ b/docs/changelog/101488.yaml @@ -0,0 +1,5 @@ +pr: 101488 +summary: "ESQL: More tracking in `BlockHash` impls" +area: ES|QL +type: enhancement +issues: [] diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BooleanBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BooleanBlockHash.java index 3f696e3387c54..6f041a6681659 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BooleanBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BooleanBlockHash.java @@ -61,7 +61,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { private IntVector add(BooleanVector vector) { int positions = vector.getPositionCount(); - try (var builder = IntVector.newVectorFixedBuilder(positions, blockFactory)) { + try (var builder = blockFactory.newIntVectorFixedBuilder(positions)) { for (int i = 0; i < positions; i++) { builder.appendInt(MultivalueDedupeBoolean.hashOrd(everSeen, vector.getBoolean(i))); } @@ -75,28 +75,30 @@ private IntBlock add(BooleanBlock block) { @Override public BooleanBlock[] getKeys() { - BooleanBlock.Builder builder = BooleanBlock.newBlockBuilder(everSeen.length); - if (everSeen[NULL_ORD]) { - builder.appendNull(); - } - if (everSeen[FALSE_ORD]) { - builder.appendBoolean(false); - } - if (everSeen[TRUE_ORD]) { - builder.appendBoolean(true); + try (BooleanBlock.Builder builder = blockFactory.newBooleanBlockBuilder(everSeen.length)) { + if (everSeen[NULL_ORD]) { + builder.appendNull(); + } + if (everSeen[FALSE_ORD]) { + builder.appendBoolean(false); + } + if (everSeen[TRUE_ORD]) { + builder.appendBoolean(true); + } + return new BooleanBlock[] { builder.build() }; } - return new BooleanBlock[] { builder.build() }; } @Override public IntVector nonEmpty() { - IntVector.Builder builder = IntVector.newVectorBuilder(everSeen.length); - for (int i = 0; i < everSeen.length; i++) { - if (everSeen[i]) { - builder.appendInt(i); + try (IntVector.Builder builder = blockFactory.newIntVectorBuilder(everSeen.length)) { + for (int i = 0; i < everSeen.length; i++) { + if (everSeen[i]) { + builder.appendInt(i); + } } + return builder.build(); } - return builder.build(); } public BitArray seenGroupIds(BigArrays bigArrays) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefBlockHash.java index 27e40e82f7df1..2f1bb4f858ff4 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefBlockHash.java @@ -68,8 +68,8 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { addInput.add(0, groupIds); } } else { - try (IntBlock groupIds = add(bytesVector).asBlock()) { - addInput.add(0, groupIds.asVector()); + try (IntVector groupIds = add(bytesVector)) { + addInput.add(0, groupIds); } } } @@ -77,7 +77,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { private IntVector add(BytesRefVector vector) { int positions = vector.getPositionCount(); - try (var builder = IntVector.newVectorFixedBuilder(positions, blockFactory)) { + try (var builder = blockFactory.newIntVectorFixedBuilder(positions)) { for (int i = 0; i < positions; i++) { builder.appendInt(Math.toIntExact(hashOrdToGroupNullReserved(bytesRefHash.add(vector.getBytesRef(i, bytes))))); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java index 1721ded09849e..da2c85e532016 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/BytesRefLongBlockHash.java @@ -74,7 +74,9 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { if (vector1 != null && vector2 != null) { addInput.add(0, add(vector1, vector2)); } else { - new AddBlock(block1, block2, addInput).add(); + try (AddWork work = new AddWork(block1, block2, addInput)) { + work.add(); + } } } @@ -91,12 +93,12 @@ public IntVector add(BytesRefVector vector1, LongVector vector2) { private static final long[] EMPTY = new long[0]; - private class AddBlock extends LongLongBlockHash.AbstractAddBlock { + private class AddWork extends LongLongBlockHash.AbstractAddBlock { private final BytesRefBlock block1; private final LongBlock block2; - AddBlock(BytesRefBlock block1, LongBlock block2, GroupingAggregatorFunction.AddInput addInput) { - super(emitBatchSize, addInput); + AddWork(BytesRefBlock block1, LongBlock block2, GroupingAggregatorFunction.AddInput addInput) { + super(blockFactory, emitBatchSize, addInput); this.block1 = block1; this.block2 = block2; } @@ -165,18 +167,29 @@ void add() { @Override public Block[] getKeys() { int positions = (int) finalHash.size(); - BytesRefVector.Builder keys1 = BytesRefVector.newVectorBuilder(positions); - LongVector.Builder keys2 = LongVector.newVectorBuilder(positions); - BytesRef scratch = new BytesRef(); - for (long i = 0; i < positions; i++) { - keys2.appendLong(finalHash.getKey2(i)); - long h1 = finalHash.getKey1(i); - keys1.appendBytesRef(bytesHash.get(h1, scratch)); + BytesRefVector k1 = null; + LongVector k2 = null; + try ( + BytesRefVector.Builder keys1 = blockFactory.newBytesRefVectorBuilder(positions); + LongVector.Builder keys2 = blockFactory.newLongVectorBuilder(positions) + ) { + BytesRef scratch = new BytesRef(); + for (long i = 0; i < positions; i++) { + keys2.appendLong(finalHash.getKey2(i)); + long h1 = finalHash.getKey1(i); + keys1.appendBytesRef(bytesHash.get(h1, scratch)); + } + k1 = keys1.build(); + k2 = keys2.build(); + } finally { + if (k2 == null) { + Releasables.closeExpectNoException(k1); + } } if (reverseOutput) { - return new Block[] { keys2.build().asBlock(), keys1.build().asBlock() }; + return new Block[] { k2.asBlock(), k1.asBlock() }; } else { - return new Block[] { keys1.build().asBlock(), keys2.build().asBlock() }; + return new Block[] { k1.asBlock(), k2.asBlock() }; } } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java index bbf5911521122..a8a67180775fb 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java @@ -62,8 +62,8 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { addInput.add(0, groupIds); } } else { - try (IntBlock groupIds = add(doubleVector).asBlock()) { - addInput.add(0, groupIds.asVector()); + try (IntVector groupIds = add(doubleVector)) { + addInput.add(0, groupIds); } } } @@ -71,7 +71,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { private IntVector add(DoubleVector vector) { int positions = vector.getPositionCount(); - try (var builder = IntVector.newVectorFixedBuilder(positions, blockFactory)) { + try (var builder = blockFactory.newIntVectorFixedBuilder(positions)) { for (int i = 0; i < positions; i++) { builder.appendInt(Math.toIntExact(hashOrdToGroupNullReserved(longHash.add(Double.doubleToLongBits(vector.getDouble(i)))))); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java index 17c7fab73d2be..79e03e4dc0ed5 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/IntBlockHash.java @@ -59,8 +59,8 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { addInput.add(0, groupIds); } } else { - try (IntBlock groupIds = add(intVector).asBlock()) { - addInput.add(0, groupIds.asVector()); + try (IntVector groupIds = add(intVector)) { + addInput.add(0, groupIds); } } } @@ -68,7 +68,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { private IntVector add(IntVector vector) { int positions = vector.getPositionCount(); - try (var builder = IntVector.newVectorFixedBuilder(positions, blockFactory)) { + try (var builder = blockFactory.newIntVectorFixedBuilder(positions)) { for (int i = 0; i < positions; i++) { builder.appendInt(Math.toIntExact(hashOrdToGroupNullReserved(longHash.add(vector.getInt(i))))); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java index 1d3be57bb40d7..c736cfae65ee7 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongBlockHash.java @@ -62,8 +62,8 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { addInput.add(0, groupIds); } } else { - try (IntBlock groupIds = add(longVector).asBlock()) { // Ugh!! - addInput.add(0, groupIds.asVector()); + try (IntVector groupIds = add(longVector)) { + addInput.add(0, groupIds); } } } @@ -71,7 +71,7 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { private IntVector add(LongVector vector) { int positions = vector.getPositionCount(); - try (var builder = IntVector.newVectorFixedBuilder(positions, blockFactory)) { + try (var builder = blockFactory.newIntVectorFixedBuilder(positions)) { for (int i = 0; i < positions; i++) { builder.appendInt(Math.toIntExact(hashOrdToGroupNullReserved(longHash.add(vector.getLong(i))))); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongLongBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongLongBlockHash.java index f36e8c76ec98b..ce53f0bb8e7f4 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongLongBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/LongLongBlockHash.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; import org.elasticsearch.compute.aggregation.SeenGroupIds; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; @@ -74,13 +75,12 @@ private IntVector add(LongVector vector1, LongVector vector2) { private static final long[] EMPTY = new long[0]; - // TODO: this uses the non-breaking block factory - update to use this blockFactory - private class AddBlock extends AbstractAddBlock implements Releasable { + private class AddBlock extends AbstractAddBlock { private final LongBlock block1; private final LongBlock block2; AddBlock(LongBlock block1, LongBlock block2, GroupingAggregatorFunction.AddInput addInput) { - super(emitBatchSize, addInput); + super(blockFactory, emitBatchSize, addInput); this.block1 = block1; this.block2 = block2; } @@ -137,14 +137,10 @@ void add() { } emitOrds(); } - - @Override - public void close() { - Releasables.closeExpectNoException(block1, block2); - } } - static class AbstractAddBlock { + static class AbstractAddBlock implements Releasable { + private final BlockFactory blockFactory; private final int emitBatchSize; private final GroupingAggregatorFunction.AddInput addInput; @@ -152,11 +148,12 @@ static class AbstractAddBlock { private int added = 0; protected IntBlock.Builder ords; - AbstractAddBlock(int emitBatchSize, GroupingAggregatorFunction.AddInput addInput) { + AbstractAddBlock(BlockFactory blockFactory, int emitBatchSize, GroupingAggregatorFunction.AddInput addInput) { + this.blockFactory = blockFactory; this.emitBatchSize = emitBatchSize; this.addInput = addInput; - this.ords = IntBlock.newBlockBuilder(emitBatchSize); + this.ords = blockFactory.newIntBlockBuilder(emitBatchSize); } protected final void addedValue(int position) { @@ -174,13 +171,20 @@ protected final void addedValueInMultivaluePosition(int position) { } protected final void emitOrds() { - addInput.add(positionOffset, ords.build()); + try (IntBlock ordsBlock = ords.build()) { + addInput.add(positionOffset, ordsBlock); + } } private void rollover(int position) { emitOrds(); positionOffset = position; - ords = IntBlock.newBlockBuilder(emitBatchSize); // TODO add a clear method to the builder? + ords = blockFactory.newIntBlockBuilder(emitBatchSize); // TODO add a clear method to the builder? + } + + @Override + public final void close() { + ords.close(); } } @@ -197,13 +201,24 @@ static int add(long[] seen, int nextSeen, long v) { @Override public Block[] getKeys() { int positions = (int) hash.size(); - LongVector.Builder keys1 = blockFactory.newLongVectorBuilder(positions); - LongVector.Builder keys2 = blockFactory.newLongVectorBuilder(positions); - for (long i = 0; i < positions; i++) { - keys1.appendLong(hash.getKey1(i)); - keys2.appendLong(hash.getKey2(i)); + LongVector k1 = null; + LongVector k2 = null; + try ( + LongVector.Builder keys1 = blockFactory.newLongVectorBuilder(positions); + LongVector.Builder keys2 = blockFactory.newLongVectorBuilder(positions) + ) { + for (long i = 0; i < positions; i++) { + keys1.appendLong(hash.getKey1(i)); + keys2.appendLong(hash.getKey2(i)); + } + k1 = keys1.build(); + k2 = keys2.build(); + } finally { + if (k2 == null) { + Releasables.close(k1); + } } - return new Block[] { keys1.build().asBlock(), keys2.build().asBlock() }; + return new Block[] { k1.asBlock(), k2.asBlock() }; } @Override diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java index a624a3ec68100..06b833974a5db 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/blockhash/PackedValuesBlockHash.java @@ -73,7 +73,9 @@ public void add(Page page, GroupingAggregatorFunction.AddInput addInput) { } void add(Page page, GroupingAggregatorFunction.AddInput addInput, int batchSize) { - new AddWork(page, addInput, batchSize).add(); + try (AddWork work = new AddWork(page, addInput, batchSize)) { + work.add(); + } } private static class Group { @@ -95,7 +97,7 @@ class AddWork extends LongLongBlockHash.AbstractAddBlock { int position; AddWork(Page page, GroupingAggregatorFunction.AddInput addInput, int batchSize) { - super(emitBatchSize, addInput); + super(blockFactory, emitBatchSize, addInput); for (Group group : groups) { group.encoder = MultivalueDedupe.batchEncoder(new Block.Ref(page.getBlock(group.spec.channel()), page), batchSize, true); } @@ -193,45 +195,54 @@ public Block[] getKeys() { int size = Math.toIntExact(bytesRefHash.size()); BatchEncoder.Decoder[] decoders = new BatchEncoder.Decoder[groups.length]; Block.Builder[] builders = new Block.Builder[groups.length]; - for (int g = 0; g < builders.length; g++) { - ElementType elementType = groups[g].spec.elementType(); - decoders[g] = BatchEncoder.decoder(elementType); - builders[g] = elementType.newBlockBuilder(size, blockFactory); - } + try { + for (int g = 0; g < builders.length; g++) { + ElementType elementType = groups[g].spec.elementType(); + decoders[g] = BatchEncoder.decoder(elementType); + builders[g] = elementType.newBlockBuilder(size, blockFactory); + } - BytesRef[] values = new BytesRef[(int) Math.min(100, bytesRefHash.size())]; - BytesRef[] nulls = new BytesRef[values.length]; - for (int offset = 0; offset < values.length; offset++) { - values[offset] = new BytesRef(); - nulls[offset] = new BytesRef(); - nulls[offset].length = nullTrackingBytes; - } - int offset = 0; - for (int i = 0; i < bytesRefHash.size(); i++) { - values[offset] = bytesRefHash.get(i, values[offset]); + BytesRef[] values = new BytesRef[(int) Math.min(100, bytesRefHash.size())]; + BytesRef[] nulls = new BytesRef[values.length]; + for (int offset = 0; offset < values.length; offset++) { + values[offset] = new BytesRef(); + nulls[offset] = new BytesRef(); + nulls[offset].length = nullTrackingBytes; + } + int offset = 0; + for (int i = 0; i < bytesRefHash.size(); i++) { + values[offset] = bytesRefHash.get(i, values[offset]); - // Reference the null bytes in the nulls array and values in the values - nulls[offset].bytes = values[offset].bytes; - nulls[offset].offset = values[offset].offset; - values[offset].offset += nullTrackingBytes; - values[offset].length -= nullTrackingBytes; + // Reference the null bytes in the nulls array and values in the values + nulls[offset].bytes = values[offset].bytes; + nulls[offset].offset = values[offset].offset; + values[offset].offset += nullTrackingBytes; + values[offset].length -= nullTrackingBytes; - offset++; - if (offset == values.length) { + offset++; + if (offset == values.length) { + readKeys(decoders, builders, nulls, values, offset); + offset = 0; + } + } + if (offset > 0) { readKeys(decoders, builders, nulls, values, offset); - offset = 0; } - } - if (offset > 0) { - readKeys(decoders, builders, nulls, values, offset); - } - Block[] keyBlocks = new Block[groups.length]; - for (int g = 0; g < keyBlocks.length; g++) { - keyBlocks[g] = builders[g].build(); + Block[] keyBlocks = new Block[groups.length]; + try { + for (int g = 0; g < keyBlocks.length; g++) { + keyBlocks[g] = builders[g].build(); + } + } finally { + if (keyBlocks[keyBlocks.length - 1] == null) { + Releasables.closeExpectNoException(keyBlocks); + } + } + return keyBlocks; + } finally { + Releasables.closeExpectNoException(builders); } - Releasables.closeExpectNoException(builders); - return keyBlocks; } private void readKeys(BatchEncoder.Decoder[] decoders, Block.Builder[] builders, BytesRef[] nulls, BytesRef[] values, int count) { diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java index ad5dfbf298200..d27f872881460 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/data/BlockFactory.java @@ -109,7 +109,7 @@ public BooleanBlock.Builder newBooleanBlockBuilder(int estimatedSize) { return new BooleanBlockBuilder(estimatedSize, this); } - BooleanVector.FixedBuilder newBooleanVectorFixedBuilder(int size) { + public BooleanVector.FixedBuilder newBooleanVectorFixedBuilder(int size) { return new BooleanVectorFixedBuilder(size, this); } @@ -172,7 +172,7 @@ public IntVector.Builder newIntVectorBuilder(int estimatedSize) { return new IntVectorBuilder(estimatedSize, this); } - IntVector.FixedBuilder newIntVectorFixedBuilder(int size) { + public IntVector.FixedBuilder newIntVectorFixedBuilder(int size) { return new IntVectorFixedBuilder(size, this); } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java index 4cd2c717eb57a..5dfba49b404e8 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashRandomizedTests.java @@ -11,22 +11,24 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.elasticsearch.common.breaker.CircuitBreaker; +import org.elasticsearch.common.breaker.CircuitBreakingException; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.compute.data.BasicBlockTests; import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.MockBlockFactory; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.HashAggregationOperator; import org.elasticsearch.compute.operator.MultivalueDedupeTests; import org.elasticsearch.core.Releasables; +import org.elasticsearch.indices.CrankyCircuitBreakerService; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ListMatcher; -import org.junit.After; import java.util.ArrayList; import java.util.Comparator; @@ -45,11 +47,6 @@ //@TestLogging(value = "org.elasticsearch.compute:TRACE", reason = "debug") public class BlockHashRandomizedTests extends ESTestCase { - - final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); - final BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); - final MockBlockFactory blockFactory = new MockBlockFactory(breaker, bigArrays); - @ParametersFactory public static List params() { List params = new ArrayList<>(); @@ -101,19 +98,33 @@ public BlockHashRandomizedTests( this.allowedTypes = allowedTypes; } - @After - public void checkBreaker() { - assertThat(breaker.getUsed(), is(0L)); + public void test() { + CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); + test(new MockBlockFactory(breaker, bigArrays)); } - public void test() { + public void testWithCranky() { + CircuitBreakerService service = new CrankyCircuitBreakerService(); + CircuitBreaker breaker = service.getBreaker(CircuitBreaker.REQUEST); + BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, service); + try { + test(new MockBlockFactory(breaker, bigArrays)); + logger.info("cranky let us finish!"); + } catch (CircuitBreakingException e) { + logger.info("cranky", e); + assertThat(e.getMessage(), equalTo(CrankyCircuitBreakerService.ERROR_MESSAGE)); + } + } + + private void test(MockBlockFactory blockFactory) { List types = randomList(groups, groups, () -> randomFrom(allowedTypes)); BasicBlockTests.RandomBlock[] randomBlocks = new BasicBlockTests.RandomBlock[types.size()]; Block[] blocks = new Block[types.size()]; int pageCount = between(1, 10); int positionCount = 100; int emitBatchSize = 100; - try (BlockHash blockHash = newBlockHash(emitBatchSize, types)) { + try (BlockHash blockHash = newBlockHash(blockFactory, emitBatchSize, types)) { /* * Only the long/long, long/bytes_ref, and bytes_ref/long implementations don't collect nulls. */ @@ -146,7 +157,6 @@ public void test() { assertThat(ordsAndKeys.ords().getTotalValueCount(), lessThanOrEqualTo(emitBatchSize)); } batchCount[0]++; - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty().asBlock()); }, blocks); if (usingSingle) { assertThat(batchCount[0], equalTo(1)); @@ -184,14 +194,15 @@ public void test() { blockFactory.ensureAllBlocksAreReleased(); } } + assertThat(blockFactory.breaker().getUsed(), is(0L)); } - private BlockHash newBlockHash(int emitBatchSize, List types) { + private BlockHash newBlockHash(BlockFactory blockFactory, int emitBatchSize, List types) { List specs = new ArrayList<>(types.size()); for (int c = 0; c < types.size(); c++) { specs.add(new HashAggregationOperator.GroupSpec(c, types.get(c))); } - DriverContext driverContext = new DriverContext(bigArrays, blockFactory); + DriverContext driverContext = new DriverContext(blockFactory.bigArrays(), blockFactory); return forcePackedHash ? new PackedValuesBlockHash(specs, driverContext, emitBatchSize) : BlockHash.build(specs, driverContext, emitBatchSize, true); diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java index 86c7f64dc3816..567f58d0dee75 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/aggregation/blockhash/BlockHashTests.java @@ -7,11 +7,11 @@ package org.elasticsearch.compute.aggregation.blockhash; +import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.breaker.CircuitBreaker; -import org.elasticsearch.common.inject.name.Named; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; @@ -25,6 +25,7 @@ import org.elasticsearch.compute.data.IntBlock; import org.elasticsearch.compute.data.IntVector; import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.MockBlockFactory; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.HashAggregationOperator; @@ -55,7 +56,7 @@ public class BlockHashTests extends ESTestCase { final CircuitBreaker breaker = new MockBigArrays.LimitedBreaker("esql-test-breaker", ByteSizeValue.ofGb(1)); final BigArrays bigArrays = new MockBigArrays(PageCacheRecycler.NON_RECYCLING_INSTANCE, mockBreakerService(breaker)); - final BlockFactory blockFactory = BlockFactory.getInstance(breaker, bigArrays); + final MockBlockFactory blockFactory = new MockBlockFactory(breaker, bigArrays); @ParametersFactory public static List params() { @@ -67,603 +68,565 @@ public static List params() { @After public void checkBreaker() { + blockFactory.ensureAllBlocksAreReleased(); assertThat(breaker.getUsed(), is(0L)); } private final boolean forcePackedHash; - public BlockHashTests(@Named("forcePackedHash") boolean forcePackedHash) { + public BlockHashTests(@Name("forcePackedHash") boolean forcePackedHash) { this.forcePackedHash = forcePackedHash; } public void testIntHash() { int[] values = new int[] { 1, 2, 3, 1, 2, 3, 1, 2, 3 }; - IntBlock block = BlockFactory.getNonBreakingInstance().newIntArrayVector(values, values.length).asBlock(); - - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT], entries=3, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 1, 2, 0, 1, 2); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - } else { - assertThat(ordsAndKeys.description, equalTo("IntBlockHash{channel=0, entries=3, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 2, 3, 1, 2, 3); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 4))); - } - assertKeys(ordsAndKeys.keys, 1, 2, 3); - // we close these explicitly in the test. In common operation the operator is in charge of closing these. - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT], entries=3, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 1, 2, 0, 1, 2); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + } else { + assertThat(ordsAndKeys.description, equalTo("IntBlockHash{channel=0, entries=3, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 2, 3, 1, 2, 3); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 4))); + } + assertKeys(ordsAndKeys.keys, 1, 2, 3); + }, blockFactory.newIntArrayVector(values, values.length).asBlock()); } public void testIntHashWithNulls() { - IntBlock.Builder builder = BlockFactory.getNonBreakingInstance().newIntBlockBuilder(4); - builder.appendInt(0); - builder.appendNull(); - builder.appendInt(2); - builder.appendNull(); - - OrdsAndKeys ordsAndKeys = hash(builder.build()); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT], entries=3, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); - assertKeys(ordsAndKeys.keys, 0, null, 2); - } else { - assertThat(ordsAndKeys.description, equalTo("IntBlockHash{channel=0, entries=2, seenNull=true}")); - assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); - assertKeys(ordsAndKeys.keys, null, 0, 2); + try (IntBlock.Builder builder = blockFactory.newIntBlockBuilder(4)) { + builder.appendInt(0); + builder.appendNull(); + builder.appendInt(2); + builder.appendNull(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT], entries=3, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); + assertKeys(ordsAndKeys.keys, 0, null, 2); + } else { + assertThat(ordsAndKeys.description, equalTo("IntBlockHash{channel=0, entries=2, seenNull=true}")); + assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); + assertKeys(ordsAndKeys.keys, null, 0, 2); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testIntHashWithMultiValuedFields() { - var builder = BlockFactory.getNonBreakingInstance().newIntBlockBuilder(8); - builder.appendInt(1); - builder.beginPositionEntry(); - builder.appendInt(1); - builder.appendInt(2); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendInt(3); - builder.appendInt(1); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendInt(3); - builder.appendInt(3); - builder.endPositionEntry(); - builder.appendNull(); - builder.beginPositionEntry(); - builder.appendInt(3); - builder.appendInt(2); - builder.appendInt(1); - builder.endPositionEntry(); - - OrdsAndKeys ordsAndKeys = hash(builder.build()); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT], entries=4, size=")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0 }, - new int[] { 0, 1 }, - new int[] { 2, 0 }, - new int[] { 2 }, - new int[] { 3 }, - new int[] { 2, 1, 0 } - ); - assertKeys(ordsAndKeys.keys, 1, 2, 3, null); - } else { - assertThat(ordsAndKeys.description, equalTo("IntBlockHash{channel=0, entries=3, seenNull=true}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 1 }, - new int[] { 1, 2 }, - new int[] { 3, 1 }, - new int[] { 3 }, - new int[] { 0 }, - new int[] { 3, 2, 1 } - ); - assertKeys(ordsAndKeys.keys, null, 1, 2, 3); + try (IntBlock.Builder builder = blockFactory.newIntBlockBuilder(8)) { + builder.appendInt(1); + builder.beginPositionEntry(); + builder.appendInt(1); + builder.appendInt(2); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendInt(3); + builder.appendInt(1); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendInt(3); + builder.appendInt(3); + builder.endPositionEntry(); + builder.appendNull(); + builder.beginPositionEntry(); + builder.appendInt(3); + builder.appendInt(2); + builder.appendInt(1); + builder.endPositionEntry(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT], entries=4, size=")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0 }, + new int[] { 0, 1 }, + new int[] { 2, 0 }, + new int[] { 2 }, + new int[] { 3 }, + new int[] { 2, 1, 0 } + ); + assertKeys(ordsAndKeys.keys, 1, 2, 3, null); + } else { + assertThat(ordsAndKeys.description, equalTo("IntBlockHash{channel=0, entries=3, seenNull=true}")); + assertOrds( + ordsAndKeys.ords, + new int[] { 1 }, + new int[] { 1, 2 }, + new int[] { 3, 1 }, + new int[] { 3 }, + new int[] { 0 }, + new int[] { 3, 2, 1 } + ); + assertKeys(ordsAndKeys.keys, null, 1, 2, 3); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testLongHash() { long[] values = new long[] { 2, 1, 4, 2, 4, 1, 3, 4 }; - LongBlock block = BlockFactory.getNonBreakingInstance().newLongArrayVector(values, values.length).asBlock(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=4, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 2, 1, 3, 2); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - } else { - assertThat(ordsAndKeys.description, equalTo("LongBlockHash{channel=0, entries=4, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 3, 2, 4, 3); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 5))); - } - assertKeys(ordsAndKeys.keys, 2L, 1L, 4L, 3L); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=4, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 2, 1, 3, 2); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + } else { + assertThat(ordsAndKeys.description, equalTo("LongBlockHash{channel=0, entries=4, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 3, 2, 4, 3); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 5))); + } + assertKeys(ordsAndKeys.keys, 2L, 1L, 4L, 3L); + }, blockFactory.newLongArrayVector(values, values.length).asBlock()); } public void testLongHashWithNulls() { - LongBlock.Builder builder = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(4); - builder.appendLong(0); - builder.appendNull(); - builder.appendLong(2); - builder.appendNull(); - - OrdsAndKeys ordsAndKeys = hash(builder.build()); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=3, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); - assertKeys(ordsAndKeys.keys, 0L, null, 2L); - } else { - assertThat(ordsAndKeys.description, equalTo("LongBlockHash{channel=0, entries=2, seenNull=true}")); - assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); - assertKeys(ordsAndKeys.keys, null, 0L, 2L); + try (LongBlock.Builder builder = blockFactory.newLongBlockBuilder(4)) { + builder.appendLong(0); + builder.appendNull(); + builder.appendLong(2); + builder.appendNull(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=3, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); + assertKeys(ordsAndKeys.keys, 0L, null, 2L); + } else { + assertThat(ordsAndKeys.description, equalTo("LongBlockHash{channel=0, entries=2, seenNull=true}")); + assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); + assertKeys(ordsAndKeys.keys, null, 0L, 2L); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testLongHashWithMultiValuedFields() { - var builder = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(8); - builder.appendLong(1); - builder.beginPositionEntry(); - builder.appendLong(1); - builder.appendLong(2); - builder.appendLong(3); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendLong(1); - builder.appendLong(1); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendLong(3); - builder.endPositionEntry(); - builder.appendNull(); - builder.beginPositionEntry(); - builder.appendLong(3); - builder.appendLong(2); - builder.appendLong(1); - builder.endPositionEntry(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=4, size=")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0 }, - new int[] { 0, 1, 2 }, - new int[] { 0 }, - new int[] { 2 }, - new int[] { 3 }, - new int[] { 2, 1, 0 } - ); - assertKeys(ordsAndKeys.keys, 1L, 2L, 3L, null); - } else { - assertThat(ordsAndKeys.description, equalTo("LongBlockHash{channel=0, entries=3, seenNull=true}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 1 }, - new int[] { 1, 2, 3 }, - new int[] { 1 }, - new int[] { 3 }, - new int[] { 0 }, - new int[] { 3, 2, 1 } - ); - assertKeys(ordsAndKeys.keys, null, 1L, 2L, 3L); + try (LongBlock.Builder builder = blockFactory.newLongBlockBuilder(8)) { + builder.appendLong(1); + builder.beginPositionEntry(); + builder.appendLong(1); + builder.appendLong(2); + builder.appendLong(3); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendLong(1); + builder.appendLong(1); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendLong(3); + builder.endPositionEntry(); + builder.appendNull(); + builder.beginPositionEntry(); + builder.appendLong(3); + builder.appendLong(2); + builder.appendLong(1); + builder.endPositionEntry(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG], entries=4, size=")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0 }, + new int[] { 0, 1, 2 }, + new int[] { 0 }, + new int[] { 2 }, + new int[] { 3 }, + new int[] { 2, 1, 0 } + ); + assertKeys(ordsAndKeys.keys, 1L, 2L, 3L, null); + } else { + assertThat(ordsAndKeys.description, equalTo("LongBlockHash{channel=0, entries=3, seenNull=true}")); + assertOrds( + ordsAndKeys.ords, + new int[] { 1 }, + new int[] { 1, 2, 3 }, + new int[] { 1 }, + new int[] { 3 }, + new int[] { 0 }, + new int[] { 3, 2, 1 } + ); + assertKeys(ordsAndKeys.keys, null, 1L, 2L, 3L); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testDoubleHash() { double[] values = new double[] { 2.0, 1.0, 4.0, 2.0, 4.0, 1.0, 3.0, 4.0 }; - DoubleBlock block = BlockFactory.getNonBreakingInstance().newDoubleArrayVector(values, values.length).asBlock(); - OrdsAndKeys ordsAndKeys = hash(block); - - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=4, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 2, 1, 3, 2); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - } else { - assertThat(ordsAndKeys.description, equalTo("DoubleBlockHash{channel=0, entries=4, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 3, 2, 4, 3); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 5))); - } - assertKeys(ordsAndKeys.keys, 2.0, 1.0, 4.0, 3.0); - // we close these explicitly in the test. In common operation the operator is in charge of closing these. - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=4, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 2, 1, 3, 2); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + } else { + assertThat(ordsAndKeys.description, equalTo("DoubleBlockHash{channel=0, entries=4, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 3, 2, 4, 3); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 5))); + } + assertKeys(ordsAndKeys.keys, 2.0, 1.0, 4.0, 3.0); + }, blockFactory.newDoubleArrayVector(values, values.length).asBlock()); } public void testDoubleHashWithNulls() { - DoubleBlock.Builder builder = BlockFactory.getNonBreakingInstance().newDoubleBlockBuilder(4); - builder.appendDouble(0); - builder.appendNull(); - builder.appendDouble(2); - builder.appendNull(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=3, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); - assertKeys(ordsAndKeys.keys, 0.0, null, 2.0); - } else { - assertThat(ordsAndKeys.description, equalTo("DoubleBlockHash{channel=0, entries=2, seenNull=true}")); - assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); - assertKeys(ordsAndKeys.keys, null, 0.0, 2.0); + try (DoubleBlock.Builder builder = blockFactory.newDoubleBlockBuilder(4)) { + builder.appendDouble(0); + builder.appendNull(); + builder.appendDouble(2); + builder.appendNull(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=3, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); + assertKeys(ordsAndKeys.keys, 0.0, null, 2.0); + } else { + assertThat(ordsAndKeys.description, equalTo("DoubleBlockHash{channel=0, entries=2, seenNull=true}")); + assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); + assertKeys(ordsAndKeys.keys, null, 0.0, 2.0); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testDoubleHashWithMultiValuedFields() { - var builder = BlockFactory.getNonBreakingInstance().newDoubleBlockBuilder(8); - builder.appendDouble(1); - builder.beginPositionEntry(); - builder.appendDouble(2); - builder.appendDouble(3); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendDouble(3); - builder.appendDouble(2); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendDouble(1); - builder.endPositionEntry(); - builder.appendNull(); - builder.beginPositionEntry(); - builder.appendDouble(1); - builder.appendDouble(1); - builder.appendDouble(2); - builder.endPositionEntry(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=4, size=")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0 }, - new int[] { 1, 2 }, - new int[] { 2, 1 }, - new int[] { 0 }, - new int[] { 3 }, - new int[] { 0, 1 } - ); - assertKeys(ordsAndKeys.keys, 1.0, 2.0, 3.0, null); - } else { - assertThat(ordsAndKeys.description, equalTo("DoubleBlockHash{channel=0, entries=3, seenNull=true}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 1 }, - new int[] { 2, 3 }, - new int[] { 3, 2 }, - new int[] { 1 }, - new int[] { 0 }, - new int[] { 1, 2 } - ); - assertKeys(ordsAndKeys.keys, null, 1.0, 2.0, 3.0); + try (DoubleBlock.Builder builder = blockFactory.newDoubleBlockBuilder(8)) { + builder.appendDouble(1); + builder.beginPositionEntry(); + builder.appendDouble(2); + builder.appendDouble(3); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendDouble(3); + builder.appendDouble(2); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendDouble(1); + builder.endPositionEntry(); + builder.appendNull(); + builder.beginPositionEntry(); + builder.appendDouble(1); + builder.appendDouble(1); + builder.appendDouble(2); + builder.endPositionEntry(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:DOUBLE], entries=4, size=")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0 }, + new int[] { 1, 2 }, + new int[] { 2, 1 }, + new int[] { 0 }, + new int[] { 3 }, + new int[] { 0, 1 } + ); + assertKeys(ordsAndKeys.keys, 1.0, 2.0, 3.0, null); + } else { + assertThat(ordsAndKeys.description, equalTo("DoubleBlockHash{channel=0, entries=3, seenNull=true}")); + assertOrds( + ordsAndKeys.ords, + new int[] { 1 }, + new int[] { 2, 3 }, + new int[] { 3, 2 }, + new int[] { 1 }, + new int[] { 0 }, + new int[] { 1, 2 } + ); + assertKeys(ordsAndKeys.keys, null, 1.0, 2.0, 3.0); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testBasicBytesRefHash() { - var builder = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(8); - builder.appendBytesRef(new BytesRef("item-2")); - builder.appendBytesRef(new BytesRef("item-1")); - builder.appendBytesRef(new BytesRef("item-4")); - builder.appendBytesRef(new BytesRef("item-2")); - builder.appendBytesRef(new BytesRef("item-4")); - builder.appendBytesRef(new BytesRef("item-1")); - builder.appendBytesRef(new BytesRef("item-3")); - builder.appendBytesRef(new BytesRef("item-4")); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=4, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 2, 1, 3, 2); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - } else { - assertThat(ordsAndKeys.description, startsWith("BytesRefBlockHash{channel=0, entries=4, size=")); - assertThat(ordsAndKeys.description, endsWith("b, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 3, 2, 4, 3); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 5))); + try (BytesRefBlock.Builder builder = blockFactory.newBytesRefBlockBuilder(8)) { + builder.appendBytesRef(new BytesRef("item-2")); + builder.appendBytesRef(new BytesRef("item-1")); + builder.appendBytesRef(new BytesRef("item-4")); + builder.appendBytesRef(new BytesRef("item-2")); + builder.appendBytesRef(new BytesRef("item-4")); + builder.appendBytesRef(new BytesRef("item-1")); + builder.appendBytesRef(new BytesRef("item-3")); + builder.appendBytesRef(new BytesRef("item-4")); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=4, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 0, 2, 1, 3, 2); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + } else { + assertThat(ordsAndKeys.description, startsWith("BytesRefBlockHash{channel=0, entries=4, size=")); + assertThat(ordsAndKeys.description, endsWith("b, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 1, 2, 3, 1, 3, 2, 4, 3); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 5))); + } + assertKeys(ordsAndKeys.keys, "item-2", "item-1", "item-4", "item-3"); + }, builder); } - assertKeys(ordsAndKeys.keys, "item-2", "item-1", "item-4", "item-3"); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testBytesRefHashWithNulls() { - BytesRefBlock.Builder builder = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(4); - builder.appendBytesRef(new BytesRef("cat")); - builder.appendNull(); - builder.appendBytesRef(new BytesRef("dog")); - builder.appendNull(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=3, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); - assertKeys(ordsAndKeys.keys, "cat", null, "dog"); - } else { - assertThat(ordsAndKeys.description, startsWith("BytesRefBlockHash{channel=0, entries=2, size=")); - assertThat(ordsAndKeys.description, endsWith("b, seenNull=true}")); - assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); - assertKeys(ordsAndKeys.keys, null, "cat", "dog"); + try (BytesRefBlock.Builder builder = blockFactory.newBytesRefBlockBuilder(4)) { + builder.appendBytesRef(new BytesRef("cat")); + builder.appendNull(); + builder.appendBytesRef(new BytesRef("dog")); + builder.appendNull(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=3, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); + assertKeys(ordsAndKeys.keys, "cat", null, "dog"); + } else { + assertThat(ordsAndKeys.description, startsWith("BytesRefBlockHash{channel=0, entries=2, size=")); + assertThat(ordsAndKeys.description, endsWith("b, seenNull=true}")); + assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); + assertKeys(ordsAndKeys.keys, null, "cat", "dog"); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testBytesRefHashWithMultiValuedFields() { - var builder = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(8); - builder.appendBytesRef(new BytesRef("foo")); - builder.beginPositionEntry(); - builder.appendBytesRef(new BytesRef("foo")); - builder.appendBytesRef(new BytesRef("bar")); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendBytesRef(new BytesRef("bar")); - builder.appendBytesRef(new BytesRef("bort")); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendBytesRef(new BytesRef("bort")); - builder.appendBytesRef(new BytesRef("bar")); - builder.endPositionEntry(); - builder.appendNull(); - builder.beginPositionEntry(); - builder.appendBytesRef(new BytesRef("bort")); - builder.appendBytesRef(new BytesRef("bort")); - builder.appendBytesRef(new BytesRef("bar")); - builder.endPositionEntry(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=4, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0 }, - new int[] { 0, 1 }, - new int[] { 1, 2 }, - new int[] { 2, 1 }, - new int[] { 3 }, - new int[] { 2, 1 } - ); - assertKeys(ordsAndKeys.keys, "foo", "bar", "bort", null); - } else { - assertThat(ordsAndKeys.description, startsWith("BytesRefBlockHash{channel=0, entries=3, size=")); - assertThat(ordsAndKeys.description, endsWith("b, seenNull=true}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 1 }, - new int[] { 1, 2 }, - new int[] { 2, 3 }, - new int[] { 3, 2 }, - new int[] { 0 }, - new int[] { 3, 2 } - ); - assertKeys(ordsAndKeys.keys, null, "foo", "bar", "bort"); + try (BytesRefBlock.Builder builder = blockFactory.newBytesRefBlockBuilder(8)) { + builder.appendBytesRef(new BytesRef("foo")); + builder.beginPositionEntry(); + builder.appendBytesRef(new BytesRef("foo")); + builder.appendBytesRef(new BytesRef("bar")); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendBytesRef(new BytesRef("bar")); + builder.appendBytesRef(new BytesRef("bort")); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendBytesRef(new BytesRef("bort")); + builder.appendBytesRef(new BytesRef("bar")); + builder.endPositionEntry(); + builder.appendNull(); + builder.beginPositionEntry(); + builder.appendBytesRef(new BytesRef("bort")); + builder.appendBytesRef(new BytesRef("bort")); + builder.appendBytesRef(new BytesRef("bar")); + builder.endPositionEntry(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BYTES_REF], entries=4, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0 }, + new int[] { 0, 1 }, + new int[] { 1, 2 }, + new int[] { 2, 1 }, + new int[] { 3 }, + new int[] { 2, 1 } + ); + assertKeys(ordsAndKeys.keys, "foo", "bar", "bort", null); + } else { + assertThat(ordsAndKeys.description, startsWith("BytesRefBlockHash{channel=0, entries=3, size=")); + assertThat(ordsAndKeys.description, endsWith("b, seenNull=true}")); + assertOrds( + ordsAndKeys.ords, + new int[] { 1 }, + new int[] { 1, 2 }, + new int[] { 2, 3 }, + new int[] { 3, 2 }, + new int[] { 0 }, + new int[] { 3, 2 } + ); + assertKeys(ordsAndKeys.keys, null, "foo", "bar", "bort"); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testBooleanHashFalseFirst() { boolean[] values = new boolean[] { false, true, true, true, true }; - BooleanBlock block = BlockFactory.getNonBreakingInstance().newBooleanArrayVector(values, values.length).asBlock(); - - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=2, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 1, 1, 1); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); - } else { - assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 1, 2, 2, 2, 2); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 3))); - } - assertKeys(ordsAndKeys.keys, false, true); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=2, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 1, 1, 1); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); + } else { + assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 1, 2, 2, 2, 2); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 3))); + } + assertKeys(ordsAndKeys.keys, false, true); + }, blockFactory.newBooleanArrayVector(values, values.length).asBlock()); } public void testBooleanHashTrueFirst() { boolean[] values = new boolean[] { true, false, false, true, true }; - BooleanBlock block = BlockFactory.getNonBreakingInstance().newBooleanArrayVector(values, values.length).asBlock(); - - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=2, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 1, 0, 0); - assertKeys(ordsAndKeys.keys, true, false); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); - } else { - assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 2, 1, 1, 2, 2); - assertKeys(ordsAndKeys.keys, false, true); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 3))); - } - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=2, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 1, 0, 0); + assertKeys(ordsAndKeys.keys, true, false); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); + } else { + assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 2, 1, 1, 2, 2); + assertKeys(ordsAndKeys.keys, false, true); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(1, 3))); + } + }, blockFactory.newBooleanArrayVector(values, values.length).asBlock()); } public void testBooleanHashTrueOnly() { boolean[] values = new boolean[] { true, true, true, true }; - BooleanBlock block = BlockFactory.getNonBreakingInstance().newBooleanArrayVector(values, values.length).asBlock(); - - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=1, size=")); - assertOrds(ordsAndKeys.ords, 0, 0, 0, 0); - assertKeys(ordsAndKeys.keys, true); - assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(0).build())); - } else { - assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=false, seenTrue=true, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 2, 2, 2, 2); - assertKeys(ordsAndKeys.keys, true); - assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(2).build())); - } - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=1, size=")); + assertOrds(ordsAndKeys.ords, 0, 0, 0, 0); + assertKeys(ordsAndKeys.keys, true); + assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(0).build())); + } else { + assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=false, seenTrue=true, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 2, 2, 2, 2); + assertKeys(ordsAndKeys.keys, true); + assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(2).build())); + } + }, blockFactory.newBooleanArrayVector(values, values.length).asBlock()); } public void testBooleanHashFalseOnly() { boolean[] values = new boolean[] { false, false, false, false }; - BooleanBlock block = BlockFactory.getNonBreakingInstance().newBooleanArrayVector(values, values.length).asBlock(); - - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=1, size=")); - assertOrds(ordsAndKeys.ords, 0, 0, 0, 0); - assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(0).build())); - } else { - assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=false, seenNull=false}")); - assertOrds(ordsAndKeys.ords, 1, 1, 1, 1); - assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(1).build())); - } - assertKeys(ordsAndKeys.keys, false); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=1, size=")); + assertOrds(ordsAndKeys.ords, 0, 0, 0, 0); + assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(0).build())); + } else { + assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=false, seenNull=false}")); + assertOrds(ordsAndKeys.ords, 1, 1, 1, 1); + assertThat(ordsAndKeys.nonEmpty, equalTo(IntVector.newVectorBuilder(1).appendInt(1).build())); + } + assertKeys(ordsAndKeys.keys, false); + }, blockFactory.newBooleanArrayVector(values, values.length).asBlock()); } public void testBooleanHashWithNulls() { - BooleanBlock.Builder builder = BlockFactory.getNonBreakingInstance().newBooleanBlockBuilder(4); - builder.appendBoolean(false); - builder.appendNull(); - builder.appendBoolean(true); - builder.appendNull(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=3, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); - assertKeys(ordsAndKeys.keys, false, null, true); - } else { - assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=true}")); - assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); - assertKeys(ordsAndKeys.keys, null, false, true); + try (BooleanBlock.Builder builder = blockFactory.newBooleanBlockBuilder(4)) { + builder.appendBoolean(false); + builder.appendNull(); + builder.appendBoolean(true); + builder.appendNull(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=3, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 1); + assertKeys(ordsAndKeys.keys, false, null, true); + } else { + assertThat( + ordsAndKeys.description, + equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=true}") + ); + assertOrds(ordsAndKeys.ords, 1, 0, 2, 0); + assertKeys(ordsAndKeys.keys, null, false, true); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testBooleanHashWithMultiValuedFields() { - var builder = BlockFactory.getNonBreakingInstance().newBooleanBlockBuilder(8); - builder.appendBoolean(false); - builder.beginPositionEntry(); - builder.appendBoolean(false); - builder.appendBoolean(true); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendBoolean(true); - builder.appendBoolean(false); - builder.endPositionEntry(); - builder.beginPositionEntry(); - builder.appendBoolean(true); - builder.endPositionEntry(); - builder.appendNull(); - builder.beginPositionEntry(); - builder.appendBoolean(true); - builder.appendBoolean(true); - builder.appendBoolean(false); - builder.endPositionEntry(); - - Block block = builder.build(); - OrdsAndKeys ordsAndKeys = hash(block); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=3, size=")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0 }, - new int[] { 0, 1 }, - new int[] { 0, 1 }, // Order is not preserved - new int[] { 1 }, - new int[] { 2 }, - new int[] { 0, 1 } - ); - assertKeys(ordsAndKeys.keys, false, true, null); - } else { - assertThat(ordsAndKeys.description, equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=true}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 1 }, - new int[] { 1, 2 }, - new int[] { 1, 2 }, // Order is not preserved - new int[] { 2 }, - new int[] { 0 }, - new int[] { 1, 2 } - ); - assertKeys(ordsAndKeys.keys, null, false, true); + try (BooleanBlock.Builder builder = blockFactory.newBooleanBlockBuilder(8)) { + builder.appendBoolean(false); + builder.beginPositionEntry(); + builder.appendBoolean(false); + builder.appendBoolean(true); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendBoolean(true); + builder.appendBoolean(false); + builder.endPositionEntry(); + builder.beginPositionEntry(); + builder.appendBoolean(true); + builder.endPositionEntry(); + builder.appendNull(); + builder.beginPositionEntry(); + builder.appendBoolean(true); + builder.appendBoolean(true); + builder.appendBoolean(false); + builder.endPositionEntry(); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:BOOLEAN], entries=3, size=")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0 }, + new int[] { 0, 1 }, + new int[] { 0, 1 }, // Order is not preserved + new int[] { 1 }, + new int[] { 2 }, + new int[] { 0, 1 } + ); + assertKeys(ordsAndKeys.keys, false, true, null); + } else { + assertThat( + ordsAndKeys.description, + equalTo("BooleanBlockHash{channel=0, seenFalse=true, seenTrue=true, seenNull=true}") + ); + assertOrds( + ordsAndKeys.ords, + new int[] { 1 }, + new int[] { 1, 2 }, + new int[] { 1, 2 }, // Order is not preserved + new int[] { 2 }, + new int[] { 0 }, + new int[] { 1, 2 } + ); + assertKeys(ordsAndKeys.keys, null, false, true); + } + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); + }, builder); } - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 3))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testLongLongHash() { long[] values1 = new long[] { 0, 1, 0, 1, 0, 1 }; - LongBlock block1 = BlockFactory.getNonBreakingInstance().newLongArrayVector(values1, values1.length).asBlock(); long[] values2 = new long[] { 0, 0, 0, 1, 1, 1 }; - LongBlock block2 = BlockFactory.getNonBreakingInstance().newLongArrayVector(values2, values2.length).asBlock(); - Object[][] expectedKeys = { new Object[] { 0L, 0L }, new Object[] { 1L, 0L }, new Object[] { 1L, 1L }, new Object[] { 0L, 1L } }; - - OrdsAndKeys ordsAndKeys = hash(block1, block2); - assertThat( - ordsAndKeys.description, - forcePackedHash - ? startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=4, size=") - : equalTo("LongLongBlockHash{channels=[0,1], entries=4}") + hash(ordsAndKeys -> { + Object[][] expectedKeys = { + new Object[] { 0L, 0L }, + new Object[] { 1L, 0L }, + new Object[] { 1L, 1L }, + new Object[] { 0L, 1L } }; + + assertThat( + ordsAndKeys.description, + forcePackedHash + ? startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=4, size=") + : equalTo("LongLongBlockHash{channels=[0,1], entries=4}") + ); + assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); + assertKeys(ordsAndKeys.keys, expectedKeys); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + }, + blockFactory.newLongArrayVector(values1, values1.length).asBlock(), + blockFactory.newLongArrayVector(values2, values2.length).asBlock() ); - assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); - assertKeys(ordsAndKeys.keys, expectedKeys); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } private void append(LongBlock.Builder b1, LongBlock.Builder b2, long[] v1, long[] v2) { @@ -692,289 +655,282 @@ private void append(LongBlock.Builder b1, LongBlock.Builder b2, long[] v1, long[ } public void testLongLongHashWithMultiValuedFields() { - var b1 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(8); - var b2 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(8); - append(b1, b2, new long[] { 1, 2 }, new long[] { 10, 20 }); - append(b1, b2, new long[] { 1, 2 }, new long[] { 10 }); - append(b1, b2, new long[] { 1 }, new long[] { 10, 20 }); - append(b1, b2, new long[] { 1 }, new long[] { 10 }); - append(b1, b2, null, new long[] { 10 }); - append(b1, b2, new long[] { 1 }, null); - append(b1, b2, new long[] { 1, 1, 1 }, new long[] { 10, 10, 10 }); - append(b1, b2, new long[] { 1, 1, 2, 2 }, new long[] { 10, 20, 20 }); - append(b1, b2, new long[] { 1, 2, 3 }, new long[] { 30, 30, 10 }); - - Block block1 = b1.build(); - Block block2 = b2.build(); - OrdsAndKeys ordsAndKeys = hash(block1, block2); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=10, size=")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0, 1, 2, 3 }, - new int[] { 0, 2 }, - new int[] { 0, 1 }, - new int[] { 0 }, - new int[] { 4 }, - new int[] { 5 }, - new int[] { 0 }, - new int[] { 0, 1, 2, 3 }, - new int[] { 6, 0, 7, 2, 8, 9 } - ); - assertKeys( - ordsAndKeys.keys, - new Object[][] { - new Object[] { 1L, 10L }, - new Object[] { 1L, 20L }, - new Object[] { 2L, 10L }, - new Object[] { 2L, 20L }, - new Object[] { null, 10L }, - new Object[] { 1L, null }, - new Object[] { 1L, 30L }, - new Object[] { 2L, 30L }, - new Object[] { 3L, 30L }, - new Object[] { 3L, 10L }, } - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 10))); - } else { - assertThat(ordsAndKeys.description, equalTo("LongLongBlockHash{channels=[0,1], entries=8}")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0, 1, 2, 3 }, - new int[] { 0, 2 }, - new int[] { 0, 1 }, - new int[] { 0 }, - null, - null, - new int[] { 0 }, - new int[] { 0, 1, 2, 3 }, - new int[] { 4, 0, 5, 2, 6, 7 } - ); - assertKeys( - ordsAndKeys.keys, - new Object[][] { - new Object[] { 1L, 10L }, - new Object[] { 1L, 20L }, - new Object[] { 2L, 10L }, - new Object[] { 2L, 20L }, - new Object[] { 1L, 30L }, - new Object[] { 2L, 30L }, - new Object[] { 3L, 30L }, - new Object[] { 3L, 10L }, } - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 8))); + try (LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(8); LongBlock.Builder b2 = blockFactory.newLongBlockBuilder(8)) { + append(b1, b2, new long[] { 1, 2 }, new long[] { 10, 20 }); + append(b1, b2, new long[] { 1, 2 }, new long[] { 10 }); + append(b1, b2, new long[] { 1 }, new long[] { 10, 20 }); + append(b1, b2, new long[] { 1 }, new long[] { 10 }); + append(b1, b2, null, new long[] { 10 }); + append(b1, b2, new long[] { 1 }, null); + append(b1, b2, new long[] { 1, 1, 1 }, new long[] { 10, 10, 10 }); + append(b1, b2, new long[] { 1, 1, 2, 2 }, new long[] { 10, 20, 20 }); + append(b1, b2, new long[] { 1, 2, 3 }, new long[] { 30, 30, 10 }); + + hash(ordsAndKeys -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=10, size=")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0, 1, 2, 3 }, + new int[] { 0, 2 }, + new int[] { 0, 1 }, + new int[] { 0 }, + new int[] { 4 }, + new int[] { 5 }, + new int[] { 0 }, + new int[] { 0, 1, 2, 3 }, + new int[] { 6, 0, 7, 2, 8, 9 } + ); + assertKeys( + ordsAndKeys.keys, + new Object[][] { + new Object[] { 1L, 10L }, + new Object[] { 1L, 20L }, + new Object[] { 2L, 10L }, + new Object[] { 2L, 20L }, + new Object[] { null, 10L }, + new Object[] { 1L, null }, + new Object[] { 1L, 30L }, + new Object[] { 2L, 30L }, + new Object[] { 3L, 30L }, + new Object[] { 3L, 10L }, } + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 10))); + } else { + assertThat(ordsAndKeys.description, equalTo("LongLongBlockHash{channels=[0,1], entries=8}")); + assertOrds( + ordsAndKeys.ords, + new int[] { 0, 1, 2, 3 }, + new int[] { 0, 2 }, + new int[] { 0, 1 }, + new int[] { 0 }, + null, + null, + new int[] { 0 }, + new int[] { 0, 1, 2, 3 }, + new int[] { 4, 0, 5, 2, 6, 7 } + ); + assertKeys( + ordsAndKeys.keys, + new Object[][] { + new Object[] { 1L, 10L }, + new Object[] { 1L, 20L }, + new Object[] { 2L, 10L }, + new Object[] { 2L, 20L }, + new Object[] { 1L, 30L }, + new Object[] { 2L, 30L }, + new Object[] { 3L, 30L }, + new Object[] { 3L, 10L }, } + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 8))); + } + }, b1, b2); } - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testLongLongHashHugeCombinatorialExplosion() { long[] v1 = LongStream.range(0, 5000).toArray(); long[] v2 = LongStream.range(100, 200).toArray(); - var b1 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(v1.length); - var b2 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(v2.length); - append(b1, b2, v1, v2); - var block1 = b1.build(); - var block2 = b2.build(); - - int[] expectedEntries = new int[1]; - int pageSize = between(1000, 16 * 1024); - hash(ordsAndKeys -> { - int start = expectedEntries[0]; - expectedEntries[0] = Math.min(expectedEntries[0] + pageSize, v1.length * v2.length); - assertThat( - ordsAndKeys.description, - forcePackedHash - ? startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=" + expectedEntries[0] + ", size=") - : equalTo("LongLongBlockHash{channels=[0,1], entries=" + expectedEntries[0] + "}") - ); - assertOrds(ordsAndKeys.ords, IntStream.range(start, expectedEntries[0]).toArray()); - assertKeys( - ordsAndKeys.keys, - IntStream.range(0, expectedEntries[0]) - .mapToObj(i -> new Object[] { v1[i / v2.length], v2[i % v2.length] }) - .toArray(l -> new Object[l][]) - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, expectedEntries[0]))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - }, pageSize, block1, block2); + try ( + LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(v1.length); + LongBlock.Builder b2 = blockFactory.newLongBlockBuilder(v2.length) + ) { + append(b1, b2, v1, v2); + int[] expectedEntries = new int[1]; + int pageSize = between(1000, 16 * 1024); + hash(ordsAndKeys -> { + int start = expectedEntries[0]; + expectedEntries[0] = Math.min(expectedEntries[0] + pageSize, v1.length * v2.length); + assertThat( + ordsAndKeys.description, + forcePackedHash + ? startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=" + expectedEntries[0] + ", size=") + : equalTo("LongLongBlockHash{channels=[0,1], entries=" + expectedEntries[0] + "}") + ); + assertOrds(ordsAndKeys.ords, IntStream.range(start, expectedEntries[0]).toArray()); + assertKeys( + ordsAndKeys.keys, + IntStream.range(0, expectedEntries[0]) + .mapToObj(i -> new Object[] { v1[i / v2.length], v2[i % v2.length] }) + .toArray(l -> new Object[l][]) + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, expectedEntries[0]))); + }, pageSize, b1, b2); - assertThat("misconfigured test", expectedEntries[0], greaterThan(0)); - assertThat(breaker.getUsed(), is(0L)); + assertThat("misconfigured test", expectedEntries[0], greaterThan(0)); + } } public void testIntLongHash() { int[] values1 = new int[] { 0, 1, 0, 1, 0, 1 }; - IntBlock block1 = BlockFactory.getNonBreakingInstance().newIntArrayVector(values1, values1.length).asBlock(); long[] values2 = new long[] { 0, 0, 0, 1, 1, 1 }; - LongBlock block2 = BlockFactory.getNonBreakingInstance().newLongArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0, 0L }, new Object[] { 1, 0L }, new Object[] { 1, 1L }, new Object[] { 0, 1L } }; - - OrdsAndKeys ordsAndKeys = hash(block1, block2); - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT, 1:LONG], entries=4, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); - assertKeys(ordsAndKeys.keys, expectedKeys); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash(ordsAndKeys -> { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT, 1:LONG], entries=4, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); + assertKeys(ordsAndKeys.keys, expectedKeys); + }, + blockFactory.newIntArrayVector(values1, values1.length).asBlock(), + blockFactory.newLongArrayVector(values2, values2.length).asBlock() + ); } public void testLongDoubleHash() { long[] values1 = new long[] { 0, 1, 0, 1, 0, 1 }; - LongBlock block1 = BlockFactory.getNonBreakingInstance().newLongArrayVector(values1, values1.length).asBlock(); double[] values2 = new double[] { 0, 0, 0, 1, 1, 1 }; - DoubleBlock block2 = BlockFactory.getNonBreakingInstance().newDoubleArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0L, 0d }, new Object[] { 1L, 0d }, new Object[] { 1L, 1d }, new Object[] { 0L, 1d } }; - OrdsAndKeys ordsAndKeys = hash(block1, block2); - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:DOUBLE], entries=4, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); - assertKeys(ordsAndKeys.keys, expectedKeys); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash((OrdsAndKeys ordsAndKeys) -> { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:DOUBLE], entries=4, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); + assertKeys(ordsAndKeys.keys, expectedKeys); + }, + blockFactory.newLongArrayVector(values1, values1.length).asBlock(), + blockFactory.newDoubleArrayVector(values2, values2.length).asBlock() + ); } public void testIntBooleanHash() { int[] values1 = new int[] { 0, 1, 0, 1, 0, 1 }; - IntBlock block1 = BlockFactory.getNonBreakingInstance().newIntArrayVector(values1, values1.length).asBlock(); boolean[] values2 = new boolean[] { false, false, false, true, true, true }; - BooleanBlock block2 = BlockFactory.getNonBreakingInstance().newBooleanArrayVector(values2, values2.length).asBlock(); Object[][] expectedKeys = { new Object[] { 0, false }, new Object[] { 1, false }, new Object[] { 1, true }, new Object[] { 0, true } }; - - OrdsAndKeys ordsAndKeys = hash(block1, block2); - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT, 1:BOOLEAN], entries=4, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); - assertKeys(ordsAndKeys.keys, expectedKeys); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + hash((OrdsAndKeys ordsAndKeys) -> { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:INT, 1:BOOLEAN], entries=4, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); + assertKeys(ordsAndKeys.keys, expectedKeys); + }, + blockFactory.newIntArrayVector(values1, values1.length).asBlock(), + blockFactory.newBooleanArrayVector(values2, values2.length).asBlock() + ); } public void testLongLongHashWithNull() { - LongBlock.Builder b1 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(2); - LongBlock.Builder b2 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(2); - b1.appendLong(1); - b2.appendLong(0); - b1.appendNull(); - b2.appendNull(); - b1.appendLong(0); - b2.appendLong(1); - b1.appendLong(0); - b2.appendNull(); - b1.appendNull(); - b2.appendLong(0); - - OrdsAndKeys ordsAndKeys = hash(b1.build(), b2.build()); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=5, size=")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 3, 4); - assertKeys( - ordsAndKeys.keys, - new Object[][] { - new Object[] { 1L, 0L }, - new Object[] { null, null }, - new Object[] { 0L, 1L }, - new Object[] { 0L, null }, - new Object[] { null, 0L }, } - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 5))); - } else { - assertThat(ordsAndKeys.description, equalTo("LongLongBlockHash{channels=[0,1], entries=2}")); - assertOrds(ordsAndKeys.ords, 0, null, 1, null, null); - assertKeys(ordsAndKeys.keys, new Object[][] { new Object[] { 1L, 0L }, new Object[] { 0L, 1L } }); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); + try (LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(2); LongBlock.Builder b2 = blockFactory.newLongBlockBuilder(2)) { + b1.appendLong(1); + b2.appendLong(0); + b1.appendNull(); + b2.appendNull(); + b1.appendLong(0); + b2.appendLong(1); + b1.appendLong(0); + b2.appendNull(); + b1.appendNull(); + b2.appendLong(0); + + hash((OrdsAndKeys ordsAndKeys) -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:LONG], entries=5, size=")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 3, 4); + assertKeys( + ordsAndKeys.keys, + new Object[][] { + new Object[] { 1L, 0L }, + new Object[] { null, null }, + new Object[] { 0L, 1L }, + new Object[] { 0L, null }, + new Object[] { null, 0L }, } + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 5))); + } else { + assertThat(ordsAndKeys.description, equalTo("LongLongBlockHash{channels=[0,1], entries=2}")); + assertOrds(ordsAndKeys.ords, 0, null, 1, null, null); + assertKeys(ordsAndKeys.keys, new Object[][] { new Object[] { 1L, 0L }, new Object[] { 0L, 1L } }); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); + } + }, b1, b2); } - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); } public void testLongBytesRefHash() { - long[] values1 = new long[] { 0, 1, 0, 1, 0, 1 }; - LongBlock block1 = BlockFactory.getNonBreakingInstance().newLongArrayVector(values1, values1.length).asBlock(); - BytesRefBlock.Builder builder = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(8); - builder.appendBytesRef(new BytesRef("cat")); - builder.appendBytesRef(new BytesRef("cat")); - builder.appendBytesRef(new BytesRef("cat")); - builder.appendBytesRef(new BytesRef("dog")); - builder.appendBytesRef(new BytesRef("dog")); - builder.appendBytesRef(new BytesRef("dog")); - BytesRefBlock block2 = builder.build(); - Object[][] expectedKeys = { - new Object[] { 0L, "cat" }, - new Object[] { 1L, "cat" }, - new Object[] { 1L, "dog" }, - new Object[] { 0L, "dog" } }; - - OrdsAndKeys ordsAndKeys = hash(block1, block2); - assertThat( - ordsAndKeys.description, - startsWith( - forcePackedHash - ? "PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=4, size=" - : "BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=4, size=" - ) - ); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); - assertKeys(ordsAndKeys.keys, expectedKeys); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); + try ( + LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(8); + BytesRefBlock.Builder b2 = blockFactory.newBytesRefBlockBuilder(8) + ) { + b1.appendLong(0); + b2.appendBytesRef(new BytesRef("cat")); + b1.appendLong(1); + b2.appendBytesRef(new BytesRef("cat")); + b1.appendLong(0); + b2.appendBytesRef(new BytesRef("cat")); + b1.appendLong(1); + b2.appendBytesRef(new BytesRef("dog")); + b1.appendLong(0); + b2.appendBytesRef(new BytesRef("dog")); + b1.appendLong(1); + b2.appendBytesRef(new BytesRef("dog")); + Object[][] expectedKeys = { + new Object[] { 0L, "cat" }, + new Object[] { 1L, "cat" }, + new Object[] { 1L, "dog" }, + new Object[] { 0L, "dog" } }; + + hash((OrdsAndKeys ordsAndKeys) -> { + assertThat( + ordsAndKeys.description, + startsWith( + forcePackedHash + ? "PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=4, size=" + : "BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=4, size=" + ) + ); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 0, 2, 3, 2); + assertKeys(ordsAndKeys.keys, expectedKeys); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 4))); + }, b1, b2); + } } public void testLongBytesRefHashWithNull() { - LongBlock.Builder b1 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(2); - BytesRefBlock.Builder b2 = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(2); - b1.appendLong(1); - b2.appendBytesRef(new BytesRef("cat")); - b1.appendNull(); - b2.appendNull(); - b1.appendLong(0); - b2.appendBytesRef(new BytesRef("dog")); - b1.appendLong(0); - b2.appendNull(); - b1.appendNull(); - b2.appendBytesRef(new BytesRef("vanish")); - - OrdsAndKeys ordsAndKeys = hash(b1.build(), b2.build()); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=5, size=")); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, 1, 2, 3, 4); - assertKeys( - ordsAndKeys.keys, - new Object[][] { - new Object[] { 1L, "cat" }, - new Object[] { null, null }, - new Object[] { 0L, "dog" }, - new Object[] { 1L, null }, - new Object[] { null, "vanish" } } - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 5))); - } else { - assertThat( - ordsAndKeys.description, - startsWith("BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=2, size=") - ); - assertThat(ordsAndKeys.description, endsWith("b}")); - assertOrds(ordsAndKeys.ords, 0, null, 1, null, null); - assertKeys(ordsAndKeys.keys, new Object[][] { new Object[] { 1L, "cat" }, new Object[] { 0L, "dog" } }); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); + try ( + LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(2); + BytesRefBlock.Builder b2 = blockFactory.newBytesRefBlockBuilder(2) + ) { + b1.appendLong(1); + b2.appendBytesRef(new BytesRef("cat")); + b1.appendNull(); + b2.appendNull(); + b1.appendLong(0); + b2.appendBytesRef(new BytesRef("dog")); + b1.appendLong(0); + b2.appendNull(); + b1.appendNull(); + b2.appendBytesRef(new BytesRef("vanish")); + + hash((OrdsAndKeys ordsAndKeys) -> { + if (forcePackedHash) { + assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=5, size=")); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, 1, 2, 3, 4); + assertKeys( + ordsAndKeys.keys, + new Object[][] { + new Object[] { 1L, "cat" }, + new Object[] { null, null }, + new Object[] { 0L, "dog" }, + new Object[] { 1L, null }, + new Object[] { null, "vanish" } } + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 5))); + } else { + assertThat( + ordsAndKeys.description, + startsWith("BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=2, size=") + ); + assertThat(ordsAndKeys.description, endsWith("b}")); + assertOrds(ordsAndKeys.ords, 0, null, 1, null, null); + assertKeys(ordsAndKeys.keys, new Object[][] { new Object[] { 1L, "cat" }, new Object[] { 0L, "dog" } }); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 2))); + } + }, b1, b2); } - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } private void append(LongBlock.Builder b1, BytesRefBlock.Builder b2, long[] v1, String[] v2) { @@ -1003,123 +959,128 @@ private void append(LongBlock.Builder b1, BytesRefBlock.Builder b2, long[] v1, S } public void testLongBytesRefHashWithMultiValuedFields() { - var b1 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(8); - var b2 = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(8); - append(b1, b2, new long[] { 1, 2 }, new String[] { "a", "b" }); - append(b1, b2, new long[] { 1, 2 }, new String[] { "a" }); - append(b1, b2, new long[] { 1 }, new String[] { "a", "b" }); - append(b1, b2, new long[] { 1 }, new String[] { "a" }); - append(b1, b2, null, new String[] { "a" }); - append(b1, b2, new long[] { 1 }, null); - append(b1, b2, new long[] { 1, 1, 1 }, new String[] { "a", "a", "a" }); - append(b1, b2, new long[] { 1, 1, 2, 2 }, new String[] { "a", "b", "b" }); - append(b1, b2, new long[] { 1, 2, 3 }, new String[] { "c", "c", "a" }); - - OrdsAndKeys ordsAndKeys = hash(b1.build(), b2.build()); - if (forcePackedHash) { - assertThat(ordsAndKeys.description, startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=10, size=")); - assertOrds( - ordsAndKeys.ords, - new int[] { 0, 1, 2, 3 }, - new int[] { 0, 2 }, - new int[] { 0, 1 }, - new int[] { 0 }, - new int[] { 4 }, - new int[] { 5 }, - new int[] { 0 }, - new int[] { 0, 1, 2, 3 }, - new int[] { 6, 0, 7, 2, 8, 9 } - ); - assertKeys( - ordsAndKeys.keys, - new Object[][] { - new Object[] { 1L, "a" }, - new Object[] { 1L, "b" }, - new Object[] { 2L, "a" }, - new Object[] { 2L, "b" }, - new Object[] { null, "a" }, - new Object[] { 1L, null }, - new Object[] { 1L, "c" }, - new Object[] { 2L, "c" }, - new Object[] { 3L, "c" }, - new Object[] { 3L, "a" }, } - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 10))); - } else { - assertThat( - ordsAndKeys.description, - equalTo("BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=8, size=491b}") - ); - assertOrds( - ordsAndKeys.ords, - new int[] { 0, 1, 2, 3 }, - new int[] { 0, 1 }, - new int[] { 0, 2 }, - new int[] { 0 }, - null, - null, - new int[] { 0 }, - new int[] { 0, 1, 2, 3 }, - new int[] { 4, 5, 6, 0, 1, 7 } - ); - assertKeys( - ordsAndKeys.keys, - new Object[][] { - new Object[] { 1L, "a" }, - new Object[] { 2L, "a" }, - new Object[] { 1L, "b" }, - new Object[] { 2L, "b" }, - new Object[] { 1L, "c" }, - new Object[] { 2L, "c" }, - new Object[] { 3L, "c" }, - new Object[] { 3L, "a" }, } - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 8))); + try ( + LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(8); + BytesRefBlock.Builder b2 = blockFactory.newBytesRefBlockBuilder(8) + ) { + append(b1, b2, new long[] { 1, 2 }, new String[] { "a", "b" }); + append(b1, b2, new long[] { 1, 2 }, new String[] { "a" }); + append(b1, b2, new long[] { 1 }, new String[] { "a", "b" }); + append(b1, b2, new long[] { 1 }, new String[] { "a" }); + append(b1, b2, null, new String[] { "a" }); + append(b1, b2, new long[] { 1 }, null); + append(b1, b2, new long[] { 1, 1, 1 }, new String[] { "a", "a", "a" }); + append(b1, b2, new long[] { 1, 1, 2, 2 }, new String[] { "a", "b", "b" }); + append(b1, b2, new long[] { 1, 2, 3 }, new String[] { "c", "c", "a" }); + + hash((OrdsAndKeys ordsAndKeys) -> { + if (forcePackedHash) { + assertThat( + ordsAndKeys.description, + startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=10, size=") + ); + assertOrds( + ordsAndKeys.ords, + new int[] { 0, 1, 2, 3 }, + new int[] { 0, 2 }, + new int[] { 0, 1 }, + new int[] { 0 }, + new int[] { 4 }, + new int[] { 5 }, + new int[] { 0 }, + new int[] { 0, 1, 2, 3 }, + new int[] { 6, 0, 7, 2, 8, 9 } + ); + assertKeys( + ordsAndKeys.keys, + new Object[][] { + new Object[] { 1L, "a" }, + new Object[] { 1L, "b" }, + new Object[] { 2L, "a" }, + new Object[] { 2L, "b" }, + new Object[] { null, "a" }, + new Object[] { 1L, null }, + new Object[] { 1L, "c" }, + new Object[] { 2L, "c" }, + new Object[] { 3L, "c" }, + new Object[] { 3L, "a" }, } + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 10))); + } else { + assertThat( + ordsAndKeys.description, + equalTo("BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=8, size=491b}") + ); + assertOrds( + ordsAndKeys.ords, + new int[] { 0, 1, 2, 3 }, + new int[] { 0, 1 }, + new int[] { 0, 2 }, + new int[] { 0 }, + null, + null, + new int[] { 0 }, + new int[] { 0, 1, 2, 3 }, + new int[] { 4, 5, 6, 0, 1, 7 } + ); + assertKeys( + ordsAndKeys.keys, + new Object[][] { + new Object[] { 1L, "a" }, + new Object[] { 2L, "a" }, + new Object[] { 1L, "b" }, + new Object[] { 2L, "b" }, + new Object[] { 1L, "c" }, + new Object[] { 2L, "c" }, + new Object[] { 3L, "c" }, + new Object[] { 3L, "a" }, } + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, 8))); + } + }, b1, b2); } - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - assertThat(breaker.getUsed(), is(0L)); } public void testBytesRefLongHashHugeCombinatorialExplosion() { long[] v1 = LongStream.range(0, 3000).toArray(); String[] v2 = LongStream.range(100, 200).mapToObj(l -> "a" + l).toArray(String[]::new); - var b1 = BlockFactory.getNonBreakingInstance().newLongBlockBuilder(v1.length); - var b2 = BlockFactory.getNonBreakingInstance().newBytesRefBlockBuilder(v2.length); - append(b1, b2, v1, v2); - - int[] expectedEntries = new int[1]; - int pageSize = between(1000, 16 * 1024); - hash(ordsAndKeys -> { - int start = expectedEntries[0]; - expectedEntries[0] = Math.min(expectedEntries[0] + pageSize, v1.length * v2.length); - assertThat( - ordsAndKeys.description, - forcePackedHash - ? startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=" + expectedEntries[0] + ", size=") - : startsWith( - "BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=" + expectedEntries[0] + ", size=" - ) - ); - assertOrds(ordsAndKeys.ords, IntStream.range(start, expectedEntries[0]).toArray()); - assertKeys( - ordsAndKeys.keys, - IntStream.range(0, expectedEntries[0]) - .mapToObj( - i -> forcePackedHash - ? new Object[] { v1[i / v2.length], v2[i % v2.length] } - : new Object[] { v1[i % v1.length], v2[i / v1.length] } - ) - .toArray(l -> new Object[l][]) - ); - assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, expectedEntries[0]))); - Releasables.closeExpectNoException(ordsAndKeys.keys); - Releasables.closeExpectNoException(ordsAndKeys.nonEmpty); - }, pageSize, b1.build(), b2.build()); + try ( + LongBlock.Builder b1 = blockFactory.newLongBlockBuilder(v1.length); + BytesRefBlock.Builder b2 = blockFactory.newBytesRefBlockBuilder(v2.length); + ) { + append(b1, b2, v1, v2); + int[] expectedEntries = new int[1]; + int pageSize = between(1000, 16 * 1024); + hash(ordsAndKeys -> { + int start = expectedEntries[0]; + expectedEntries[0] = Math.min(expectedEntries[0] + pageSize, v1.length * v2.length); + assertThat( + ordsAndKeys.description, + forcePackedHash + ? startsWith("PackedValuesBlockHash{groups=[0:LONG, 1:BYTES_REF], entries=" + expectedEntries[0] + ", size=") + : startsWith( + "BytesRefLongBlockHash{keys=[BytesRefKey[channel=1], LongKey[channel=0]], entries=" + + expectedEntries[0] + + ", size=" + ) + ); + assertOrds(ordsAndKeys.ords, IntStream.range(start, expectedEntries[0]).toArray()); + assertKeys( + ordsAndKeys.keys, + IntStream.range(0, expectedEntries[0]) + .mapToObj( + i -> forcePackedHash + ? new Object[] { v1[i / v2.length], v2[i % v2.length] } + : new Object[] { v1[i % v1.length], v2[i / v1.length] } + ) + .toArray(l -> new Object[l][]) + ); + assertThat(ordsAndKeys.nonEmpty, equalTo(intRange(0, expectedEntries[0]))); + }, pageSize, b1, b2); - assertThat("misconfigured test", expectedEntries[0], greaterThan(0)); - assertThat(breaker.getUsed(), is(0L)); + assertThat("misconfigured test", expectedEntries[0], greaterThan(0)); + } } record OrdsAndKeys(String description, int positionOffset, IntBlock ords, Block[] keys, IntVector nonEmpty) {} @@ -1128,29 +1089,53 @@ record OrdsAndKeys(String description, int positionOffset, IntBlock ords, Block[ * Hash some values into a single block of group ids. If the hash produces * more than one block of group ids this will fail. */ - private OrdsAndKeys hash(Block... values) { - OrdsAndKeys[] result = new OrdsAndKeys[1]; + private void hash(Consumer callback, Block.Builder... values) { + Block[] blocks = new Block[values.length]; + for (int i = 0; i < blocks.length; i++) { + blocks[i] = values[i].build(); + } + hash(callback, blocks); + } + + /** + * Hash some values into a single block of group ids. If the hash produces + * more than one block of group ids this will fail. + */ + private void hash(Consumer callback, Block... values) { + boolean[] called = new boolean[] { false }; hash(ordsAndKeys -> { - if (result[0] != null) { + if (called[0]) { throw new IllegalStateException("hash produced more than one block"); } - result[0] = ordsAndKeys; + called[0] = true; + callback.accept(ordsAndKeys); }, 16 * 1024, values); - return result[0]; } - private void hash(Consumer callback, int emitBatchSize, Block... values) { - List specs = new ArrayList<>(values.length); - for (int c = 0; c < values.length; c++) { - specs.add(new HashAggregationOperator.GroupSpec(c, values[c].elementType())); + private void hash(Consumer callback, int emitBatchSize, Block.Builder... values) { + Block[] blocks = new Block[values.length]; + for (int i = 0; i < blocks.length; i++) { + blocks[i] = values[i].build(); } - DriverContext driverContext = new DriverContext(bigArrays, blockFactory); - try ( - BlockHash blockHash = forcePackedHash - ? new PackedValuesBlockHash(specs, driverContext, emitBatchSize) - : BlockHash.build(specs, driverContext, emitBatchSize, true) - ) { - hash(true, blockHash, callback, values); + hash(callback, emitBatchSize, blocks); + } + + private void hash(Consumer callback, int emitBatchSize, Block... values) { + try { + List specs = new ArrayList<>(values.length); + for (int c = 0; c < values.length; c++) { + specs.add(new HashAggregationOperator.GroupSpec(c, values[c].elementType())); + } + DriverContext driverContext = new DriverContext(bigArrays, blockFactory); + try ( + BlockHash blockHash = forcePackedHash + ? new PackedValuesBlockHash(specs, driverContext, emitBatchSize) + : BlockHash.build(specs, driverContext, emitBatchSize, true) + ) { + hash(true, blockHash, callback, values); + } + } finally { + Releasables.closeExpectNoException(values); } } @@ -1166,24 +1151,28 @@ public void add(int positionOffset, IntBlock groupIds) { blockHash.nonEmpty() ); - Set allowedOrds = new HashSet<>(); - for (int p = 0; p < result.nonEmpty.getPositionCount(); p++) { - allowedOrds.add(result.nonEmpty.getInt(p)); - } - for (int p = 0; p < result.ords.getPositionCount(); p++) { - if (result.ords.isNull(p)) { - continue; + try { + Set allowedOrds = new HashSet<>(); + for (int p = 0; p < result.nonEmpty.getPositionCount(); p++) { + allowedOrds.add(result.nonEmpty.getInt(p)); } - int start = result.ords.getFirstValueIndex(p); - int end = start + result.ords.getValueCount(p); - for (int i = start; i < end; i++) { - int ord = result.ords.getInt(i); - if (false == allowedOrds.contains(ord)) { - fail("ord is not allowed " + ord); + for (int p = 0; p < result.ords.getPositionCount(); p++) { + if (result.ords.isNull(p)) { + continue; + } + int start = result.ords.getFirstValueIndex(p); + int end = start + result.ords.getValueCount(p); + for (int i = start; i < end; i++) { + int ord = result.ords.getInt(i); + if (false == allowedOrds.contains(ord)) { + fail("ord is not allowed " + ord); + } } } + callback.accept(result); + } finally { + Releasables.close(result.keys == null ? null : Releasables.wrap(result.keys), result.nonEmpty); } - callback.accept(result); } @Override diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/MockBlockFactory.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/MockBlockFactory.java index 6ba2482ab6d8d..0a63043ecc14b 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/MockBlockFactory.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/data/MockBlockFactory.java @@ -146,7 +146,8 @@ public BooleanBlock.Builder newBooleanBlockBuilder(int estimatedSize) { return b; } - BooleanVector.FixedBuilder newBooleanVectorFixedBuilder(int size) { + @Override + public BooleanVector.FixedBuilder newBooleanVectorFixedBuilder(int size) { var b = super.newBooleanVectorFixedBuilder(size); track(b, trackDetail()); return b; From fb03858245ba2e39dd0201036109c0876ab1a096 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Sun, 29 Oct 2023 10:55:22 -0400 Subject: [PATCH 181/203] ESQL: Fix loading constant_keyword when unset (#101496) When a `constant_keyword` field is first created you don't *have* to set it's value - it'll default to `null` and then take on the value of the first document that contains the field. Our block loading code didn't like this state and would fail to run the query. This fixes that. Closes #101455 --- .../resources/rest-api-spec/test/30_types.yml | 33 ++++++++++ .../mapper/ConstantKeywordFieldMapper.java | 3 + .../ConstantKeywordFieldMapperTests.java | 66 +++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml index 7ec0f671253b9..bf159455d00ca 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml +++ b/x-pack/plugin/esql/qa/server/single-node/src/yamlRestTest/resources/rest-api-spec/test/30_types.yml @@ -53,6 +53,39 @@ constant_keyword: - length: {values: 1} - match: {values.0.0: 17} +--- +constant_keyword with null value: + - do: + indices.create: + index: test + body: + mappings: + properties: + kind: + type: constant_keyword + color: + type: keyword + + - do: + bulk: + index: test + refresh: true + body: + - { "index": { } } + - { "color": "red" } + + - do: + esql.query: + body: + query: 'from test | limit 1' + - match: { columns.0.name: color } + - match: { columns.0.type: keyword } + - match: { columns.1.name: kind } + - match: { columns.1.type: keyword } + - length: { values: 1 } + - match: { values.0.0: red } + - match: { values.0.1: null } + --- multivalued keyword: - do: diff --git a/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java b/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java index 61db9dcd54a02..6c8462c9e4948 100644 --- a/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java +++ b/x-pack/plugin/mapper-constant-keyword/src/main/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapper.java @@ -138,6 +138,9 @@ public String familyTypeName() { @Override public BlockLoader blockLoader(BlockLoaderContext blContext) { // TODO build a constant block directly + if (value == null) { + return BlockDocValuesReader.nulls(); + } BytesRef bytes = new BytesRef(value); return context -> new BlockDocValuesReader() { private int docId; diff --git a/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java b/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java index 5fb8f3f7dc345..aaa28e28b72c9 100644 --- a/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java +++ b/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java @@ -7,12 +7,18 @@ package org.elasticsearch.xpack.constantkeyword.mapper; +import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Strings; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.CheckedFunction; +import org.elasticsearch.index.mapper.BlockDocValuesReader; +import org.elasticsearch.index.mapper.BlockLoader; import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentParsingException; import org.elasticsearch.index.mapper.LuceneDocument; @@ -22,7 +28,9 @@ import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.MapperTestCase; import org.elasticsearch.index.mapper.ParsedDocument; +import org.elasticsearch.index.mapper.TestBlock; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xpack.constantkeyword.ConstantKeywordMapperPlugin; import org.elasticsearch.xpack.constantkeyword.mapper.ConstantKeywordFieldMapper.ConstantKeywordFieldType; @@ -31,11 +39,13 @@ import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.function.Function; import static org.elasticsearch.index.mapper.MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.nullValue; public class ConstantKeywordFieldMapperTests extends MapperTestCase { @@ -214,6 +224,62 @@ protected boolean allowsNullValues() { return false; // null is an error for constant keyword } + /** + * Test loading blocks when there is no defined value. This is allowed + * for newly created indices that haven't received any documents that + * contain the field. + */ + public void testNullValueBlockLoaderReadValues() throws IOException { + testNullBlockLoader(blockReader -> (TestBlock) blockReader.readValues(TestBlock.FACTORY, TestBlock.docs(0))); + } + + /** + * Test loading blocks when there is no defined value. This is allowed + * for newly created indices that haven't received any documents that + * contain the field. + */ + public void testNullValueBlockLoaderReadValuesFromSingleDoc() throws IOException { + testNullBlockLoader(blockReader -> { + TestBlock block = (TestBlock) blockReader.builder(TestBlock.FACTORY, 1); + blockReader.readValuesFromSingleDoc(0, block); + return block; + }); + } + + private void testNullBlockLoader(CheckedFunction body) throws IOException { + MapperService mapper = createMapperService(syntheticSourceMapping(b -> { + b.startObject("field"); + b.field("type", "constant_keyword"); + b.endObject(); + })); + BlockLoader loader = mapper.fieldType("field").blockLoader(new MappedFieldType.BlockLoaderContext() { + @Override + public String indexName() { + throw new UnsupportedOperationException(); + } + + @Override + public SearchLookup lookup() { + throw new UnsupportedOperationException(); + } + + @Override + public Set sourcePaths(String name) { + return mapper.mappingLookup().sourcePaths(name); + } + }); + try (Directory directory = newDirectory()) { + RandomIndexWriter iw = new RandomIndexWriter(random(), directory); + LuceneDocument doc = mapper.documentMapper().parse(source(b -> {})).rootDoc(); + iw.addDocument(doc); + iw.close(); + try (DirectoryReader reader = DirectoryReader.open(directory)) { + TestBlock block = body.apply(loader.reader(reader.leaves().get(0))); + assertThat(block.get(0), nullValue()); + } + } + } + @Override protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) { assertFalse("constant_keyword doesn't support ignore_malformed", ignoreMalformed); From 26c7b9f42a72d3f122405954571053f67ef93498 Mon Sep 17 00:00:00 2001 From: Kostas Krikellas <131142368+kkrik-es@users.noreply.github.com> Date: Mon, 30 Oct 2023 09:59:35 +0200 Subject: [PATCH 182/203] Use larger compression values in InternalMedianAbsoluteDeviationTests (#101449) InternalMedianAbsoluteDeviation uses TDigest internally to track median values. For small compression values, TDigest may switch to the MergingDigest implementation that's not deterministic. This may lead to noise in tests like testConcurrentEquals, that create a clone of InternalMedianAbsoluteDeviation through (de)serialization and expect it to be an exact copy of the original object. Larger compression values avoid this. Since this is not a unittest for TDigest, it's deemed ok to limit coverage to a simpler version of the latter. Fixes #101417 --- .../metrics/InternalMedianAbsoluteDeviationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMedianAbsoluteDeviationTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMedianAbsoluteDeviationTests.java index 0c72abfec48e0..190ab9914d933 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMedianAbsoluteDeviationTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMedianAbsoluteDeviationTests.java @@ -24,7 +24,7 @@ public class InternalMedianAbsoluteDeviationTests extends InternalAggregationTes @Override protected InternalMedianAbsoluteDeviation createTestInstance(String name, Map metadata) { - final TDigestState valuesSketch = TDigestState.create(randomDoubleBetween(20, 1000, true)); + final TDigestState valuesSketch = TDigestState.create(randomFrom(50.0, 100.0, 200.0, 500.0, 1000.0)); final int numberOfValues = frequently() ? randomIntBetween(0, 1000) : 0; for (int i = 0; i < numberOfValues; i++) { valuesSketch.add(randomDouble()); From 9b51d9972d047afb076a585c1b4ab2ea43d40a2f Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Oct 2023 08:25:40 +0000 Subject: [PATCH 183/203] More specific `cluster.initial_master_nodes` instructions (#101493) In the note on forming a single cluster we describe what to do if inadvertently forming extra clusters, but we can be more explicit about what to do with `cluster.initial_master_nodes` in these instructions. This commit adds the missing details. --- docs/reference/modules/discovery/bootstrapping.asciidoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/reference/modules/discovery/bootstrapping.asciidoc b/docs/reference/modules/discovery/bootstrapping.asciidoc index fc43177bd74b4..81ac3f6cc4cdc 100644 --- a/docs/reference/modules/discovery/bootstrapping.asciidoc +++ b/docs/reference/modules/discovery/bootstrapping.asciidoc @@ -151,7 +151,8 @@ a separate single-node cluster then you must start again: folder>>. . Configure `discovery.seed_hosts` or `discovery.seed_providers` and other -relevant discovery settings. +relevant discovery settings. Ensure `cluster.initial_master_nodes` is not set +on any node. . Restart the node and verify that it joins the existing cluster rather than forming its own one-node cluster. @@ -171,4 +172,6 @@ relevant discovery settings. . Restart all the nodes and verify that they have formed a single cluster. +. Remove `cluster.initial_master_nodes` from every node's configuration. + **** From 61ff924ee3b14930407858164ed293ecb439db1d Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Mon, 30 Oct 2023 09:26:22 +0100 Subject: [PATCH 184/203] ValuesSourceConfig to not depend on entire AggregationContext (#101459) ValuesSourceConfig takes in the entire AggregationContext as a constructor argument, but only context::nowInMillis is ever used in the internal replaceMissing call. The constructor argument can be replaced by a LongSupplier. --- .../support/CoreValuesSourceType.java | 23 ++++++++++--------- .../support/TimeSeriesValuesSourceType.java | 7 +++--- .../support/ValuesSourceConfig.java | 19 +++++++-------- .../support/ValuesSourceType.java | 9 ++++---- .../support/AnalyticsValuesSourceType.java | 4 ++-- .../rate/RateAggregationBuilder.java | 12 +++++++++- .../lucene/UnsupportedValueSourceType.java | 5 ++-- .../AggregateMetricsValuesSourceType.java | 4 ++-- .../CartesianPointValuesSourceType.java | 4 ++-- .../CartesianShapeValuesSourceType.java | 4 ++-- .../support/GeoShapeValuesSourceType.java | 4 ++-- 11 files changed, 53 insertions(+), 42 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/CoreValuesSourceType.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/CoreValuesSourceType.java index fd4fbac82334e..35b8230a48554 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/CoreValuesSourceType.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/CoreValuesSourceType.java @@ -40,6 +40,7 @@ import java.util.List; import java.util.Locale; import java.util.function.Function; +import java.util.function.LongSupplier; /** * {@link CoreValuesSourceType} holds the {@link ValuesSourceType} implementations for the core aggregations package. @@ -79,13 +80,13 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { Number missing; if (rawMissing instanceof Number) { missing = (Number) rawMissing; } else { - missing = docValueFormat.parseDouble(rawMissing.toString(), false, context::nowInMillis); + missing = docValueFormat.parseDouble(rawMissing.toString(), false, nowInMillis); } return MissingValues.replaceMissing((ValuesSource.Numeric) valuesSource, missing); } @@ -138,7 +139,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { final BytesRef missing = docValueFormat.parseBytesRef(rawMissing.toString()); if (valuesSource instanceof ValuesSource.Bytes.WithOrdinals) { @@ -174,7 +175,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { // TODO: also support the structured formats of geo points final GeoPoint missing = new GeoPoint(rawMissing.toString()); @@ -213,7 +214,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { throw new IllegalArgumentException("Can't apply missing values on a " + valuesSource.getClass()); } @@ -239,9 +240,9 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { - return KEYWORD.replaceMissing(valuesSource, rawMissing, docValueFormat, context); + return KEYWORD.replaceMissing(valuesSource, rawMissing, docValueFormat, nowInMillis); } @Override @@ -385,9 +386,9 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { - return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, context); + return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, nowInMillis); } @Override @@ -422,9 +423,9 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { - return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, context); + return NUMERIC.replaceMissing(valuesSource, rawMissing, docValueFormat, nowInMillis); } @Override diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/TimeSeriesValuesSourceType.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/TimeSeriesValuesSourceType.java index ae720528b680f..e1e249466aea6 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/TimeSeriesValuesSourceType.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/TimeSeriesValuesSourceType.java @@ -14,6 +14,7 @@ import org.elasticsearch.search.aggregations.AggregationErrors; import java.util.Locale; +import java.util.function.LongSupplier; import static org.elasticsearch.search.aggregations.support.CoreValuesSourceType.GEOPOINT; @@ -51,7 +52,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { throw new IllegalArgumentException("Cannot replace missing values for time-series counters"); } @@ -77,9 +78,9 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { - return GEOPOINT.replaceMissing(valuesSource, rawMissing, docValueFormat, context); + return GEOPOINT.replaceMissing(valuesSource, rawMissing, docValueFormat, nowInMillis); } }; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java index 028e2b922ee05..85788c1964b40 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java @@ -23,6 +23,7 @@ import java.time.ZoneId; import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.LongSupplier; /** * A configuration that tells aggregations how to retrieve data from the index @@ -183,7 +184,7 @@ private static ValuesSourceConfig internalResolve( missing, timeZone, docValueFormat, - context + context::nowInMillis ); return config; } @@ -257,14 +258,14 @@ private static DocValueFormat resolveFormat( public static ValuesSourceConfig resolveFieldOnly(MappedFieldType fieldType, AggregationContext context) { FieldContext fieldContext = context.buildFieldContext(fieldType); ValuesSourceType vstype = fieldContext.indexFieldData().getValuesSourceType(); - return new ValuesSourceConfig(vstype, fieldContext, false, null, null, null, null, null, context); + return new ValuesSourceConfig(vstype, fieldContext, false, null, null, null, null, null, context::nowInMillis); } /** * Convenience method for creating unmapped configs */ public static ValuesSourceConfig resolveUnmapped(ValuesSourceType valuesSourceType, AggregationContext context) { - return new ValuesSourceConfig(valuesSourceType, null, true, null, null, null, null, null, context); + return new ValuesSourceConfig(valuesSourceType, null, true, null, null, null, null, null, context::nowInMillis); } private final ValuesSourceType valuesSourceType; @@ -277,10 +278,6 @@ public static ValuesSourceConfig resolveUnmapped(ValuesSourceType valuesSourceTy private final ZoneId timeZone; private final ValuesSource valuesSource; - private ValuesSourceConfig() { - throw new UnsupportedOperationException(); - } - @SuppressWarnings("this-escape") public ValuesSourceConfig( ValuesSourceType valuesSourceType, @@ -291,7 +288,7 @@ public ValuesSourceConfig( Object missing, ZoneId timeZone, DocValueFormat format, - AggregationContext context + LongSupplier nowInMillis ) { if (unmapped && fieldContext != null) { throw new IllegalStateException("value source config is invalid; marked as unmapped but specified a mapped field"); @@ -311,10 +308,10 @@ public ValuesSourceConfig( "value source config is invalid; must have either a field context or a script or marked as unwrapped" ); } - valuesSource = constructValuesSource(missing, format, context); + valuesSource = constructValuesSource(missing, format, nowInMillis); } - private ValuesSource constructValuesSource(Object missing, DocValueFormat format, AggregationContext context) { + private ValuesSource constructValuesSource(Object missing, DocValueFormat format, LongSupplier nowInMillis) { final ValuesSource vs; if (this.unmapped) { vs = valueSourceType().getEmpty(); @@ -329,7 +326,7 @@ private ValuesSource constructValuesSource(Object missing, DocValueFormat format } if (missing() != null) { - return valueSourceType().replaceMissing(vs, missing, format, context); + return valueSourceType().replaceMissing(vs, missing, format, nowInMillis); } else { return vs; } diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceType.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceType.java index 684fecb88e740..00482ea380e1f 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceType.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceType.java @@ -12,6 +12,7 @@ import org.elasticsearch.search.DocValueFormat; import java.time.ZoneId; +import java.util.function.LongSupplier; /** * {@link ValuesSourceType} represents a collection of fields that share a common set of operations, for example all numeric fields. @@ -64,13 +65,13 @@ public interface ValuesSourceType { * Apply the given missing value to an already-constructed {@link ValuesSource}. Types which do not support missing values should throw * {@link org.elasticsearch.search.aggregations.AggregationExecutionException} * - * @param valuesSource - The original {@link ValuesSource} - * @param rawMissing - The missing value we got from the parser, typically a string or number + * @param valuesSource - The original {@link ValuesSource} + * @param rawMissing - The missing value we got from the parser, typically a string or number * @param docValueFormat - The format to use for further parsing the user supplied value, e.g. a date format - * @param context - Context for this aggregation used to handle {@link AggregationContext#nowInMillis() "now"} + * @param nowInMillis - supplier of current time and date in milliseconds * @return - Wrapper over the provided {@link ValuesSource} to apply the given missing value */ - ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, AggregationContext context); + ValuesSource replaceMissing(ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, LongSupplier nowInMillis); /** * This method provides a hook for specifying a type-specific formatter. When {@link ValuesSourceConfig} can resolve a diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/support/AnalyticsValuesSourceType.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/support/AnalyticsValuesSourceType.java index 9ff3024cfa59e..f66008bcc932c 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/support/AnalyticsValuesSourceType.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/aggregations/support/AnalyticsValuesSourceType.java @@ -11,13 +11,13 @@ import org.elasticsearch.script.AggregationScript; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationErrors; -import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.util.Locale; +import java.util.function.LongSupplier; public enum AnalyticsValuesSourceType implements ValuesSourceType { HISTOGRAM() { @@ -49,7 +49,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { throw new IllegalArgumentException("Can't apply missing values on a " + valuesSource.getClass()); } diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/RateAggregationBuilder.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/RateAggregationBuilder.java index 37332fa49bfbf..634c76b819ea0 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/RateAggregationBuilder.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/rate/RateAggregationBuilder.java @@ -194,7 +194,17 @@ static Rounding.DateTimeUnit parse(String rateUnit) { @Override protected ValuesSourceConfig resolveConfig(AggregationContext context) { if (field() == null && script() == null) { - return new ValuesSourceConfig(CoreValuesSourceType.NUMERIC, null, true, null, null, 1.0, null, DocValueFormat.RAW, context); + return new ValuesSourceConfig( + CoreValuesSourceType.NUMERIC, + null, + true, + null, + null, + 1.0, + null, + DocValueFormat.RAW, + context::nowInMillis + ); } else { return super.resolveConfig(context); } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/UnsupportedValueSourceType.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/UnsupportedValueSourceType.java index ead3d9b46d6a5..18e1c49d15390 100644 --- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/UnsupportedValueSourceType.java +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/UnsupportedValueSourceType.java @@ -9,12 +9,13 @@ import org.elasticsearch.script.AggregationScript; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceType; +import java.util.function.LongSupplier; + // just a placeholder class for unsupported data types public class UnsupportedValueSourceType implements ValuesSourceType { @@ -44,7 +45,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { throw new UnsupportedOperationException(); } diff --git a/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/aggregations/support/AggregateMetricsValuesSourceType.java b/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/aggregations/support/AggregateMetricsValuesSourceType.java index 4d5b572583bd1..bcac3f12fd131 100644 --- a/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/aggregations/support/AggregateMetricsValuesSourceType.java +++ b/x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/aggregations/support/AggregateMetricsValuesSourceType.java @@ -11,7 +11,6 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationErrors; import org.elasticsearch.search.aggregations.UnsupportedAggregationOnDownsampledIndex; -import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; @@ -19,6 +18,7 @@ import org.elasticsearch.xpack.aggregatemetric.fielddata.IndexAggregateDoubleMetricFieldData; import java.util.Locale; +import java.util.function.LongSupplier; public enum AggregateMetricsValuesSourceType implements ValuesSourceType { @@ -60,7 +60,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { throw new IllegalArgumentException("Can't apply missing values on a " + valuesSource.getClass()); } diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianPointValuesSourceType.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianPointValuesSourceType.java index d1878dbba2856..29fdaa3c0cfd2 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianPointValuesSourceType.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianPointValuesSourceType.java @@ -16,7 +16,6 @@ import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.script.AggregationScript; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.MissingValues; import org.elasticsearch.search.aggregations.support.ValueType; @@ -26,6 +25,7 @@ import org.elasticsearch.xpack.spatial.index.fielddata.IndexCartesianPointFieldData; import java.io.IOException; +import java.util.function.LongSupplier; public class CartesianPointValuesSourceType implements Writeable, ValuesSourceType { @@ -61,7 +61,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { // TODO: also support the structured formats of points final CartesianPointValuesSource pointValuesSource = (CartesianPointValuesSource) valuesSource; diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianShapeValuesSourceType.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianShapeValuesSourceType.java index 5ec5cfc36b8b3..1b64eb7692fed 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianShapeValuesSourceType.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/CartesianShapeValuesSourceType.java @@ -12,7 +12,6 @@ import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.script.AggregationScript; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.MissingValues; import org.elasticsearch.search.aggregations.support.ValuesSource; @@ -23,6 +22,7 @@ import org.elasticsearch.xpack.spatial.index.fielddata.plain.CartesianShapeIndexFieldData; import java.io.IOException; +import java.util.function.LongSupplier; public class CartesianShapeValuesSourceType extends ShapeValuesSourceType { @@ -61,7 +61,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { CartesianShapeValuesSource shapeValuesSource = (CartesianShapeValuesSource) valuesSource; final CartesianShapeValues.CartesianShapeValue missing = CartesianShapeValues.EMPTY.missing(rawMissing.toString()); diff --git a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/GeoShapeValuesSourceType.java b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/GeoShapeValuesSourceType.java index 56857605999c8..8286eab085c70 100644 --- a/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/GeoShapeValuesSourceType.java +++ b/x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/search/aggregations/support/GeoShapeValuesSourceType.java @@ -13,7 +13,6 @@ import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.script.AggregationScript; import org.elasticsearch.search.DocValueFormat; -import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.MissingValues; import org.elasticsearch.search.aggregations.support.ValuesSource; @@ -22,6 +21,7 @@ import org.elasticsearch.xpack.spatial.index.fielddata.IndexShapeFieldData; import java.io.IOException; +import java.util.function.LongSupplier; public class GeoShapeValuesSourceType extends ShapeValuesSourceType { @@ -61,7 +61,7 @@ public ValuesSource replaceMissing( ValuesSource valuesSource, Object rawMissing, DocValueFormat docValueFormat, - AggregationContext context + LongSupplier nowInMillis ) { GeoShapeValuesSource shapeValuesSource = (GeoShapeValuesSource) valuesSource; final GeoShapeValues.GeoShapeValue missing = GeoShapeValues.EMPTY.missing(rawMissing.toString()); From a170d73b0690f6eded730aa81336a710d4b029e4 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Oct 2023 09:30:33 +0000 Subject: [PATCH 185/203] Suggest a strategy for using repo analysis (#101507) Adds some docs to suggest running a sequence of increasingly large analyses, and to set a very generous timeout. --- .../apis/repo-analysis-api.asciidoc | 15 +++++++++------ .../snapshot-restore/repository-s3.asciidoc | 13 ++++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/reference/snapshot-restore/apis/repo-analysis-api.asciidoc b/docs/reference/snapshot-restore/apis/repo-analysis-api.asciidoc index 190eb43d996f8..4f80ffcf3a8b9 100644 --- a/docs/reference/snapshot-restore/apis/repo-analysis-api.asciidoc +++ b/docs/reference/snapshot-restore/apis/repo-analysis-api.asciidoc @@ -54,12 +54,15 @@ The Repository analysis API performs a collection of read and write operations on your repository which are designed to detect incorrect behaviour and to measure the performance characteristics of your storage system. -The default values for the parameters to this API are deliberately low to -reduce the impact of running an analysis inadvertently. A realistic experiment -should set `blob_count` to at least `2000`, `max_blob_size` to at least `2gb`, -and `max_total_data_size` to at least `1tb`, and will almost certainly need to -increase the `timeout` to allow time for the process to complete successfully. -You should run the analysis on a multi-node cluster of a similar size to your +The default values for the parameters to this API are deliberately low to reduce +the impact of running an analysis inadvertently and to provide a sensible +starting point for your investigations. Run your first analysis with the default +parameter values to check for simple problems. If successful, run a sequence of +increasingly large analyses until you encounter a failure or you reach a +`blob_count` of at least `2000`, a `max_blob_size` of at least `2gb`, and a +`max_total_data_size` of at least `1tb`. Always specify a generous timeout, +possibly `1h` or longer, to allow time for each analysis to run to completion. +Perform the analyses using a multi-node cluster of a similar size to your production cluster so that it can detect any problems that only arise when the repository is accessed by many nodes at once. diff --git a/docs/reference/snapshot-restore/repository-s3.asciidoc b/docs/reference/snapshot-restore/repository-s3.asciidoc index 49f0bc5fb598c..70993f5b515b3 100644 --- a/docs/reference/snapshot-restore/repository-s3.asciidoc +++ b/docs/reference/snapshot-restore/repository-s3.asciidoc @@ -257,9 +257,16 @@ PUT /_cluster/settings ---- // TEST[skip:we don't really want to change this logger] -The supplier of your storage system will be able to analyse these logs to determine the problem. See -the https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-logging.html[AWS Java SDK] -documentation for further information. +Collect the Elasticsearch logs covering the time period of the failed analysis +from all nodes in your cluster and share them with the supplier of your storage +system along with the analysis response so they can use them to determine the +problem. See the +https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-logging.html[AWS Java SDK] +documentation for further information, including details about other loggers +that can be used to obtain even more verbose logs. When you have finished +collecting the logs needed by your supplier, set the logger settings back to +`null` to return to the default logging configuration. See <> +and <> for more information. [[repository-s3-repository]] ==== Repository settings From 26fcb82b42c3391cda841aa8780708710b22791f Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 30 Oct 2023 20:41:06 +1100 Subject: [PATCH 186/203] Fix snapshot double finalization (#101497) When a snapshot is completed as a SnapshotsInProgress entry in cluster state and also queued for next operations, it can lead to double finalization of the snapshot if the entry in cluster state is processed first. This PR fixes it by only start finalization if it is *not* already in endingSnapshots. The PR also adds a specific test case for the double finalization issue (manifested as NPE). Resolves: #101028 --- docs/changelog/101497.yaml | 5 + .../SnapshotsServiceDoubleFinalizationIT.java | 279 ++++++++++++++++++ .../snapshots/SnapshotsService.java | 13 +- 3 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/101497.yaml create mode 100644 server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotsServiceDoubleFinalizationIT.java diff --git a/docs/changelog/101497.yaml b/docs/changelog/101497.yaml new file mode 100644 index 0000000000000..7909cb1ecdc0d --- /dev/null +++ b/docs/changelog/101497.yaml @@ -0,0 +1,5 @@ +pr: 101497 +summary: Fix snapshot double finalization +area: Snapshot/Restore +type: bug +issues: [] diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotsServiceDoubleFinalizationIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotsServiceDoubleFinalizationIT.java new file mode 100644 index 0000000000000..d9e6a8eff5ad1 --- /dev/null +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/SnapshotsServiceDoubleFinalizationIT.java @@ -0,0 +1,279 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.snapshots; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateObserver; +import org.elasticsearch.cluster.SnapshotDeletionsInProgress; +import org.elasticsearch.cluster.SnapshotsInProgress; +import org.elasticsearch.cluster.metadata.RepositoryMetadata; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.blobstore.BlobContainer; +import org.elasticsearch.common.blobstore.BlobPath; +import org.elasticsearch.common.blobstore.BlobStore; +import org.elasticsearch.common.blobstore.OperationPurpose; +import org.elasticsearch.common.blobstore.support.BlobMetadata; +import org.elasticsearch.common.blobstore.support.FilterBlobContainer; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.env.Environment; +import org.elasticsearch.indices.recovery.RecoverySettings; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.plugins.RepositoryPlugin; +import org.elasticsearch.repositories.Repository; +import org.elasticsearch.repositories.fs.FsRepository; +import org.elasticsearch.snapshots.mockstore.BlobStoreWrapper; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.xcontent.NamedXContentRegistry; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.equalTo; + +@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SnapshotsServiceDoubleFinalizationIT extends AbstractSnapshotIntegTestCase { + + public void testNoDoubleFinalization() throws Exception { + // 0 - Basic setup + final String masterNodeName = internalCluster().startNode(); + final String dataNodeName = internalCluster().startDataOnlyNode(); + createIndex( + "index-1", + Settings.builder().put("index.number_of_replicas", 0).put("index.routing.allocation.require._name", masterNodeName).build() + ); + indexRandomDocs("index-1", 50); + createIndex( + "index-2", + Settings.builder().put("index.number_of_replicas", 0).put("index.routing.allocation.require._name", dataNodeName).build() + ); + indexRandomDocs("index-2", 50); + createIndex( + "index-3", + Settings.builder().put("index.number_of_replicas", 0).put("index.routing.allocation.require._name", dataNodeName).build() + ); + indexRandomDocs("index-3", 50); + + // 1 - create repository and take a snapshot + final String repoName = "repo"; + createRepository(repoName, TestRepositoryPlugin.REPO_TYPE); + final TestRepository testRepository = getRepositoryOnMaster(repoName); + logger.info("--> create snapshot snap-1"); + createSnapshot(repoName, "snap-1", List.of("index-1")); + + // 2 - Start deleting the snap-1 and block it at listing root blobs + PlainActionFuture future = setWaitForClusterState(state -> { + final SnapshotDeletionsInProgress snapshotDeletionsInProgress = SnapshotDeletionsInProgress.get(state); + return snapshotDeletionsInProgress.getEntries() + .stream() + .flatMap(entry -> entry.getSnapshots().stream()) + .anyMatch(snapshotId -> snapshotId.getName().equals("snap-1")); + + }); + final CyclicBarrier barrier = testRepository.blockOnceForListBlobs(); + new Thread(() -> { + logger.info("--> start deleting snapshot snap-1 "); + startDeleteSnapshot(repoName, "snap-1"); + }).start(); + assertBusy(() -> assertThat(barrier.getNumberWaiting(), equalTo(1))); + future.actionGet(); + logger.info("--> repository blocked at listing root blobs"); + + // 3 - Stop data node so that index-2, index-3 become unassigned + internalCluster().stopNode(dataNodeName); + internalCluster().validateClusterFormed(); + + // 4 - Create new snapshot for the unassigned index and its shards should have both QUEUED and MISSING + future = setWaitForClusterState(state -> { + final SnapshotsInProgress snapshotsInProgress = SnapshotsInProgress.get(state); + return snapshotsInProgress.asStream() + .anyMatch( + entry -> entry.snapshot().getSnapshotId().getName().equals("snap-2") + && entry.state() == SnapshotsInProgress.State.STARTED + && entry.shards() + .values() + .stream() + .map(SnapshotsInProgress.ShardSnapshotStatus::state) + .collect(Collectors.toSet()) + .equals(Set.of(SnapshotsInProgress.ShardState.QUEUED, SnapshotsInProgress.ShardState.MISSING)) + ); + }); + clusterAdmin().prepareCreateSnapshot(repoName, "snap-2") + .setIndices("index-2", "index-3") + .setPartial(true) + .setWaitForCompletion(false) + .get(); + // Delete index-3 so that it becomes MISSING for snapshot + indicesAdmin().prepareDelete("index-3").get(); + future.actionGet(); + + // 5 - Start deleting snap-2, itself should be WAITING. But changes InProgress snap-2 to SUCCESS + future = setWaitForClusterState(state -> { + final SnapshotsInProgress snapshotsInProgress = SnapshotsInProgress.get(state); + final boolean foundSnapshot = snapshotsInProgress.asStream() + .anyMatch( + entry -> entry.snapshot().getSnapshotId().getName().equals("snap-2") + && entry.state() == SnapshotsInProgress.State.SUCCESS + && entry.shards() + .values() + .stream() + .map(SnapshotsInProgress.ShardSnapshotStatus::state) + .collect(Collectors.toSet()) + .equals(Set.of(SnapshotsInProgress.ShardState.FAILED, SnapshotsInProgress.ShardState.MISSING)) + ); + if (false == foundSnapshot) { + return false; + } + final SnapshotDeletionsInProgress snapshotDeletionsInProgress = SnapshotDeletionsInProgress.get(state); + return snapshotDeletionsInProgress.getEntries() + .stream() + .anyMatch( + entry -> entry.state() == SnapshotDeletionsInProgress.State.WAITING + && entry.getSnapshots().stream().anyMatch(snapshotId -> snapshotId.getName().equals("snap-2")) + ); + }); + new Thread(() -> { + logger.info("--> start deleting snapshot snap-2 "); + startDeleteSnapshot(repoName, "snap-2"); + }).start(); + future.actionGet(); + + // 6 - Let the deletion of snap-1 to complete. It should *not* lead to double finalization + barrier.await(); + + awaitNoMoreRunningOperations(); + } + + private PlainActionFuture setWaitForClusterState(Predicate predicate) { + final var clusterStateObserver = new ClusterStateObserver( + internalCluster().getCurrentMasterNodeInstance(ClusterService.class), + logger, + new ThreadContext(Settings.EMPTY) + ); + final PlainActionFuture future = new PlainActionFuture<>(); + clusterStateObserver.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + future.onResponse(null); + } + + @Override + public void onClusterServiceClose() { + future.onFailure(new IllegalStateException("cluster service closed")); + } + + @Override + public void onTimeout(TimeValue timeout) { + future.onFailure(new IllegalStateException("timeout")); + } + }, predicate, TimeValue.timeValueSeconds(30)); + return future; + } + + @Override + protected Collection> nodePlugins() { + return List.of(TestRepositoryPlugin.class); + } + + public static class TestRepositoryPlugin extends Plugin implements RepositoryPlugin { + + public static final String REPO_TYPE = "test"; + + @Override + public Map getRepositories( + Environment env, + NamedXContentRegistry namedXContentRegistry, + ClusterService clusterService, + BigArrays bigArrays, + RecoverySettings recoverySettings + ) { + return Map.of( + REPO_TYPE, + metadata -> new TestRepository(metadata, env, namedXContentRegistry, clusterService, bigArrays, recoverySettings) + ); + } + } + + public static class TestRepository extends FsRepository { + + private static final Logger logger = LogManager.getLogger(TestRepository.class); + private final AtomicReference barrierRef = new AtomicReference<>(); + + public TestRepository( + RepositoryMetadata metadata, + Environment environment, + NamedXContentRegistry namedXContentRegistry, + ClusterService clusterService, + BigArrays bigArrays, + RecoverySettings recoverySettings + ) { + super(metadata, environment, namedXContentRegistry, clusterService, bigArrays, recoverySettings); + } + + public CyclicBarrier blockOnceForListBlobs() { + final CyclicBarrier barrier = new CyclicBarrier(2); + if (barrierRef.compareAndSet(null, barrier)) { + return barrier; + } else { + throw new AssertionError("must unblock first"); + } + } + + @Override + protected BlobStore createBlobStore() throws Exception { + final var blobStore = super.createBlobStore(); + return new BlobStoreWrapper(blobStore) { + + @Override + public BlobContainer blobContainer(BlobPath path) { + final var blobContainer = super.blobContainer(path); + + return new FilterBlobContainer(blobContainer) { + + @Override + protected BlobContainer wrapChild(BlobContainer child) { + return child; + } + + @Override + public Map listBlobs(OperationPurpose purpose) throws IOException { + final CyclicBarrier barrier = barrierRef.get(); + if (barrier != null) { + try { + logger.info("--> Start blocking blobLists"); + barrier.await(); + if (false == barrierRef.compareAndSet(barrier, null)) { + throw new AssertionError("barrier changed while blocking"); + } + logger.info("--> Done blocking blobLists"); + } catch (InterruptedException | BrokenBarrierException e) { + throw new AssertionError(e); + } + } + return super.listBlobs(purpose); + } + }; + } + }; + } + } +} diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 2ad9b21bcb88f..e2a9746df4c66 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -1271,7 +1271,11 @@ private void endSnapshot(SnapshotsInProgress.Entry entry, Metadata metadata, @Nu repositoriesService.repository(repoName).getRepositoryData(new ActionListener<>() { @Override public void onResponse(RepositoryData repositoryData) { - finalizeSnapshotEntry(snapshot, metadata, repositoryData); + if (newFinalization) { + finalizeSnapshotEntry(snapshot, metadata, repositoryData); + } else { + runNextQueuedOperation(repositoryData, repoName, false); + } } @Override @@ -1280,7 +1284,11 @@ public void onFailure(Exception e) { } }); } else { - finalizeSnapshotEntry(snapshot, metadata, repositoryData); + if (newFinalization) { + finalizeSnapshotEntry(snapshot, metadata, repositoryData); + } else { + runNextQueuedOperation(repositoryData, repoName, false); + } } } else { if (newFinalization) { @@ -1312,6 +1320,7 @@ private void leaveRepoLoop(String repository) { private void finalizeSnapshotEntry(Snapshot snapshot, Metadata metadata, RepositoryData repositoryData) { assert currentlyFinalizing.contains(snapshot.getRepository()); + assert repositoryOperations.assertNotQueued(snapshot); try { SnapshotsInProgress.Entry entry = SnapshotsInProgress.get(clusterService.state()).snapshot(snapshot); final String failure = entry.failure(); From 43ed6768027b3d36b24a370c7c2a80a7334e0814 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 30 Oct 2023 20:41:37 +1100 Subject: [PATCH 187/203] Logging index name on unfound mapping hash (#101499) Relates: #101331 --- .../org/elasticsearch/cluster/metadata/IndexMetadata.java | 4 +++- .../gateway/PersistedClusterStateServiceTests.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java index c19b6b801c3c5..742b52365c8d7 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadata.java @@ -2506,7 +2506,9 @@ public static IndexMetadata fromXContent(XContentParser parser, Map { assert mappingsByHash != null : "no deduplicated mappings given"; if (mappingsByHash.containsKey(parser.text()) == false) { - throw new IllegalArgumentException("mapping with hash [" + parser.text() + "] not found"); + throw new IllegalArgumentException( + "mapping of index [" + builder.index + "] with hash [" + parser.text() + "] not found" + ); } builder.putMapping(mappingsByHash.get(parser.text())); } diff --git a/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java b/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java index 7373e62eba72f..bad6702e8ad83 100644 --- a/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java +++ b/server/src/test/java/org/elasticsearch/gateway/PersistedClusterStateServiceTests.java @@ -1687,7 +1687,7 @@ public void testFailsIfMappingIsMissing() throws IOException { final String message = expectThrows(CorruptStateException.class, () -> persistedClusterStateService.loadBestOnDiskState()) .getCause() .getMessage(); - assertEquals("java.lang.IllegalArgumentException: mapping with hash [" + hash + "] not found", message); + assertEquals("java.lang.IllegalArgumentException: mapping of index [test-1] with hash [" + hash + "] not found", message); } } From 50d4a282099cf075aec6d3380b5150d02083fd0e Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Oct 2023 10:19:37 +0000 Subject: [PATCH 188/203] Remove more `throw new AssertionError(e)` calls (#101508) Replaces them with calls to `fail(e)` or an `assertNoFailureListener` as appropriate. --- .../reindex/remote/RemoteScrollableHitSourceTests.java | 3 +-- .../cluster/routing/ShardRoutingRoleIT.java | 2 +- .../elasticsearch/discovery/ClusterDisruptionIT.java | 2 +- .../elasticsearch/index/mapper/DynamicMappingIT.java | 4 ++-- .../elasticsearch/index/seqno/RetentionLeaseIT.java | 10 +++------- ...ransportClearVotingConfigExclusionsActionTests.java | 2 +- .../cluster/reroute/ClusterRerouteResponseTests.java | 2 +- .../action/support/SubscribableListenerTests.java | 2 +- .../action/support/ThreadedActionListenerTests.java | 4 ++-- .../cluster/coordination/FollowersCheckerTests.java | 2 +- .../coordination/PublicationTransportHandlerTests.java | 6 +++--- .../cluster/node/DiscoveryNodesTests.java | 2 +- .../cluster/service/MasterServiceTests.java | 6 +++--- .../common/logging/ChunkedLoggingStreamTests.java | 4 ++-- .../common/util/CancellableSingleObjectCacheTests.java | 2 +- .../PrioritizedThrottledTaskRunnerTests.java | 2 +- .../common/util/concurrent/ThrottledIteratorTests.java | 4 ++-- .../org/elasticsearch/discovery/PeerFinderTests.java | 2 +- .../elasticsearch/http/DefaultRestChannelTests.java | 2 +- .../transport/ClusterConnectionManagerTests.java | 4 ++-- .../src/test/java/fixture/s3/S3HttpHandlerTests.java | 2 +- .../elasticsearch/cluster/DiskUsageIntegTestCase.java | 2 +- .../coordination/AbstractCoordinatorTestCase.java | 2 +- .../cluster/service/ClusterStateTaskExecutorUtils.java | 3 ++- .../org/elasticsearch/index/engine/EngineTestCase.java | 4 ++-- .../java/org/elasticsearch/rest/RestResponseUtils.java | 5 +++-- .../test/AbstractChunkedSerializingTestCase.java | 2 +- .../main/java/org/elasticsearch/test/ESTestCase.java | 8 ++++---- .../org/elasticsearch/test/InternalTestCluster.java | 4 ++-- .../org/elasticsearch/test/ReachabilityChecker.java | 4 ++-- .../compute/operator/exchange/ExchangeBufferTests.java | 7 +------ .../SearchableSnapshotsBlobStoreCacheIntegTests.java | 2 +- ...leSnapshotsBlobStoreCacheMaintenanceIntegTests.java | 2 +- .../recovery/SnapshotBasedIndexRecoveryIT.java | 2 +- 34 files changed, 54 insertions(+), 62 deletions(-) diff --git a/modules/reindex/src/test/java/org/elasticsearch/reindex/remote/RemoteScrollableHitSourceTests.java b/modules/reindex/src/test/java/org/elasticsearch/reindex/remote/RemoteScrollableHitSourceTests.java index 3d656bfa175d5..4b76045c5dc63 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/reindex/remote/RemoteScrollableHitSourceTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/reindex/remote/RemoteScrollableHitSourceTests.java @@ -615,8 +615,7 @@ private class TestRemoteScrollableHitSource extends RemoteScrollableHitSource { } private RejectAwareActionListener wrapAsListener(Consumer consumer) { - Consumer throwing = e -> { throw new AssertionError(e); }; - return RejectAwareActionListener.wrap(consumer::accept, throwing, throwing); + return RejectAwareActionListener.wrap(consumer::accept, ESTestCase::fail, ESTestCase::fail); } @SuppressWarnings("unchecked") diff --git a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java index 4d451971a656e..72d165ea0cf73 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/ShardRoutingRoleIT.java @@ -253,7 +253,7 @@ private static void assertRolesInRoutingTableXContent(ClusterState state) { } } } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java b/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java index 6f387128fb581..586e95484afa4 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/discovery/ClusterDisruptionIT.java @@ -566,7 +566,7 @@ protected boolean blockingAllowed() { try { Thread.sleep(100); } catch (InterruptedException e) { - throw new AssertionError("unexpected", e); + fail(e); } } } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java index d8a94bbb00ee3..38349e14bdf05 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/mapper/DynamicMappingIT.java @@ -179,7 +179,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } }); @@ -216,7 +216,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } }); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java b/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java index 61ea5f8c54f69..c9906ccf1fbee 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/index/seqno/RetentionLeaseIT.java @@ -429,7 +429,7 @@ public void testCanRenewRetentionLeaseUnderBlock() throws InterruptedException { ) ); } catch (final Exception e) { - failWithException(e); + fail(e); } }); @@ -485,7 +485,7 @@ public void onResponse(final ReplicationResponse replicationResponse) { @Override public void onFailure(final Exception e) { - failWithException(e); + fail(e); } }); @@ -530,7 +530,7 @@ public void testCanRenewRetentionLeaseWithoutWaitingForShards() throws Interrupt ) ); } catch (final Exception e) { - failWithException(e); + fail(e); } }); @@ -586,10 +586,6 @@ private void runWaitForShardsTest( afterSync.accept(primary); } - private static void failWithException(Exception e) { - throw new AssertionError("unexpected", e); - } - private static ActionListener countDownLatchListener(CountDownLatch latch) { return ActionTestUtils.assertNoFailureListener(r -> latch.countDown()); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsActionTests.java index fea15a77c48b1..2193e07f84f38 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsActionTests.java @@ -199,7 +199,7 @@ public void testCannotClearVotingConfigurationWhenItIsDisabled() { } private TransportResponseHandler expectSuccess(Consumer onResponse) { - return responseHandler(onResponse, e -> { throw new AssertionError("unexpected", e); }); + return responseHandler(onResponse, ESTestCase::fail); } private TransportResponseHandler expectError(Consumer onException) { diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java index e2e58e81f2f13..e6eccfebc15e2 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java @@ -292,7 +292,7 @@ private void assertXContent( ChunkedToXContent.wrapAsToXContent(response).toXContent(builder, params); assertEquals(XContentHelper.stripWhitespace(expectedBody), XContentHelper.stripWhitespace(Strings.toString(builder))); } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } final var expectedChunks = Objects.equals(params.param("metric"), "none") diff --git a/server/src/test/java/org/elasticsearch/action/support/SubscribableListenerTests.java b/server/src/test/java/org/elasticsearch/action/support/SubscribableListenerTests.java index 2a0391a57eba6..d2acf8f397f2f 100644 --- a/server/src/test/java/org/elasticsearch/action/support/SubscribableListenerTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/SubscribableListenerTests.java @@ -494,7 +494,7 @@ private static void assertComplete(SubscribableListener listener, @Nulla try { listener.rawResult(); } catch (Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } else { assertEquals(expectedFailureMessage, expectThrows(ElasticsearchException.class, listener::rawResult).getMessage()); diff --git a/server/src/test/java/org/elasticsearch/action/support/ThreadedActionListenerTests.java b/server/src/test/java/org/elasticsearch/action/support/ThreadedActionListenerTests.java index eef1882676cfd..b5f07b7d8d087 100644 --- a/server/src/test/java/org/elasticsearch/action/support/ThreadedActionListenerTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/ThreadedActionListenerTests.java @@ -83,7 +83,7 @@ public void onFailure(Exception e) { e = elasticsearchException; assertNull(e.getCause()); } else { - throw new AssertionError("unexpected", e); + fail(e); } } @@ -91,7 +91,7 @@ public void onFailure(Exception e) { assertEquals("simulated", e.getMessage()); assertEquals(0, e.getSuppressed().length); } else { - throw new AssertionError("unexpected", e); + fail(e); } } diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/FollowersCheckerTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/FollowersCheckerTests.java index 97a2e4f7ad35c..ee5b8b652d2d9 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/FollowersCheckerTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/FollowersCheckerTests.java @@ -839,7 +839,7 @@ public void handleResponse() { @Override public void handleException(TransportException exp) { - throw new AssertionError("unexpected", exp); + fail(exp); } public boolean succeeded() { diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java index bc8ddb9c741e2..0cdc5de86a8d3 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/PublicationTransportHandlerTests.java @@ -155,7 +155,7 @@ private static boolean isDiff(BytesTransportRequest request, TransportVersion ve IOUtils.close(in); } } catch (IOException e) { - throw new AssertionError("unexpected", e); + return fail(e); } } @@ -387,13 +387,13 @@ public void onResponse(TransportResponse transportResponse) { @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } }), new Task(randomNonNegativeLong(), "test", "test", "", TaskId.EMPTY_TASK_ID, Map.of())); } catch (IncompatibleClusterStateVersionException e) { context.handler().handleException(new RemoteTransportException("wrapped", e)); } catch (Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } }; diff --git a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java index f3df9693dbdf3..b01ad0f5b0493 100644 --- a/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/node/DiscoveryNodesTests.java @@ -464,7 +464,7 @@ public void accept(Consumer update, long expectedGenerat TransportVersion.current() ); } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } } assertEquals(expectedGeneration, discoveryNodes.getNodeLeftGeneration()); diff --git a/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java index 966520070ad70..51701cf9233e4 100644 --- a/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/service/MasterServiceTests.java @@ -2302,7 +2302,7 @@ public void clusterStateProcessed(ClusterState initialState, ClusterState newSta @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } @@ -2397,7 +2397,7 @@ public void clusterStateProcessed(ClusterState initialState, ClusterState newSta @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } @@ -2474,7 +2474,7 @@ public ClusterState execute(ClusterState currentState) { @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } }); } diff --git a/server/src/test/java/org/elasticsearch/common/logging/ChunkedLoggingStreamTests.java b/server/src/test/java/org/elasticsearch/common/logging/ChunkedLoggingStreamTests.java index a0534ea52dfc4..0b8085fc6d9a7 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/ChunkedLoggingStreamTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/ChunkedLoggingStreamTests.java @@ -137,7 +137,7 @@ public void append(LogEvent event) { Loggers.addAppender(captureLogger, appender); runnable.run(); } catch (Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } finally { Loggers.removeAppender(captureLogger, appender); appender.stop(); @@ -177,7 +177,7 @@ public static BytesReference getDecodedLoggedBody( Streams.copy(gzipInputStream, bytesStreamOutput); return bytesStreamOutput.bytes(); } catch (Exception e) { - throw new AssertionError("unexpected", e); + return fail(e); } } diff --git a/server/src/test/java/org/elasticsearch/common/util/CancellableSingleObjectCacheTests.java b/server/src/test/java/org/elasticsearch/common/util/CancellableSingleObjectCacheTests.java index e36d5eed61c1f..30412059394cd 100644 --- a/server/src/test/java/org/elasticsearch/common/util/CancellableSingleObjectCacheTests.java +++ b/server/src/test/java/org/elasticsearch/common/util/CancellableSingleObjectCacheTests.java @@ -355,7 +355,7 @@ protected boolean isFresh(String currentKey, String newKey) { if (e instanceof TaskCancelledException) { assertTrue(cancel); } else { - throw new AssertionError("unexpected", e); + fail(e); } })); }); diff --git a/server/src/test/java/org/elasticsearch/common/util/concurrent/PrioritizedThrottledTaskRunnerTests.java b/server/src/test/java/org/elasticsearch/common/util/concurrent/PrioritizedThrottledTaskRunnerTests.java index 79266f12c0208..49c091c65c71a 100644 --- a/server/src/test/java/org/elasticsearch/common/util/concurrent/PrioritizedThrottledTaskRunnerTests.java +++ b/server/src/test/java/org/elasticsearch/common/util/concurrent/PrioritizedThrottledTaskRunnerTests.java @@ -68,7 +68,7 @@ public void doRun() { @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } diff --git a/server/src/test/java/org/elasticsearch/common/util/concurrent/ThrottledIteratorTests.java b/server/src/test/java/org/elasticsearch/common/util/concurrent/ThrottledIteratorTests.java index 7bc147b77dfd6..c4b9b36c449ac 100644 --- a/server/src/test/java/org/elasticsearch/common/util/concurrent/ThrottledIteratorTests.java +++ b/server/src/test/java/org/elasticsearch/common/util/concurrent/ThrottledIteratorTests.java @@ -84,7 +84,7 @@ protected void doRun() { try { assertTrue(itemStartLatch.await(30, TimeUnit.SECONDS)); } catch (InterruptedException e) { - throw new AssertionError("unexpected", e); + fail(e); } finally { blockPermits.release(); } @@ -99,7 +99,7 @@ public void onAfter() { @Override public void onFailure(Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } }); } else { diff --git a/server/src/test/java/org/elasticsearch/discovery/PeerFinderTests.java b/server/src/test/java/org/elasticsearch/discovery/PeerFinderTests.java index c61187066487d..ce5841d066d88 100644 --- a/server/src/test/java/org/elasticsearch/discovery/PeerFinderTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/PeerFinderTests.java @@ -556,7 +556,7 @@ public void handleResponse(PeersResponse response) { @Override public void handleException(TransportException exp) { - throw new AssertionError("unexpected", exp); + fail(exp); } } ); diff --git a/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java b/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java index 63cdc2c485197..409023afc4576 100644 --- a/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java +++ b/server/src/test/java/org/elasticsearch/http/DefaultRestChannelTests.java @@ -692,7 +692,7 @@ public HttpResponse createResponse(RestStatus status, ChunkedRestResponseBody co } return new TestHttpResponse(status, bso.bytes()); } catch (IOException e) { - throw new AssertionError("unexpected", e); + return fail(e); } } }; diff --git a/server/src/test/java/org/elasticsearch/transport/ClusterConnectionManagerTests.java b/server/src/test/java/org/elasticsearch/transport/ClusterConnectionManagerTests.java index d16b1f304939f..712f44bfa1115 100644 --- a/server/src/test/java/org/elasticsearch/transport/ClusterConnectionManagerTests.java +++ b/server/src/test/java/org/elasticsearch/transport/ClusterConnectionManagerTests.java @@ -510,7 +510,7 @@ public void onFailure(Exception e) { connectionPermits.release(); runAgain(); } else { - throw new AssertionError("unexpected", e); + fail(e); } } @@ -592,7 +592,7 @@ public void onFailure(Exception e) { && e.getMessage().contains("concurrently connecting and disconnecting")) { runAgain(); } else { - throw new AssertionError("unexpected", e); + fail(e); } } diff --git a/test/fixtures/s3-fixture/src/test/java/fixture/s3/S3HttpHandlerTests.java b/test/fixtures/s3-fixture/src/test/java/fixture/s3/S3HttpHandlerTests.java index d36265dfec227..5227c333e5659 100644 --- a/test/fixtures/s3-fixture/src/test/java/fixture/s3/S3HttpHandlerTests.java +++ b/test/fixtures/s3-fixture/src/test/java/fixture/s3/S3HttpHandlerTests.java @@ -294,7 +294,7 @@ private static TestHttpResponse handleRequest(S3HttpHandler handler, String meth try { handler.handle(httpExchange); } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } assertNotEquals(0, httpExchange.getResponseCode()); return new TestHttpResponse( diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/DiskUsageIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/DiskUsageIntegTestCase.java index c82c635453edc..19eb072cb0c3f 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/DiskUsageIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/DiskUsageIntegTestCase.java @@ -83,7 +83,7 @@ protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { try { Files.createDirectories(dataPath); } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } fileSystemProvider.addTrackedPath(dataPath); return Settings.builder() diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java index 0b19f1aa422f3..11ea7765535e7 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java @@ -2254,7 +2254,7 @@ public void close() { try { delegate.close(); } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } } diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/service/ClusterStateTaskExecutorUtils.java b/test/framework/src/main/java/org/elasticsearch/cluster/service/ClusterStateTaskExecutorUtils.java index 0ecd0ac27c0f0..784b82c713d3b 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/service/ClusterStateTaskExecutorUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/service/ClusterStateTaskExecutorUtils.java @@ -19,6 +19,7 @@ import java.util.function.Consumer; import java.util.stream.StreamSupport; +import static org.elasticsearch.test.ESTestCase.fail; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -36,7 +37,7 @@ public static ClusterState executeAndAssert ClusterStateTaskExecutor executor, Iterable tasks ) throws Exception { - return executeHandlingResults(originalState, executor, tasks, task -> {}, (task, e) -> { throw new AssertionError(e); }); + return executeHandlingResults(originalState, executor, tasks, task -> {}, (task, e) -> fail(e)); } public static ClusterState executeAndThrowFirstFailure( diff --git a/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java index ab9d80b801863..21001f0ac2fac 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java @@ -1637,12 +1637,12 @@ public static void recoverFromTranslog(Engine engine, Engine.TranslogRecoveryRun && executionException.getCause() instanceof IOException ioException) { throw ioException; } else { - throw new AssertionError("unexpected", e); + fail(e); } } catch (RuntimeException e) { throw e; } catch (Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } } diff --git a/test/framework/src/main/java/org/elasticsearch/rest/RestResponseUtils.java b/test/framework/src/main/java/org/elasticsearch/rest/RestResponseUtils.java index 2562b53350272..dfbd7266cc4a2 100644 --- a/test/framework/src/main/java/org/elasticsearch/rest/RestResponseUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/rest/RestResponseUtils.java @@ -11,6 +11,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.core.CheckedConsumer; +import org.elasticsearch.test.ESTestCase; import java.io.IOException; import java.io.StringWriter; @@ -45,7 +46,7 @@ public static BytesReference getBodyContent(RestResponse restResponse) { out.flush(); return out.bytes(); } catch (Exception e) { - throw new AssertionError("unexpected", e); + return ESTestCase.fail(e); } } @@ -57,7 +58,7 @@ public static String getTextBodyContent(Iterator void assertChunkCount( builder.endObject(); } } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } // closing the builder verifies that the XContent is well-formed assertEquals(expectedChunkCount.applyAsInt(instance), chunkCount); } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index c7e74bde890f8..562777e995fdc 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -2013,9 +2013,9 @@ public static void safeAwait(CyclicBarrier barrier) { barrier.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new AssertionError("unexpected", e); + fail(e); } catch (Exception e) { - throw new AssertionError("unexpected", e); + fail(e); } } @@ -2024,7 +2024,7 @@ public static void safeAwait(CountDownLatch countDownLatch) { assertTrue(countDownLatch.await(10, TimeUnit.SECONDS)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new AssertionError("unexpected", e); + fail(e); } } @@ -2033,7 +2033,7 @@ public static void safeSleep(long millis) { Thread.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new AssertionError("unexpected", e); + fail(e); } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java index b666226698570..bd0f2c88c2219 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java @@ -1912,7 +1912,7 @@ private Set excludeMasters(Collection nodeAndClients) { new AddVotingConfigExclusionsRequest(excludedNodeNames.toArray(Strings.EMPTY_ARRAY)) ).get(); } catch (InterruptedException | ExecutionException e) { - throw new AssertionError("unexpected", e); + ESTestCase.fail(e); } } } @@ -1927,7 +1927,7 @@ private void removeExclusions(Set excludedNodeIds) { Client client = getRandomNodeAndClient(node -> excludedNodeIds.contains(node.name) == false).client(); client.execute(ClearVotingConfigExclusionsAction.INSTANCE, new ClearVotingConfigExclusionsRequest()).get(); } catch (InterruptedException | ExecutionException e) { - throw new AssertionError("unexpected", e); + ESTestCase.fail(e); } } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ReachabilityChecker.java b/test/framework/src/main/java/org/elasticsearch/test/ReachabilityChecker.java index f3a5fdf836199..b44ae52c08511 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ReachabilityChecker.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ReachabilityChecker.java @@ -116,7 +116,7 @@ public void assertReferenceEnqueuedForCollection(MemoryMXBean memoryMXBean, long assertNull(phantomReference.get()); // always succeeds, we're just doing this to use the phantomReference for something } } catch (Exception e) { - throw new AssertionError("unexpected", e); + ESTestCase.fail(e); } } @@ -128,7 +128,7 @@ public void assertReferenceNotEnqueuedForCollection(MemoryMXBean memoryMXBean) { memoryMXBean.gc(); assertNull("became unreachable: " + description, referenceQueue.remove(100)); } catch (Exception e) { - throw new AssertionError("unexpected", e); + ESTestCase.fail(e); } } } diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeBufferTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeBufferTests.java index 4c975c6c07834..a006a984eb178 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeBufferTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/operator/exchange/ExchangeBufferTests.java @@ -21,7 +21,6 @@ import org.elasticsearch.test.ESTestCase; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -38,11 +37,7 @@ public void testDrainPages() throws Exception { AtomicInteger addedPages = new AtomicInteger(); for (int t = 0; t < producers.length; t++) { producers[t] = new Thread(() -> { - try { - latch.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new AssertionError(e); - } + safeAwait(latch); while (stopped.get() == false && addedPages.incrementAndGet() < 10_000) { buffer.addPage(randomPage(blockFactory)); } diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java index 0a2056c94ded2..9847eb101531c 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheIntegTests.java @@ -360,7 +360,7 @@ private void refreshSystemIndex() { assertThat(refreshResponse.getSuccessfulShards(), greaterThan(0)); assertThat(refreshResponse.getFailedShards(), equalTo(0)); } catch (IndexNotFoundException indexNotFoundException) { - throw new AssertionError("unexpected", indexNotFoundException); + fail(indexNotFoundException); } } diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheMaintenanceIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheMaintenanceIntegTests.java index 5048eace460c6..68b702469d138 100644 --- a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheMaintenanceIntegTests.java +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/cache/blob/SearchableSnapshotsBlobStoreCacheMaintenanceIntegTests.java @@ -335,7 +335,7 @@ private void refreshSystemIndex(boolean failIfNotExist) { assertThat(refreshResponse.getSuccessfulShards(), failIfNotExist ? greaterThan(0) : greaterThanOrEqualTo(0)); assertThat(refreshResponse.getFailedShards(), equalTo(0)); } catch (IndexNotFoundException indexNotFoundException) { - throw new AssertionError("unexpected", indexNotFoundException); + fail(indexNotFoundException); } } diff --git a/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java b/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java index 4c9d8d1ab29e5..c37cc7b092aba 100644 --- a/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java +++ b/x-pack/plugin/snapshot-based-recoveries/src/internalClusterTest/java/org/elasticsearch/xpack/snapshotbasedrecoveries/recovery/SnapshotBasedIndexRecoveryIT.java @@ -716,7 +716,7 @@ public void sendResponse(Exception exception) throws IOException { try { channel.sendResponse(exception); } catch (IOException e) { - throw new AssertionError("unexpected", e); + fail(e); } }); } From 4db0d14cab0bcb0fd0096a718d7edc36c7122f8b Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 30 Oct 2023 10:30:08 +0000 Subject: [PATCH 189/203] Remove unused fields (#101509) --- .../health/plugin/ShardsAvailabilityPlugin.java | 5 +---- .../org/elasticsearch/node/NodeConstruction.java | 12 ++---------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java b/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java index 3ec59ad8a9b49..aeb45424ebc58 100644 --- a/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java +++ b/modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java @@ -10,9 +10,7 @@ import org.apache.lucene.util.SetOnce; import org.elasticsearch.cluster.routing.allocation.ShardsAvailabilityHealthIndicatorService; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.health.HealthIndicatorService; -import org.elasticsearch.plugin.Inject; import org.elasticsearch.plugins.HealthPlugin; import org.elasticsearch.plugins.Plugin; @@ -23,8 +21,7 @@ public class ShardsAvailabilityPlugin extends Plugin implements HealthPlugin { private final SetOnce shardHealthService = new SetOnce<>(); - @Inject - public ShardsAvailabilityPlugin(Settings settings) {} + public ShardsAvailabilityPlugin() {} @Override public Collection createComponents(PluginServices services) { diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index 1e547a405c651..f951a1ecba7e6 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -1043,13 +1043,7 @@ record PluginServiceInstances( discoveryModule.getCoordinator(), masterHistoryService ); - final HealthService healthService = createHealthService( - clusterService, - clusterModule, - coordinationDiagnosticsService, - threadPool, - systemIndices - ); + final HealthService healthService = createHealthService(clusterService, coordinationDiagnosticsService, threadPool); HealthPeriodicLogger healthPeriodicLogger = createHealthPeriodicLogger(clusterService, settings, client, healthService); healthPeriodicLogger.init(); HealthMetadataService healthMetadataService = HealthMetadataService.create(clusterService, settings); @@ -1261,10 +1255,8 @@ private static ReloadablePlugin wrapPlugins(List reloadablePlu private HealthService createHealthService( ClusterService clusterService, - ClusterModule clusterModule, CoordinationDiagnosticsService coordinationDiagnosticsService, - ThreadPool threadPool, - SystemIndices systemIndices + ThreadPool threadPool ) { var serverHealthIndicatorServices = Stream.of( new StableMasterHealthIndicatorService(coordinationDiagnosticsService, clusterService), From dbaeff7ae47d65f4a34ba008dd7c514d2db8d33c Mon Sep 17 00:00:00 2001 From: David Kyle Date: Mon, 30 Oct 2023 11:08:05 +0000 Subject: [PATCH 190/203] [ML] Unmute MlModelDeploymentsUpgradeIT test and remove logging (#101513) This reverts commit 2ce5392ebd78830ad24fd3024df42df7544b37ab. --- .../TrainedModelAssignmentClusterService.java | 10 ---------- .../upgrades/MLModelDeploymentsUpgradeIT.java | 3 +-- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java index ca9a99f2d96f5..2caf338d2a3c7 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/inference/assignment/TrainedModelAssignmentClusterService.java @@ -164,8 +164,6 @@ public void clusterChanged(ClusterChangedEvent event) { } if (eventStateMinTransportVersionIsBeforeDistributedModelAllocationTransportVersion(event)) { - logger.trace("min transport version is before assignment change on " + event.state().nodes().getAllNodes().size() + " nodes"); - // we should not try to rebalance assignments while there may be nodes running on a version // prior to introducing distributed model allocation. // But we should remove routing to removed or shutting down nodes. @@ -240,7 +238,6 @@ public void onFailure(Exception e) { } private void removeRoutingToRemovedOrShuttingDownNodes(ClusterChangedEvent event) { - logger.trace("remove routing to removed or shutting down nodes "); if (areAssignedNodesRemoved(event)) { submitUnbatchedTask("removing routing entries for removed or shutting down nodes", new ClusterStateUpdateTask() { @Override @@ -285,7 +282,6 @@ static boolean areAssignedNodesRemoved(ClusterChangedEvent event) { // Visible for testing static ClusterState removeRoutingToUnassignableNodes(ClusterState currentState) { - logger.trace("remove routing to unassignable nodes"); Set assignableNodes = getAssignableNodes(currentState).stream().map(DiscoveryNode::getId).collect(Collectors.toSet()); TrainedModelAssignmentMetadata metadata = TrainedModelAssignmentMetadata.fromState(currentState); TrainedModelAssignmentMetadata.Builder builder = TrainedModelAssignmentMetadata.builder(currentState); @@ -435,7 +431,6 @@ public void createNewModelAssignment( } public void setModelAssignmentToStopping(String modelId, ActionListener listener) { - logger.trace("set to stopping"); submitUnbatchedTask("set model assignment stopping", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { @@ -455,7 +450,6 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) } public void removeModelAssignment(String deploymentId, ActionListener listener) { - logger.trace("remove model assignments"); submitUnbatchedTask("delete model deployment assignment", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { @@ -492,7 +486,6 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) // Used by the reset action directly public void removeAllModelAssignments(ActionListener listener) { - logger.trace("remove all assignments"); submitUnbatchedTask("delete all model assignments", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { @@ -525,11 +518,9 @@ private static ClusterState forceUpdate(ClusterState currentState, TrainedModelA logger.debug(() -> format("updated assignments: %s", modelAssignments.build())); Metadata.Builder metadata = Metadata.builder(currentState.metadata()); if (currentState.getMinTransportVersion().onOrAfter(RENAME_ALLOCATION_TO_ASSIGNMENT_TRANSPORT_VERSION)) { - logger.trace("putting custom new name"); metadata.putCustom(TrainedModelAssignmentMetadata.NAME, modelAssignments.build()) .removeCustom(TrainedModelAssignmentMetadata.DEPRECATED_NAME); } else { - logger.trace("putting custom old name"); metadata.putCustom(TrainedModelAssignmentMetadata.DEPRECATED_NAME, modelAssignments.buildOld()); } return ClusterState.builder(currentState).metadata(metadata).build(); @@ -625,7 +616,6 @@ ClusterState stopPlatformSpecificModelsInHeterogeneousClusters( modelToAdd.get().getModelId(), mlNodesArchitectures ); - logger.info(reasonToStop); updatedState = callSetToStopping(reasonToStop, modelToAdd.get().getDeploymentId(), clusterState); } return updatedState; diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java index 848dfa6e12ae2..e1845e901447e 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MLModelDeploymentsUpgradeIT.java @@ -73,7 +73,7 @@ public void setUpLogging() throws IOException { { "persistent": { "logger.org.elasticsearch.xpack.ml.inference": "TRACE", - "logger.org.elasticsearch.xpack.ml.inference.assignments": "TRACE", + "logger.org.elasticsearch.xpack.ml.inference.assignments": "DEBUG", "logger.org.elasticsearch.xpack.ml.process": "DEBUG", "logger.org.elasticsearch.xpack.ml.action": "TRACE" } @@ -97,7 +97,6 @@ public void removeLogging() throws IOException { client().performRequest(request); } - @AwaitsFix(bugUrl = "mute to try and reproduce https://github.com/elastic/elasticsearch/issues/100379") public void testTrainedModelDeployment() throws Exception { assumeTrue("NLP model deployments added in 8.0", isOriginalClusterVersionAtLeast(Version.V_8_0_0)); From bf2921fab717c4fc5364c29aadfbb2d73e980198 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 30 Oct 2023 11:41:15 +0000 Subject: [PATCH 191/203] Remove Plugin.createComponents method that specifies individual services (#101457) This is a breaking change for plugins implementing createComponents. Subclasses should be refactored to implement the method that takes a PluginServices object instead. --- docs/changelog/101457.yaml | 14 +++++ .../org/elasticsearch/plugins/Plugin.java | 61 ------------------- 2 files changed, 14 insertions(+), 61 deletions(-) create mode 100644 docs/changelog/101457.yaml diff --git a/docs/changelog/101457.yaml b/docs/changelog/101457.yaml new file mode 100644 index 0000000000000..03bdbe39b5b8e --- /dev/null +++ b/docs/changelog/101457.yaml @@ -0,0 +1,14 @@ +pr: 101457 +summary: "Remove Plugin.createComponents method in favour of overload with a PluginServices object" +area: Infra/Plugins +type: breaking-java +breaking: + area: "Java API" + title: "Plugin.createComponents method has been refactored to take a single PluginServices object" + details: > + Plugin.createComponents currently takes several different service arguments. The signature of this method changes + every time a new service is added. The method has now been modified to take a single interface object + that new services are added to. This will reduce API incompatibility issues when a new service + is introduced in the future. + impact: "Plugins that override createComponents will need to be refactored to override the new method on ES 8.12+" + notable: false diff --git a/server/src/main/java/org/elasticsearch/plugins/Plugin.java b/server/src/main/java/org/elasticsearch/plugins/Plugin.java index 5530f8bf9f307..a391881f0def9 100644 --- a/server/src/main/java/org/elasticsearch/plugins/Plugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/Plugin.java @@ -155,67 +155,6 @@ public interface PluginServices { * @param services Provides access to various Elasticsearch services */ public Collection createComponents(PluginServices services) { - return createComponents( - services.client(), - services.clusterService(), - services.threadPool(), - services.resourceWatcherService(), - services.scriptService(), - services.xContentRegistry(), - services.environment(), - services.nodeEnvironment(), - services.namedWriteableRegistry(), - services.indexNameExpressionResolver(), - services.repositoriesServiceSupplier(), - services.telemetryProvider(), - services.allocationService(), - services.indicesService() - ); - } - - /** - * Returns components added by this plugin. Either this method or {@link #createComponents(PluginServices)} - * should be implemented. - *

    - * Any components returned that implement {@link LifecycleComponent} will have their lifecycle managed. - * Note: To aid in the migration away from guice, all objects returned as components will be bound in guice - * to themselves. - * - * @param client A client to make requests to the system - * @param clusterService A service to allow watching and updating cluster state - * @param threadPool A service to allow retrieving an executor to run an async action - * @param resourceWatcherService A service to watch for changes to node local files - * @param scriptService A service to allow running scripts on the local node - * @param xContentRegistry The registry for extensible xContent parsing - * @param environment The environment for path and setting configurations - * @param nodeEnvironment The node environment used coordinate access to the data paths - * @param namedWriteableRegistry The registry for {@link NamedWriteable} object parsing - * @param indexNameExpressionResolver A service that resolves expression to index and alias names - * @param repositoriesServiceSupplier A supplier for the service that manages snapshot repositories; will return null when this method - * is called, but will return the repositories service once the node is initialized. - * @param telemetryProvider An interface for distributed tracing - * @param allocationService A service to manage shard allocation in the cluster - * @param indicesService A service to manage indices in the cluster - * - * @deprecated New services will only be added to {@link PluginServices}; this method is maintained for compatibility. - */ - @Deprecated - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier, - TelemetryProvider telemetryProvider, - AllocationService allocationService, - IndicesService indicesService - ) { return Collections.emptyList(); } From 6d9d5469eb1ec2b7432af1b24d10c82c332c3f95 Mon Sep 17 00:00:00 2001 From: Ievgen Degtiarenko Date: Mon, 30 Oct 2023 13:30:26 +0100 Subject: [PATCH 192/203] Fix oom in LinearizabilityChecker (#101357) LinearizabilityChecker is known to throw OOM when processing long history or partition. This change moves runtime limit for big histories or partitions inside the checker and exposes failure to complete the check via checked exceptions. --- .../ConcurrentSeqNoVersioningIT.java | 23 ++----- .../LinearizabilityCheckerTests.java | 29 ++++----- .../AbstractCoordinatorTestCase.java | 23 ++----- .../coordination/LinearizabilityChecker.java | 62 +++++++++++-------- 4 files changed, 62 insertions(+), 75 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/versioning/ConcurrentSeqNoVersioningIT.java b/server/src/internalClusterTest/java/org/elasticsearch/versioning/ConcurrentSeqNoVersioningIT.java index b9cc8ef8ac515..a68d56e05cb48 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/versioning/ConcurrentSeqNoVersioningIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/versioning/ConcurrentSeqNoVersioningIT.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.coordination.LinearizabilityChecker; +import org.elasticsearch.cluster.coordination.LinearizabilityChecker.LinearizabilityCheckAborted; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -26,8 +27,6 @@ import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.disruption.ServiceDisruptionScheme; -import org.elasticsearch.threadpool.Scheduler; -import org.elasticsearch.threadpool.ThreadPool; import java.io.FileInputStream; import java.io.IOException; @@ -40,10 +39,8 @@ import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; -import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; @@ -434,18 +431,9 @@ public void assertLinearizable() { LinearizabilityChecker.SequentialSpec spec = new CASSequentialSpec(initialVersion); boolean linearizable = false; try { - final ScheduledThreadPoolExecutor scheduler = Scheduler.initScheduler(Settings.EMPTY, "test-scheduler"); - final AtomicBoolean abort = new AtomicBoolean(); - // Large histories can be problematic and have the linearizability checker run OOM - // Bound the time how long the checker can run on such histories (Values empirically determined) - if (history.size() > 300) { - scheduler.schedule(() -> abort.set(true), 10, TimeUnit.SECONDS); - } - linearizable = LinearizabilityChecker.isLinearizable(spec, history, missingResponseGenerator(), abort::get); - ThreadPool.terminate(scheduler, 1, TimeUnit.SECONDS); - if (abort.get() && linearizable == false) { - linearizable = true; // let the test pass - } + linearizable = LinearizabilityChecker.isLinearizable(spec, history, missingResponseGenerator()); + } catch (LinearizabilityCheckAborted e) { + logger.warn("linearizability check check was aborted", e); } finally { // implicitly test that we can serialize all histories. String serializedHistory = base64Serialize(history); @@ -683,7 +671,8 @@ public static void main(String[] args) throws Exception { } @SuppressForbidden(reason = "system out is ok for a command line tool") - private static void runLinearizabilityChecker(FileInputStream fileInputStream, long primaryTerm, long seqNo) throws IOException { + private static void runLinearizabilityChecker(FileInputStream fileInputStream, long primaryTerm, long seqNo) throws IOException, + LinearizabilityCheckAborted { StreamInput is = new InputStreamStreamInput(Base64.getDecoder().wrap(fileInputStream)); is = new NamedWriteableAwareStreamInput(is, createNamedWriteableRegistry()); diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/LinearizabilityCheckerTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/LinearizabilityCheckerTests.java index d6df8e26721c0..34fe7eae32fcb 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/LinearizabilityCheckerTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/LinearizabilityCheckerTests.java @@ -9,6 +9,7 @@ import org.elasticsearch.cluster.coordination.LinearizabilityChecker.History; import org.elasticsearch.cluster.coordination.LinearizabilityChecker.KeyedSpec; +import org.elasticsearch.cluster.coordination.LinearizabilityChecker.LinearizabilityCheckAborted; import org.elasticsearch.cluster.coordination.LinearizabilityChecker.SequentialSpec; import org.elasticsearch.core.Tuple; import org.elasticsearch.test.ESTestCase; @@ -58,7 +59,7 @@ public void testLockConsistent() { assertThat(lockSpec.nextState(true, null, true), equalTo(Optional.empty())); } - public void testLockWithLinearizableHistory1() { + public void testLockWithLinearizableHistory1() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(null); // 0: acquire lock history.respond(call0, true); // 0: lock acquisition succeeded @@ -67,7 +68,7 @@ public void testLockWithLinearizableHistory1() { assertTrue(LinearizabilityChecker.isLinearizable(lockSpec, history)); } - public void testLockWithLinearizableHistory2() { + public void testLockWithLinearizableHistory2() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(null); // 0: acquire lock int call1 = history.invoke(null); // 1: acquire lock @@ -76,7 +77,7 @@ public void testLockWithLinearizableHistory2() { assertTrue(LinearizabilityChecker.isLinearizable(lockSpec, history)); } - public void testLockWithLinearizableHistory3() { + public void testLockWithLinearizableHistory3() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(null); // 0: acquire lock int call1 = history.invoke(null); // 1: acquire lock @@ -85,7 +86,7 @@ public void testLockWithLinearizableHistory3() { assertTrue(LinearizabilityChecker.isLinearizable(lockSpec, history)); } - public void testLockWithNonLinearizableHistory() { + public void testLockWithNonLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(null); // 0: acquire lock history.respond(call0, false); // 0: lock acquisition failed @@ -127,7 +128,7 @@ public void testRegisterConsistent() { assertThat(registerSpec.nextState(7, null, 42), equalTo(Optional.empty())); } - public void testRegisterWithLinearizableHistory() { + public void testRegisterWithLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(42); // 0: invoke write 42 int call1 = history.invoke(null); // 1: invoke read @@ -135,14 +136,14 @@ public void testRegisterWithLinearizableHistory() { history.respond(call2, 0); // 2: read returns 0 history.respond(call1, 42); // 1: read returns 42 - expectThrows(IllegalArgumentException.class, () -> LinearizabilityChecker.isLinearizable(registerSpec, history)); + expectThrows(AssertionError.class, () -> LinearizabilityChecker.isLinearizable(registerSpec, history)); assertTrue(LinearizabilityChecker.isLinearizable(registerSpec, history, i -> null)); history.respond(call0, null); // 0: write returns assertTrue(LinearizabilityChecker.isLinearizable(registerSpec, history)); } - public void testRegisterWithNonLinearizableHistory() { + public void testRegisterWithNonLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(42); // 0: invoke write 42 int call1 = history.invoke(null); // 1: invoke read @@ -150,14 +151,14 @@ public void testRegisterWithNonLinearizableHistory() { int call2 = history.invoke(null); // 2: invoke read history.respond(call2, 0); // 2: read returns 0, not allowed - expectThrows(IllegalArgumentException.class, () -> LinearizabilityChecker.isLinearizable(registerSpec, history)); + expectThrows(AssertionError.class, () -> LinearizabilityChecker.isLinearizable(registerSpec, history)); assertFalse(LinearizabilityChecker.isLinearizable(registerSpec, history, i -> null)); history.respond(call0, null); // 0: write returns assertFalse(LinearizabilityChecker.isLinearizable(registerSpec, history)); } - public void testRegisterObservedSequenceOfUpdatesWitLinearizableHistory() { + public void testRegisterObservedSequenceOfUpdatesWitLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(42); // 0: invoke write 42 int call1 = history.invoke(43); // 1: invoke write 43 @@ -174,7 +175,7 @@ public void testRegisterObservedSequenceOfUpdatesWitLinearizableHistory() { assertTrue(LinearizabilityChecker.isLinearizable(registerSpec, history)); } - public void testRegisterObservedSequenceOfUpdatesWithNonLinearizableHistory() { + public void testRegisterObservedSequenceOfUpdatesWithNonLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int call0 = history.invoke(42); // 0: invoke write 42 int call1 = history.invoke(43); // 1: invoke write 43 @@ -214,7 +215,7 @@ public Optional nextState(Object currentState, Object input, Object outp } }; - public void testMultiRegisterWithLinearizableHistory() { + public void testMultiRegisterWithLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int callX0 = history.invoke(new Tuple<>("x", 42)); // 0: invoke write 42 on key x int callX1 = history.invoke(new Tuple<>("x", null)); // 1: invoke read on key x @@ -227,7 +228,7 @@ public void testMultiRegisterWithLinearizableHistory() { history.respond(callY1, 42); // 1: read returns 42 on key y history.respond(callX1, 42); // 1: read returns 42 on key x - expectThrows(IllegalArgumentException.class, () -> LinearizabilityChecker.isLinearizable(multiRegisterSpec, history)); + expectThrows(AssertionError.class, () -> LinearizabilityChecker.isLinearizable(multiRegisterSpec, history)); assertTrue(LinearizabilityChecker.isLinearizable(multiRegisterSpec, history, i -> null)); history.respond(callX0, null); // 0: write returns on key x @@ -235,7 +236,7 @@ public void testMultiRegisterWithLinearizableHistory() { assertTrue(LinearizabilityChecker.isLinearizable(multiRegisterSpec, history)); } - public void testMultiRegisterWithNonLinearizableHistory() { + public void testMultiRegisterWithNonLinearizableHistory() throws LinearizabilityCheckAborted { final History history = new History(); int callX0 = history.invoke(new Tuple<>("x", 42)); // 0: invoke write 42 on key x int callX1 = history.invoke(new Tuple<>("x", null)); // 1: invoke read on key x @@ -248,7 +249,7 @@ public void testMultiRegisterWithNonLinearizableHistory() { history.respond(callY2, 0); // 2: read returns 0 on key y, not allowed history.respond(callX1, 42); // 1: read returns 42 on key x - expectThrows(IllegalArgumentException.class, () -> LinearizabilityChecker.isLinearizable(multiRegisterSpec, history)); + expectThrows(AssertionError.class, () -> LinearizabilityChecker.isLinearizable(multiRegisterSpec, history)); assertFalse(LinearizabilityChecker.isLinearizable(multiRegisterSpec, history, i -> null)); history.respond(callX0, null); // 0: write returns on key x diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java index 11ea7765535e7..7c856328d4384 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java @@ -31,6 +31,7 @@ import org.elasticsearch.cluster.coordination.AbstractCoordinatorTestCase.Cluster.ClusterNode; import org.elasticsearch.cluster.coordination.CoordinationMetadata.VotingConfiguration; import org.elasticsearch.cluster.coordination.LinearizabilityChecker.History; +import org.elasticsearch.cluster.coordination.LinearizabilityChecker.LinearizabilityCheckAborted; import org.elasticsearch.cluster.coordination.LinearizabilityChecker.SequentialSpec; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -75,7 +76,6 @@ import org.elasticsearch.monitor.NodeHealthService; import org.elasticsearch.monitor.StatusInfo; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.threadpool.Scheduler; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.BytesRefRecycler; import org.elasticsearch.transport.DisruptableMockTransport; @@ -103,9 +103,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -747,22 +744,12 @@ private void stabilise(long stabilisationDurationMillis, boolean expectIdleJoinV ); logger.info("checking linearizability of history with size {}: {}", history.size(), history); - final AtomicBoolean abort = new AtomicBoolean(); - // Large histories can be problematic and have the linearizability checker run OOM - // Bound the time how long the checker can run on such histories (Values empirically determined) - final ScheduledThreadPoolExecutor scheduler = Scheduler.initScheduler(Settings.EMPTY, "test-scheduler"); try { - if (history.size() > 300) { - scheduler.schedule(() -> abort.set(true), 10, TimeUnit.SECONDS); - } - final boolean linearizable = LinearizabilityChecker.isLinearizable(spec, history, i -> null, abort::get); - if (abort.get() == false) { - assertTrue("history not linearizable: " + history, linearizable); - } - } finally { - ThreadPool.terminate(scheduler, 1, TimeUnit.SECONDS); + final boolean linearizable = LinearizabilityChecker.isLinearizable(spec, history, i -> null); + assertTrue("history is not linearizable: " + history, linearizable); + } catch (LinearizabilityCheckAborted e) { + logger.warn("linearizability check check was aborted", e); } - logger.info("linearizability check completed"); } void bootstrapIfNecessary() { diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/LinearizabilityChecker.java b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/LinearizabilityChecker.java index ceca88ef7573e..223b0dc5a546b 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/LinearizabilityChecker.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/LinearizabilityChecker.java @@ -11,8 +11,11 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.util.FixedBitSet; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Tuple; +import org.elasticsearch.threadpool.Scheduler; +import org.elasticsearch.threadpool.ThreadPool; import java.util.ArrayList; import java.util.Collection; @@ -27,6 +30,9 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; import java.util.function.Consumer; @@ -207,15 +213,10 @@ public String toString() { } /** - * Checks whether the provided history is linearizable with respect to the given sequential specification - * - * @param spec the sequential specification of the datatype - * @param history the history of events to check for linearizability - * @param missingResponseGenerator used to complete the history with missing responses - * @return true iff the history is linearizable w.r.t. the given spec + * Convenience method for {@link #isLinearizable(SequentialSpec, History, Function)} that requires the history to be complete */ - public static boolean isLinearizable(SequentialSpec spec, History history, Function missingResponseGenerator) { - return isLinearizable(spec, history, missingResponseGenerator, () -> false); + public static boolean isLinearizable(SequentialSpec spec, History history) throws LinearizabilityCheckAborted { + return isLinearizable(spec, history, o -> { throw new AssertionError("history is not complete"); }); } /** @@ -224,21 +225,37 @@ public static boolean isLinearizable(SequentialSpec spec, History history, Funct * @param spec the sequential specification of the datatype * @param history the history of events to check for linearizability * @param missingResponseGenerator used to complete the history with missing responses - * @param terminateEarly a condition upon which to terminate early * @return true iff the history is linearizable w.r.t. the given spec */ - public static boolean isLinearizable( - SequentialSpec spec, - History history, - Function missingResponseGenerator, - BooleanSupplier terminateEarly - ) { - history = history.clone(); // clone history before completing it - history.complete(missingResponseGenerator); // complete history - final Collection> partitions = spec.partition(history.copyEvents()); - return partitions.stream().allMatch(h -> isLinearizable(spec, h, terminateEarly)); + public static boolean isLinearizable(SequentialSpec spec, History history, Function missingResponseGenerator) + throws LinearizabilityCheckAborted { + final ScheduledThreadPoolExecutor scheduler = Scheduler.initScheduler(Settings.EMPTY, "test-scheduler"); + final AtomicBoolean abort = new AtomicBoolean(); + try { + history = history.clone(); // clone history before completing it + history.complete(missingResponseGenerator); // complete history + final Collection> partitions = spec.partition(history.copyEvents()); + // Large histories can be problematic and have the linearizability checker run OOM + // Bound the time how long the checker can run on such histories (Values empirically determined) + if (history.size() > 300 || partitions.stream().anyMatch(p -> p.size() > 25)) { + logger.warn("Detected large history or partition for linearizable check. Limiting execution time"); + scheduler.schedule(() -> abort.set(true), 10, TimeUnit.SECONDS); + } + var allLinearizable = partitions.stream().allMatch(h -> isLinearizable(spec, h, abort::get)); + if (abort.get()) { + throw new LinearizabilityCheckAborted(); + } + return allLinearizable; + } finally { + ThreadPool.terminate(scheduler, 1, TimeUnit.SECONDS); + } } + /** + * This exception is thrown if the check could not be completed due to timeout or OOM (that could be caused by long event history) + */ + public static final class LinearizabilityCheckAborted extends Exception {} + private static boolean isLinearizable(SequentialSpec spec, List history, BooleanSupplier terminateEarly) { logger.debug("Checking history of size: {}: {}", history.size(), history); Object state = spec.initialState(); // the current state of the datatype @@ -287,13 +304,6 @@ private static boolean isLinearizable(SequentialSpec spec, List history, return true; } - /** - * Convenience method for {@link #isLinearizable(SequentialSpec, History, Function)} that requires the history to be complete - */ - public static boolean isLinearizable(SequentialSpec spec, History history) { - return isLinearizable(spec, history, o -> { throw new IllegalArgumentException("history is not complete"); }); - } - /** * Return a visual representation of the history */ From a59180459a3cb30b71399d778943cab4ac2191c4 Mon Sep 17 00:00:00 2001 From: Keith Massey Date: Mon, 30 Oct 2023 07:41:18 -0500 Subject: [PATCH 193/203] Improving tika PDF handling (#101486) Correctly handling PDF files with public key encryption in attachment processor --- docs/changelog/101486.yaml | 5 +++++ .../ingest/attachment/TikaImpl.java | 9 +++++++++ .../attachment/AttachmentProcessorTests.java | 16 +++++++++++++++- .../test/sample-files/encrypted-with-key.pdf | Bin 0 -> 15512 bytes 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/101486.yaml create mode 100644 modules/ingest-attachment/src/test/resources/org/elasticsearch/ingest/attachment/test/sample-files/encrypted-with-key.pdf diff --git a/docs/changelog/101486.yaml b/docs/changelog/101486.yaml new file mode 100644 index 0000000000000..99795feda328f --- /dev/null +++ b/docs/changelog/101486.yaml @@ -0,0 +1,5 @@ +pr: 101486 +summary: Improving tika handling +area: Ingest Node +type: bug +issues: [] diff --git a/modules/ingest-attachment/src/main/java/org/elasticsearch/ingest/attachment/TikaImpl.java b/modules/ingest-attachment/src/main/java/org/elasticsearch/ingest/attachment/TikaImpl.java index 13fd3bdbcb531..c8d0e0f96f85b 100644 --- a/modules/ingest-attachment/src/main/java/org/elasticsearch/ingest/attachment/TikaImpl.java +++ b/modules/ingest-attachment/src/main/java/org/elasticsearch/ingest/attachment/TikaImpl.java @@ -107,6 +107,15 @@ static String parse(final byte content[], final Metadata metadata, final int lim } else { throw new AssertionError(cause); } + } catch (LinkageError e) { + if (e.getMessage().contains("bouncycastle")) { + /* + * Elasticsearch does not ship with bouncycastle. It is only used for public-key-encrypted PDFs, which this module does + * not support anyway. + */ + throw new RuntimeException("document is encrypted", e); + } + throw new RuntimeException(e); } } diff --git a/modules/ingest-attachment/src/test/java/org/elasticsearch/ingest/attachment/AttachmentProcessorTests.java b/modules/ingest-attachment/src/test/java/org/elasticsearch/ingest/attachment/AttachmentProcessorTests.java index 1fead50a600e7..20b0f19e9549e 100644 --- a/modules/ingest-attachment/src/test/java/org/elasticsearch/ingest/attachment/AttachmentProcessorTests.java +++ b/modules/ingest-attachment/src/test/java/org/elasticsearch/ingest/attachment/AttachmentProcessorTests.java @@ -243,11 +243,25 @@ public void testVisioIsExcluded() throws Exception { assertThat(attachmentData.get("content_length"), is(0L)); } - public void testEncryptedPdf() throws Exception { + public void testEncryptedWithPasswordPdf() throws Exception { + /* + * This tests that a PDF that has been encrypted with a password fails in the way expected + */ ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> parseDocument("encrypted.pdf", processor)); assertThat(e.getDetailedMessage(), containsString("document is encrypted")); } + public void testEncryptedWithKeyPdf() throws Exception { + /* + * This tests that a PDF that has been encrypted with a public key fails in the way expected + */ + ElasticsearchParseException e = expectThrows( + ElasticsearchParseException.class, + () -> parseDocument("encrypted-with-key.pdf", processor) + ); + assertThat(e.getDetailedMessage(), containsString("document is encrypted")); + } + public void testHtmlDocument() throws Exception { Map attachmentData = parseDocument("htmlWithEmptyDateMeta.html", processor); diff --git a/modules/ingest-attachment/src/test/resources/org/elasticsearch/ingest/attachment/test/sample-files/encrypted-with-key.pdf b/modules/ingest-attachment/src/test/resources/org/elasticsearch/ingest/attachment/test/sample-files/encrypted-with-key.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6bf5f6a0ba2877813317c8eb7b6c05ceed37a4d6 GIT binary patch literal 15512 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 Date: Mon, 30 Oct 2023 13:25:39 +0000 Subject: [PATCH 194/203] Check that scripts produce correct json in render template action (#101518) If a mustache script that outputs badly-formed json is referred to in a render template request, then the error returned will be a 500 server error, rather than a 400 json parsing error. This is because rendering templates skips json parsing, and so the error ends up being caught in the REST layer instead. This commit changes the template rendering logic to always parse the output of the script, catching json errors higher in the stack and allowing us to return the correct status code. This also means that errors are correctly detected and returned as part of multi search template requests. Fixes #101477 --- docs/changelog/101518.yaml | 6 ++ .../search-your-data/search-template.asciidoc | 8 +-- .../mustache/MultiSearchTemplateIT.java | 8 +-- .../script/mustache/SearchTemplateIT.java | 65 +++++++++++++++++++ .../TransportSearchTemplateAction.java | 18 ++--- 5 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 docs/changelog/101518.yaml diff --git a/docs/changelog/101518.yaml b/docs/changelog/101518.yaml new file mode 100644 index 0000000000000..53db542640348 --- /dev/null +++ b/docs/changelog/101518.yaml @@ -0,0 +1,6 @@ +pr: 101518 +summary: Check that scripts produce correct json in render template action +area: Search +type: bug +issues: + - 101477 diff --git a/docs/reference/search/search-your-data/search-template.asciidoc b/docs/reference/search/search-your-data/search-template.asciidoc index 9fb6ee85ffcca..7a7f09f4a37a7 100644 --- a/docs/reference/search/search-your-data/search-template.asciidoc +++ b/docs/reference/search/search-your-data/search-template.asciidoc @@ -795,7 +795,7 @@ PUT _scripts/my-search-template "lang": "mustache", "source": { "query":{ - "multi-match":{ + "multi_match":{ "query": "{{query_string}}", "fields": """[{{#text_fields}}{{user_name}},{{/text_fields}}]""" } @@ -834,7 +834,7 @@ When rendered, template outputs: { "template_output": { "query": { - "multi-match": { + "multi_match": { "query": "My string", "fields": "[John,kimchy,]" } @@ -855,7 +855,7 @@ PUT _scripts/my-search-template "lang": "mustache", "source": { "query":{ - "multi-match":{ + "multi_match":{ "query": "{{query_string}}", "fields": """[{{#text_fields}}{{user_name}}{{^last}},{{/last}}{{/text_fields}}]""" } @@ -895,7 +895,7 @@ When rendered the template outputs: { "template_output": { "query": { - "multi-match": { + "multi_match": { "query": "My string", "fields": "[John,kimchy]" } diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java index b2a8de93a2c32..000728209456f 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/MultiSearchTemplateIT.java @@ -21,6 +21,7 @@ import org.elasticsearch.search.FailBeforeCurrentVersionQueryBuilder; import org.elasticsearch.search.SearchService; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.xcontent.XContentParseException; import org.elasticsearch.xcontent.json.JsonXContent; import java.util.Arrays; @@ -175,10 +176,9 @@ public void testBasic() throws Exception { assertThat(response4.getFailure().getMessage(), equalTo("no such index [unknown]")); MultiSearchTemplateResponse.Item response5 = response.getResponses()[4]; - assertThat(response5.isFailure(), is(false)); - SearchTemplateResponse searchTemplateResponse5 = response5.getResponse(); - assertThat(searchTemplateResponse5.hasResponse(), is(false)); - assertThat(searchTemplateResponse5.getSource().utf8ToString(), equalTo("{\"query\":{\"terms\":{\"group\":[1,2,3,]}}}")); + assertThat(response5.isFailure(), is(true)); + assertNull(response5.getResponse()); + assertThat(response5.getFailure(), instanceOf(XContentParseException.class)); } /** diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index 18365abc820d0..ed0da415693d0 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -11,9 +11,11 @@ import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.DummyQueryParserPlugin; import org.elasticsearch.search.SearchService; @@ -189,6 +191,69 @@ public void testIndexedTemplateClient() throws Exception { assertNull(getResponse.getSource()); } + public void testBadTemplate() { + + // This template will produce badly formed json if given a multi-valued `text_fields` parameter, + // as it does not add commas between the entries. We test that it produces a 400 json parsing + // error both when used directly and when used in a render template request. + + String script = """ + { + "script": { + "lang": "mustache", + "source": ""\" + { + "query": { + "multi_match": { + "query": "{{query_string}}", + "fields": [{{#text_fields}}"{{name}}^{{boost}}"{{/text_fields}}] + } + }, + "from": "{{from}}", + "size": "{{size}}" + } + ""\", + "params": { + "query_string": "My query string" + } + } + }"""; + + Map params = Map.of( + "text_fields", + List.of(Map.of("name", "title", "boost", 10), Map.of("name", "description", "boost", 2)), + "from", + 0, + "size", + 0 + ); + + { + ParsingException e = expectThrows(ParsingException.class, () -> { + new SearchTemplateRequestBuilder(client()).setRequest(new SearchRequest()) + .setScript(script) + .setScriptParams(params) + .setScriptType(ScriptType.INLINE) + .get(); + }); + assertThat(e.getMessage(), containsString("Unknown key")); + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + + { + ParsingException e = expectThrows(ParsingException.class, () -> { + new SearchTemplateRequestBuilder(client()).setRequest(new SearchRequest()) + .setScript(script) + .setScriptParams(params) + .setScriptType(ScriptType.INLINE) + .setSimulate(true) + .get(); + }); + assertThat(e.getMessage(), containsString("Unknown key")); + assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + } + } + public void testIndexedTemplate() throws Exception { String script = """ diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java index ef19579c87625..2b315f48dcce4 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/TransportSearchTemplateAction.java @@ -104,20 +104,22 @@ static SearchRequest convert( response.setSource(new BytesArray(source)); SearchRequest searchRequest = searchTemplateRequest.getRequest(); - if (searchTemplateRequest.isSimulate()) { - return null; - } + + SearchSourceBuilder builder = SearchSourceBuilder.searchSource(); XContentParserConfiguration parserConfig = XContentParserConfiguration.EMPTY.withRegistry(xContentRegistry) .withDeprecationHandler(LoggingDeprecationHandler.INSTANCE); try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(parserConfig, source)) { - SearchSourceBuilder builder = SearchSourceBuilder.searchSource(); builder.parseXContent(parser, false, searchUsageHolder); - builder.explain(searchTemplateRequest.isExplain()); - builder.profile(searchTemplateRequest.isProfile()); - checkRestTotalHitsAsInt(searchRequest, builder); - searchRequest.source(builder); } + + if (searchTemplateRequest.isSimulate()) { + return null; + } + builder.explain(searchTemplateRequest.isExplain()); + builder.profile(searchTemplateRequest.isProfile()); + checkRestTotalHitsAsInt(searchRequest, builder); + searchRequest.source(builder); return searchRequest; } From 4c1fd8864f3f52a2e8239f9e9225c5c3c538f172 Mon Sep 17 00:00:00 2001 From: Max Hniebergall Date: Mon, 30 Oct 2023 10:06:04 -0400 Subject: [PATCH 195/203] Muted esql scaler conditional tests for test triage --- .../expression/function/scalar/conditional/GreatestTests.java | 3 ++- .../expression/function/scalar/conditional/LeastTests.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java index ca8e74757a6f1..2d6898d251f28 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java @@ -10,6 +10,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.util.BytesRef; import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; @@ -25,7 +26,7 @@ import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; - +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101527") public class GreatestTests extends AbstractFunctionTestCase { public GreatestTests(@Name("TestCase") Supplier testCaseSupplier) { this.testCase = testCaseSupplier.get(); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java index c3ea444e068b0..49c7e23901c42 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java @@ -10,6 +10,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.util.BytesRef; import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; @@ -24,6 +25,7 @@ import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101527") public class LeastTests extends AbstractFunctionTestCase { public LeastTests(@Name("TestCase") Supplier testCaseSupplier) { From dc75cab98921688b8d92ea33922a1656be0cf4cf Mon Sep 17 00:00:00 2001 From: Max Hniebergall Date: Mon, 30 Oct 2023 10:17:01 -0400 Subject: [PATCH 196/203] Revert "Muted esql scaler conditional tests for test triage" This reverts commit 4c1fd8864f3f52a2e8239f9e9225c5c3c538f172. --- .../expression/function/scalar/conditional/GreatestTests.java | 3 +-- .../expression/function/scalar/conditional/LeastTests.java | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java index 2d6898d251f28..ca8e74757a6f1 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/GreatestTests.java @@ -10,7 +10,6 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.util.BytesRef; import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; @@ -26,7 +25,7 @@ import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; -@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101527") + public class GreatestTests extends AbstractFunctionTestCase { public GreatestTests(@Name("TestCase") Supplier testCaseSupplier) { this.testCase = testCaseSupplier.get(); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java index 49c7e23901c42..c3ea444e068b0 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/LeastTests.java @@ -10,7 +10,6 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.util.BytesRef; import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; @@ -25,7 +24,6 @@ import java.util.stream.LongStream; import static org.hamcrest.Matchers.equalTo; -@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/101527") public class LeastTests extends AbstractFunctionTestCase { public LeastTests(@Name("TestCase") Supplier testCaseSupplier) { From d5c9c7ca21e8f8cac69aabc50092be7abccacec2 Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Mon, 30 Oct 2023 15:19:38 +0100 Subject: [PATCH 197/203] Make ObjectMapper Immutable (#101485) It's hard to reason about the thread safety of these objects (and their behavior in general) when they are mutable. This removes the cloning and mutating of these objects and replaces it with just copy constructors for merging. Also removes the unused cloneable on field mappers. --- .../index/mapper/DocumentParser.java | 5 + .../index/mapper/FieldMapper.java | 2 +- .../index/mapper/NestedObjectMapper.java | 78 +++++--- .../index/mapper/ObjectMapper.java | 173 +++++++++++------- .../index/mapper/RootObjectMapper.java | 66 ++++--- 5 files changed, 204 insertions(+), 120 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index a7a565d821443..996c6243064e9 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -824,6 +824,11 @@ private static class NoOpObjectMapper extends ObjectMapper { NoOpObjectMapper(String name, String fullPath) { super(name, fullPath, Explicit.IMPLICIT_TRUE, Explicit.IMPLICIT_TRUE, Dynamic.RUNTIME, Collections.emptyMap()); } + + @Override + public ObjectMapper merge(Mapper mergeWith, MapperBuilderContext mapperBuilderContext) { + return this; + } } /** diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java index 32c87a856b6d0..974d935a35e50 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldMapper.java @@ -54,7 +54,7 @@ import static org.elasticsearch.core.Strings.format; -public abstract class FieldMapper extends Mapper implements Cloneable { +public abstract class FieldMapper extends Mapper { private static final Logger logger = LogManager.getLogger(FieldMapper.class); public static final Setting IGNORE_MALFORMED_SETTING = Setting.boolSetting( diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java index de2466dc327c4..257b2270176bc 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java @@ -63,7 +63,24 @@ public NestedObjectMapper build(MapperBuilderContext context) { } } NestedMapperBuilderContext nestedContext = new NestedMapperBuilderContext(context.buildFullName(name), parentIncludedInRoot); - return new NestedObjectMapper(name, context.buildFullName(name), buildMappers(nestedContext), this); + final String fullPath = context.buildFullName(name); + final String nestedTypePath; + if (indexCreatedVersion.before(IndexVersions.V_8_0_0)) { + nestedTypePath = "__" + fullPath; + } else { + nestedTypePath = fullPath; + } + return new NestedObjectMapper( + name, + fullPath, + buildMappers(nestedContext), + enabled, + dynamic, + includeInParent, + includeInRoot, + nestedTypePath, + NestedPathFieldMapper.filter(indexCreatedVersion, nestedTypePath) + ); } } @@ -111,21 +128,27 @@ public MapperBuilderContext createChildContext(String name) { } } - private Explicit includeInRoot; - private Explicit includeInParent; + private final Explicit includeInRoot; + private final Explicit includeInParent; private final String nestedTypePath; private final Query nestedTypeFilter; - NestedObjectMapper(String name, String fullPath, Map mappers, Builder builder) { - super(name, fullPath, builder.enabled, Explicit.IMPLICIT_TRUE, builder.dynamic, mappers); - if (builder.indexCreatedVersion.before(IndexVersions.V_8_0_0)) { - this.nestedTypePath = "__" + fullPath; - } else { - this.nestedTypePath = fullPath; - } - this.nestedTypeFilter = NestedPathFieldMapper.filter(builder.indexCreatedVersion, nestedTypePath); - this.includeInParent = builder.includeInParent; - this.includeInRoot = builder.includeInRoot; + NestedObjectMapper( + String name, + String fullPath, + Map mappers, + Explicit enabled, + ObjectMapper.Dynamic dynamic, + Explicit includeInParent, + Explicit includeInRoot, + String nestedTypePath, + Query nestedTypeFilter + ) { + super(name, fullPath, enabled, Explicit.IMPLICIT_TRUE, dynamic, mappers); + this.nestedTypePath = nestedTypePath; + this.nestedTypeFilter = nestedTypeFilter; + this.includeInParent = includeInParent; + this.includeInRoot = includeInRoot; } public Query nestedTypeFilter() { @@ -189,13 +212,15 @@ public ObjectMapper merge(Mapper mergeWith, MapperService.MergeReason reason, Ma MapperErrors.throwNestedMappingConflictError(mergeWith.name()); } NestedObjectMapper mergeWithObject = (NestedObjectMapper) mergeWith; - NestedObjectMapper toMerge = (NestedObjectMapper) clone(); + var mergeResult = MergeResult.build(this, mergeWith, reason, parentBuilderContext); + Explicit incInParent = this.includeInParent; + Explicit incInRoot = this.includeInRoot; if (reason == MapperService.MergeReason.INDEX_TEMPLATE) { if (mergeWithObject.includeInParent.explicit()) { - toMerge.includeInParent = mergeWithObject.includeInParent; + incInParent = mergeWithObject.includeInParent; } if (mergeWithObject.includeInRoot.explicit()) { - toMerge.includeInRoot = mergeWithObject.includeInRoot; + incInRoot = mergeWithObject.includeInRoot; } } else { if (includeInParent.value() != mergeWithObject.includeInParent.value()) { @@ -206,16 +231,25 @@ public ObjectMapper merge(Mapper mergeWith, MapperService.MergeReason reason, Ma } } if (parentBuilderContext instanceof NestedMapperBuilderContext nc) { - if (nc.parentIncludedInRoot && toMerge.includeInParent.value()) { - toMerge.includeInRoot = Explicit.IMPLICIT_FALSE; + if (nc.parentIncludedInRoot && incInParent.value()) { + incInRoot = Explicit.IMPLICIT_FALSE; } } else { - if (toMerge.includeInParent.value()) { - toMerge.includeInRoot = Explicit.IMPLICIT_FALSE; + if (incInParent.value()) { + incInRoot = Explicit.IMPLICIT_FALSE; } } - toMerge.doMerge(mergeWithObject, reason, parentBuilderContext); - return toMerge; + return new NestedObjectMapper( + simpleName(), + fullPath(), + mergeResult.mappers(), + mergeResult.enabled(), + mergeResult.dynamic(), + incInParent, + incInRoot, + nestedTypePath, + nestedTypeFilter + ); } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java index d50b8b0dc088b..d67763879433f 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java @@ -34,7 +34,7 @@ import java.util.Set; import java.util.stream.Stream; -public class ObjectMapper extends Mapper implements Cloneable { +public class ObjectMapper extends Mapper { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(ObjectMapper.class); public static final String CONTENT_TYPE = "object"; @@ -370,11 +370,11 @@ private static void validateFieldName(String fieldName, IndexVersion indexCreate private final String fullPath; - protected Explicit enabled; - protected Explicit subobjects; - protected volatile Dynamic dynamic; + protected final Explicit enabled; + protected final Explicit subobjects; + protected final Dynamic dynamic; - protected Map mappers; + protected final Map mappers; ObjectMapper( String name, @@ -398,18 +398,6 @@ private static void validateFieldName(String fieldName, IndexVersion indexCreate } } - @Override - protected ObjectMapper clone() { - ObjectMapper clone; - try { - clone = (ObjectMapper) super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - clone.mappers = Map.copyOf(clone.mappers); - return clone; - } - /** * @return a Builder that will produce an empty ObjectMapper with the same configuration as this one */ @@ -476,73 +464,116 @@ protected MapperBuilderContext createChildContext(MapperBuilderContext mapperBui } public ObjectMapper merge(Mapper mergeWith, MergeReason reason, MapperBuilderContext parentBuilderContext) { - if ((mergeWith instanceof ObjectMapper) == false) { - MapperErrors.throwObjectMappingConflictError(mergeWith.name()); - } - if (mergeWith instanceof NestedObjectMapper) { - // TODO stop NestedObjectMapper extending ObjectMapper? - MapperErrors.throwNestedMappingConflictError(mergeWith.name()); - } - ObjectMapper mergeWithObject = (ObjectMapper) mergeWith; - ObjectMapper merged = clone(); - merged.doMerge(mergeWithObject, reason, parentBuilderContext); - return merged; + var mergeResult = MergeResult.build(this, mergeWith, reason, parentBuilderContext); + return new ObjectMapper( + simpleName(), + fullPath, + mergeResult.enabled, + mergeResult.subObjects, + mergeResult.dynamic, + mergeResult.mappers + ); } - protected void doMerge(final ObjectMapper mergeWith, MergeReason reason, MapperBuilderContext parentBuilderContext) { - if (mergeWith.dynamic != null) { - this.dynamic = mergeWith.dynamic; - } + protected record MergeResult( + Explicit enabled, + Explicit subObjects, + ObjectMapper.Dynamic dynamic, + Map mappers + ) { - if (mergeWith.enabled.explicit()) { - if (reason == MergeReason.INDEX_TEMPLATE) { - this.enabled = mergeWith.enabled; - } else if (isEnabled() != mergeWith.isEnabled()) { - throw new MapperException("the [enabled] parameter can't be updated for the object mapping [" + name() + "]"); + public static MergeResult build( + ObjectMapper existing, + Mapper mergeWith, + MergeReason reason, + MapperBuilderContext parentBuilderContext + ) { + if ((mergeWith instanceof ObjectMapper) == false) { + MapperErrors.throwObjectMappingConflictError(mergeWith.name()); } - } - - if (mergeWith.subobjects.explicit()) { - if (reason == MergeReason.INDEX_TEMPLATE) { - this.subobjects = mergeWith.subobjects; - } else if (subobjects != mergeWith.subobjects) { - throw new MapperException("the [subobjects] parameter can't be updated for the object mapping [" + name() + "]"); + if (existing instanceof NestedObjectMapper == false && mergeWith instanceof NestedObjectMapper) { + // TODO stop NestedObjectMapper extending ObjectMapper? + MapperErrors.throwNestedMappingConflictError(mergeWith.name()); } - } - - MapperBuilderContext objectBuilderContext = createChildContext(parentBuilderContext, simpleName()); - Map mergedMappers = null; - for (Mapper mergeWithMapper : mergeWith) { - Mapper mergeIntoMapper = (mergedMappers == null ? mappers : mergedMappers).get(mergeWithMapper.simpleName()); - - Mapper merged; - if (mergeIntoMapper == null) { - merged = mergeWithMapper; - } else if (mergeIntoMapper instanceof ObjectMapper objectMapper) { - merged = objectMapper.merge(mergeWithMapper, reason, objectBuilderContext); + ObjectMapper mergeWithObject = (ObjectMapper) mergeWith; + final Explicit enabled; + if (mergeWithObject.enabled.explicit()) { + if (reason == MergeReason.INDEX_TEMPLATE) { + enabled = mergeWithObject.enabled; + } else if (existing.isEnabled() != mergeWithObject.isEnabled()) { + throw new MapperException("the [enabled] parameter can't be updated for the object mapping [" + existing.name() + "]"); + } else { + enabled = existing.enabled; + } } else { - assert mergeIntoMapper instanceof FieldMapper || mergeIntoMapper instanceof FieldAliasMapper; - if (mergeWithMapper instanceof NestedObjectMapper) { - MapperErrors.throwNestedMappingConflictError(mergeWithMapper.name()); - } else if (mergeWithMapper instanceof ObjectMapper) { - MapperErrors.throwObjectMappingConflictError(mergeWithMapper.name()); + enabled = existing.enabled; + } + final Explicit subObjects; + if (mergeWithObject.subobjects.explicit()) { + if (reason == MergeReason.INDEX_TEMPLATE) { + subObjects = mergeWithObject.subobjects; + } else if (existing.subobjects != mergeWithObject.subobjects) { + throw new MapperException( + "the [subobjects] parameter can't be updated for the object mapping [" + existing.name() + "]" + ); + } else { + subObjects = existing.subobjects; } + } else { + subObjects = existing.subobjects; + } + MapperBuilderContext objectBuilderContext = existing.createChildContext(parentBuilderContext, existing.simpleName()); + Map mergedMappers = buildMergedMappers(existing, mergeWith, reason, objectBuilderContext); + return new MergeResult( + enabled, + subObjects, + mergeWithObject.dynamic != null ? mergeWithObject.dynamic : existing.dynamic, + mergedMappers + ); + } - // If we're merging template mappings when creating an index, then a field definition always - // replaces an existing one. - if (reason == MergeReason.INDEX_TEMPLATE) { + private static Map buildMergedMappers( + ObjectMapper existing, + Mapper mergeWith, + MergeReason reason, + MapperBuilderContext objectBuilderContext + ) { + Map mergedMappers = null; + for (Mapper mergeWithMapper : mergeWith) { + Mapper mergeIntoMapper = (mergedMappers == null ? existing.mappers : mergedMappers).get(mergeWithMapper.simpleName()); + + Mapper merged; + if (mergeIntoMapper == null) { merged = mergeWithMapper; + } else if (mergeIntoMapper instanceof ObjectMapper objectMapper) { + merged = objectMapper.merge(mergeWithMapper, reason, objectBuilderContext); } else { - merged = mergeIntoMapper.merge(mergeWithMapper, objectBuilderContext); + assert mergeIntoMapper instanceof FieldMapper || mergeIntoMapper instanceof FieldAliasMapper; + if (mergeWithMapper instanceof NestedObjectMapper) { + MapperErrors.throwNestedMappingConflictError(mergeWithMapper.name()); + } else if (mergeWithMapper instanceof ObjectMapper) { + MapperErrors.throwObjectMappingConflictError(mergeWithMapper.name()); + } + + // If we're merging template mappings when creating an index, then a field definition always + // replaces an existing one. + if (reason == MergeReason.INDEX_TEMPLATE) { + merged = mergeWithMapper; + } else { + merged = mergeIntoMapper.merge(mergeWithMapper, objectBuilderContext); + } + } + if (mergedMappers == null) { + mergedMappers = new HashMap<>(existing.mappers); } + mergedMappers.put(merged.simpleName(), merged); } - if (mergedMappers == null) { - mergedMappers = new HashMap<>(mappers); + if (mergedMappers != null) { + mergedMappers = Map.copyOf(mergedMappers); + } else { + mergedMappers = Map.copyOf(existing.mappers); } - mergedMappers.put(merged.simpleName(), merged); - } - if (mergedMappers != null) { - mappers = Map.copyOf(mergedMappers); + return mergedMappers; } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java index b5f9681655481..65fce1b69b8cc 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java @@ -112,7 +112,7 @@ public RootObjectMapper build(MapperBuilderContext context) { subobjects, dynamic, buildMappers(context), - runtimeFields, + new HashMap<>(runtimeFields), dynamicDateTimeFormatters, dynamicTemplates, dateDetection, @@ -121,11 +121,11 @@ public RootObjectMapper build(MapperBuilderContext context) { } } - private Explicit dynamicDateTimeFormatters; - private Explicit dateDetection; - private Explicit numericDetection; - private Explicit dynamicTemplates; - private Map runtimeFields; + private final Explicit dynamicDateTimeFormatters; + private final Explicit dateDetection; + private final Explicit numericDetection; + private final Explicit dynamicTemplates; + private final Map runtimeFields; RootObjectMapper( String name, @@ -147,13 +147,6 @@ public RootObjectMapper build(MapperBuilderContext context) { this.numericDetection = numericDetection; } - @Override - protected ObjectMapper clone() { - ObjectMapper clone = super.clone(); - ((RootObjectMapper) clone).runtimeFields = new HashMap<>(this.runtimeFields); - return clone; - } - @Override public RootObjectMapper.Builder newBuilder(IndexVersion indexVersionCreated) { RootObjectMapper.Builder builder = new RootObjectMapper.Builder(name(), subobjects); @@ -206,25 +199,30 @@ protected MapperBuilderContext createChildContext(MapperBuilderContext mapperBui @Override public RootObjectMapper merge(Mapper mergeWith, MergeReason reason, MapperBuilderContext parentBuilderContext) { - return (RootObjectMapper) super.merge(mergeWith, reason, parentBuilderContext); - } - - @Override - protected void doMerge(ObjectMapper mergeWith, MergeReason reason, MapperBuilderContext parentBuilderContext) { - super.doMerge(mergeWith, reason, parentBuilderContext); + final var mergeResult = MergeResult.build(this, mergeWith, reason, parentBuilderContext); + final Explicit numericDetection; RootObjectMapper mergeWithObject = (RootObjectMapper) mergeWith; if (mergeWithObject.numericDetection.explicit()) { - this.numericDetection = mergeWithObject.numericDetection; + numericDetection = mergeWithObject.numericDetection; + } else { + numericDetection = this.numericDetection; } + final Explicit dateDetection; if (mergeWithObject.dateDetection.explicit()) { - this.dateDetection = mergeWithObject.dateDetection; + dateDetection = mergeWithObject.dateDetection; + } else { + dateDetection = this.dateDetection; } + final Explicit dynamicDateTimeFormatters; if (mergeWithObject.dynamicDateTimeFormatters.explicit()) { - this.dynamicDateTimeFormatters = mergeWithObject.dynamicDateTimeFormatters; + dynamicDateTimeFormatters = mergeWithObject.dynamicDateTimeFormatters; + } else { + dynamicDateTimeFormatters = this.dynamicDateTimeFormatters; } + final Explicit dynamicTemplates; if (mergeWithObject.dynamicTemplates.explicit()) { if (reason == MergeReason.INDEX_TEMPLATE) { Map templatesByKey = new LinkedHashMap<>(); @@ -236,19 +234,35 @@ protected void doMerge(ObjectMapper mergeWith, MergeReason reason, MapperBuilder } DynamicTemplate[] mergedTemplates = templatesByKey.values().toArray(new DynamicTemplate[0]); - this.dynamicTemplates = new Explicit<>(mergedTemplates, true); + dynamicTemplates = new Explicit<>(mergedTemplates, true); } else { - this.dynamicTemplates = mergeWithObject.dynamicTemplates; + dynamicTemplates = mergeWithObject.dynamicTemplates; } + } else { + dynamicTemplates = this.dynamicTemplates; } + final Map runtimeFields = new HashMap<>(this.runtimeFields); assert this.runtimeFields != mergeWithObject.runtimeFields; for (Map.Entry runtimeField : mergeWithObject.runtimeFields.entrySet()) { if (runtimeField.getValue() == null) { - this.runtimeFields.remove(runtimeField.getKey()); + runtimeFields.remove(runtimeField.getKey()); } else { - this.runtimeFields.put(runtimeField.getKey(), runtimeField.getValue()); + runtimeFields.put(runtimeField.getKey(), runtimeField.getValue()); } } + + return new RootObjectMapper( + simpleName(), + mergeResult.enabled(), + mergeResult.subObjects(), + mergeResult.dynamic(), + mergeResult.mappers(), + Map.copyOf(runtimeFields), + dynamicDateTimeFormatters, + dynamicTemplates, + dateDetection, + numericDetection + ); } @Override From bfad5e5b130f37b555168981bd3ca822dc9c5344 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 30 Oct 2023 14:38:30 +0000 Subject: [PATCH 198/203] Create new feature API for querying features present on a cluster (#100974) This adds an internal API and service to manage & get information on features that are present on nodes in a cluster. New features can be declared as supported, and historical features can be added to previous node versions to eventually replace node version comparisons --- .../forbidden/es-server-signatures.txt | 5 + docs/changelog/100974.yaml | 5 + .../upgrades/ClusterFeatureMigrationIT.java | 60 ++++ .../features/ClusterFeaturesIT.java | 48 ++++ server/src/main/java/module-info.java | 5 + .../org/elasticsearch/TransportVersions.java | 1 + .../state/TransportClusterStateAction.java | 7 + .../cluster/ClusterFeatures.java | 266 ++++++++++++++++++ .../elasticsearch/cluster/ClusterState.java | 78 ++++- .../cluster/coordination/Coordinator.java | 13 +- .../cluster/coordination/JoinHelper.java | 43 ++- .../cluster/coordination/JoinRequest.java | 25 +- .../cluster/coordination/JoinTask.java | 7 +- .../coordination/NodeJoinExecutor.java | 35 ++- .../coordination/NodeLeftExecutor.java | 31 +- .../discovery/DiscoveryModule.java | 6 +- .../features/FeatureService.java | 123 ++++++++ .../features/FeatureSpecification.java | 33 +++ .../FeaturesSupportedSpecification.java | 24 ++ .../elasticsearch/features/NodeFeature.java | 23 ++ .../elasticsearch/node/NodeConstruction.java | 10 +- .../org/elasticsearch/plugins/Plugin.java | 6 + ...lasticsearch.features.FeatureSpecification | 9 + .../reroute/ClusterRerouteResponseTests.java | 6 + .../cluster/ClusterStateTests.java | 73 ++++- .../cluster/coordination/JoinHelperTests.java | 11 +- .../cluster/coordination/MessagesTests.java | 16 +- .../coordination/NodeJoinExecutorTests.java | 107 ++++++- .../cluster/coordination/NodeJoinTests.java | 32 ++- .../coordination/NodeLeftExecutorTests.java | 13 +- .../discovery/DiscoveryModuleTests.java | 4 +- .../features/FeatureServiceTests.java | 166 +++++++++++ .../indices/ShardLimitValidatorTests.java | 33 +-- .../indices/cluster/ClusterStateChanges.java | 15 +- .../snapshots/SnapshotResiliencyTests.java | 3 +- .../AbstractCoordinatorTestCase.java | 3 +- ...actClusterStateLicenseServiceTestCase.java | 4 +- .../license/LicenseClusterChangeTests.java | 2 +- .../ml/utils/TransportVersionUtilsTests.java | 2 + .../ClusterStatsMonitoringDocTests.java | 8 +- 40 files changed, 1249 insertions(+), 112 deletions(-) create mode 100644 docs/changelog/100974.yaml create mode 100644 qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ClusterFeatureMigrationIT.java create mode 100644 server/src/internalClusterTest/java/org/elasticsearch/features/ClusterFeaturesIT.java create mode 100644 server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java create mode 100644 server/src/main/java/org/elasticsearch/features/FeatureService.java create mode 100644 server/src/main/java/org/elasticsearch/features/FeatureSpecification.java create mode 100644 server/src/main/java/org/elasticsearch/features/FeaturesSupportedSpecification.java create mode 100644 server/src/main/java/org/elasticsearch/features/NodeFeature.java create mode 100644 server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification create mode 100644 server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java diff --git a/build-tools-internal/src/main/resources/forbidden/es-server-signatures.txt b/build-tools-internal/src/main/resources/forbidden/es-server-signatures.txt index 791823b722a36..d19500c3c332d 100644 --- a/build-tools-internal/src/main/resources/forbidden/es-server-signatures.txt +++ b/build-tools-internal/src/main/resources/forbidden/es-server-signatures.txt @@ -155,3 +155,8 @@ org.elasticsearch.cluster.ClusterStateTaskExecutor$TaskContext#success(java.util @defaultMessage ClusterState#compatibilityVersions are for internal use only. Use ClusterState#getMinVersions or a different version. See TransportVersion javadocs for more info. org.elasticsearch.cluster.ClusterState#compatibilityVersions() + +@defaultMessage ClusterFeatures#nodeFeatures is for internal use only. Use FeatureService#clusterHasFeature to determine if a feature is present on the cluster. +org.elasticsearch.cluster.ClusterFeatures#nodeFeatures() +@defaultMessage ClusterFeatures#clusterHasFeature is for internal use only. Use FeatureService#clusterHasFeature to determine if a feature is present on the cluster. +org.elasticsearch.cluster.ClusterFeatures#clusterHasFeature(org.elasticsearch.features.NodeFeature) diff --git a/docs/changelog/100974.yaml b/docs/changelog/100974.yaml new file mode 100644 index 0000000000000..e5d3a4ad3c9df --- /dev/null +++ b/docs/changelog/100974.yaml @@ -0,0 +1,5 @@ +pr: 100974 +summary: Create new cluster state API for querying features present on a cluster +area: "Infra/Core" +type: feature +issues: [] diff --git a/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ClusterFeatureMigrationIT.java b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ClusterFeatureMigrationIT.java new file mode 100644 index 0000000000000..2d8ff8b747323 --- /dev/null +++ b/qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/ClusterFeatureMigrationIT.java @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.upgrades; + +import com.carrotsearch.randomizedtesting.annotations.Name; + +import org.elasticsearch.client.Request; +import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.features.FeatureService; +import org.junit.BeforeClass; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; + +public class ClusterFeatureMigrationIT extends ParameterizedRollingUpgradeTestCase { + + @BeforeClass + public static void checkMigrationVersion() { + assumeTrue( + "This checks migrations from before cluster features were introduced", + getOldClusterVersion().before(FeatureService.CLUSTER_FEATURES_ADDED_VERSION) + ); + } + + public ClusterFeatureMigrationIT(@Name("upgradedNodes") int upgradedNodes) { + super(upgradedNodes); + } + + public void testClusterFeatureMigration() throws IOException { + if (isUpgradedCluster()) { + // check the nodes all have a feature in their cluster state (there should always be features_supported) + var response = entityAsMap(adminClient().performRequest(new Request("GET", "/_cluster/state/nodes"))); + List nodeFeatures = (List) XContentMapValues.extractValue("nodes_features", response); + assertThat(nodeFeatures, hasSize(adminClient().getNodes().size())); + + Map> features = nodeFeatures.stream() + .map(o -> (Map) o) + .collect(Collectors.toMap(m -> (String) m.get("node_id"), m -> (List) m.get("features"))); + + Set missing = features.entrySet() + .stream() + .filter(e -> e.getValue().contains(FeatureService.FEATURES_SUPPORTED.id()) == false) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + assertThat(missing + " out of " + features.keySet() + " does not have the required feature", missing, empty()); + } + } +} diff --git a/server/src/internalClusterTest/java/org/elasticsearch/features/ClusterFeaturesIT.java b/server/src/internalClusterTest/java/org/elasticsearch/features/ClusterFeaturesIT.java new file mode 100644 index 0000000000000..fe447eca6e8fd --- /dev/null +++ b/server/src/internalClusterTest/java/org/elasticsearch/features/ClusterFeaturesIT.java @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.features; + +import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; +import org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.test.ESIntegTestCase; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasItem; + +public class ClusterFeaturesIT extends ESIntegTestCase { + + @SuppressForbidden(reason = "Directly checking node features in cluster state") + public void testClusterHasFeatures() { + internalCluster().startNodes(2); + ensureGreen(); + + FeatureService service = internalCluster().getCurrentMasterNodeInstance(FeatureService.class); + + assertThat(service.getNodeFeatures(), hasItem(FeatureService.FEATURES_SUPPORTED.id())); + + // check the nodes all have a feature in their cluster state (there should always be features_supported) + var response = clusterAdmin().state(new ClusterStateRequest().clear().nodes(true)).actionGet(); + var features = response.getState().clusterFeatures().nodeFeatures(); + Set missing = features.entrySet() + .stream() + .filter(e -> e.getValue().contains(FeatureService.FEATURES_SUPPORTED.id()) == false) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + assertThat(missing + " out of " + features.keySet() + " does not have the required feature", missing, empty()); + + // check that all nodes have the same features + var featureList = List.copyOf(response.getState().clusterFeatures().nodeFeatures().values()); + assertEquals("Nodes do not have the same features", featureList.get(0), featureList.get(1)); + } +} diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 1a082e7558577..3399be18077a3 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -224,6 +224,7 @@ exports org.elasticsearch.common.xcontent.support; exports org.elasticsearch.discovery; exports org.elasticsearch.env; + exports org.elasticsearch.features; exports org.elasticsearch.gateway; exports org.elasticsearch.health; exports org.elasticsearch.health.node; @@ -400,6 +401,10 @@ uses org.elasticsearch.node.internal.TerminationHandlerProvider; uses org.elasticsearch.internal.VersionExtension; uses org.elasticsearch.internal.BuildExtension; + uses org.elasticsearch.features.FeatureSpecification; + + provides org.elasticsearch.features.FeatureSpecification with org.elasticsearch.features.FeaturesSupportedSpecification; + uses org.elasticsearch.plugins.internal.SettingsExtension; uses RestExtension; uses org.elasticsearch.action.admin.cluster.node.info.ComponentVersionNumber; diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index ac3d693df52f6..f265ebb240dcf 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -148,6 +148,7 @@ static TransportVersion def(int id) { public static final TransportVersion TRANSFORM_GET_CHECKPOINT_TIMEOUT_ADDED = def(8_523_00_0); public static final TransportVersion IP_ADDRESS_WRITEABLE = def(8_524_00_0); public static final TransportVersion PRIMARY_TERM_ADDED = def(8_525_00_0); + public static final TransportVersion CLUSTER_FEATURES_ADDED = def(8_526_00_0); /* * STOP! READ THIS FIRST! No, really, diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java index 5af2c12d39bc1..f4e301e0748bb 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java @@ -38,6 +38,7 @@ import java.io.IOException; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; public class TransportClusterStateAction extends TransportMasterNodeReadAction { @@ -143,6 +144,11 @@ private static Map getCompatibilityVersions(Clust return clusterState.compatibilityVersions(); } + @SuppressForbidden(reason = "exposing ClusterState#clusterFeatures requires reading them") + private static Map> getClusterFeatures(ClusterState clusterState) { + return clusterState.clusterFeatures().nodeFeatures(); + } + private ClusterStateResponse buildResponse(final ClusterStateRequest request, final ClusterState currentState) { logger.trace("Serving cluster state request using version {}", currentState.version()); ClusterState.Builder builder = ClusterState.builder(currentState.getClusterName()); @@ -152,6 +158,7 @@ private ClusterStateResponse buildResponse(final ClusterStateRequest request, fi if (request.nodes()) { builder.nodes(currentState.nodes()); builder.nodeIdsToCompatibilityVersions(getCompatibilityVersions(currentState)); + builder.nodeFeatures(getClusterFeatures(currentState)); } if (request.routingTable()) { if (request.indices().length > 0) { diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java b/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java new file mode 100644 index 0000000000000..ae68bfafdd6c5 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java @@ -0,0 +1,266 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.cluster; + +import org.elasticsearch.common.collect.Iterators; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ChunkedToXContentObject; +import org.elasticsearch.features.NodeFeature; +import org.elasticsearch.xcontent.ToXContent; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + +/** + * Stores information on what features are present throughout the cluster + */ +public class ClusterFeatures implements Diffable, ChunkedToXContentObject { + + /** + * The features on each individual node + */ + private final Map> nodeFeatures; + /** + * The features present on all nodes + */ + private Set allNodeFeatures; + + public ClusterFeatures(Map> nodeFeatures) { + this.nodeFeatures = nodeFeatures.entrySet() + .stream() + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> Set.copyOf(e.getValue()))); + } + + private Set calculateAllNodeFeatures() { + if (nodeFeatures.isEmpty()) { + return Set.of(); + } + + Set allNodeFeatures = null; + for (Set featureSet : nodeFeatures.values()) { + if (allNodeFeatures == null) { + allNodeFeatures = new HashSet<>(featureSet); + } else { + allNodeFeatures.retainAll(featureSet); + } + } + return Set.copyOf(allNodeFeatures); + } + + /** + * Returns the features reported by each node in the cluster. + *

    + * NOTE: This should not be used directly. + * Please use {@link org.elasticsearch.features.FeatureService#clusterHasFeature} instead. + */ + public Map> nodeFeatures() { + return nodeFeatures; + } + + /** + * {@code true} if {@code feature} is present on all nodes in the cluster. + *

    + * NOTE: This should not be used directly, as it does not read historical features. + * Please use {@link org.elasticsearch.features.FeatureService#clusterHasFeature} instead. + */ + public boolean clusterHasFeature(NodeFeature feature) { + if (allNodeFeatures == null) { + allNodeFeatures = calculateAllNodeFeatures(); + } + return allNodeFeatures.contains(feature.id()); + } + + /** + * Writes a canonical set of feature sets to {@code StreamOutput}. + * This aims to minimise the data serialized by assuming that most feature sets are going to be identical + * in any one cluster state. + */ + private static void writeCanonicalSets(StreamOutput out, Map> featureSets) throws IOException { + List> canonicalFeatureSets = new ArrayList<>(); + Map nodeFeatureSetIndexes = new HashMap<>(); + + IdentityHashMap, Integer> identityLookup = new IdentityHashMap<>(); + Map, Integer> lookup = new HashMap<>(); + for (var fse : featureSets.entrySet()) { + // do a fast identity lookup first + Integer idx = identityLookup.get(fse.getValue()); + if (idx != null) { + nodeFeatureSetIndexes.put(fse.getKey(), idx); + continue; + } + + // do a contents equality lookup next + idx = lookup.get(fse.getValue()); + if (idx != null) { + nodeFeatureSetIndexes.put(fse.getKey(), idx); + continue; + } + + // we've found a new feature set - insert appropriately + idx = canonicalFeatureSets.size(); + canonicalFeatureSets.add(fse.getValue()); + nodeFeatureSetIndexes.put(fse.getKey(), idx); + identityLookup.put(fse.getValue(), idx); + lookup.put(fse.getValue(), idx); + } + + out.writeCollection(canonicalFeatureSets, (o, c) -> o.writeCollection(c, StreamOutput::writeString)); + out.writeMap(nodeFeatureSetIndexes, StreamOutput::writeVInt); + } + + private static Map> readCanonicalSets(StreamInput in) throws IOException { + List> featureSets = in.readCollectionAsList(i -> i.readCollectionAsImmutableSet(StreamInput::readString)); + Map nodeIndexes = in.readMap(StreamInput::readVInt); + + return nodeIndexes.entrySet().stream().collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> featureSets.get(e.getValue()))); + } + + public static ClusterFeatures readFrom(StreamInput in) throws IOException { + return new ClusterFeatures(readCanonicalSets(in)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + writeCanonicalSets(out, nodeFeatures); + } + + @Override + public Diff diff(ClusterFeatures previousState) { + Set deletes = new HashSet<>(); + Map> removals = new HashMap<>(); + Map> additions = new HashMap<>(); + + for (var prevNodeFeatures : previousState.nodeFeatures.entrySet()) { + Set newFeatures = nodeFeatures.get(prevNodeFeatures.getKey()); + if (newFeatures == null) { + deletes.add(prevNodeFeatures.getKey()); + } else { + Set removed = new HashSet<>(prevNodeFeatures.getValue()); + removed.removeAll(newFeatures); + if (removed.isEmpty() == false) { + removals.put(prevNodeFeatures.getKey(), removed); + } + + Set added = new HashSet<>(newFeatures); + added.removeAll(prevNodeFeatures.getValue()); + if (added.isEmpty() == false) { + additions.put(prevNodeFeatures.getKey(), added); + } + } + } + + // find any completely new nodes + for (var newNodeFeatures : nodeFeatures.entrySet()) { + if (previousState.nodeFeatures.containsKey(newNodeFeatures.getKey()) == false) { + additions.put(newNodeFeatures.getKey(), newNodeFeatures.getValue()); + } + } + + return new ClusterFeaturesDiff(deletes, removals, additions); + } + + public static Diff readDiffFrom(StreamInput in) throws IOException { + return new ClusterFeaturesDiff(in); + } + + private static class ClusterFeaturesDiff implements Diff { + + private final Set deletes; + private final Map> removals; + private final Map> additions; + + private ClusterFeaturesDiff(Set deletes, Map> removals, Map> additions) { + this.deletes = deletes; + this.removals = removals; + this.additions = additions; + } + + private ClusterFeaturesDiff(StreamInput in) throws IOException { + deletes = in.readCollectionAsImmutableSet(StreamInput::readString); + removals = readCanonicalSets(in); + additions = readCanonicalSets(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeCollection(deletes, StreamOutput::writeString); + writeCanonicalSets(out, removals); + writeCanonicalSets(out, additions); + } + + @Override + public ClusterFeatures apply(ClusterFeatures part) { + if (deletes.isEmpty() && removals.isEmpty() && additions.isEmpty()) { + return part; // nothing changing + } + + Map> newFeatures = new HashMap<>(part.nodeFeatures); + deletes.forEach(newFeatures::remove); + + // make sure each value is mutable when we modify it + for (var removes : removals.entrySet()) { + newFeatures.compute(removes.getKey(), (k, v) -> v instanceof HashSet ? v : new HashSet<>(v)).removeAll(removes.getValue()); + } + for (var adds : additions.entrySet()) { + newFeatures.compute(adds.getKey(), (k, v) -> v == null ? new HashSet<>() : v instanceof HashSet ? v : new HashSet<>(v)) + .addAll(adds.getValue()); + } + + return new ClusterFeatures(newFeatures); + } + } + + @Override + public Iterator toXContentChunked(ToXContent.Params params) { + return Iterators.concat( + Iterators.single((builder, p) -> builder.startArray()), + nodeFeatures.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(e -> (builder, p) -> { + String[] features = e.getValue().toArray(String[]::new); + Arrays.sort(features); + return builder.startObject().field("node_id", e.getKey()).array("features", features).endObject(); + }).iterator(), + Iterators.single((builder, p) -> builder.endArray()) + ); + } + + @Override + public String toString() { + // sort for ease of debugging + var features = new TreeMap<>(nodeFeatures); + features.replaceAll((k, v) -> new TreeSet<>(v)); + return "ClusterFeatures" + features; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ClusterFeatures == false) return false; + if (this == obj) return true; + + ClusterFeatures that = (ClusterFeatures) obj; + return nodeFeatures.equals(that.nodeFeatures); + } + + @Override + public int hashCode() { + return Objects.hash(nodeFeatures); + } +} diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java index aa1fba7aecc81..0e72aa545e427 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterState.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterState.java @@ -45,6 +45,7 @@ import org.elasticsearch.common.xcontent.ChunkedToXContent; import org.elasticsearch.common.xcontent.ChunkedToXContentHelper; import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.XContent; @@ -57,6 +58,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Function; @@ -168,6 +170,8 @@ public CompatibilityVersions read(StreamInput in, String key) throws IOException private final Map compatibilityVersions; private final CompatibilityVersions minVersions; + private final ClusterFeatures clusterFeatures; + private final Metadata metadata; private final ClusterBlocks blocks; @@ -190,6 +194,7 @@ public ClusterState(long version, String stateUUID, ClusterState state) { state.routingTable(), state.nodes(), state.compatibilityVersions, + state.clusterFeatures(), state.blocks(), state.customs(), false, @@ -205,6 +210,7 @@ public ClusterState( RoutingTable routingTable, DiscoveryNodes nodes, Map compatibilityVersions, + ClusterFeatures clusterFeatures, ClusterBlocks blocks, Map customs, boolean wasReadFromDiff, @@ -217,6 +223,7 @@ public ClusterState( this.routingTable = routingTable; this.nodes = nodes; this.compatibilityVersions = Map.copyOf(compatibilityVersions); + this.clusterFeatures = clusterFeatures; this.blocks = blocks; this.customs = customs; this.wasReadFromDiff = wasReadFromDiff; @@ -298,6 +305,10 @@ public Map getMinSystemIndexMappi return this.minVersions.systemIndexMappingsVersion(); } + public ClusterFeatures clusterFeatures() { + return clusterFeatures; + } + public Metadata metadata() { return this.metadata; } @@ -485,11 +496,15 @@ public String toString() { sb.append(blocks()); sb.append(nodes()); if (compatibilityVersions.isEmpty() == false) { - sb.append("node versions:\n"); + sb.append("node versions:\n"); for (var tv : compatibilityVersions.entrySet()) { sb.append(TAB).append(tv.getKey()).append(": ").append(tv.getValue()).append("\n"); } } + sb.append("cluster features:\n"); + for (var nf : getNodeFeatures(clusterFeatures).entrySet()) { + sb.append(TAB).append(nf.getKey()).append(": ").append(new TreeSet<>(nf.getValue())).append("\n"); + } sb.append(routingTable()); sb.append(getRoutingNodes()); if (customs.isEmpty() == false) { @@ -653,6 +668,14 @@ public Iterator toXContentChunked(ToXContent.Params outerP (builder, params) -> builder.endArray() ), + // per-node feature information + metrics.contains(Metric.NODES) + ? Iterators.concat( + Iterators.single((b, p) -> b.field("nodes_features")), + clusterFeatures.toXContentChunked(outerParams) + ) + : Collections.emptyIterator(), + // metadata metrics.contains(Metric.METADATA) ? metadata.toXContentChunked(outerParams) : Collections.emptyIterator(), @@ -735,6 +758,11 @@ public ClusterState copyAndUpdateMetadata(Consumer updater) { return copyAndUpdate(builder -> builder.metadata(metadata().copyAndUpdate(updater))); } + @SuppressForbidden(reason = "directly reading ClusterState#clusterFeatures") + private static Map> getNodeFeatures(ClusterFeatures features) { + return features.nodeFeatures(); + } + public static class Builder { private ClusterState previous; @@ -746,6 +774,7 @@ public static class Builder { private RoutingTable routingTable = RoutingTable.EMPTY_ROUTING_TABLE; private DiscoveryNodes nodes = DiscoveryNodes.EMPTY_NODES; private final Map compatibilityVersions; + private final Map> nodeFeatures; private ClusterBlocks blocks = ClusterBlocks.EMPTY_CLUSTER_BLOCK; private final ImmutableOpenMap.Builder customs; private boolean fromDiff; @@ -757,6 +786,7 @@ public Builder(ClusterState state) { this.uuid = state.stateUUID(); this.nodes = state.nodes(); this.compatibilityVersions = new HashMap<>(state.compatibilityVersions); + this.nodeFeatures = new HashMap<>(getNodeFeatures(state.clusterFeatures())); this.routingTable = state.routingTable(); this.metadata = state.metadata(); this.blocks = state.blocks(); @@ -766,6 +796,7 @@ public Builder(ClusterState state) { public Builder(ClusterName clusterName) { this.compatibilityVersions = new HashMap<>(); + this.nodeFeatures = new HashMap<>(); customs = ImmutableOpenMap.builder(); this.clusterName = clusterName; } @@ -823,6 +854,22 @@ public Map compatibilityVersions() { return Collections.unmodifiableMap(this.compatibilityVersions); } + public Builder nodeFeatures(ClusterFeatures features) { + this.nodeFeatures.clear(); + this.nodeFeatures.putAll(getNodeFeatures(features)); + return this; + } + + public Builder nodeFeatures(Map> nodeFeatures) { + this.nodeFeatures.clear(); + this.nodeFeatures.putAll(nodeFeatures); + return this; + } + + public Map> nodeFeatures() { + return Collections.unmodifiableMap(this.nodeFeatures); + } + public Builder routingTable(RoutingTable.Builder routingTableBuilder) { return routingTable(routingTableBuilder.build()); } @@ -901,6 +948,15 @@ public ClusterState build() { } else { routingNodes = null; } + + // ensure every node in the cluster has a feature set + // nodes can be null in some tests + if (nodes != null) { + for (DiscoveryNode node : nodes) { + nodeFeatures.putIfAbsent(node.getId(), Set.of()); + } + } + return new ClusterState( clusterName, version, @@ -909,6 +965,7 @@ public ClusterState build() { routingTable, nodes, compatibilityVersions, + new ClusterFeatures(nodeFeatures), blocks, customs.build(), fromDiff, @@ -961,6 +1018,9 @@ public static ClusterState readFrom(StreamInput in, DiscoveryNode localNode) thr .values() .forEach(n -> builder.putCompatibilityVersions(n.getId(), inferTransportVersion(n), Map.of())); } + if (in.getTransportVersion().onOrAfter(TransportVersions.CLUSTER_FEATURES_ADDED)) { + builder.nodeFeatures(ClusterFeatures.readFrom(in)); + } builder.blocks = ClusterBlocks.readFrom(in); int customSize = in.readVInt(); for (int i = 0; i < customSize; i++) { @@ -1002,6 +1062,9 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_8_0)) { out.writeMap(compatibilityVersions, (streamOutput, versions) -> versions.writeTo(streamOutput)); } + if (out.getTransportVersion().onOrAfter(TransportVersions.CLUSTER_FEATURES_ADDED)) { + clusterFeatures.writeTo(out); + } blocks.writeTo(out); VersionedNamedWriteable.writeVersionedWritables(out, customs); if (out.getTransportVersion().before(TransportVersions.V_8_0_0)) { @@ -1025,6 +1088,7 @@ private static class ClusterStateDiff implements Diff { @Nullable private final Diff> versions; + private final Diff features; private final Diff metadata; @@ -1045,6 +1109,7 @@ private static class ClusterStateDiff implements Diff { DiffableUtils.getStringKeySerializer(), COMPATIBILITY_VERSIONS_VALUE_SERIALIZER ); + features = after.clusterFeatures.diff(before.clusterFeatures); metadata = after.metadata.diff(before.metadata); blocks = after.blocks.diff(before.blocks); customs = DiffableUtils.diff(before.customs, after.customs, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); @@ -1066,6 +1131,11 @@ private static class ClusterStateDiff implements Diff { } else { versions = null; // infer at application time } + if (in.getTransportVersion().onOrAfter(TransportVersions.CLUSTER_FEATURES_ADDED)) { + features = ClusterFeatures.readDiffFrom(in); + } else { + features = null; // fill in when nodes re-register with a master that understands features + } metadata = Metadata.readDiffFrom(in); blocks = ClusterBlocks.readDiffFrom(in); customs = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); @@ -1085,6 +1155,9 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_8_0)) { out.writeOptionalWriteable(versions); } + if (out.getTransportVersion().onOrAfter(TransportVersions.CLUSTER_FEATURES_ADDED)) { + features.writeTo(out); + } metadata.writeTo(out); blocks.writeTo(out); customs.writeTo(out); @@ -1117,6 +1190,9 @@ public ClusterState apply(ClusterState state) { .values() .forEach(n -> builder.putCompatibilityVersions(n.getId(), inferTransportVersion(n), Map.of())); } + if (features != null) { + builder.nodeFeatures(this.features.apply(state.clusterFeatures)); + } builder.metadata(metadata.apply(state.metadata)); builder.blocks(blocks.apply(state.blocks)); builder.customs(customs.apply(state.customs)); diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java index 8df26075c13f7..08e31e11ae256 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java @@ -211,7 +211,8 @@ public Coordinator( Reconfigurator reconfigurator, LeaderHeartbeatService leaderHeartbeatService, PreVoteCollector.Factory preVoteCollectorFactory, - CompatibilityVersions compatibilityVersions + CompatibilityVersions compatibilityVersions, + Set features ) { this.settings = settings; this.transportService = transportService; @@ -236,7 +237,8 @@ public Coordinator( circuitBreakerService, reconfigurator::maybeReconfigureAfterNewMasterIsElected, this::getLatestStoredStateAfterWinningAnElection, - compatibilityVersions + compatibilityVersions, + features ); this.joinValidationService = new JoinValidationService( settings, @@ -776,7 +778,12 @@ private void processJoinRequest(JoinRequest joinRequest, ActionListener jo && optionalJoin.stream().allMatch(j -> j.getTerm() <= getCurrentTerm()); optionalJoin.ifPresent(this::handleJoin); - joinAccumulator.handleJoinRequest(joinRequest.getSourceNode(), joinRequest.getCompatibilityVersions(), joinListener); + joinAccumulator.handleJoinRequest( + joinRequest.getSourceNode(), + joinRequest.getCompatibilityVersions(), + joinRequest.getFeatures(), + joinListener + ); if (prevElectionWon == false && coordState.electionWon()) { becomeLeader(); diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinHelper.java b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinHelper.java index 247034c88ed62..e5dee6aeb67e2 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinHelper.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinHelper.java @@ -51,6 +51,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; @@ -78,6 +79,7 @@ public class JoinHelper { private final CircuitBreakerService circuitBreakerService; private final ObjLongConsumer> latestStoredStateSupplier; private final CompatibilityVersions compatibilityVersions; + private final Set features; private final Map, PendingJoinInfo> pendingOutgoingJoins = ConcurrentCollections.newConcurrentMap(); private final AtomicReference lastFailedJoinAttempt = new AtomicReference<>(); @@ -97,7 +99,8 @@ public class JoinHelper { CircuitBreakerService circuitBreakerService, Function maybeReconfigureAfterMasterElection, ObjLongConsumer> latestStoredStateSupplier, - CompatibilityVersions compatibilityVersions + CompatibilityVersions compatibilityVersions, + Set features ) { this.joinTaskQueue = masterService.createTaskQueue( "node-join", @@ -112,6 +115,7 @@ public class JoinHelper { this.joinReasonService = joinReasonService; this.latestStoredStateSupplier = latestStoredStateSupplier; this.compatibilityVersions = compatibilityVersions; + this.features = features; transportService.registerRequestHandler( JOIN_ACTION_NAME, @@ -239,7 +243,13 @@ public void sendJoinRequest(DiscoveryNode destination, long term, Optional logger.debug("dropping join request to [{}]: [{}]", destination, statusInfo.getInfo()); return; } - final JoinRequest joinRequest = new JoinRequest(transportService.getLocalNode(), compatibilityVersions, term, optionalJoin); + final JoinRequest joinRequest = new JoinRequest( + transportService.getLocalNode(), + compatibilityVersions, + features, + term, + optionalJoin + ); final Tuple dedupKey = Tuple.tuple(destination, joinRequest); final var pendingJoinInfo = new PendingJoinInfo(transportService.getThreadPool().relativeTimeInMillis()); if (pendingOutgoingJoins.putIfAbsent(dedupKey, pendingJoinInfo) == null) { @@ -405,7 +415,12 @@ List getInFlightJoinStatuses() { } interface JoinAccumulator { - void handleJoinRequest(DiscoveryNode sender, CompatibilityVersions compatibilityVersions, ActionListener joinListener); + void handleJoinRequest( + DiscoveryNode sender, + CompatibilityVersions compatibilityVersions, + Set features, + ActionListener joinListener + ); default void close(Mode newMode) {} } @@ -415,11 +430,13 @@ class LeaderJoinAccumulator implements JoinAccumulator { public void handleJoinRequest( DiscoveryNode sender, CompatibilityVersions compatibilityVersions, + Set features, ActionListener joinListener ) { final JoinTask task = JoinTask.singleNode( sender, compatibilityVersions, + features, joinReasonService.getJoinReason(sender, Mode.LEADER), joinListener, currentTermSupplier.getAsLong() @@ -438,6 +455,7 @@ static class InitialJoinAccumulator implements JoinAccumulator { public void handleJoinRequest( DiscoveryNode sender, CompatibilityVersions compatibilityVersions, + Set features, ActionListener joinListener ) { assert false : "unexpected join from " + sender + " during initialisation"; @@ -455,6 +473,7 @@ static class FollowerJoinAccumulator implements JoinAccumulator { public void handleJoinRequest( DiscoveryNode sender, CompatibilityVersions compatibilityVersions, + Set features, ActionListener joinListener ) { joinListener.onFailure(new CoordinationStateRejectedException("join target is a follower")); @@ -468,19 +487,22 @@ public String toString() { class CandidateJoinAccumulator implements JoinAccumulator { - private final Map>> joinRequestAccumulator = new HashMap<>(); + private record JoinInformation(CompatibilityVersions compatibilityVersions, Set features, ActionListener listener) {} + + private final Map joinRequestAccumulator = new HashMap<>(); boolean closed; @Override public void handleJoinRequest( DiscoveryNode sender, CompatibilityVersions compatibilityVersions, + Set features, ActionListener joinListener ) { assert closed == false : "CandidateJoinAccumulator closed"; - var prev = joinRequestAccumulator.put(sender, Tuple.tuple(compatibilityVersions, joinListener)); + var prev = joinRequestAccumulator.put(sender, new JoinInformation(compatibilityVersions, features, joinListener)); if (prev != null) { - prev.v2().onFailure(new CoordinationStateRejectedException("received a newer join from " + sender)); + prev.listener().onFailure(new CoordinationStateRejectedException("received a newer join from " + sender)); } } @@ -495,9 +517,10 @@ public void close(Mode newMode) { final var data = entry.getValue(); return new JoinTask.NodeJoinTask( discoveryNode, - data.v1(), + data.compatibilityVersions(), + data.features(), joinReasonService.getJoinReason(discoveryNode, Mode.CANDIDATE), - data.v2() + data.listener() ); }), joiningTerm); latestStoredStateSupplier.accept(new ActionListener<>() { @@ -516,13 +539,13 @@ public void onFailure(Exception e) { Strings.format("failed to retrieve latest stored state after winning election in term [%d]", joiningTerm), e ); - joinRequestAccumulator.values().forEach(joinCallback -> joinCallback.v2().onFailure(e)); + joinRequestAccumulator.values().forEach(joinCallback -> joinCallback.listener().onFailure(e)); } }, joiningTerm); } else { assert newMode == Mode.FOLLOWER : newMode; joinRequestAccumulator.values() - .forEach(joinCallback -> joinCallback.v2().onFailure(new CoordinationStateRejectedException("became follower"))); + .forEach(joinCallback -> joinCallback.listener().onFailure(new CoordinationStateRejectedException("became follower"))); } // CandidateJoinAccumulator is only closed when becoming leader or follower, otherwise it accumulates all joins received diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinRequest.java b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinRequest.java index d8958f75c7aa5..2ba65873738a0 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinRequest.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinRequest.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; public class JoinRequest extends TransportRequest { @@ -32,6 +33,11 @@ public class JoinRequest extends TransportRequest { */ private final CompatibilityVersions compatibilityVersions; + /** + * The features that are supported by the joining node. + */ + private final Set features; + /** * The minimum term for which the joining node will accept any cluster state publications. If the joining node is in a strictly greater * term than the master it wants to join then the master must enter a new term and hold another election. Doesn't necessarily match @@ -50,12 +56,14 @@ public class JoinRequest extends TransportRequest { public JoinRequest( DiscoveryNode sourceNode, CompatibilityVersions compatibilityVersions, + Set features, long minimumTerm, Optional optionalJoin ) { assert optionalJoin.isPresent() == false || optionalJoin.get().getSourceNode().equals(sourceNode); this.sourceNode = sourceNode; this.compatibilityVersions = compatibilityVersions; + this.features = features; this.minimumTerm = minimumTerm; this.optionalJoin = optionalJoin; } @@ -70,6 +78,11 @@ public JoinRequest(StreamInput in) throws IOException { // no known mapping versions here compatibilityVersions = new CompatibilityVersions(TransportVersion.fromId(sourceNode.getVersion().id), Map.of()); } + if (in.getTransportVersion().onOrAfter(TransportVersions.CLUSTER_FEATURES_ADDED)) { + features = in.readCollectionAsSet(StreamInput::readString); + } else { + features = Set.of(); + } minimumTerm = in.readLong(); optionalJoin = Optional.ofNullable(in.readOptionalWriteable(Join::new)); } @@ -81,6 +94,9 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_8_0)) { compatibilityVersions.writeTo(out); } + if (out.getTransportVersion().onOrAfter(TransportVersions.CLUSTER_FEATURES_ADDED)) { + out.writeCollection(features, StreamOutput::writeString); + } out.writeLong(minimumTerm); out.writeOptionalWriteable(optionalJoin.orElse(null)); } @@ -93,6 +109,10 @@ public CompatibilityVersions getCompatibilityVersions() { return compatibilityVersions; } + public Set getFeatures() { + return features; + } + public long getMinimumTerm() { return minimumTerm; } @@ -118,12 +138,13 @@ public boolean equals(Object o) { if (minimumTerm != that.minimumTerm) return false; if (sourceNode.equals(that.sourceNode) == false) return false; if (compatibilityVersions.equals(that.compatibilityVersions) == false) return false; + if (features.equals(that.features) == false) return false; return optionalJoin.equals(that.optionalJoin); } @Override public int hashCode() { - return Objects.hash(sourceNode, compatibilityVersions, minimumTerm, optionalJoin); + return Objects.hash(sourceNode, compatibilityVersions, features, minimumTerm, optionalJoin); } @Override @@ -133,6 +154,8 @@ public String toString() { + sourceNode + ", compatibilityVersions=" + compatibilityVersions + + ", features=" + + features + ", minimumTerm=" + minimumTerm + ", optionalJoin=" diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTask.java b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTask.java index ac1c4e888e6ca..198912646cd65 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTask.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/JoinTask.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.stream.Stream; public record JoinTask(List nodeJoinTasks, boolean isBecomingMaster, long term, ClusterState initialState) @@ -26,11 +27,12 @@ public record JoinTask(List nodeJoinTasks, boolean isBecomingMaste public static JoinTask singleNode( DiscoveryNode node, CompatibilityVersions compatibilityVersions, + Set features, JoinReason reason, ActionListener listener, long term ) { - return new JoinTask(List.of(new NodeJoinTask(node, compatibilityVersions, reason, listener)), false, term, null); + return new JoinTask(List.of(new NodeJoinTask(node, compatibilityVersions, features, reason, listener)), false, term, null); } public static JoinTask completingElection(Stream nodeJoinTaskStream, long term) { @@ -78,6 +80,7 @@ public JoinTask alsoRefreshState(ClusterState latestState) { public record NodeJoinTask( DiscoveryNode node, CompatibilityVersions compatibilityVersions, + Set features, JoinReason reason, ActionListener listener ) { @@ -85,11 +88,13 @@ public record NodeJoinTask( public NodeJoinTask( DiscoveryNode node, CompatibilityVersions compatibilityVersions, + Set features, JoinReason reason, ActionListener listener ) { this.node = Objects.requireNonNull(node); this.compatibilityVersions = Objects.requireNonNull(compatibilityVersions); + this.features = Objects.requireNonNull(features); this.reason = reason; this.listener = listener; } diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java index a9c1332c59915..00086c42ed4ae 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java @@ -25,7 +25,6 @@ import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.version.CompatibilityVersions; import org.elasticsearch.common.Priority; -import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersions; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; @@ -123,6 +122,7 @@ public ClusterState execute(BatchExecutionContext batchExecutionContex DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(newState.nodes()); Map compatibilityVersionsMap = new HashMap<>(newState.compatibilityVersions()); + Map> nodeFeatures = new HashMap<>(newState.nodeFeatures()); assert nodesBuilder.isLocalNodeElectedMaster(); @@ -139,12 +139,23 @@ public ClusterState execute(BatchExecutionContext batchExecutionContex final DiscoveryNode node = nodeJoinTask.node(); if (currentNodes.nodeExistsWithSameRoles(node)) { logger.debug("received a join request for an existing node [{}]", node); + + // update the node's feature set if it has one + // this can happen if the master has just moved from a pre-features version to a post-features version + assert Version.V_8_12_0.onOrBefore(Version.CURRENT) : "This can be removed once 8.12.0 is no longer a valid version"; + if (Objects.equals(nodeFeatures.get(node.getId()), nodeJoinTask.features()) == false) { + logger.debug("updating node [{}] features {}", node.getId(), nodeJoinTask.features()); + nodeFeatures.put(node.getId(), nodeJoinTask.features()); + nodesChanged = true; + } } else { try { CompatibilityVersions compatibilityVersions = nodeJoinTask.compatibilityVersions(); + Set features = nodeJoinTask.features(); if (enforceVersionBarrier) { ensureVersionBarrier(node.getVersion(), minClusterNodeVersion); CompatibilityVersions.ensureVersionsCompatibility(compatibilityVersions, compatibilityVersionsMap.values()); + // TODO: enforce feature ratchet barrier } blockForbiddenVersions(compatibilityVersions.transportVersion()); ensureNodesCompatibility(node.getVersion(), minClusterNodeVersion, maxClusterNodeVersion); @@ -153,6 +164,7 @@ public ClusterState execute(BatchExecutionContext batchExecutionContex ensureIndexCompatibility(node.getMinIndexVersion(), node.getMaxIndexVersion(), initialState.getMetadata()); nodesBuilder.add(node); compatibilityVersionsMap.put(node.getId(), compatibilityVersions); + nodeFeatures.put(node.getId(), features); nodesChanged = true; minClusterNodeVersion = Version.min(minClusterNodeVersion, node.getVersion()); maxClusterNodeVersion = Version.max(maxClusterNodeVersion, node.getVersion()); @@ -223,7 +235,7 @@ public ClusterState execute(BatchExecutionContext batchExecutionContex } final ClusterState clusterStateWithNewNodesAndDesiredNodes = DesiredNodes.updateDesiredNodesStatusIfNeeded( - newState.nodes(nodesBuilder).nodeIdsToCompatibilityVersions(compatibilityVersionsMap).build() + newState.nodes(nodesBuilder).nodeIdsToCompatibilityVersions(compatibilityVersionsMap).nodeFeatures(nodeFeatures).build() ); final ClusterState updatedState = allocationService.adaptAutoExpandReplicas(clusterStateWithNewNodesAndDesiredNodes); assert enforceVersionBarrier == false @@ -241,11 +253,6 @@ public ClusterState execute(BatchExecutionContext batchExecutionContex } } - @SuppressForbidden(reason = "maintaining ClusterState#compatibilityVersions requires reading them") - private static Map getCompatibilityVersions(ClusterState clusterState) { - return clusterState.compatibilityVersions(); - } - protected ClusterState.Builder becomeMasterAndTrimConflictingNodes( ClusterState currentState, List> taskContexts, @@ -265,9 +272,13 @@ protected ClusterState.Builder becomeMasterAndTrimConflictingNodes( assert currentState.nodes().getMasterNodeId() == null : currentState; assert currentState.term() < term : term + " vs " + currentState; - DiscoveryNodes currentNodes = currentState.nodes(); + + ClusterState.Builder builder = ClusterState.builder(currentState); + + DiscoveryNodes currentNodes = builder.nodes(); DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(currentNodes); - Map compatibilityVersions = new HashMap<>(getCompatibilityVersions(currentState)); + Map compatibilityVersions = new HashMap<>(builder.compatibilityVersions()); + Map> nodeFeatures = new HashMap<>(builder.nodeFeatures()); nodesBuilder.masterNodeId(currentState.nodes().getLocalNodeId()); nodesBuilder.resetNodeLeftGeneration(); @@ -278,6 +289,7 @@ protected ClusterState.Builder becomeMasterAndTrimConflictingNodes( logger.debug("removing existing node [{}], which conflicts with incoming join from [{}]", nodeWithSameId, joiningNode); nodesBuilder.remove(nodeWithSameId.getId()); compatibilityVersions.remove(nodeWithSameId.getId()); + nodeFeatures.remove(nodeWithSameId.getId()); } final DiscoveryNode nodeWithSameAddress = currentNodes.findByAddress(joiningNode.getAddress()); if (nodeWithSameAddress != null && nodeWithSameAddress.equals(joiningNode) == false) { @@ -288,15 +300,16 @@ protected ClusterState.Builder becomeMasterAndTrimConflictingNodes( ); nodesBuilder.remove(nodeWithSameAddress.getId()); compatibilityVersions.remove(nodeWithSameAddress.getId()); + nodeFeatures.remove(nodeWithSameAddress.getId()); } } } // now trim any left over dead nodes - either left there when the previous master stepped down // or removed by us above - ClusterState tmpState = ClusterState.builder(currentState) - .nodes(nodesBuilder) + ClusterState tmpState = builder.nodes(nodesBuilder) .nodeIdsToCompatibilityVersions(compatibilityVersions) + .nodeFeatures(nodeFeatures) .blocks(ClusterBlocks.builder().blocks(currentState.blocks()).removeGlobalBlock(NoMasterBlockService.NO_MASTER_BLOCK_ID)) .metadata( Metadata.builder(currentState.metadata()) diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeLeftExecutor.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeLeftExecutor.java index 39230d0255ae7..5cbe742aec628 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeLeftExecutor.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeLeftExecutor.java @@ -18,11 +18,11 @@ import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.cluster.version.CompatibilityVersions; -import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.persistent.PersistentTasksCustomMetadata; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class NodeLeftExecutor implements ClusterStateTaskExecutor { @@ -50,16 +50,14 @@ public NodeLeftExecutor(AllocationService allocationService) { this.allocationService = allocationService; } - @SuppressForbidden(reason = "maintaining ClusterState#compatibilityVersions requires reading them") - private static Map getCompatibilityVersions(ClusterState clusterState) { - return clusterState.compatibilityVersions(); - } - @Override public ClusterState execute(BatchExecutionContext batchExecutionContext) throws Exception { ClusterState initialState = batchExecutionContext.initialState(); + + ClusterState.Builder builder = ClusterState.builder(initialState); DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(initialState.nodes()); - Map compatibilityVersions = new HashMap<>(getCompatibilityVersions(initialState)); + Map compatibilityVersions = new HashMap<>(builder.compatibilityVersions()); + Map> nodeFeatures = new HashMap<>(builder.nodeFeatures()); boolean removed = false; for (final var taskContext : batchExecutionContext.taskContexts()) { final var task = taskContext.getTask(); @@ -67,6 +65,7 @@ public ClusterState execute(BatchExecutionContext batchExecutionContext) t if (initialState.nodes().nodeExists(task.node())) { remainingNodesBuilder.remove(task.node()); compatibilityVersions.remove(task.node().getId()); + nodeFeatures.remove(task.node().getId()); removed = true; reason = task.reason(); } else { @@ -89,7 +88,11 @@ public ClusterState execute(BatchExecutionContext batchExecutionContext) t try (var ignored = batchExecutionContext.dropHeadersContext()) { // suppress deprecation warnings e.g. from reroute() - final var remainingNodesClusterState = remainingNodesClusterState(initialState, remainingNodesBuilder, compatibilityVersions); + final var remainingNodesClusterState = builder.nodes(remainingNodesBuilder) + .nodeIdsToCompatibilityVersions(compatibilityVersions) + .nodeFeatures(nodeFeatures) + .build(); + remainingNodesClusterState(remainingNodesClusterState); final var ptasksDisassociatedState = PersistentTasksCustomMetadata.disassociateDeadNodes(remainingNodesClusterState); return allocationService.disassociateDeadNodes( ptasksDisassociatedState, @@ -102,15 +105,5 @@ public ClusterState execute(BatchExecutionContext batchExecutionContext) t // visible for testing // hook is used in testing to ensure that correct cluster state is used to test whether a // rejoin or reroute is needed - protected ClusterState remainingNodesClusterState( - ClusterState currentState, - DiscoveryNodes.Builder remainingNodesBuilder, - Map compatibilityVersions - ) { - return ClusterState.builder(currentState) - .nodes(remainingNodesBuilder) - .nodeIdsToCompatibilityVersions(compatibilityVersions) - .build(); - } - + void remainingNodesClusterState(ClusterState state) {} } diff --git a/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java b/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java index 6a00077c4330e..fabc10e336368 100644 --- a/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java +++ b/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java @@ -111,7 +111,8 @@ public DiscoveryModule( RerouteService rerouteService, NodeHealthService nodeHealthService, CircuitBreakerService circuitBreakerService, - CompatibilityVersions compatibilityVersions + CompatibilityVersions compatibilityVersions, + Set features ) { final Collection> joinValidators = new ArrayList<>(); final Map> hostProviders = new HashMap<>(); @@ -213,7 +214,8 @@ public DiscoveryModule( reconfigurator, leaderHeartbeatService, preVoteCollectorFactory, - compatibilityVersions + compatibilityVersions, + features ); } else { throw new IllegalArgumentException("Unknown discovery type [" + discoveryType + "]"); diff --git a/server/src/main/java/org/elasticsearch/features/FeatureService.java b/server/src/main/java/org/elasticsearch/features/FeatureService.java new file mode 100644 index 0000000000000..5d7632a91b0b8 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/features/FeatureService.java @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.features; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.common.Strings; +import org.elasticsearch.core.SuppressForbidden; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; + +/** + * Manages information on the features supported by nodes in the cluster + */ +public class FeatureService { + + /** + * A feature indicating that node features are supported. + */ + public static final NodeFeature FEATURES_SUPPORTED = new NodeFeature("features_supported"); + + private static final Logger logger = LogManager.getLogger(FeatureService.class); + + public static final Version CLUSTER_FEATURES_ADDED_VERSION = Version.V_8_12_0; + + private final NavigableMap> historicalFeatures; + private final Set nodeFeatures; + + public FeatureService(List specs) { + Map allFeatures = new HashMap<>(); + + NavigableMap> historicalFeatures = new TreeMap<>(); + Set nodeFeatures = new HashSet<>(); + for (FeatureSpecification spec : specs) { + for (var hfe : spec.getHistoricalFeatures().entrySet()) { + FeatureSpecification existing = allFeatures.putIfAbsent(hfe.getKey().id(), spec); + // the same SPI class can be loaded multiple times if it's in the base classloader + if (existing != null && existing.getClass() != spec.getClass()) { + throw new IllegalArgumentException( + Strings.format("Duplicate feature - [%s] is declared by both [%s] and [%s]", hfe.getKey().id(), existing, spec) + ); + } + + if (hfe.getValue().onOrAfter(CLUSTER_FEATURES_ADDED_VERSION)) { + throw new IllegalArgumentException( + Strings.format( + "Historical feature [%s] declared by [%s] for version [%s] is not a historical version", + hfe.getKey().id(), + spec, + hfe.getValue() + ) + ); + } + + historicalFeatures.computeIfAbsent(hfe.getValue(), k -> new HashSet<>()).add(hfe.getKey().id()); + } + + for (NodeFeature f : spec.getFeatures()) { + FeatureSpecification existing = allFeatures.putIfAbsent(f.id(), spec); + if (existing != null && existing.getClass() != spec.getClass()) { + throw new IllegalArgumentException( + Strings.format("Duplicate feature - [%s] is declared by both [%s] and [%s]", f.id(), existing, spec) + ); + } + + nodeFeatures.add(f.id()); + } + } + + this.historicalFeatures = consolidateHistoricalFeatures(historicalFeatures); + this.nodeFeatures = Set.copyOf(nodeFeatures); + + logger.info("Registered local node features {}", nodeFeatures.stream().sorted().toList()); + } + + private static NavigableMap> consolidateHistoricalFeatures( + NavigableMap> declaredHistoricalFeatures + ) { + // update each version by adding in all features from previous versions + Set featureAggregator = new HashSet<>(); + for (Map.Entry> versions : declaredHistoricalFeatures.entrySet()) { + featureAggregator.addAll(versions.getValue()); + versions.setValue(Set.copyOf(featureAggregator)); + } + + return Collections.unmodifiableNavigableMap(declaredHistoricalFeatures); + } + + /** + * The non-historical features supported by this node. + */ + public Set getNodeFeatures() { + return nodeFeatures; + } + + /** + * Returns {@code true} if all nodes in {@code state} support feature {@code feature}. + */ + @SuppressForbidden(reason = "We need basic feature information from cluster state") + public boolean clusterHasFeature(ClusterState state, NodeFeature feature) { + if (state.clusterFeatures().clusterHasFeature(feature)) { + return true; + } + + var features = historicalFeatures.floorEntry(state.getNodes().getMinNodeVersion()); + return features != null && features.getValue().contains(feature.id()); + } +} diff --git a/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java b/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java new file mode 100644 index 0000000000000..7df9ac7c4c203 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.features; + +import org.elasticsearch.Version; + +import java.util.Map; +import java.util.Set; + +/** + * Specifies one or more features that are supported by this node. + */ +public interface FeatureSpecification { + /** + * Returns a set of regular features that this node supports. + */ + default Set getFeatures() { + return Set.of(); + } + + /** + * Returns information on historical features that should be added to all nodes at or above the {@link Version} specified. + */ + default Map getHistoricalFeatures() { + return Map.of(); + } +} diff --git a/server/src/main/java/org/elasticsearch/features/FeaturesSupportedSpecification.java b/server/src/main/java/org/elasticsearch/features/FeaturesSupportedSpecification.java new file mode 100644 index 0000000000000..3918d64226530 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/features/FeaturesSupportedSpecification.java @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.features; + +import java.util.Set; + +/** + * This adds a feature {@code features_supported} indicating that a node supports node features. + * Nodes that do not support features won't have this feature in its feature set, + * so this can be checked without needing to look at the node version. + */ +public class FeaturesSupportedSpecification implements FeatureSpecification { + + @Override + public Set getFeatures() { + return Set.of(FeatureService.FEATURES_SUPPORTED); + } +} diff --git a/server/src/main/java/org/elasticsearch/features/NodeFeature.java b/server/src/main/java/org/elasticsearch/features/NodeFeature.java new file mode 100644 index 0000000000000..aef907c952c4a --- /dev/null +++ b/server/src/main/java/org/elasticsearch/features/NodeFeature.java @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.features; + +import java.util.Objects; + +/** + * A feature published by a node. + * + * @param id The feature id. Must be unique in the node. + */ +public record NodeFeature(String id) { + + public NodeFeature { + Objects.requireNonNull(id); + } +} diff --git a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java index f951a1ecba7e6..5217308d39e6a 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeConstruction.java +++ b/server/src/main/java/org/elasticsearch/node/NodeConstruction.java @@ -81,6 +81,8 @@ import org.elasticsearch.discovery.DiscoveryModule; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.features.FeatureService; +import org.elasticsearch.features.FeatureSpecification; import org.elasticsearch.gateway.GatewayAllocator; import org.elasticsearch.gateway.GatewayMetaState; import org.elasticsearch.gateway.GatewayModule; @@ -740,6 +742,8 @@ private void construct(Environment initialEnvironment, NodeServiceProvider servi threadPool ); + FeatureService featureService = new FeatureService(pluginsService.loadServiceProviders(FeatureSpecification.class)); + record PluginServiceInstances( Client client, ClusterService clusterService, @@ -755,6 +759,7 @@ record PluginServiceInstances( TelemetryProvider telemetryProvider, AllocationService allocationService, IndicesService indicesService, + FeatureService featureService, SystemIndices systemIndices ) implements Plugin.PluginServices {} PluginServiceInstances pluginServices = new PluginServiceInstances( @@ -772,6 +777,7 @@ record PluginServiceInstances( telemetryProvider, clusterModule.getAllocationService(), indicesService, + featureService, systemIndices ); @@ -954,7 +960,8 @@ record PluginServiceInstances( rerouteService, fsHealthService, circuitBreakerService, - compatibilityVersions + compatibilityVersions, + featureService.getNodeFeatures() ); this.nodeService = new NodeService( settings, @@ -1093,6 +1100,7 @@ record PluginServiceInstances( b.bind(ClusterInfoService.class).toInstance(clusterInfoService); b.bind(SnapshotsInfoService.class).toInstance(snapshotsInfoService); b.bind(GatewayMetaState.class).toInstance(gatewayMetaState); + b.bind(FeatureService.class).toInstance(featureService); b.bind(Coordinator.class).toInstance(discoveryModule.getCoordinator()); b.bind(Reconfigurator.class).toInstance(discoveryModule.getReconfigurator()); { diff --git a/server/src/main/java/org/elasticsearch/plugins/Plugin.java b/server/src/main/java/org/elasticsearch/plugins/Plugin.java index a391881f0def9..de9f8186865aa 100644 --- a/server/src/main/java/org/elasticsearch/plugins/Plugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/Plugin.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.features.FeatureService; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexSettingProvider; import org.elasticsearch.indices.IndicesService; @@ -139,6 +140,11 @@ public interface PluginServices { */ IndicesService indicesService(); + /** + * A service to access features supported by nodes in the cluster + */ + FeatureService featureService(); + /** * The system indices for the cluster */ diff --git a/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification b/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification new file mode 100644 index 0000000000000..699b48cd3b20b --- /dev/null +++ b/server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification @@ -0,0 +1,9 @@ +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License +# 2.0 and the Server Side Public License, v 1; you may not use this file except +# in compliance with, at your election, the Elastic License 2.0 or the Server +# Side Public License, v 1. +# + +org.elasticsearch.features.FeaturesSupportedSpecification diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java index e6eccfebc15e2..70c33283c7475 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/reroute/ClusterRerouteResponseTests.java @@ -140,6 +140,12 @@ public void testToXContentWithDeprecatedClusterState() { } } ], + "nodes_features": [ + { + "node_id": "node0", + "features": [] + } + ], "metadata": { "cluster_uuid": "_na_", "cluster_uuid_committed": false, diff --git a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java index 0282e75434562..56c82ae12dc45 100644 --- a/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/ClusterStateTests.java @@ -64,6 +64,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import static java.util.Collections.emptySet; @@ -224,6 +225,12 @@ public void testToXContent() throws IOException { } } ], + "nodes_features" : [ + { + "node_id" : "nodeId1", + "features" : [ "f1", "f2" ] + } + ], "metadata": { "cluster_uuid": "clusterUUID", "cluster_uuid_committed": false, @@ -485,6 +492,15 @@ public void testToXContent_FlatSettingTrue_ReduceMappingFalse() throws IOExcepti } } ], + "nodes_features" : [ + { + "node_id" : "nodeId1", + "features" : [ + "f1", + "f2" + ] + } + ], "metadata" : { "cluster_uuid" : "clusterUUID", "cluster_uuid_committed" : false, @@ -742,6 +758,15 @@ public void testToXContent_FlatSettingFalse_ReduceMappingTrue() throws IOExcepti } } ], + "nodes_features" : [ + { + "node_id" : "nodeId1", + "features" : [ + "f1", + "f2" + ] + } + ], "metadata" : { "cluster_uuid" : "clusterUUID", "cluster_uuid_committed" : false, @@ -955,6 +980,7 @@ public void testToXContentSameTypeName() throws IOException { "blocks" : { }, "nodes" : { }, "nodes_versions" : [ ], + "nodes_features" : [ ], "metadata" : { "cluster_uuid" : "clusterUUID", "cluster_uuid_committed" : false, @@ -1016,6 +1042,46 @@ public void testToXContentSameTypeName() throws IOException { }""", IndexVersion.current()), Strings.toString(builder)); } + public void testNodeFeaturesSorted() throws IOException { + ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) + .nodeFeatures(Map.of("node2", Set.of("nf1", "f2", "nf2"), "node1", Set.of("f3", "f2", "f1"), "node3", Set.of())) + .build(); + + XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint(); + builder.startObject(); + writeChunks(clusterState, builder, new ToXContent.MapParams(Map.of("metric", ClusterState.Metric.NODES.toString()))); + builder.endObject(); + + assertThat(Strings.toString(builder), equalTo(""" + { + "cluster_uuid" : "_na_", + "nodes" : { }, + "nodes_versions" : [ ], + "nodes_features" : [ + { + "node_id" : "node1", + "features" : [ + "f1", + "f2", + "f3" + ] + }, + { + "node_id" : "node2", + "features" : [ + "f2", + "nf1", + "nf2" + ] + }, + { + "node_id" : "node3", + "features" : [ ] + } + ] + }""")); + } + private ClusterState buildClusterState() throws IOException { IndexMetadata indexMetadata = IndexMetadata.builder("index") .state(IndexMetadata.State.OPEN) @@ -1057,6 +1123,7 @@ private ClusterState buildClusterState() throws IOException { new CompatibilityVersions(TransportVersion.current(), Map.of(".tasks", new SystemIndexDescriptor.MappingsVersion(1, 1))) ) ) + .nodeFeatures(Map.of("nodeId1", Set.of("f1", "f2"))) .blocks( ClusterBlocks.builder() .addGlobalBlock( @@ -1257,9 +1324,11 @@ public static int expectedChunkCount(ToXContent.Params params, ClusterState clus chunkCount += 2 + clusterState.blocks().indices().size(); } - // nodes, nodes_versions + // nodes, nodes_versions, nodes_features if (metrics.contains(ClusterState.Metric.NODES)) { - chunkCount += 4 + clusterState.nodes().size() + clusterState.compatibilityVersions().size(); + chunkCount += 7 + clusterState.nodes().size() + clusterState.compatibilityVersions().size() + clusterState.clusterFeatures() + .nodeFeatures() + .size(); } // metadata diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/JoinHelperTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/JoinHelperTests.java index 4877ece7712bd..50bbbad05a778 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/JoinHelperTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/JoinHelperTests.java @@ -91,7 +91,8 @@ public void testJoinDeduplication() { new NoneCircuitBreakerService(), Function.identity(), (listener, term) -> listener.onResponse(null), - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); transportService.start(); @@ -258,7 +259,8 @@ public void testJoinFailureOnUnhealthyNodes() { new NoneCircuitBreakerService(), Function.identity(), (listener, term) -> listener.onResponse(null), - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); transportService.start(); @@ -334,12 +336,13 @@ public void testLatestStoredStateFailure() { new NoneCircuitBreakerService(), Function.identity(), (listener, term) -> listener.onFailure(new ElasticsearchException("simulated")), - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); final var joinAccumulator = joinHelper.new CandidateJoinAccumulator(); final var joinListener = new PlainActionFuture(); - joinAccumulator.handleJoinRequest(localNode, CompatibilityVersionsUtils.staticCurrent(), joinListener); + joinAccumulator.handleJoinRequest(localNode, CompatibilityVersionsUtils.staticCurrent(), Set.of(), joinListener); assert joinListener.isDone() == false; final var mockAppender = new MockLogAppender(); diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java index f4eb05b573463..7a79dc44c29fd 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/MessagesTests.java @@ -226,18 +226,20 @@ public void testJoinRequestEqualsHashCodeSerialization() { JoinRequest initialJoinRequest = new JoinRequest( initialJoin.getSourceNode(), CompatibilityVersionsUtils.fakeSystemIndicesRandom(), + Set.of(generateRandomStringArray(10, 10, false)), randomNonNegativeLong(), randomBoolean() ? Optional.empty() : Optional.of(initialJoin) ); EqualsHashCodeTestUtils.checkEqualsAndHashCode( initialJoinRequest, joinRequest -> copyWriteable(joinRequest, writableRegistry(), JoinRequest::new), - joinRequest -> switch (randomInt(3)) { + joinRequest -> switch (randomInt(4)) { case 0 -> { assumeTrue("Optional join needs to be empty", joinRequest.getOptionalJoin().isEmpty()); yield new JoinRequest( createNode(randomAlphaOfLength(10)), joinRequest.getCompatibilityVersions(), + joinRequest.getFeatures(), joinRequest.getMinimumTerm(), joinRequest.getOptionalJoin() ); @@ -248,16 +250,25 @@ yield new JoinRequest( TransportVersionUtils.randomVersion(Set.of(joinRequest.getCompatibilityVersions().transportVersion())), Map.of() ), + joinRequest.getFeatures(), joinRequest.getMinimumTerm(), joinRequest.getOptionalJoin() ); case 2 -> new JoinRequest( joinRequest.getSourceNode(), joinRequest.getCompatibilityVersions(), + Set.of(generateRandomStringArray(10, 10, false)), + joinRequest.getMinimumTerm(), + joinRequest.getOptionalJoin() + ); + case 3 -> new JoinRequest( + joinRequest.getSourceNode(), + joinRequest.getCompatibilityVersions(), + joinRequest.getFeatures(), randomValueOtherThan(joinRequest.getMinimumTerm(), ESTestCase::randomNonNegativeLong), joinRequest.getOptionalJoin() ); - case 3 -> { + case 4 -> { // change OptionalJoin Join newJoin = new Join( joinRequest.getSourceNode(), @@ -269,6 +280,7 @@ yield new JoinRequest( yield new JoinRequest( joinRequest.getSourceNode(), joinRequest.getCompatibilityVersions(), + joinRequest.getFeatures(), joinRequest.getMinimumTerm(), joinRequest.getOptionalJoin().isPresent() && randomBoolean() ? Optional.empty() : Optional.of(newJoin) ); diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java index 63142b4bf62f9..559c0a3628059 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinExecutorTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.features.NodeFeature; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ClusterServiceUtils; @@ -44,6 +45,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; @@ -224,7 +226,16 @@ public void testUpdatesNodeWithNewRoles() throws Exception { final var resultingState = ClusterStateTaskExecutorUtils.executeAndAssertSuccessful( clusterState, executor, - List.of(JoinTask.singleNode(actualNode, CompatibilityVersionsUtils.staticCurrent(), TEST_REASON, NOT_COMPLETED_LISTENER, 0L)) + List.of( + JoinTask.singleNode( + actualNode, + CompatibilityVersionsUtils.staticCurrent(), + Set.of(), + TEST_REASON, + NOT_COMPLETED_LISTENER, + 0L + ) + ) ); assertThat(resultingState.getNodes().get(actualNode.getId()).getRoles(), equalTo(actualNode.getRoles())); @@ -258,6 +269,7 @@ public void testRejectsStatesWithStaleTerm() { JoinTask.singleNode( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, executorTerm @@ -269,6 +281,7 @@ public void testRejectsStatesWithStaleTerm() { new JoinTask.NodeJoinTask( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ) @@ -320,6 +333,7 @@ public void testRejectsStatesWithOtherMaster() { JoinTask.singleNode( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, executorTerm @@ -331,6 +345,7 @@ public void testRejectsStatesWithOtherMaster() { new JoinTask.NodeJoinTask( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ) @@ -373,6 +388,7 @@ public void testRejectsStatesWithNoMasterIfNotBecomingMaster() { JoinTask.singleNode( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, executorTerm @@ -421,12 +437,14 @@ public void testRemovesOlderNodeInstancesWhenBecomingMaster() throws Exception { new JoinTask.NodeJoinTask( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ), new JoinTask.NodeJoinTask( otherNodeNew, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ) @@ -453,6 +471,7 @@ public void testRemovesOlderNodeInstancesWhenBecomingMaster() throws Exception { JoinTask.singleNode( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, executorTerm @@ -460,6 +479,7 @@ public void testRemovesOlderNodeInstancesWhenBecomingMaster() throws Exception { JoinTask.singleNode( otherNodeOld, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, executorTerm @@ -516,12 +536,14 @@ public void testUpdatesVotingConfigExclusionsIfNeeded() throws Exception { new JoinTask.NodeJoinTask( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ), new JoinTask.NodeJoinTask( otherNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ) @@ -540,6 +562,7 @@ public void testUpdatesVotingConfigExclusionsIfNeeded() throws Exception { new JoinTask.NodeJoinTask( masterNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ) @@ -555,6 +578,7 @@ public void testUpdatesVotingConfigExclusionsIfNeeded() throws Exception { JoinTask.singleNode( otherNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, executorTerm @@ -624,7 +648,16 @@ public void testDesiredNodesMembershipIsUpgradedWhenNewNodesJoin() throws Except final var desiredNodes = DesiredNodes.latestFromClusterState(clusterState); var tasks = joiningNodes.stream() - .map(node -> JoinTask.singleNode(node, CompatibilityVersionsUtils.staticCurrent(), TEST_REASON, NOT_COMPLETED_LISTENER, 0L)) + .map( + node -> JoinTask.singleNode( + node, + CompatibilityVersionsUtils.staticCurrent(), + Set.of(), + TEST_REASON, + NOT_COMPLETED_LISTENER, + 0L + ) + ) .toList(); final var updatedClusterState = ClusterStateTaskExecutorUtils.executeAndAssertSuccessful(clusterState, executor, tasks); @@ -661,7 +694,13 @@ public void testDesiredNodesMembershipIsUpgradedWhenANewMasterIsElected() throws clusterState.nodes() .stream() .map( - node -> new JoinTask.NodeJoinTask(node, CompatibilityVersionsUtils.staticCurrent(), TEST_REASON, NOT_COMPLETED_LISTENER) + node -> new JoinTask.NodeJoinTask( + node, + CompatibilityVersionsUtils.staticCurrent(), + Set.of(), + TEST_REASON, + NOT_COMPLETED_LISTENER + ) ), 1L ); @@ -716,7 +755,7 @@ public void testPerNodeLogging() { .createTaskQueue("test", Priority.NORMAL, executor) .submitTask( "test", - JoinTask.singleNode(node1, CompatibilityVersionsUtils.staticCurrent(), TEST_REASON, future, 0L), + JoinTask.singleNode(node1, CompatibilityVersionsUtils.staticCurrent(), Set.of(), TEST_REASON, future, 0L), null ), 10, @@ -745,7 +784,14 @@ public void testPerNodeLogging() { .createTaskQueue("test", Priority.NORMAL, executor) .submitTask( "test", - JoinTask.singleNode(node2, CompatibilityVersionsUtils.staticCurrent(), testReasonWithLink, future, 0L), + JoinTask.singleNode( + node2, + CompatibilityVersionsUtils.staticCurrent(), + Set.of(), + testReasonWithLink, + future, + 0L + ), null ), 10, @@ -785,6 +831,7 @@ public void testResetsNodeLeftGenerationOnNewTerm() throws Exception { new JoinTask.NodeJoinTask( otherNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER ) @@ -798,6 +845,44 @@ public void testResetsNodeLeftGenerationOnNewTerm() throws Exception { assertEquals(0L, resultingState.nodes().getNodeLeftGeneration()); } + public void testSetsNodeFeaturesWhenRejoining() throws Exception { + final AllocationService allocationService = createAllocationService(); + final RerouteService rerouteService = (reason, priority, listener) -> listener.onResponse(null); + + final NodeJoinExecutor executor = new NodeJoinExecutor(allocationService, rerouteService); + + final DiscoveryNode masterNode = DiscoveryNodeUtils.create(UUIDs.base64UUID()); + + final DiscoveryNode rejoinNode = DiscoveryNodeUtils.create(UUIDs.base64UUID()); + final ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT) + .nodes( + DiscoveryNodes.builder().add(masterNode).localNodeId(masterNode.getId()).masterNodeId(masterNode.getId()).add(rejoinNode) + ) + .nodeFeatures(Map.of(masterNode.getId(), Set.of("f1", "f2"), rejoinNode.getId(), Set.of())) + .build(); + + assertThat(clusterState.clusterFeatures().clusterHasFeature(new NodeFeature("f1")), is(false)); + assertThat(clusterState.clusterFeatures().clusterHasFeature(new NodeFeature("f2")), is(false)); + + final var resultingState = ClusterStateTaskExecutorUtils.executeAndAssertSuccessful( + clusterState, + executor, + List.of( + JoinTask.singleNode( + rejoinNode, + CompatibilityVersionsUtils.staticCurrent(), + Set.of("f1", "f2"), + TEST_REASON, + NOT_COMPLETED_LISTENER, + 0L + ) + ) + ); + + assertThat(resultingState.clusterFeatures().clusterHasFeature(new NodeFeature("f1")), is(true)); + assertThat(resultingState.clusterFeatures().clusterHasFeature(new NodeFeature("f2")), is(true)); + } + private DesiredNodeWithStatus createActualizedDesiredNode() { return new DesiredNodeWithStatus(randomDesiredNode(), DesiredNodeWithStatus.Status.ACTUALIZED); } @@ -808,9 +893,17 @@ private DesiredNodeWithStatus createPendingDesiredNode() { private static JoinTask createRandomTask(DiscoveryNode node, long term) { return randomBoolean() - ? JoinTask.singleNode(node, CompatibilityVersionsUtils.staticCurrent(), TEST_REASON, NOT_COMPLETED_LISTENER, term) + ? JoinTask.singleNode(node, CompatibilityVersionsUtils.staticCurrent(), Set.of(), TEST_REASON, NOT_COMPLETED_LISTENER, term) : JoinTask.completingElection( - Stream.of(new JoinTask.NodeJoinTask(node, CompatibilityVersionsUtils.staticCurrent(), TEST_REASON, NOT_COMPLETED_LISTENER)), + Stream.of( + new JoinTask.NodeJoinTask( + node, + CompatibilityVersionsUtils.staticCurrent(), + Set.of(), + TEST_REASON, + NOT_COMPLETED_LISTENER + ) + ), term ); } diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinTests.java index a068029667eb2..e9e5b1c5338df 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeJoinTests.java @@ -230,7 +230,8 @@ protected void onSendRequest(long requestId, String action, TransportRequest req new Reconfigurator(Settings.EMPTY, clusterSettings), LeaderHeartbeatService.NO_OP, StatefulPreVoteCollector::new, - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); transportService.start(); transportService.acceptIncomingRequests(); @@ -318,7 +319,7 @@ public void testJoinWithHigherTermElectsLeader() { assertNull(coordinator.getStateForMasterService().nodes().getMasterNodeId()); long newTerm = initialTerm + randomLongBetween(1, 10); Future fut = joinNodeAsync( - new JoinRequest(node1, version1, newTerm, Optional.of(new Join(node1, node0, newTerm, initialTerm, initialVersion))) + new JoinRequest(node1, version1, Set.of(), newTerm, Optional.of(new Join(node1, node0, newTerm, initialTerm, initialVersion))) ); assertEquals(Coordinator.Mode.LEADER, coordinator.getMode()); assertNull(coordinator.getStateForMasterService().nodes().getMasterNodeId()); @@ -345,7 +346,13 @@ public void testJoinWithHigherTermButBetterStateGetsRejected() { expectThrows( CoordinationStateRejectedException.class, () -> joinNodeAndRun( - new JoinRequest(node1, version1, newTerm, Optional.of(new Join(node1, node0, newTerm, initialTerm, higherVersion))) + new JoinRequest( + node1, + version1, + Set.of(), + newTerm, + Optional.of(new Join(node1, node0, newTerm, initialTerm, higherVersion)) + ) ) ); assertFalse(isLocalNodeElectedMaster()); @@ -368,6 +375,7 @@ public void testJoinWithHigherTermButBetterStateStillElectsMasterThroughSelfJoin new JoinRequest( node1, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node1, node0, newTerm, initialTerm, higherVersion)) ) @@ -391,6 +399,7 @@ public void testJoinElectedLeader() { new JoinRequest( node0, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node0, node0, newTerm, initialTerm, initialVersion)) ) @@ -401,6 +410,7 @@ public void testJoinElectedLeader() { new JoinRequest( node1, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node1, node0, newTerm, initialTerm, initialVersion)) ) @@ -425,6 +435,7 @@ public void testJoinElectedLeaderWithHigherTerm() { new JoinRequest( node0, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node0, node0, newTerm, initialTerm, initialVersion)) ) @@ -432,7 +443,7 @@ public void testJoinElectedLeaderWithHigherTerm() { assertTrue(isLocalNodeElectedMaster()); long newerTerm = newTerm + randomLongBetween(1, 10); - joinNodeAndRun(new JoinRequest(node1, CompatibilityVersionsUtils.staticCurrent(), newerTerm, Optional.empty())); + joinNodeAndRun(new JoinRequest(node1, CompatibilityVersionsUtils.staticCurrent(), Set.of(), newerTerm, Optional.empty())); assertThat(coordinator.getCurrentTerm(), greaterThanOrEqualTo(newerTerm)); assertTrue(isLocalNodeElectedMaster()); } @@ -454,6 +465,7 @@ public void testJoinAccumulation() { new JoinRequest( node0, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node0, node0, newTerm, initialTerm, initialVersion)) ) @@ -465,6 +477,7 @@ public void testJoinAccumulation() { new JoinRequest( node1, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node1, node0, newTerm, initialTerm, initialVersion)) ) @@ -476,6 +489,7 @@ public void testJoinAccumulation() { new JoinRequest( node2, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node2, node0, newTerm, initialTerm, initialVersion)) ) @@ -505,6 +519,7 @@ public void testJoinFollowerWithHigherTerm() throws Exception { new JoinRequest( node1, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newerTerm, Optional.of(new Join(node1, node0, newerTerm, initialTerm, initialVersion)) ) @@ -539,6 +554,7 @@ public void testJoinUpdateVotingConfigExclusion() throws Exception { new JoinRequest( knownJoiningNode, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), initialTerm, Optional.of(new Join(knownJoiningNode, initialNode, newerTerm, initialTerm, initialVersion)) ) @@ -627,7 +643,7 @@ public void testJoinFollowerFails() throws Exception { assertThat( expectThrows( CoordinationStateRejectedException.class, - () -> joinNodeAndRun(new JoinRequest(node1, version1, newTerm, Optional.empty())) + () -> joinNodeAndRun(new JoinRequest(node1, version1, Set.of(), newTerm, Optional.empty())) ).getMessage(), containsString("join target is a follower") ); @@ -647,7 +663,7 @@ public void testBecomeFollowerFailsPendingJoin() throws Exception { ); long newTerm = initialTerm + randomLongBetween(1, 10); Future fut = joinNodeAsync( - new JoinRequest(node0, version0, newTerm, Optional.of(new Join(node0, node0, newTerm, initialTerm, initialVersion))) + new JoinRequest(node0, version0, Set.of(), newTerm, Optional.of(new Join(node0, node0, newTerm, initialTerm, initialVersion))) ); deterministicTaskQueue.runAllRunnableTasks(); assertFalse(fut.isDone()); @@ -698,6 +714,7 @@ public void testConcurrentJoining() { node -> new JoinRequest( node, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node, localNode, newTerm, initialTerm, initialVersion)) ) @@ -715,6 +732,7 @@ public void testConcurrentJoining() { return new JoinRequest( node, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node, localNode, newTerm, initialTerm, initialVersion)) ); @@ -723,6 +741,7 @@ public void testConcurrentJoining() { return new JoinRequest( node, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node, localNode, randomLongBetween(0, initialTerm), initialTerm, initialVersion)) ); @@ -731,6 +750,7 @@ public void testConcurrentJoining() { return new JoinRequest( node, CompatibilityVersionsUtils.staticCurrent(), + Set.of(), newTerm, Optional.of(new Join(node, localNode, newTerm, initialTerm, initialVersion + randomLongBetween(1, 10))) ); diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeLeftExecutorTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeLeftExecutorTests.java index 4f03cbe3a1fc0..2c4bd7444f060 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/NodeLeftExecutorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/NodeLeftExecutorTests.java @@ -17,7 +17,6 @@ import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterStateTaskExecutorUtils; -import org.elasticsearch.cluster.version.CompatibilityVersions; import org.elasticsearch.common.Priority; import org.elasticsearch.test.ClusterServiceUtils; import org.elasticsearch.test.ESTestCase; @@ -27,7 +26,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -69,15 +67,8 @@ public void testRerouteAfterRemovingNodes() throws Exception { final AtomicReference remainingNodesClusterState = new AtomicReference<>(); final NodeLeftExecutor executor = new NodeLeftExecutor(allocationService) { @Override - protected ClusterState remainingNodesClusterState( - ClusterState currentState, - DiscoveryNodes.Builder remainingNodesBuilder, - Map compatibilityVersions - ) { - remainingNodesClusterState.set( - super.remainingNodesClusterState(currentState, remainingNodesBuilder, compatibilityVersions) - ); - return remainingNodesClusterState.get(); + void remainingNodesClusterState(ClusterState state) { + remainingNodesClusterState.set(state); } }; diff --git a/server/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java b/server/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java index 3b1b316e44bae..da1f2aa89642b 100644 --- a/server/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/DiscoveryModuleTests.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; import java.util.function.Supplier; @@ -115,7 +116,8 @@ private DiscoveryModule newModule( mock(RerouteService.class), null, new NoneCircuitBreakerService(), - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); } diff --git a/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java b/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java new file mode 100644 index 0000000000000..0a799934ae64e --- /dev/null +++ b/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.features; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.node.DiscoveryNodeUtils; +import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.index.IndexVersion; +import org.elasticsearch.index.IndexVersions; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.VersionUtils; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; + +public class FeatureServiceTests extends ESTestCase { + + private static class TestFeatureSpecification implements FeatureSpecification { + private final Set features; + private final Map historicalFeatures; + + private TestFeatureSpecification(Set features, Map historicalFeatures) { + this.features = features; + this.historicalFeatures = historicalFeatures; + } + + @Override + public Set getFeatures() { + return features; + } + + @Override + public Map getHistoricalFeatures() { + return historicalFeatures; + } + } + + public void testFailsDuplicateFeatures() { + // these all need to be separate classes to trigger the exception + FeatureSpecification fs1 = new TestFeatureSpecification(Set.of(new NodeFeature("f1")), Map.of()) { + }; + FeatureSpecification fs2 = new TestFeatureSpecification(Set.of(new NodeFeature("f1")), Map.of()) { + }; + FeatureSpecification hfs1 = new TestFeatureSpecification(Set.of(), Map.of(new NodeFeature("f1"), Version.V_8_11_0)) { + }; + FeatureSpecification hfs2 = new TestFeatureSpecification(Set.of(), Map.of(new NodeFeature("f1"), Version.V_8_11_0)) { + }; + + assertThat( + expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs1, fs2))).getMessage(), + containsString("Duplicate feature") + ); + assertThat( + expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(hfs1, hfs2))).getMessage(), + containsString("Duplicate feature") + ); + assertThat( + expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs1, hfs1))).getMessage(), + containsString("Duplicate feature") + ); + } + + public void testFailsNonHistoricalVersion() { + FeatureSpecification fs = new TestFeatureSpecification( + Set.of(), + Map.of(new NodeFeature("f1"), FeatureService.CLUSTER_FEATURES_ADDED_VERSION) + ); + + assertThat( + expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs))).getMessage(), + containsString("not a historical version") + ); + } + + public void testGetNodeFeaturesCombinesAllSpecs() { + List specs = List.of( + new TestFeatureSpecification(Set.of(new NodeFeature("f1"), new NodeFeature("f2")), Map.of()), + new TestFeatureSpecification(Set.of(new NodeFeature("f3")), Map.of()), + new TestFeatureSpecification(Set.of(new NodeFeature("f4"), new NodeFeature("f5")), Map.of()), + new TestFeatureSpecification(Set.of(), Map.of()) + ); + + FeatureService service = new FeatureService(specs); + assertThat(service.getNodeFeatures(), containsInAnyOrder("f1", "f2", "f3", "f4", "f5")); + } + + public void testStateHasFeatures() { + List specs = List.of( + new TestFeatureSpecification(Set.of(new NodeFeature("f1"), new NodeFeature("f2")), Map.of()), + new TestFeatureSpecification(Set.of(new NodeFeature("f3")), Map.of()), + new TestFeatureSpecification(Set.of(new NodeFeature("f4"), new NodeFeature("f5")), Map.of()), + new TestFeatureSpecification(Set.of(), Map.of()) + ); + + ClusterState state = ClusterState.builder(ClusterName.DEFAULT) + .nodeFeatures( + Map.of("node1", Set.of("f1", "f2", "nf1"), "node2", Set.of("f1", "f2", "nf2"), "node3", Set.of("f1", "f2", "nf1")) + ) + .build(); + + FeatureService service = new FeatureService(specs); + assertTrue(service.clusterHasFeature(state, new NodeFeature("f1"))); + assertTrue(service.clusterHasFeature(state, new NodeFeature("f2"))); + assertFalse(service.clusterHasFeature(state, new NodeFeature("nf1"))); + assertFalse(service.clusterHasFeature(state, new NodeFeature("nf2"))); + assertFalse(service.clusterHasFeature(state, new NodeFeature("nf3"))); + } + + private static ClusterState stateWithMinVersion(Version version) { + DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(); + nodes.add(DiscoveryNodeUtils.builder("node").version(version, IndexVersions.ZERO, IndexVersion.current()).build()); + for (int n = randomInt(5); n >= 0; n--) { + nodes.add( + DiscoveryNodeUtils.builder("node" + n) + .version( + VersionUtils.randomVersionBetween(random(), version, Version.CURRENT), + IndexVersions.ZERO, + IndexVersion.current() + ) + .build() + ); + } + + return ClusterState.builder(ClusterName.DEFAULT).nodes(nodes).build(); + } + + public void testStateHasHistoricalFeatures() { + NodeFeature v8_11_0 = new NodeFeature("hf_8.11.0"); + NodeFeature v8_10_0 = new NodeFeature("hf_8.10.0"); + NodeFeature v7_17_0 = new NodeFeature("hf_7.17.0"); + List specs = List.of( + new TestFeatureSpecification(Set.of(), Map.of(v8_11_0, Version.V_8_11_0)), + new TestFeatureSpecification(Set.of(), Map.of(v8_10_0, Version.V_8_10_0)), + new TestFeatureSpecification(Set.of(), Map.of(v7_17_0, Version.V_7_17_0)) + ); + + FeatureService service = new FeatureService(specs); + assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_11_0), v8_11_0)); + assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_11_0), v8_10_0)); + assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_11_0), v7_17_0)); + + assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_8_10_0), v8_11_0)); + assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_10_0), v8_10_0)); + assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_10_0), v7_17_0)); + + assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_17_0), v8_11_0)); + assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_17_0), v8_10_0)); + assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_7_17_0), v7_17_0)); + + assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_16_0), v8_11_0)); + assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_16_0), v8_10_0)); + assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_16_0), v7_17_0)); + } +} diff --git a/server/src/test/java/org/elasticsearch/indices/ShardLimitValidatorTests.java b/server/src/test/java/org/elasticsearch/indices/ShardLimitValidatorTests.java index 90f3cb8eacf68..e1b3bb4fe9c49 100644 --- a/server/src/test/java/org/elasticsearch/indices/ShardLimitValidatorTests.java +++ b/server/src/test/java/org/elasticsearch/indices/ShardLimitValidatorTests.java @@ -12,8 +12,8 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.Metadata; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodeRole; +import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.shards.ShardCounts; @@ -25,8 +25,6 @@ import org.elasticsearch.index.IndexVersion; import org.elasticsearch.test.ESTestCase; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import static org.elasticsearch.cluster.metadata.MetadataIndexStateServiceTests.addClosedIndex; @@ -260,22 +258,13 @@ public static ClusterState createClusterForShardLimitTest( } public static DiscoveryNodes createDiscoveryNodes(int nodesInCluster, String group) { - Map dataNodes = new HashMap<>(); + DiscoveryNodes.Builder builder = DiscoveryNodes.builder(); for (int i = 0; i < nodesInCluster; i++) { - dataNodes.put(randomAlphaOfLengthBetween(5, 15), createNode(group)); - } - DiscoveryNodes nodes = mock(DiscoveryNodes.class); - when(nodes.getDataNodes()).thenReturn(dataNodes); - return nodes; - } - - private static DiscoveryNode createNode(String group) { - DiscoveryNode mock = mock(DiscoveryNode.class); - if (ShardLimitValidator.FROZEN_GROUP.equals(group)) { - when(mock.getRoles()).thenReturn(randomBoolean() ? DiscoveryNodeRole.roles() : Set.of(DiscoveryNodeRole.DATA_FROZEN_NODE_ROLE)); - } else { - when(mock.getRoles()).thenReturn( - randomBoolean() + Set roles; + if (ShardLimitValidator.FROZEN_GROUP.equals(group)) { + roles = randomBoolean() ? DiscoveryNodeRole.roles() : Set.of(DiscoveryNodeRole.DATA_FROZEN_NODE_ROLE); + } else { + roles = randomBoolean() ? DiscoveryNodeRole.roles() : Set.of( randomFrom( @@ -284,10 +273,12 @@ private static DiscoveryNode createNode(String group) { DiscoveryNodeRole.DATA_WARM_NODE_ROLE, DiscoveryNodeRole.DATA_COLD_NODE_ROLE ) - ) - ); + ); + } + + builder.add(DiscoveryNodeUtils.builder(randomAlphaOfLengthBetween(5, 15)).roles(roles).build()); } - return mock; + return builder.build(); } private static Metadata.Builder freezeMetadata(Metadata.Builder builder, Metadata metadata) { diff --git a/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java b/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java index c95e72d24bfaa..c81ea1579828f 100644 --- a/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java +++ b/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java @@ -72,7 +72,6 @@ import org.elasticsearch.cluster.service.ClusterStateTaskExecutorUtils; import org.elasticsearch.cluster.service.MasterService; import org.elasticsearch.cluster.version.CompatibilityVersions; -import org.elasticsearch.cluster.version.CompatibilityVersionsUtils; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; @@ -409,9 +408,16 @@ public ClusterState addNode(ClusterState clusterState, DiscoveryNode discoveryNo new NodeJoinExecutor(allocationService, (s, p, r) -> {}), clusterState, List.of( - JoinTask.singleNode(discoveryNode, CompatibilityVersionsUtils.staticCurrent(), DUMMY_REASON, ActionListener.running(() -> { - throw new AssertionError("should not complete publication"); - }), clusterState.term()) + JoinTask.singleNode( + discoveryNode, + new CompatibilityVersions(transportVersion, Map.of()), + Set.of(), + DUMMY_REASON, + ActionListener.running(() -> { + throw new AssertionError("should not complete publication"); + }), + clusterState.term() + ) ) ); } @@ -427,6 +433,7 @@ public ClusterState joinNodesAndBecomeMaster(ClusterState clusterState, List new JoinTask.NodeJoinTask( node, new CompatibilityVersions(transportVersion, Map.of()), + Set.of(), DUMMY_REASON, ActionListener.running(() -> { throw new AssertionError("should not complete publication"); diff --git a/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java b/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java index 1b5ff3f39be22..c43ae59e9b84c 100644 --- a/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java +++ b/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java @@ -2203,7 +2203,8 @@ public void start(ClusterState initialState) { new Reconfigurator(clusterService.getSettings(), clusterService.getClusterSettings()), LeaderHeartbeatService.NO_OP, StatefulPreVoteCollector::new, - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); masterService.setClusterStatePublisher(coordinator); coordinator.start(); diff --git a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java index 7c856328d4384..3d90a253e48c6 100644 --- a/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cluster/coordination/AbstractCoordinatorTestCase.java @@ -1152,7 +1152,8 @@ public RecyclerBytesStreamOutput newNetworkBytesStream() { coordinationServices.getReconfigurator(), coordinationServices.getLeaderHeartbeatService(), coordinationServices.getPreVoteCollectorFactory(), - CompatibilityVersionsUtils.staticCurrent() + CompatibilityVersionsUtils.staticCurrent(), + Set.of() ); coordinationDiagnosticsService = new CoordinationDiagnosticsService( clusterService, diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractClusterStateLicenseServiceTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractClusterStateLicenseServiceTestCase.java index ad9ffe61df928..fc257258eb213 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractClusterStateLicenseServiceTestCase.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractClusterStateLicenseServiceTestCase.java @@ -14,6 +14,7 @@ import org.elasticsearch.cluster.node.DiscoveryNodeUtils; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; @@ -72,7 +73,8 @@ protected void setInitialState(License license, XPackLicenseState licenseState, when(state.metadata()).thenReturn(metadata); final DiscoveryNode mockNode = getLocalNode(); when(discoveryNodes.getMasterNode()).thenReturn(mockNode); - when(discoveryNodes.stream()).thenAnswer(invocation -> Stream.of(mockNode)); + when(discoveryNodes.stream()).thenAnswer(i -> Stream.of(mockNode)); + when(discoveryNodes.iterator()).thenAnswer(i -> Iterators.single(mockNode)); when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(false); when(discoveryNodes.getMinNodeVersion()).thenReturn(mockNode.getVersion()); when(state.nodes()).thenReturn(discoveryNodes); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java index 3e52fcfce2bc0..5eba6ff46d093 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicenseClusterChangeTests.java @@ -65,7 +65,7 @@ public void testNoNotificationOnExistingLicense() throws Exception { public void testSelfGeneratedLicenseGeneration() throws Exception { DiscoveryNode master = DiscoveryNodeUtils.builder("b").roles(emptySet()).build(); ClusterState oldState = ClusterState.builder(new ClusterName("a")) - .nodes(DiscoveryNodes.builder().masterNodeId(master.getId()).add(master)) + .nodes(DiscoveryNodes.builder().masterNodeId(master.getId()).localNodeId(master.getId()).add(master)) .build(); when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true); ClusterState newState = ClusterState.builder(oldState).nodes(discoveryNodes).build(); diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/TransportVersionUtilsTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/TransportVersionUtilsTests.java index c85fda5eee4b3..939ccde7df6c4 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/TransportVersionUtilsTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/utils/TransportVersionUtilsTests.java @@ -40,6 +40,7 @@ public class TransportVersionUtilsTests extends ESTestCase { null, null, transportVersions, + null, ClusterBlocks.EMPTY_CLUSTER_BLOCK, null, false, @@ -66,6 +67,7 @@ public void testIsMinTransformVersionSameAsCurrent() { null, null, transportVersions1, + null, ClusterBlocks.EMPTY_CLUSTER_BLOCK, null, false, diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java index dd85aa6bf7bf7..c782f25fdad4c 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java @@ -769,7 +769,13 @@ public void testToXContent() throws IOException { "max_index_version":%s } }, - "nodes_versions": [] + "nodes_versions": [], + "nodes_features": [ + { + "node_id": "_node_id", + "features": [] + } + ] }, "cluster_settings": { "cluster": { From cea1323c7f1f8fd5f43e694b426d6564e800ff4d Mon Sep 17 00:00:00 2001 From: Andrei Dan Date: Mon, 30 Oct 2023 14:40:01 +0000 Subject: [PATCH 199/203] [DSL] skip deleting indices that have in-progress downsampling operations (#101495) A backing index for which we kicked off downsampling will have a target `downsample-{interval}-.ds...` index created outside the data stream. The source (backing) index will have a downsample status of `STARTED` at this point. When downsampling completes te downsample status of the source index is changed to `SUCCESS` and DSL will atomically swap the downsample index in the data stream instead of the source index, and delete the source index. If retention time for the source index lapses before we finished the downsample operation, the target downsample index will remain in the system, outside the data stream as the source index will be deleted. This makes the DSL retention wait for a potential in-progress downsampling operation to complete, before executing retention. --- docs/changelog/101495.yaml | 5 ++ .../lifecycle/DataStreamLifecycleService.java | 27 +++++-- .../DataStreamLifecycleServiceTests.java | 79 +++++++++++++++++++ 3 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 docs/changelog/101495.yaml diff --git a/docs/changelog/101495.yaml b/docs/changelog/101495.yaml new file mode 100644 index 0000000000000..f61c9b824b77c --- /dev/null +++ b/docs/changelog/101495.yaml @@ -0,0 +1,5 @@ +pr: 101495 +summary: "[DSL] skip deleting indices that have in-progress downsampling operations" +area: Data streams +type: bug +issues: [] diff --git a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java index d1ea1b589b5a5..5d2d95a3dc954 100644 --- a/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java +++ b/modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java @@ -90,6 +90,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetadata.APIBlock.WRITE; import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.STARTED; +import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.UNKNOWN; import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_DOWNSAMPLE_STATUS; import static org.elasticsearch.datastreams.DataStreamsPlugin.LIFECYCLE_CUSTOM_INDEX_METADATA_KEY; @@ -764,14 +765,30 @@ private Set maybeExecuteRetention(ClusterState state, DataStream dataStre for (Index index : backingIndicesOlderThanRetention) { if (indicesToExcludeForRemainingRun.contains(index) == false) { - indicesToBeRemoved.add(index); IndexMetadata backingIndex = metadata.index(index); assert backingIndex != null : "the data stream backing indices must exist"; - // there's an opportunity here to batch the delete requests (i.e. delete 100 indices / request) - // let's start simple and reevaluate - String indexName = backingIndex.getIndex().getName(); - deleteIndexOnce(indexName, "the lapsed [" + retention + "] retention period"); + IndexMetadata.DownsampleTaskStatus downsampleStatus = INDEX_DOWNSAMPLE_STATUS.get(backingIndex.getSettings()); + // we don't want to delete the source index if they have an in-progress downsampling operation because the + // target downsample index will remain in the system as a standalone index + if (downsampleStatus.equals(UNKNOWN)) { + indicesToBeRemoved.add(index); + + // there's an opportunity here to batch the delete requests (i.e. delete 100 indices / request) + // let's start simple and reevaluate + String indexName = backingIndex.getIndex().getName(); + deleteIndexOnce(indexName, "the lapsed [" + retention + "] retention period"); + } else { + // there's an opportunity here to cancel downsampling and delete the source index now + logger.trace( + "Data stream lifecycle skips deleting index [{}] even though its retention period [{}] has lapsed " + + "because there's a downsampling operation currently in progress for this index. Current downsampling " + + "status is [{}]. When downsampling completes, DSL will delete this index.", + index.getName(), + retention, + downsampleStatus + ); + } } } } diff --git a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java index f1e74a936e781..fd9664dd94493 100644 --- a/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java +++ b/modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java @@ -98,6 +98,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetadata.APIBlock.WRITE; import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.STARTED; import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.SUCCESS; +import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.UNKNOWN; import static org.elasticsearch.datastreams.DataStreamsPlugin.LIFECYCLE_CUSTOM_INDEX_METADATA_KEY; import static org.elasticsearch.datastreams.lifecycle.DataStreamLifecycleFixtures.createDataStream; import static org.elasticsearch.datastreams.lifecycle.DataStreamLifecycleService.DATA_STREAM_MERGE_POLICY_TARGET_FACTOR_SETTING; @@ -293,6 +294,84 @@ public void testRetentionNotExecutedForTSIndicesWithinTimeBounds() { assertThat(((DeleteIndexRequest) deleteIndexRequest).indices(), is(new String[] { dataStream.getIndices().get(0).getName() })); } + public void testRetentionSkippedWhilstDownsamplingInProgress() { + String dataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); + int numBackingIndices = 3; + Metadata.Builder builder = Metadata.builder(); + DataStream dataStream = createDataStream( + builder, + dataStreamName, + numBackingIndices, + settings(IndexVersion.current()), + DataStreamLifecycle.newBuilder().dataRetention(TimeValue.timeValueMillis(0)).build(), + now + ); + builder.put(dataStream); + + ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(builder).build(); + + { + Metadata metadata = state.metadata(); + Metadata.Builder metaBuilder = Metadata.builder(metadata); + + String firstBackingIndex = DataStream.getDefaultBackingIndexName(dataStreamName, 1); + IndexMetadata indexMetadata = metadata.index(firstBackingIndex); + IndexMetadata.Builder indexMetaBuilder = IndexMetadata.builder(indexMetadata); + indexMetaBuilder.settings( + Settings.builder() + .put(indexMetadata.getSettings()) + .put( + IndexMetadata.INDEX_DOWNSAMPLE_STATUS_KEY, + randomValueOtherThan(UNKNOWN, () -> randomFrom(IndexMetadata.DownsampleTaskStatus.values())) + ) + ); + indexMetaBuilder.putCustom( + LIFECYCLE_CUSTOM_INDEX_METADATA_KEY, + Map.of(FORCE_MERGE_COMPLETED_TIMESTAMP_METADATA_KEY, String.valueOf(System.currentTimeMillis())) + ); + metaBuilder.put(indexMetaBuilder); + state = ClusterState.builder(ClusterName.DEFAULT).metadata(metaBuilder).build(); + + dataStreamLifecycleService.run(state); + assertThat(clientSeenRequests.size(), is(2)); // rollover the write index and delete the second generation + assertThat(clientSeenRequests.get(0), instanceOf(RolloverRequest.class)); + assertThat(clientSeenRequests.get(1), instanceOf(DeleteIndexRequest.class)); + assertThat( + ((DeleteIndexRequest) clientSeenRequests.get(1)).indices()[0], + is(DataStream.getDefaultBackingIndexName(dataStreamName, 2)) + ); + } + + { + // a lack of downsample status (i.e. the default `UNKNOWN`) must not prevent retention + Metadata metadata = state.metadata(); + Metadata.Builder metaBuilder = Metadata.builder(metadata); + + String firstBackingIndex = DataStream.getDefaultBackingIndexName(dataStreamName, 1); + IndexMetadata indexMetadata = metadata.index(firstBackingIndex); + IndexMetadata.Builder indexMetaBuilder = IndexMetadata.builder(indexMetadata); + indexMetaBuilder.settings( + Settings.builder().put(indexMetadata.getSettings()).putNull(IndexMetadata.INDEX_DOWNSAMPLE_STATUS_KEY) + ); + metaBuilder.put(indexMetaBuilder); + state = ClusterState.builder(ClusterName.DEFAULT).metadata(metaBuilder).build(); + + dataStreamLifecycleService.run(state); + assertThat(clientSeenRequests.size(), is(3)); // rollover the write index and delete the other two generations + assertThat(clientSeenRequests.get(0), instanceOf(RolloverRequest.class)); + assertThat(clientSeenRequests.get(1), instanceOf(DeleteIndexRequest.class)); + assertThat( + ((DeleteIndexRequest) clientSeenRequests.get(1)).indices()[0], + is(DataStream.getDefaultBackingIndexName(dataStreamName, 2)) + ); + assertThat(clientSeenRequests.get(2), instanceOf(DeleteIndexRequest.class)); + assertThat( + ((DeleteIndexRequest) clientSeenRequests.get(2)).indices()[0], + is(DataStream.getDefaultBackingIndexName(dataStreamName, 1)) + ); + } + } + public void testIlmManagedIndicesAreSkipped() { String dataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); int numBackingIndices = 3; From 5ee1e72913b464852eb1e1b6bcfe2172f580d998 Mon Sep 17 00:00:00 2001 From: Alan Woodward Date: Mon, 30 Oct 2023 14:55:31 +0000 Subject: [PATCH 200/203] Fix test to check for the correct exception (#101525) --- .../script/mustache/SearchTemplateIT.java | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java index ed0da415693d0..517828cbeba3c 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/elasticsearch/script/mustache/SearchTemplateIT.java @@ -11,15 +11,14 @@ import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.rest.RestStatus; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.DummyQueryParserPlugin; import org.elasticsearch.search.SearchService; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.xcontent.XContentParseException; import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xcontent.json.JsonXContent; import org.junit.Before; @@ -199,24 +198,14 @@ public void testBadTemplate() { String script = """ { - "script": { - "lang": "mustache", - "source": ""\" - { - "query": { + "query": { "multi_match": { "query": "{{query_string}}", "fields": [{{#text_fields}}"{{name}}^{{boost}}"{{/text_fields}}] } - }, - "from": "{{from}}", - "size": "{{size}}" - } - ""\", - "params": { - "query_string": "My query string" - } - } + }, + "from": "{{from}}", + "size": "{{size}}" }"""; Map params = Map.of( @@ -229,19 +218,18 @@ public void testBadTemplate() { ); { - ParsingException e = expectThrows(ParsingException.class, () -> { + XContentParseException e = expectThrows(XContentParseException.class, () -> { new SearchTemplateRequestBuilder(client()).setRequest(new SearchRequest()) .setScript(script) .setScriptParams(params) .setScriptType(ScriptType.INLINE) .get(); }); - assertThat(e.getMessage(), containsString("Unknown key")); - assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + assertThat(e.getMessage(), containsString("Unexpected character")); } { - ParsingException e = expectThrows(ParsingException.class, () -> { + XContentParseException e = expectThrows(XContentParseException.class, () -> { new SearchTemplateRequestBuilder(client()).setRequest(new SearchRequest()) .setScript(script) .setScriptParams(params) @@ -249,8 +237,7 @@ public void testBadTemplate() { .setSimulate(true) .get(); }); - assertThat(e.getMessage(), containsString("Unknown key")); - assertThat(e.status(), equalTo(RestStatus.BAD_REQUEST)); + assertThat(e.getMessage(), containsString("Unexpected character")); } } From 47b3d4e5b00c4c85470f4fe1f84318a3ad40369e Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Oct 2023 15:18:16 +0000 Subject: [PATCH 201/203] Fix trappy threading of Repository#getRepositoryData (#101447) This method normally completes its listener on the calling thread, but in some circumstances it will use a different thread which is a little trappy. Typically we're on the right thread already so it'd be ideal if we could specify only to fork the response listener if we can't use the calling thread, similarly to `SubscribableListener#addListener`. This commit adds this functionality, but does not adjust any non-test callers to use it yet. Relates #101445 --- .../indices/recovery/IndexRecoveryIT.java | 3 +- .../plan/ShardSnapshotsServiceIT.java | 5 +- ...etadataLoadingDuringSnapshotRestoreIT.java | 3 +- .../TransportCleanupRepositoryAction.java | 5 +- .../index/shard/StoreRecovery.java | 3 + .../repositories/FilterRepository.java | 5 +- .../repositories/IndexSnapshotsService.java | 6 +- .../repositories/InvalidRepository.java | 3 +- .../repositories/RepositoriesService.java | 16 ++- .../repositories/Repository.java | 17 ++- .../repositories/UnknownTypeRepository.java | 3 +- .../blobstore/BlobStoreRepository.java | 22 ++- .../snapshots/RestoreService.java | 29 ++-- .../snapshots/SnapshotsService.java | 126 ++++++++++-------- .../RepositoriesServiceTests.java | 3 +- .../blobstore/BlobStoreRepositoryTests.java | 46 ++++++- .../snapshots/RestoreServiceTests.java | 8 +- .../snapshots/SnapshotResiliencyTests.java | 2 +- .../index/shard/RestoreOnlyRepository.java | 3 +- .../ESBlobStoreRepositoryIntegTestCase.java | 6 +- .../AbstractSnapshotIntegTestCase.java | 9 +- .../xpack/ccr/repository/CcrRepository.java | 6 +- .../SourceOnlySnapshotShardTests.java | 7 +- ...ransportMountSearchableSnapshotAction.java | 6 +- .../slm/SLMGetExpiredSnapshotsAction.java | 8 +- .../SLMGetExpiredSnapshotsActionTests.java | 7 +- 26 files changed, 239 insertions(+), 118 deletions(-) diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java index fc07643b08ef8..8fe047e6cdc0a 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/IndexRecoveryIT.java @@ -108,6 +108,7 @@ import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; +import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase; import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.test.BackgroundIndexer; @@ -868,7 +869,7 @@ public void testSnapshotRecovery() throws Exception { RecoveryResponse response = indicesAdmin().prepareRecoveries(INDEX_NAME).execute().actionGet(); Repository repository = internalCluster().getAnyMasterNodeInstance(RepositoriesService.class).repository(REPO_NAME); - final RepositoryData repositoryData = PlainActionFuture.get(repository::getRepositoryData); + final RepositoryData repositoryData = AbstractSnapshotIntegTestCase.getRepositoryData(repository); for (Map.Entry> indexRecoveryStates : response.shardRecoveryStates().entrySet()) { assertThat(indexRecoveryStates.getKey(), equalTo(INDEX_NAME)); diff --git a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/plan/ShardSnapshotsServiceIT.java b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/plan/ShardSnapshotsServiceIT.java index ea850fb1d0007..e34d5059b4991 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/plan/ShardSnapshotsServiceIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/indices/recovery/plan/ShardSnapshotsServiceIT.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Executor; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; @@ -104,12 +105,12 @@ public FailingRepo( } @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { if (failGetRepositoryData) { listener.onFailure(new IOException("Failure getting repository data")); return; } - super.getRepositoryData(listener); + super.getRepositoryData(responseExecutor, listener); } @Override diff --git a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java index ac8e1f67cd9c1..d171dd2c89c78 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java @@ -11,7 +11,6 @@ import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse; -import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.metadata.RepositoryMetadata; @@ -193,7 +192,7 @@ public Metadata getSnapshotGlobalMetadata(SnapshotId snapshotId) { public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, SnapshotId snapshotId, IndexId indexId) throws IOException { indicesMetadata.computeIfAbsent(key(snapshotId.getName(), indexId.getName()), (s) -> new AtomicInteger(0)).incrementAndGet(); - return super.getSnapshotIndexMetaData(PlainActionFuture.get(this::getRepositoryData), snapshotId, indexId); + return super.getSnapshotIndexMetaData(AbstractSnapshotIntegTestCase.getRepositoryData(this), snapshotId, indexId); } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java index 374b9b19f16ca..2c8371b0af4f2 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java @@ -154,7 +154,10 @@ private void cleanupRepo(String repositoryName, ActionListener repositoryDataListener = new ListenableFuture<>(); - repository.getRepositoryData(repositoryDataListener); + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // Listener is lightweight, only submits a cluster state update task, no need to fork + repositoryDataListener + ); repositoryDataListener.addListener(listener.delegateFailureAndWrap((delegate, repositoryData) -> { final long repositoryStateId = repositoryData.getGenId(); logger.info("Running cleanup operations on repository [{}][{}]", repositoryName, repositoryStateId); diff --git a/server/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java b/server/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java index 72f958a5fd9ca..ded3ffa4ebcc0 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java +++ b/server/src/main/java/org/elasticsearch/index/shard/StoreRecovery.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.TimeValue; import org.elasticsearch.index.Index; @@ -552,6 +553,8 @@ private void restore( if (indexId.getId().equals(IndexMetadata.INDEX_UUID_NA_VALUE)) { // BwC path, running against an old version master that did not add the IndexId to the recovery source repository.getRepositoryData( + // TODO no need to fork back to GENERIC if using cached repo data, see #101445 + EsExecutors.DIRECT_EXECUTOR_SERVICE, new ThreadedActionListener<>( indexShard.getThreadPool().generic(), indexIdListener.map(repositoryData -> repositoryData.resolveIndexId(indexId.getName())) diff --git a/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java b/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java index e0fbb082307ee..b4dd0a2f37b39 100644 --- a/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/FilterRepository.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Set; +import java.util.concurrent.Executor; public class FilterRepository implements Repository { @@ -61,8 +62,8 @@ public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, Sna } @Override - public void getRepositoryData(ActionListener listener) { - in.getRepositoryData(listener); + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { + in.getRepositoryData(responseExecutor, listener); } @Override diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexSnapshotsService.java b/server/src/main/java/org/elasticsearch/repositories/IndexSnapshotsService.java index e274268eb4b29..c89905aa3eb74 100644 --- a/server/src/main/java/org/elasticsearch/repositories/IndexSnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/repositories/IndexSnapshotsService.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.shard.ShardId; @@ -127,7 +128,10 @@ public void getLatestSuccessfulSnapshotForShard( delegate.onResponse(indexShardSnapshotInfo); })); - repository.getRepositoryData(repositoryDataStepListener); + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading here, do we need to fork, see #101445? + repositoryDataStepListener + ); } private Repository getRepository(String repositoryName) { diff --git a/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java b/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java index 55a77551702da..ad0f956a16643 100644 --- a/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/InvalidRepository.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.util.Collection; +import java.util.concurrent.Executor; /** * Represents a repository that exists in the cluster state but could not be instantiated on a node, typically due to invalid configuration. @@ -68,7 +69,7 @@ public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, Sna } @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { listener.onFailure(createCreationException()); } diff --git a/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java b/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java index da9610a77a563..095f70a3e5966 100644 --- a/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java +++ b/server/src/main/java/org/elasticsearch/repositories/RepositoriesService.java @@ -37,6 +37,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.SuppressForbidden; @@ -182,7 +183,15 @@ public void registerRepository(final PutRepositoryRequest request, final ActionL verifyStep.addListener( listener.delegateFailureAndWrap( (l, ignored) -> threadPool.generic() - .execute(ActionRunnable.wrap(getRepositoryDataStep, ll -> repository(request.name()).getRepositoryData(ll))) + .execute( + ActionRunnable.wrap( + getRepositoryDataStep, + ll -> repository(request.name()).getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading, do we need to fork, see #101445? + ll + ) + ) + ) ) ); @@ -630,7 +639,10 @@ public void getRepositoryData(final String repositoryName, final ActionListener< try { Repository repository = repository(repositoryName); assert repository != null; // should only be called once we've validated the repository exists - repository.getRepositoryData(listener); + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading here, do we need to fork, see #101445? + listener + ); } catch (Exception e) { listener.onFailure(e); } diff --git a/server/src/main/java/org/elasticsearch/repositories/Repository.java b/server/src/main/java/org/elasticsearch/repositories/Repository.java index 8f07997f3f392..1fd01631818bc 100644 --- a/server/src/main/java/org/elasticsearch/repositories/Repository.java +++ b/server/src/main/java/org/elasticsearch/repositories/Repository.java @@ -30,6 +30,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.concurrent.Executor; import java.util.function.Function; /** @@ -117,13 +118,17 @@ public void onFailure(Exception e) { IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, SnapshotId snapshotId, IndexId index) throws IOException; /** - * Returns a {@link RepositoryData} to describe the data in the repository, including the snapshots - * and the indices across all snapshots found in the repository. Throws a {@link RepositoryException} - * if there was an error in reading the data. - * @param listener listener that may be resolved on different kinds of threads including transport and cluster state applier threads - * and therefore must fork to a new thread for executing any long running actions + * Returns a {@link RepositoryData} to describe the data in the repository, including the snapshots and the indices across all snapshots + * found in the repository. Completes the listener with a {@link RepositoryException} if there was an error in reading the data. + * + * @param responseExecutor Executor to use to complete the listener if not using the calling thread. Using {@link + * org.elasticsearch.common.util.concurrent.EsExecutors#DIRECT_EXECUTOR_SERVICE} means to complete the listener + * on the thread which ultimately resolved the {@link RepositoryData}, which might be a low-latency transport or + * cluster applier thread so make sure not to do anything slow or expensive in that case. + * @param listener Listener which is either completed on the calling thread (if the {@link RepositoryData} is immediately + * available, e.g. from an in-memory cache), otherwise it is completed using {@code responseExecutor}. */ - void getRepositoryData(ActionListener listener); + void getRepositoryData(Executor responseExecutor, ActionListener listener); /** * Finalizes snapshotting process diff --git a/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java b/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java index 4b08c0f19ea8a..b9da0b1663c59 100644 --- a/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/UnknownTypeRepository.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.util.Collection; +import java.util.concurrent.Executor; /** * This class represents a repository that could not be initialized due to unknown type. @@ -66,7 +67,7 @@ public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, Sna } @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { listener.onFailure(createUnknownTypeException()); } diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index f66d406b95321..1af4a1b8f1337 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -1576,7 +1576,14 @@ record RootBlobUpdateResult(RepositoryData oldRepositoryData, RepositoryData new SubscribableListener // Get the current RepositoryData - .newForked(this::getRepositoryData) + .newForked( + listener -> getRepositoryData( + // TODO we might already be on a SNAPSHOT thread, make it so that we're always on a SNAPSHOT thread here and then we + // can avoid a little more forking below + EsExecutors.DIRECT_EXECUTOR_SERVICE, + listener + ) + ) // Identify and write the missing metadata .andThen((l, existingRepositoryData) -> { @@ -2017,7 +2024,7 @@ public void endVerification(String seed) { private final AtomicReference latestKnownRepositoryData = new AtomicReference<>(RepositoryData.EMPTY); @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { // RepositoryData is the responsibility of the elected master: we shouldn't be loading it on other nodes as we don't have good // consistency guarantees there, but electedness is too ephemeral to assert. We can say for sure that this node should be // master-eligible, which is almost as strong since all other snapshot-related activity happens on data nodes whether they be @@ -2047,7 +2054,7 @@ public void getRepositoryData(ActionListener listener) { logger.debug(""" [{}] loading repository metadata for the first time, trying to determine correct generation and to store it in the \ cluster state""", metadata.name()); - if (initializeRepoGenerationTracking(listener)) { + if (initializeRepoGenerationTracking(responseExecutor, listener)) { return; } // else there was a concurrent modification, retry from the start } else { @@ -2056,7 +2063,7 @@ public void getRepositoryData(ActionListener listener) { metadata.name(), latestKnownRepoGen ); - repoDataLoadDeduplicator.execute(listener); + repoDataLoadDeduplicator.execute(new ThreadedActionListener<>(responseExecutor, listener)); return; } } @@ -2076,13 +2083,14 @@ private RepositoryException notStartedException() { * This ensures that operations using {@code SnapshotsService#executeConsistentStateUpdate} right after mounting a fresh repository will * have a consistent view of the {@link RepositoryData} before any data has been written to the repository. * - * @param listener listener to resolve with new repository data + * @param responseExecutor executor to use to complete the listener if not completing it on the calling thread + * @param listener listener to resolve with new repository data * @return {@code true} if this method at least started the initialization process successfully and will eventually complete the * listener, {@code false} if there was some concurrent state change which prevents us from starting repo generation tracking (typically * that some other node got there first) and the caller should check again and possibly retry or complete the listener in some other * way. */ - private boolean initializeRepoGenerationTracking(ActionListener listener) { + private boolean initializeRepoGenerationTracking(Executor responseExecutor, ActionListener listener) { final SubscribableListener listenerToSubscribe; final ActionListener listenerToComplete; @@ -2170,7 +2178,7 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState) .addListener(listenerToComplete); } - listenerToSubscribe.addListener(listener, EsExecutors.DIRECT_EXECUTOR_SERVICE, threadPool.getThreadContext()); + listenerToSubscribe.addListener(listener, responseExecutor, threadPool.getThreadContext()); return true; } diff --git a/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java b/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java index d9fb26c4f4030..49a4c25258278 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/RestoreService.java @@ -55,6 +55,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.Maps; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.XContentHelper; @@ -253,7 +254,10 @@ public void restoreSnapshot( final String repositoryName = request.repository(); Repository repository = repositoriesService.repository(repositoryName); final ListenableFuture repositoryDataListener = new ListenableFuture<>(); - repository.getRepositoryData(repositoryDataListener); + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading here, do we need to fork, see #101445? + repositoryDataListener + ); repositoryDataListener.addListener( listener.delegateFailureAndWrap( @@ -516,17 +520,20 @@ static void refreshRepositoryUuids(boolean enabled, RepositoriesService reposito if (repository instanceof BlobStoreRepository && repository.getMetadata().uuid().equals(RepositoryData.MISSING_UUID)) { final var repositoryName = repository.getMetadata().name(); logger.info("refreshing repository UUID for repository [{}]", repositoryName); - repository.getRepositoryData(ActionListener.releaseAfter(new ActionListener<>() { - @Override - public void onResponse(RepositoryData repositoryData) { - logger.debug(() -> format("repository UUID [{}] refresh completed", repositoryName)); - } + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading here, do we need to fork, see #101445? + ActionListener.releaseAfter(new ActionListener<>() { + @Override + public void onResponse(RepositoryData repositoryData) { + logger.debug(() -> format("repository UUID [{}] refresh completed", repositoryName)); + } - @Override - public void onFailure(Exception e) { - logger.debug(() -> format("repository UUID [{}] refresh failed", repositoryName), e); - } - }, refs.acquire())); + @Override + public void onFailure(Exception e) { + logger.debug(() -> format("repository UUID [{}] refresh failed", repositoryName), e); + } + }, refs.acquire()) + ); } } } diff --git a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index e2a9746df4c66..7159f1a1ee5dd 100644 --- a/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/server/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -278,6 +278,7 @@ private void submitCreateSnapshotRequest( RepositoryMetadata initialRepositoryMetadata ) { repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // Listener is lightweight, only submits a cluster state update task, no need to fork listener.delegateFailure( (l, repositoryData) -> masterServiceTaskQueue.submitTask( "create_snapshot [" + snapshot.getSnapshotId().getName() + ']', @@ -462,14 +463,17 @@ private void startCloning(Repository repository, SnapshotsInProgress.Entry clone } } // 2. step, load the number of shards we have in each index to be cloned from the index metadata. - repository.getRepositoryData(ActionListener.wrap(repositoryData -> { - for (IndexId index : indices) { - executor.execute(ActionRunnable.supply(shardCountListener, () -> { - final IndexMetadata metadata = repository.getSnapshotIndexMetaData(repositoryData, sourceSnapshot, index); - return Tuple.tuple(index, metadata.getNumberOfShards()); - })); - } - }, onFailure)); + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading here, do we need to fork, see #101445? + ActionListener.wrap(repositoryData -> { + for (IndexId index : indices) { + executor.execute(ActionRunnable.supply(shardCountListener, () -> { + final IndexMetadata metadata = repository.getSnapshotIndexMetaData(repositoryData, sourceSnapshot, index); + return Tuple.tuple(index, metadata.getNumberOfShards()); + })); + } + }, onFailure) + ); }, onFailure)); // 3. step, we have all the shard counts, now update the cluster state to have clone jobs in the snap entry @@ -1268,21 +1272,25 @@ private void endSnapshot(SnapshotsInProgress.Entry entry, Metadata metadata, @Nu final String repoName = snapshot.getRepository(); if (tryEnterRepoLoop(repoName)) { if (repositoryData == null) { - repositoriesService.repository(repoName).getRepositoryData(new ActionListener<>() { - @Override - public void onResponse(RepositoryData repositoryData) { - if (newFinalization) { - finalizeSnapshotEntry(snapshot, metadata, repositoryData); - } else { - runNextQueuedOperation(repositoryData, repoName, false); - } - } + repositoriesService.repository(repoName) + .getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO contemplate threading here, do we need to fork, see #101445? + new ActionListener<>() { + @Override + public void onResponse(RepositoryData repositoryData) { + if (newFinalization) { + finalizeSnapshotEntry(snapshot, metadata, repositoryData); + } else { + runNextQueuedOperation(repositoryData, repoName, false); + } + } - @Override - public void onFailure(Exception e) { - submitUnbatchedTask("fail repo tasks for [" + repoName + "]", new FailPendingRepoTasksTask(repoName, e)); - } - }); + @Override + public void onFailure(Exception e) { + submitUnbatchedTask("fail repo tasks for [" + repoName + "]", new FailPendingRepoTasksTask(repoName, e)); + } + } + ); } else { if (newFinalization) { finalizeSnapshotEntry(snapshot, metadata, repositoryData); @@ -2271,48 +2279,52 @@ private void executeConsistentStateUpdate( Consumer onFailure ) { final RepositoryMetadata repositoryMetadataStart = repository.getMetadata(); - repository.getRepositoryData(ActionListener.wrap(repositoryData -> { - final ClusterStateUpdateTask updateTask = createUpdateTask.apply(repositoryData); - submitUnbatchedTask(source, new ClusterStateUpdateTask(updateTask.priority(), updateTask.timeout()) { + repository.getRepositoryData( + // Listener is lightweight, only submits a cluster state update task, no need to fork + EsExecutors.DIRECT_EXECUTOR_SERVICE, + ActionListener.wrap(repositoryData -> { + final ClusterStateUpdateTask updateTask = createUpdateTask.apply(repositoryData); + submitUnbatchedTask(source, new ClusterStateUpdateTask(updateTask.priority(), updateTask.timeout()) { - private boolean executedTask = false; + private boolean executedTask = false; - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - // Comparing the full metadata here on purpose instead of simply comparing the safe generation. - // If the safe generation has changed, then we have to reload repository data and start over. - // If the pending generation has changed we are in the midst of a write operation and might pick up the - // updated repository data and state on the retry. We don't want to wait for the write to finish though - // because it could fail for any number of reasons so we just retry instead of waiting on the cluster state - // to change in any form. - if (repositoryMetadataStart.equals( - RepositoriesMetadata.get(currentState).repository(repository.getMetadata().name()) - )) { - executedTask = true; - return updateTask.execute(currentState); + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + // Comparing the full metadata here on purpose instead of simply comparing the safe generation. + // If the safe generation has changed, then we have to reload repository data and start over. + // If the pending generation has changed we are in the midst of a write operation and might pick up the + // updated repository data and state on the retry. We don't want to wait for the write to finish though + // because it could fail for any number of reasons so we just retry instead of waiting on the cluster state + // to change in any form. + if (repositoryMetadataStart.equals( + RepositoriesMetadata.get(currentState).repository(repository.getMetadata().name()) + )) { + executedTask = true; + return updateTask.execute(currentState); + } + return currentState; } - return currentState; - } - @Override - public void onFailure(Exception e) { - if (executedTask) { - updateTask.onFailure(e); - } else { - onFailure.accept(e); + @Override + public void onFailure(Exception e) { + if (executedTask) { + updateTask.onFailure(e); + } else { + onFailure.accept(e); + } } - } - @Override - public void clusterStateProcessed(ClusterState oldState, ClusterState newState) { - if (executedTask) { - updateTask.clusterStateProcessed(oldState, newState); - } else { - executeConsistentStateUpdate(repository, createUpdateTask, source, onFailure); + @Override + public void clusterStateProcessed(ClusterState oldState, ClusterState newState) { + if (executedTask) { + updateTask.clusterStateProcessed(oldState, newState); + } else { + executeConsistentStateUpdate(repository, createUpdateTask, source, onFailure); + } } - } - }); - }, onFailure)); + }); + }, onFailure) + ); } /** Deletes snapshot from repository diff --git a/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java b/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java index ab2f7b8f0a9a7..d5bc5ad236b54 100644 --- a/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java +++ b/server/src/test/java/org/elasticsearch/repositories/RepositoriesServiceTests.java @@ -51,6 +51,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.isA; @@ -347,7 +348,7 @@ public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, Sna } @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { listener.onResponse(null); } diff --git a/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java b/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java index a5fbc6bf4d89e..907eedbfa7bf6 100644 --- a/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java +++ b/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.TimeValue; import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; @@ -41,6 +42,7 @@ import org.elasticsearch.repositories.ShardGenerations; import org.elasticsearch.repositories.SnapshotShardContext; import org.elasticsearch.repositories.fs.FsRepository; +import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase; import org.elasticsearch.snapshots.SnapshotId; import org.elasticsearch.snapshots.SnapshotState; import org.elasticsearch.test.ESIntegTestCase; @@ -50,12 +52,14 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -290,7 +294,7 @@ public void testRepositoryDataDetails() throws Exception { ); }; - final RepositoryData repositoryData = PlainActionFuture.get(repository::getRepositoryData); + final RepositoryData repositoryData = AbstractSnapshotIntegTestCase.getRepositoryData(repository); final RepositoryData.SnapshotDetails snapshotDetails = repositoryData.getSnapshotDetails(snapshotId); snapshotDetailsAsserter.accept(snapshotDetails); @@ -308,7 +312,7 @@ public void testRepositoryDataDetails() throws Exception { repositoryData.getGenId() ); - snapshotDetailsAsserter.accept(PlainActionFuture.get(repository::getRepositoryData).getSnapshotDetails(snapshotId)); + snapshotDetailsAsserter.accept(AbstractSnapshotIntegTestCase.getRepositoryData(repository).getSnapshotDetails(snapshotId)); } private static void writeIndexGen(BlobStoreRepository repository, RepositoryData repositoryData, long generation) throws Exception { @@ -434,7 +438,7 @@ public void testGetRepositoryDataThreadContext() { threadContext.putHeader(headerName, headerValue); threadPool.generic().execute(ActionRunnable.wrap(listeners.acquire(), l -> { safeAwait(barrier); - repo.getRepositoryData(l.map(repositoryData -> { + repo.getRepositoryData(EsExecutors.DIRECT_EXECUTOR_SERVICE, l.map(repositoryData -> { assertEquals(headerValue, threadContext.getHeader(headerName)); return null; })); @@ -445,6 +449,42 @@ public void testGetRepositoryDataThreadContext() { future.actionGet(10, TimeUnit.SECONDS); } + public void testGetRepositoryDataForking() { + final var forkedListeners = Collections.synchronizedList(new ArrayList()); + final var future = new PlainActionFuture(); + try (var listeners = new RefCountingListener(future)) { + final var repo = setupRepo(); + final int threads = between(1, 5); + final var barrier = new CyclicBarrier(threads); + final var threadPool = client().threadPool(); + final var testThread = Thread.currentThread(); + final var resultsCountDown = new CountDownLatch(threads); + for (int i = 0; i < threads; i++) { + threadPool.generic().execute(ActionRunnable.wrap(listeners.acquire(), l -> { + final var callingThread = Thread.currentThread(); + safeAwait(barrier); + repo.getRepositoryData(runnable -> { + forkedListeners.add(runnable); + resultsCountDown.countDown(); + }, l.map(repositoryData -> { + final var currentThread = Thread.currentThread(); + if (currentThread == testThread) { + assertEquals(0, resultsCountDown.getCount()); + } else { + assertSame(callingThread, currentThread); + resultsCountDown.countDown(); + } + return null; + })); + })); + } + safeAwait(resultsCountDown); + forkedListeners.forEach(Runnable::run); + repo.getRepositoryData(runnable -> fail("should use cached value and not fork"), listeners.acquire(ignored -> {})); + } + future.actionGet(10, TimeUnit.SECONDS); + } + private Environment createEnvironment() { Path home = createTempDir(); return TestEnvironment.newEnvironment( diff --git a/server/src/test/java/org/elasticsearch/snapshots/RestoreServiceTests.java b/server/src/test/java/org/elasticsearch/snapshots/RestoreServiceTests.java index fee89d54d7946..e7e6aae195bec 100644 --- a/server/src/test/java/org/elasticsearch/snapshots/RestoreServiceTests.java +++ b/server/src/test/java/org/elasticsearch/snapshots/RestoreServiceTests.java @@ -147,7 +147,7 @@ public void testRefreshRepositoryUuidsRefreshesAsNeeded() { when(freshBlobStoreRepo.getMetadata()).thenReturn( new RepositoryMetadata(repositoryName, randomAlphaOfLength(3), Settings.EMPTY).withUuid(UUIDs.randomBase64UUID()) ); - doThrow(new AssertionError("repo UUID already known")).when(freshBlobStoreRepo).getRepositoryData(any()); + doThrow(new AssertionError("repo UUID already known")).when(freshBlobStoreRepo).getRepositoryData(any(), any()); } case 3 -> { final Repository staleBlobStoreRepo = mock(BlobStoreRepository.class); @@ -158,16 +158,14 @@ public void testRefreshRepositoryUuidsRefreshesAsNeeded() { ); doAnswer(invocationOnMock -> { assertTrue(pendingRefreshes.remove(repositoryName)); - @SuppressWarnings("unchecked") - ActionListener repositoryDataListener = (ActionListener) invocationOnMock - .getArguments()[0]; + final ActionListener repositoryDataListener = invocationOnMock.getArgument(1); if (randomBoolean()) { repositoryDataListener.onResponse(null); } else { repositoryDataListener.onFailure(new Exception("simulated")); } return null; - }).when(staleBlobStoreRepo).getRepositoryData(any()); + }).when(staleBlobStoreRepo).getRepositoryData(any(), any()); } } } diff --git a/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java b/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java index c43ae59e9b84c..07acfa96a6a93 100644 --- a/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java +++ b/server/src/test/java/org/elasticsearch/snapshots/SnapshotResiliencyTests.java @@ -1223,7 +1223,7 @@ public void testRunConcurrentSnapshots() { private RepositoryData getRepositoryData(Repository repository) { final PlainActionFuture res = PlainActionFuture.newFuture(); - repository.getRepositoryData(res); + repository.getRepositoryData(deterministicTaskQueue::scheduleNow, res); deterministicTaskQueue.runAllRunnableTasks(); assertTrue(res.isDone()); return res.actionGet(); diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java b/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java index 6b6577553c64b..12f5989d560aa 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/RestoreOnlyRepository.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.concurrent.Executor; import static java.util.Collections.emptyList; import static org.elasticsearch.repositories.RepositoryData.EMPTY_REPO_GEN; @@ -75,7 +76,7 @@ public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, Sna } @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { final IndexId indexId = new IndexId(indexName, "blah"); listener.onResponse( new RepositoryData( diff --git a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java index cfff900e372f5..40c336376d6f9 100644 --- a/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/repositories/blobstore/ESBlobStoreRepositoryIntegTestCase.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.blobstore.support.BlobMetadata; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Streams; import org.elasticsearch.core.TimeValue; import org.elasticsearch.repositories.IndexId; @@ -32,6 +33,7 @@ import org.elasticsearch.repositories.Repository; import org.elasticsearch.repositories.RepositoryData; import org.elasticsearch.repositories.RepositoryMissingException; +import org.elasticsearch.snapshots.AbstractSnapshotIntegTestCase; import org.elasticsearch.snapshots.SnapshotMissingException; import org.elasticsearch.snapshots.SnapshotRestoreException; import org.elasticsearch.test.ESIntegTestCase; @@ -67,7 +69,7 @@ public abstract class ESBlobStoreRepositoryIntegTestCase extends ESIntegTestCase { public static RepositoryData getRepositoryData(Repository repository) { - return PlainActionFuture.get(repository::getRepositoryData); + return AbstractSnapshotIntegTestCase.getRepositoryData(repository); } protected abstract String repositoryType(); @@ -474,7 +476,7 @@ public void testIndicesDeletedFromRepository() throws Exception { final PlainActionFuture repositoryData = PlainActionFuture.newFuture(); threadPool.executor(ThreadPool.Names.SNAPSHOT).execute(() -> { indicesBlobContainer.set(repository.blobStore().blobContainer(repository.basePath().add("indices"))); - repository.getRepositoryData(repositoryData); + repository.getRepositoryData(EsExecutors.DIRECT_EXECUTOR_SERVICE, repositoryData); }); for (IndexId indexId : repositoryData.actionGet().getIndices().values()) { diff --git a/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java b/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java index 0ac67a4c54830..94c40b2d53b00 100644 --- a/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/snapshots/AbstractSnapshotIntegTestCase.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersions; @@ -177,8 +178,12 @@ protected RepositoryData getRepositoryData(String repository) { return getRepositoryData((Repository) getRepositoryOnMaster(repository)); } - protected RepositoryData getRepositoryData(Repository repository) { - return PlainActionFuture.get(repository::getRepositoryData); + public static RepositoryData getRepositoryData(Repository repository) { + return PlainActionFuture.get( + listener -> repository.getRepositoryData(EsExecutors.DIRECT_EXECUTOR_SERVICE, listener), + 10, + TimeUnit.SECONDS + ); } public static long getFailureCount(String repository) { diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java index 42a9710e92023..35b02b26eca9c 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/repository/CcrRepository.java @@ -257,9 +257,9 @@ public IndexMetadata getSnapshotIndexMetaData(RepositoryData repositoryData, Sna } @Override - public void getRepositoryData(ActionListener listener) { + public void getRepositoryData(Executor responseExecutor, ActionListener listener) { try { - csDeduplicator.execute(listener.map(response -> { + csDeduplicator.execute(new ThreadedActionListener<>(responseExecutor, listener.map(response -> { final Metadata remoteMetadata = response.getMetadata(); final String[] concreteAllIndices = remoteMetadata.getConcreteAllIndices(); final Map copiedSnapshotIds = Maps.newMapWithExpectedSize(concreteAllIndices.length); @@ -287,7 +287,7 @@ public void getRepositoryData(ActionListener listener) { IndexMetaDataGenerations.EMPTY, MISSING_UUID ); - })); + }))); } catch (Exception e) { assert false; listener.onFailure(e); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotShardTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotShardTests.java index de7b35ae0c78d..462e1942018ee 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotShardTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/snapshots/sourceonly/SourceOnlySnapshotShardTests.java @@ -41,6 +41,7 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.Environment; @@ -364,7 +365,11 @@ public void onFailure(Exception e) { ); IndexMetadata metadata = runAsSnapshot( threadPool, - () -> repository.getSnapshotIndexMetaData(PlainActionFuture.get(repository::getRepositoryData), snapshotId, indexId) + () -> repository.getSnapshotIndexMetaData( + PlainActionFuture.get(listener -> repository.getRepositoryData(EsExecutors.DIRECT_EXECUTOR_SERVICE, listener)), + snapshotId, + indexId + ) ); IndexShard restoredShard = newShard( shardRouting, diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java index 59d6e7a5feac3..b72fc99778a31 100644 --- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java +++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/action/TransportMountSearchableSnapshotAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexSettings; @@ -180,7 +181,10 @@ protected void masterOperation( SearchableSnapshots.getSearchableRepository(repository); // just check it's valid final ListenableFuture repositoryDataListener = new ListenableFuture<>(); - repository.getRepositoryData(repositoryDataListener); + repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO fork to SNAPSHOT_META and drop the forking below, see #101445 + repositoryDataListener + ); repositoryDataListener.addListener(listener.delegateFailureAndWrap((delegate, repoData) -> { final Map indexIds = repoData.getIndices(); if (indexIds.containsKey(indexName) == false) { diff --git a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsAction.java b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsAction.java index 20f6f2d57571d..bd2d040d76299 100644 --- a/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsAction.java +++ b/x-pack/plugin/slm/src/main/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.Tuple; import org.elasticsearch.repositories.GetSnapshotInfoContext; @@ -125,7 +126,12 @@ protected void doExecute(Task task, Request request, ActionListener li perRepositoryListener -> SubscribableListener // Get repository data - .newForked(repository::getRepositoryData) + .newForked( + l -> repository.getRepositoryData( + EsExecutors.DIRECT_EXECUTOR_SERVICE, // TODO use retentionExecutor, see #101445? + l + ) + ) // Collect snapshot details by policy, and get any missing details by reading SnapshotInfo .andThen( diff --git a/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsActionTests.java b/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsActionTests.java index 5dd48946c270c..c876bb83f919d 100644 --- a/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsActionTests.java +++ b/x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/SLMGetExpiredSnapshotsActionTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.cluster.metadata.RepositoryMetadata; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.DeterministicTaskQueue; +import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; import org.elasticsearch.index.IndexVersion; @@ -176,7 +177,7 @@ record SeenSnapshotInfo(SnapshotId snapshotId, String policyId) {} SubscribableListener - .newForked(repository::getRepositoryData) + .newForked(l -> repository.getRepositoryData(EsExecutors.DIRECT_EXECUTOR_SERVICE, l)) .andThen( (l, rd) -> SLMGetExpiredSnapshotsAction.getSnapshotDetailsByPolicy(repository, rd, l) @@ -257,7 +258,7 @@ private static RepositoryData.SnapshotDetails mkDetails(String policyId) { private static Repository createMockRepository(ThreadPool threadPool, List snapshotInfos) { final var repository = mock(Repository.class); doAnswer(invocation -> { - final ActionListener listener = invocation.getArgument(0); + final ActionListener listener = invocation.getArgument(1); threadPool.generic().execute(ActionRunnable.supply(listener, () -> { var repositoryData = RepositoryData.EMPTY; for (SnapshotInfo snapshotInfo : snapshotInfos) { @@ -282,7 +283,7 @@ private static Repository createMockRepository(ThreadPool threadPool, List { final GetSnapshotInfoContext getSnapshotInfoContext = invocation.getArgument(0); From 22cfa04ccbaa733002c4d5f3844c175580dd7da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Mon, 30 Oct 2023 16:31:03 +0100 Subject: [PATCH 202/203] Revert "Optimize ContentPath#pathAsText (#98244)" (#101521) Some benchmarks indicate that parts of the change made in #98244 is negatively affecting document parsing. This change reverts it until we have better benchmarks that support it or a solution that avoids the Stack datastructure most likely contributing to this. This reverts commit db22afa. --- docs/changelog/98244.yaml | 6 --- .../index/mapper/ContentPath.java | 48 +++++++++-------- .../index/mapper/ContentPathTests.java | 51 ++----------------- 3 files changed, 31 insertions(+), 74 deletions(-) delete mode 100644 docs/changelog/98244.yaml diff --git a/docs/changelog/98244.yaml b/docs/changelog/98244.yaml deleted file mode 100644 index e1dde59a83e47..0000000000000 --- a/docs/changelog/98244.yaml +++ /dev/null @@ -1,6 +0,0 @@ -pr: 98244 -summary: Optimize ContentPath#pathAsText -area: Search -type: enhancement -issues: - - 94544 diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ContentPath.java b/server/src/main/java/org/elasticsearch/index/mapper/ContentPath.java index 1386028b4ea5b..e90e2bbe1afd0 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ContentPath.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ContentPath.java @@ -8,35 +8,42 @@ package org.elasticsearch.index.mapper; -import java.util.Stack; - public final class ContentPath { private static final char DELIMITER = '.'; private final StringBuilder sb; - private final Stack delimiterIndexes; + + private int index = 0; + + private String[] path = new String[10]; + private boolean withinLeafObject = false; public ContentPath() { this.sb = new StringBuilder(); - this.delimiterIndexes = new Stack<>(); } - public void add(String name) { - // Store the location of the previous final delimiter onto the stack, - // which will be the index of the 2nd last delimiter after appending the new name - delimiterIndexes.add(sb.length() - 1); - sb.append(name).append(DELIMITER); + String[] getPath() { + // used for testing + return path; } - public void remove() { - if (delimiterIndexes.isEmpty()) { - throw new IllegalStateException("Content path is empty"); + public void add(String name) { + path[index++] = name; + if (index == path.length) { // expand if needed + expand(); } + } + + private void expand() { + String[] newPath = new String[path.length + 10]; + System.arraycopy(path, 0, newPath, 0, path.length); + path = newPath; + } - // Deletes the last node added to the stringbuilder by deleting from the 2nd last delimiter onwards - sb.setLength(delimiterIndexes.pop() + 1); + public void remove() { + path[--index] = null; } public void setWithinLeafObject(boolean withinLeafObject) { @@ -48,16 +55,15 @@ public boolean isWithinLeafObject() { } public String pathAsText(String name) { - // If length is 0 we know that we are at the root, so return the provided string directly - if (length() == 0) { - return name; + sb.setLength(0); + for (int i = 0; i < index; i++) { + sb.append(path[i]).append(DELIMITER); } - - return sb + name; + sb.append(name); + return sb.toString(); } public int length() { - // The amount of delimiters we've added tells us the amount of nodes that have been added to the path - return delimiterIndexes.size(); + return index; } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ContentPathTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ContentPathTests.java index d66d927e8e0d0..829e2fcbe79d6 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ContentPathTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ContentPathTests.java @@ -23,7 +23,10 @@ public void testAddPath() { public void testRemovePath() { ContentPath contentPath = new ContentPath(); contentPath.add("foo"); + String[] path = contentPath.getPath(); + assertEquals("foo", path[0]); contentPath.remove(); + assertNull(path[0]); assertEquals(0, contentPath.length()); String pathAsText = contentPath.pathAsText("bar"); assertEquals("bar", pathAsText); @@ -33,21 +36,7 @@ public void testRemovePathException() { ContentPath contentPath = new ContentPath(); contentPath.add("foo"); contentPath.remove(); - expectThrows(IllegalStateException.class, contentPath::remove); - } - - public void testRootPath() { - ContentPath contentPath = new ContentPath(); - assertEquals("root", contentPath.pathAsText("root")); - assertEquals(0, contentPath.length()); - } - - public void testNestedPath() { - ContentPath contentPath = new ContentPath(); - contentPath.add("root"); - contentPath.add("inner"); - assertEquals("root.inner.leaf1", contentPath.pathAsText("leaf1")); - assertEquals("root.inner.leaf2", contentPath.pathAsText("leaf2")); + expectThrows(IndexOutOfBoundsException.class, contentPath::remove); } public void testBehaviourWithLongerPath() { @@ -93,15 +82,6 @@ public void testPathAsText() { assertEquals("foo.bar.baz", contentPath.pathAsText("baz")); } - public void testPathTextAfterLeafRemoval() { - ContentPath contentPath = new ContentPath(); - contentPath.add("root"); - contentPath.add("inner"); - contentPath.add("leaf"); - contentPath.remove(); - assertEquals("root.inner.newLeaf", contentPath.pathAsText("newLeaf")); - } - public void testPathAsTextAfterRemove() { ContentPath contentPath = new ContentPath(); contentPath.add("foo"); @@ -120,27 +100,4 @@ public void testPathAsTextAfterRemoveAndMoreAdd() { contentPath.add("baz"); assertEquals("foo.baz.qux", contentPath.pathAsText("qux")); } - - public void testPathTextAfterRootRemovalAndNewPathAdded() { - ContentPath contentPath = new ContentPath(); - contentPath.add("root"); - contentPath.add("inner"); - contentPath.add("leaf"); - contentPath.remove(); - contentPath.remove(); - contentPath.remove(); - contentPath.add("newRoot"); - contentPath.add("newInner"); - assertEquals("newRoot.newInner.newLeaf", contentPath.pathAsText("newLeaf")); - } - - public void testPathTextRemovalAfterPathAsTextHasBeenCalled() { - ContentPath contentPath = new ContentPath(); - contentPath.add("root"); - contentPath.add("inner"); - contentPath.pathAsText("leaf"); - contentPath.remove(); - contentPath.add("newInner"); - assertEquals("root.newInner.newLeaf", contentPath.pathAsText("newLeaf")); - } } From fd11123d67fd1d41e1bb129a6715d6b01d797e29 Mon Sep 17 00:00:00 2001 From: Mike Pellegrini Date: Mon, 30 Oct 2023 12:08:01 -0400 Subject: [PATCH 203/203] [Search Applications] Return 400 response when template does not render correctly (#101474) [Search Applications] Return 400 response when template rendering produces invalid JSON --- docs/changelog/101474.yaml | 5 + x-pack/plugin/ent-search/qa/rest/roles.yml | 3 +- .../52_search_application_render_query.yml | 96 +++++++++++++++++++ .../55_search_application_search.yml | 46 +++++++++ .../SearchApplicationTemplateService.java | 16 +++- 5 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 docs/changelog/101474.yaml diff --git a/docs/changelog/101474.yaml b/docs/changelog/101474.yaml new file mode 100644 index 0000000000000..2c013fe5d2537 --- /dev/null +++ b/docs/changelog/101474.yaml @@ -0,0 +1,5 @@ +pr: 101474 +summary: "[Search Applications] Return 400 response when template rendering produces invalid JSON" +area: Application +type: bug +issues: [] diff --git a/x-pack/plugin/ent-search/qa/rest/roles.yml b/x-pack/plugin/ent-search/qa/rest/roles.yml index 8d2ad43d02d08..4d868f41e78b3 100644 --- a/x-pack/plugin/ent-search/qa/rest/roles.yml +++ b/x-pack/plugin/ent-search/qa/rest/roles.yml @@ -21,7 +21,8 @@ user: "test-index1", "test-search-application", "test-search-application-1", - "test-search-application-with-list" + "test-search-application-with-list", + "test-search-application-with-list-invalid" ] privileges: [ "read" ] diff --git a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/52_search_application_render_query.yml b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/52_search_application_render_query.yml index 3885b85ede686..7b4d0f16551d7 100644 --- a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/52_search_application_render_query.yml +++ b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/52_search_application_render_query.yml @@ -42,6 +42,44 @@ setup: field_name: field1 field_value: value1 + - do: + search_application.put: + name: test-search-application-with-list + body: + indices: [ "test-search-index1", "test-search-index2" ] + template: + script: + source: "{ \"query\": { \"multi_match\":{ \"query\": \"{{query_string}}\", \"fields\": [{{#text_fields}}\"{{name}}^{{boost}}\",{{/text_fields}}] } } }" + params: + query_string: "elastic" + text_fields: + - name: field1 + boost: 1 + - name: field2 + boost: 2 + - name: field3 + boost: 3 + lang: "mustache" + + - do: + search_application.put: + name: test-search-application-with-list-invalid + body: + indices: [ "test-search-index1", "test-search-index2" ] + template: + script: + source: "{ \"query\": { \"multi_match\":{ \"query\": \"{{query_string}}\", \"fields\": [{{#text_fields}}\"{{name}}^{{boost}}\"{{/text_fields}}] } } }" + params: + query_string: "elastic" + text_fields: + - name: field1 + boost: 1 + - name: field2 + boost: 2 + - name: field3 + boost: 3 + lang: "mustache" + - do: index: index: test-search-index1 @@ -67,6 +105,16 @@ teardown: name: test-search-application ignore: 404 + - do: + search_application.delete: + name: test-search-application-with-list + ignore: 404 + + - do: + search_application.delete: + name: test-search-application-with-list-invalid + ignore: 404 + - do: indices.delete: index: test-search-index1 @@ -141,6 +189,37 @@ teardown: } } } + +--- +"Render query for search application with a list of parameters": + + - do: + search_application.render_query: + name: test-search-application-with-list + body: + params: + query_string: value3 + text_fields: + - name: field1 + boost: 1 + - name: field2 + boost: 2 + - name: field3 + boost: 3 + + - match: { + query: { + multi_match: { + query: "value3", + fields: [ + "field1^1.0", + "field2^2.0", + "field3^3.0" + ] + } + } + } + --- "Render query for search application - not found": @@ -166,3 +245,20 @@ teardown: body: params: field_value: puggles + +--- +"Render search application query fails on invalid rendered JSON": + - do: + catch: "bad_request" + search_application.render_query: + name: test-search-application-with-list-invalid + body: + params: + query_string: value3 + text_fields: + - name: field1 + boost: 1 + - name: field2 + boost: 2 + - name: field3 + boost: 3 diff --git a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/55_search_application_search.yml b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/55_search_application_search.yml index 7b0f60dd93cb3..42a356038ae68 100644 --- a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/55_search_application_search.yml +++ b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/55_search_application_search.yml @@ -72,6 +72,7 @@ setup: type: string field_value: type: string + - do: search_application.put: name: test-search-application-with-list @@ -91,6 +92,25 @@ setup: boost: 3 lang: "mustache" + - do: + search_application.put: + name: test-search-application-with-list-invalid + body: + indices: [ "test-search-index1", "test-search-index2" ] + template: + script: + source: "{ \"query\": { \"multi_match\":{ \"query\": \"{{query_string}}\", \"fields\": [{{#text_fields}}\"{{name}}^{{boost}}\"{{/text_fields}}] } } }" + params: + query_string: "elastic" + text_fields: + - name: field1 + boost: 1 + - name: field2 + boost: 2 + - name: field3 + boost: 3 + lang: "mustache" + - do: index: index: test-search-index1 @@ -126,6 +146,11 @@ teardown: name: test-search-application-with-list ignore: 404 + - do: + search_application.delete: + name: test-search-application-with-list-invalid + ignore: 404 + - do: indices.delete: index: test-search-index1 @@ -272,3 +297,24 @@ teardown: params: field_value: puggles +--- +"Search application search fails on invalid rendered JSON": + - skip: + features: headers + + - do: + catch: "bad_request" + headers: { Authorization: "Basic ZW50c2VhcmNoLXVzZXI6ZW50c2VhcmNoLXVzZXItcGFzc3dvcmQ=" } # user + search_application.search: + name: test-search-application-with-list-invalid + body: + params: + query_string: value3 + text_fields: + - name: field1 + boost: 1 + - name: field2 + boost: 2 + - name: field3 + boost: 3 + diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/SearchApplicationTemplateService.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/SearchApplicationTemplateService.java index ccdde4a507756..e2be554f05890 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/SearchApplicationTemplateService.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/search/SearchApplicationTemplateService.java @@ -7,6 +7,9 @@ package org.elasticsearch.xpack.application.search; +import com.fasterxml.jackson.core.JsonLocation; +import com.fasterxml.jackson.core.JsonProcessingException; + import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.logging.LogManager; @@ -17,6 +20,8 @@ import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.xcontent.NamedXContentRegistry; import org.elasticsearch.xcontent.XContentFactory; +import org.elasticsearch.xcontent.XContentLocation; +import org.elasticsearch.xcontent.XContentParseException; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentParserConfiguration; import org.elasticsearch.xcontent.XContentType; @@ -38,13 +43,20 @@ public SearchApplicationTemplateService(ScriptService scriptService, NamedXConte } public SearchSourceBuilder renderQuery(SearchApplication searchApplication, Map templateParams) throws IOException, - ValidationException { + ValidationException, XContentParseException { final SearchApplicationTemplate template = searchApplication.searchApplicationTemplateOrDefault(); template.validateTemplateParams(templateParams); final Map renderedTemplateParams = renderTemplateParams(template, templateParams); final Script script = template.script(); TemplateScript compiledTemplate = scriptService.compile(script, TemplateScript.CONTEXT).newInstance(renderedTemplateParams); - final String requestSource = SearchTemplateHelper.stripTrailingComma(compiledTemplate.execute()); + String requestSource; + try { + requestSource = SearchTemplateHelper.stripTrailingComma(compiledTemplate.execute()); + } catch (JsonProcessingException e) { + JsonLocation loc = e.getLocation(); + throw new XContentParseException(new XContentLocation(loc.getLineNr(), loc.getColumnNr()), e.getMessage(), e); + } + XContentParserConfiguration parserConfig = XContentParserConfiguration.EMPTY.withRegistry(xContentRegistry) .withDeprecationHandler(LoggingDeprecationHandler.INSTANCE); try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(parserConfig, requestSource)) {

    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

  • @XQ0W~-M5QItyQqMQ1RJ2zR1}mFq?b@b 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 z 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 QKfsTw@(rcpD;D?v2ZR;NCjbBd literal 0 HcmV?d00001 diff --git a/docs/reference/images/esql/esql-enrich.png b/docs/reference/images/esql/esql-enrich.png new file mode 100644 index 0000000000000000000000000000000000000000..a710c5e54368816e406c40ab76e72ad832d8493f GIT binary patch literal 143426 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

    cI2rsmdBQ3w;x{lH4%KLRn@9&4;g zBqi&W+x&2FU<2Xj+O8lq7xU67im_UZdkn9mu`$T1?xCio#td=0r0TSO6=CT#7cv%) zQSvmpzmYbjk!l?gZ%O?mumfXWm}h%Ht|gNykc~^1YpGqt6u+XhAmm$S z%QSz>je>0T5V{rR1a{71^{FJ?@C`mnA!KIqeF&>aYOVfYMLE&DlCnUznGYz8r`O!M z8wSt%w@*MJF0AII0~ZKr1ED$>%wLhu$7~l8H-b4vCf&@Ax!r6>gVIv@>$v%A?WUQ7 ztiy;UkKXT!XiOVUfQn09#CIiT$c8G*vl;6>He8+VZy4_#``dlE=%w4#GI&gVo6piUAIm7A_iCne;+C?5RL%j6<%+c#9fD< z(sBsbW``b*+}es7K7IPuCLUw}H7iBax^wV5OI~$`yL@~V_xt4Um$(*|vf*~sKJas= zuV1BxE01&V)!=#HT`gv#B=RQ1UD3ysh0OFt!sX`lS3fZp9EQE3b_DE6#MPl|Y6~HH z%#7OWZUkiNzGh2ZtM^PaP;8oZ7}yYq%Bn1+M|Zr%^y)kdt4`k0m}?J)xhQCT9t$d* z(3cX|zZwTBR}7{SDW_U<7ph$-;d((OIb!fltMD%g<#nhnC?FYmv3ylC~V6nU$I?{cB6v0KZ6#K%oRcd-*l2FxvR_Ff)AT~tt1sTQ9tAqH zXj*`-6H%)<1)~k6m>j?e6d1n3R%a^+ZdLW?i=-#y49`ARTCJ*|s46xG9@`6I<%@Sx z=g>mNHQ+{YYselGDcUh81jI!cNcP*TOA zVj^{%_uUv`JOr?e{eCKR#?P&7dq~XIq^@y*x2u5Pb1il_g_=yVh~vJQX3Tvn{Jcu9 z)nKk{F!N>rn%r^vdA`Za?5yL6c=*zWy<#xq5)TPHnSn?p5pM1BeAxe~@=^reCLZt3 zi1%d5&5gIy^~BcpWeMSMioS@Ks2}N}hY_T8QQrz|x!U5DPjcRmK%1@awHwxP$qsQN z5q-P4FxZ>%O1(q?r$37>%oXQcWohXnI9w%}d$em+4wa-Y)T`9O)XZ$_&X>EC8m>@T zx*jc|bB*lEV)|6%+ZbVGv+c!xl-p?{?Y=y{{!w+6X5dORJAa&(;xzIoAm#ZsnQ|p? zP0CFiMEQ8P~9powJbXZuE ztGe5XgIX1msk$A0tf#ou4vZm-Fb4Ytv(Kb%h1d{}xvheKKSVM(J*--XO= zPD>w8dQBIb>#4bzh;4=As~_;wl#PvzO-~IxiBF-+C(Of_c>YTU3kUtktnVQa5e@XT zv<^7dtnc?5%ge{G4;EFzBEeWpM5mZ@=1xwN*m^R|c}Hn!B>P84mxeDfbwl#cV$?8n z^tX?P?5R7`ny-Lu>1Zhep7tX zKXHNu2xg1Zkm?m|`%c}Z;+dvFug zW?#`+Xf$d$mS&BLJ@ndrOnvZG&K^M|lXhR4y>FmXAIFj(jN5-=ExJ0>WGgbQkRdo& z)nt%GscGPM8;E^w%R>_S{ar>RSc*XYXBeK=_tI})%XLTxOPPFoEzu|LA|&wE)>0DA--l5UcmzXN)z*utWqr7e^q(1161ffQS@ zw#?A|?@|WTopHDc_NQ{TuQe>Msu3p5%1|vlJ+S!RK}j82mJyDop#!Cl(KOb&=MGMy z8S1k;Qh6q>(oTIO%9-0pWlE)_yEb082!zpkWewi0k zZv(aN&o{k`Hqj;6b3J{&XCBR18C8tMe=Qm7EYY9!hkwJQ1C`_-jRUVfm-fn95?H2; z99gAc5NnL&WH@6>#^XD*Tt4iCQj6Xz{ovsfkbF zts*hvbqwe%M6YMo)>fEVC3|rRaam+{X4f`Wn-na|%XsZJ#i?n`YD!8L)zsB#vWnRH z;V6TK1Y&J@-jaM#G`lGHWApO+THXq3y9~#`;Ty1=eCs&(JjnW1N2VL_{9?$i}d~yzL zb@!7h`9kyCR`eq5Y5Ro1e<#emAzV zeoysFKWSS})fuKwtx_{>1@(jT%b|~b0yK6pY40q2VLE3;;BWfJt2tBm^{x6p%EGP> zyvT`{krBv7QLg=ZG_HgBf}K9Anu0S|5`9njWXNQ`F2;-C`Fjt^{b;}2uEnft`kH?o zDevm3qdi~oiOLlVXlo)|V-t+X4^UTrT%cSq4S=ov9kdwKUzZkG{ zcAnC6ap9bcMsf8jJGH;TJXLm|=Ltwv<&j-;6>lZFW9!`K+sxcQUPSj_8ns~c&x2Rm zvxbleI=*!C&(9T6L29r&dme^`rPsEV(+op{yZH6vYHBz?wBa7yU7w}3bOe`Y3W#O` zV=19u@27YtuX5%O3qFiqXF4xABdW;sv=wuNOzdxnv<{_2cU_96_S2@hF_2gDG!wl{ z15+62!BPUht``L0fyY#O0!8#k`C?0#947A2-lzv>0pAF**O^ipzp8D0T4XL>05vKu zE}?PmW%@`Y-2eekWQby@H{@KgS}tE6 zn(%gh}-SKnZey9 z!6CRqa1A6RxCM8DGZ2D113`il++BhX?l8E!I|O%U@GS4${nu{YTYKyGsjI(qb$2~- zo^#G~7qOTaCx7ScSR$W@?8t|^h@@Ykri`8`{;25T_=ntg+Ha7qNhPyUx;`*oonaC6 zi#5gZ^tgkk)wU#A+CGPX^EB^Px_H#JF{rG#8<=mss%Vdr66h3Td3REEN4FRYbsiA0 zo+^Bq2lh0l&4`2+ca-QlVtt~lhHj_;W@hY=m($E5ZrexCc}%~O1d zmvw|}pGd)<(A91qSPcu3`J85!Z27EsN!!@va5uMMk&C2`JO5oU-9~*)b<@FYBEFpE zegF4@#GW{|*k1f8%AFK;h?TISd5$mRAX6fssofz6uqF`O!<+zH8Oo3tiv1PJ&q8sl(7uDn`eLE}ugYa+rBIi_=ND^IYdwqPqc$)Z+{sc)gpFN5QzC5;guV^uE z=odA8G}h4x8Vvh_GRiv0>tmdScRhfU11VmQxO-st9w4lyaKhGTH**{z%|cvyL(1O z-nxUL5H|ibTYld9vFj1+P{b|dI8T{sY3(#KLueLUBz#z2+}@RXI289IzUnZhi^Zik zNoL?+@f}y=5wlytSxMtz0sogJn&P~_#aebn*kG{Ds!Y+mU2M|P#cz}4MeVA9bZOGm z%d)EC(AikUxw$n|R<}GNA`;I`{P?Mu>6|iW_rwFIyYiZOtx*NQO;Y~&zLGEn{$-VH z0<8uR+9nbFC>;0e@DJW8?2)6R_~TjLhsUP}F~}0x+>+Y(fAmM+?P}+Ck=Gftt91-@ z%t<1xT|enmO-*?dU+m~1Q6UlS5J8%qrgx-d_Ko?4=~jg*60~@p=7d(Jus9vgCo;aJ z+(lbHUF=QqKJDCSz}w%+T}R>n`fulMS`M}zJ;?KH;|&wYAU3p7HH&JOm=d@)sE!_G z=xy*#>{z$FY9DoLSZla3f#u|1w`VHJ8Twsc^aNjwu2p6&u*Bw?6()K3RA=WX2@k)p z79DXFwd{PgahH{Zqt8 z?XEafaJ%N7?pJ)-&wN@j?8;ia=S%C1ROHzlY8~kM%Tm}t(YGfCrgxc}Q?oo-P+Uqq zf`$@Kht$z!sHujTychS_)fCM|{Qp1g4fwd4E)EV8508%-fr1MUxazUc|KkO~bK;ld zSbY`Brt`5kj4@z)YD%pE%VG(Wh+_c}=`NALRc-U?GVnw$3YT%iiDEn?Lw&RPzvA=N zLh#$;n6Tnn^YEv3O1y#R^hAP~$Cu|vr9BRMTu5_aVIP?olcvhV-n*NX<&*IEEmN z*X1=v);Q4%qiGE~syw@Hmrhs51xbd+;Sg3$UA1(`%v8cxN@WV`=s=G}laP4nIyw6< zYYN{lM3xtI5-b^F9*b)U!ym&A%(s;RnGEf*@#LRly&cOt4leJplG=u16mW|L(P~_T z#pCQ#-Sd7l{*GU)oDOAbR_5aVg+b1a+pwENzEWm3TL{?JkB)b3W|QM!7PORzCHwqU z*Ti}K5eGh{8hidx7}+ro5%k1d%n4{}8<7>xYy}lWTLPOBijqce43T19SoewyrVQoS zm@>BWW2_hV6;|@0ASAiv`1;c#oQi}1k*;9E*srm*=5ku1A>3ODfwGQjs0s3W?-Uiw zD5#U-8B-N1ioMZ4j3CY)m`{8xO^&%4af0!}<5FX(He0tpUey{D{)USc2G5Ggg+G*=V|rWy}*bsb>gDQf)=BvFs_EZi`%Bmq70u( zsTH$@tXxh<(;1!kd*7AE_pD@In;({(KMjikw-XN=uexl?aATIee)Xf3C)IL zIx+gBNYf@md803CJ`0^UZ;fRICVf`rSm?15ZW}wU<+OT!r$>{=X8@Xfq$CUi**JeT zAm+q2TSqwH3jByAq+iT`FMNc$H)5;I!yJZkjvw1@`2n+10f?=<0sM`l-KhNji&kw81 z^({4)T4QxfO_>%);9}F<^0#FoCzAa{F?!6b%5A zuAWIC@;}sV99c}OJGuV*kfo?NetxPI%vu-xAY58mS|fG% zQ8RO$8@k6Kn3b zug2P5f&Odhy}8#d)93T%!42ANU&XsQH^zUqgf<<9c^8C)WIr)Jt~~ZYJbs0gF!<;k zdnfRZ3>;zjv28y~uVKaiS$qEu3oms|um>!7K4P#+i`ugD4(Oz{$MTKQy zo^!`Ie?RIrcIr9(o&-sZ2qB|-(?4A5hQBD%27<$!(-Dat8E*{djav0Q{$~I(rF}k5B^j`874}39UOzhW{mhnmb5p0OrDc1spiqq6 z|M@|ev6nU_#llkI<3(zkDRt~d|GwW~+4V#dnWc^09B ziGkJ7lj4Tb-|b||8QoK<{qVnc{Np|47m%56`^5vPPcsx>MRZ3{5(URf?=N{jhBYhI z4z6#@nyk!_kw9;1{C+fS)W!|Y;PZL^@R$R_n-Ehw!S`D))+IKxD`eu3WKG~)Meh+W z-~ii%Xjd-w($YoE_Hu#?$zyFI1#Og@s@o!+QnmS-G zoyV!OYbJCr5pz}+j-UB-Gf6{Trk{&YMsvY`1tt9AvRNpirB#_Gm*HOc{r(ObU9tb= z^0zOG#1m@1gWS-xO1882k6?Lmj@4_}?uE-}&fn&hySTe|zP`Lqs(&{NpLi;7^x!UX zk4I(oaxe7d96BY%ZR7Mg#UYS;$$!%q&O@!5GO|<}$X`@~6o<8n8OjT^rz|Vmgo>L2 ztZ9%#>A8vYl>EdraM+=(=|ib(k`9rDlpx1AmJ2bBg1sN~y^N>7M6uCQ1f_7-!vBae zCC>L>Z7DynN!0cG3j;n+S%$I#f4nQoXK!T>%@%PCBa3oA-@3()a13M4aA>23{&imt z`DNYlt^sp=OTO){1U`lH`)8(bg@jyS&sR<&8P76H>_$7%>geMuJ%rn_N4UxMTAjRQ z>hVf3@{xUcy;6Go)2}dOnNw#qS)VVgvOLxqTkT2=O#@nN^RhLJ;rMD1I=0J?t_^N; z_G0N(bvvJybq#6@4?!*7^^46*w&mH-g-eWZZ7iJu+5)Lev$Jg^{1HjR;9Sq^4IwR|H_ul)EB z8ko>1^4vT99LKeCxAgfk;&G|-8|;6!=Mq_n*XSd^U*7FfWWb$$&rVO4PEVuCO&Im` z^_?5CucmTC=H1MhMHVz_$}L