Skip to content

Commit ead2b9e

Browse files
authored
HLRC: Add rollup search (#36334)
Relates to #29827
1 parent 51e1d40 commit ead2b9e

File tree

13 files changed

+257
-10
lines changed

13 files changed

+257
-10
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,15 @@ static Request update(UpdateRequest updateRequest) throws IOException {
354354
return request;
355355
}
356356

357-
static Request search(SearchRequest searchRequest) throws IOException {
358-
Request request = new Request(HttpPost.METHOD_NAME, endpoint(searchRequest.indices(), searchRequest.types(), "_search"));
357+
/**
358+
* Convert a {@linkplain SearchRequest} into a {@linkplain Request}.
359+
* @param searchRequest the request to convert
360+
* @param searchEndpoint the name of the search endpoint. {@literal _search}
361+
* for standard searches and {@literal _rollup_search} for rollup
362+
* searches.
363+
*/
364+
static Request search(SearchRequest searchRequest, String searchEndpoint) throws IOException {
365+
Request request = new Request(HttpPost.METHOD_NAME, endpoint(searchRequest.indices(), searchRequest.types(), searchEndpoint));
359366

360367
Params params = new Params(request);
361368
addSearchRequestParams(params, searchRequest);

client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,12 @@ public final void deleteAsync(DeleteRequest deleteRequest, RequestOptions option
908908
* @return the response
909909
*/
910910
public final SearchResponse search(SearchRequest searchRequest, RequestOptions options) throws IOException {
911-
return performRequestAndParseEntity(searchRequest, RequestConverters::search, options, SearchResponse::fromXContent, emptySet());
911+
return performRequestAndParseEntity(
912+
searchRequest,
913+
r -> RequestConverters.search(r, "_search"),
914+
options,
915+
SearchResponse::fromXContent,
916+
emptySet());
912917
}
913918

914919
/**
@@ -919,7 +924,12 @@ public final SearchResponse search(SearchRequest searchRequest, RequestOptions o
919924
* @param listener the listener to be notified upon request completion
920925
*/
921926
public final void searchAsync(SearchRequest searchRequest, RequestOptions options, ActionListener<SearchResponse> listener) {
922-
performRequestAsyncAndParseEntity(searchRequest, RequestConverters::search, options, SearchResponse::fromXContent, listener,
927+
performRequestAsyncAndParseEntity(
928+
searchRequest,
929+
r -> RequestConverters.search(r, "_search"),
930+
options,
931+
SearchResponse::fromXContent,
932+
listener,
923933
emptySet());
924934
}
925935

client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
package org.elasticsearch.client;
2121

2222
import org.elasticsearch.action.ActionListener;
23+
import org.elasticsearch.action.search.SearchRequest;
24+
import org.elasticsearch.action.search.SearchResponse;
2325
import org.elasticsearch.client.core.AcknowledgedResponse;
2426
import org.elasticsearch.client.rollup.DeleteRollupJobRequest;
2527
import org.elasticsearch.client.rollup.GetRollupIndexCapsRequest;
@@ -224,6 +226,42 @@ public void getRollupJobAsync(GetRollupJobRequest request, RequestOptions option
224226
listener, Collections.emptySet());
225227
}
226228

229+
/**
230+
* Perform a rollup search.
231+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-search.html">
232+
* the docs</a> for more.
233+
* @param request the request
234+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
235+
* @return the response
236+
* @throws IOException in case there is a problem sending the request or parsing back the response
237+
*/
238+
public SearchResponse search(SearchRequest request, RequestOptions options) throws IOException {
239+
return restHighLevelClient.performRequestAndParseEntity(
240+
request,
241+
RollupRequestConverters::search,
242+
options,
243+
SearchResponse::fromXContent,
244+
Collections.emptySet());
245+
}
246+
247+
/**
248+
* Perform a rollup search.
249+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-search.html">
250+
* the docs</a> for more.
251+
* @param request the request
252+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
253+
* @param listener the listener to be notified upon request completion
254+
*/
255+
public void searchAsync(SearchRequest request, RequestOptions options, ActionListener<SearchResponse> listener) {
256+
restHighLevelClient.performRequestAsyncAndParseEntity(
257+
request,
258+
RollupRequestConverters::search,
259+
options,
260+
SearchResponse::fromXContent,
261+
listener,
262+
Collections.emptySet());
263+
}
264+
227265
/**
228266
* Get the Rollup Capabilities of a target (non-rollup) index or pattern
229267
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-get-rollup-caps.html">

client/rest-high-level/src/main/java/org/elasticsearch/client/RollupRequestConverters.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.http.client.methods.HttpGet;
2323
import org.apache.http.client.methods.HttpPost;
2424
import org.apache.http.client.methods.HttpPut;
25+
import org.elasticsearch.action.search.SearchRequest;
2526
import org.elasticsearch.client.rollup.DeleteRollupJobRequest;
2627
import org.elasticsearch.client.rollup.GetRollupCapsRequest;
2728
import org.elasticsearch.client.rollup.GetRollupIndexCapsRequest;
@@ -93,6 +94,20 @@ static Request deleteJob(final DeleteRollupJobRequest deleteRollupJobRequest) th
9394
return request;
9495
}
9596

97+
static Request search(final SearchRequest request) throws IOException {
98+
if (request.types().length > 0) {
99+
/*
100+
* Ideally we'd check this with the standard validation framework
101+
* but we don't have a special request for rollup search so that'd
102+
* be difficult.
103+
*/
104+
ValidationException ve = new ValidationException();
105+
ve.addValidationError("types are not allowed in rollup search");
106+
throw ve;
107+
}
108+
return RequestConverters.search(request, "_rollup_search");
109+
}
110+
96111
static Request getRollupCaps(final GetRollupCapsRequest getRollupCapsRequest) throws IOException {
97112
String endpoint = new RequestConverters.EndpointBuilder()
98113
.addPathPartAsIs("_xpack", "rollup", "data")

client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -880,14 +880,16 @@ public void testGlobalPipelineOnBulkRequest() throws IOException {
880880
}
881881

882882
public void testSearchNullSource() throws IOException {
883+
String searchEndpoint = randomFrom("_" + randomAlphaOfLength(5));
883884
SearchRequest searchRequest = new SearchRequest();
884-
Request request = RequestConverters.search(searchRequest);
885+
Request request = RequestConverters.search(searchRequest, searchEndpoint);
885886
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
886-
assertEquals("/_search", request.getEndpoint());
887+
assertEquals("/" + searchEndpoint, request.getEndpoint());
887888
assertNull(request.getEntity());
888889
}
889890

890891
public void testSearch() throws Exception {
892+
String searchEndpoint = randomFrom("_" + randomAlphaOfLength(5));
891893
String[] indices = randomIndicesNames(0, 5);
892894
SearchRequest searchRequest = new SearchRequest(indices);
893895

@@ -948,7 +950,7 @@ public void testSearch() throws Exception {
948950
searchRequest.source(searchSourceBuilder);
949951
}
950952

951-
Request request = RequestConverters.search(searchRequest);
953+
Request request = RequestConverters.search(searchRequest, searchEndpoint);
952954
StringJoiner endpoint = new StringJoiner("/", "/", "");
953955
String index = String.join(",", indices);
954956
if (Strings.hasLength(index)) {
@@ -958,7 +960,7 @@ public void testSearch() throws Exception {
958960
if (Strings.hasLength(type)) {
959961
endpoint.add(type);
960962
}
961-
endpoint.add("_search");
963+
endpoint.add(searchEndpoint);
962964
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
963965
assertEquals(endpoint.toString(), request.getEndpoint());
964966
assertEquals(expectedParams, request.getParameters());

client/rest-high-level/src/test/java/org/elasticsearch/client/RollupIT.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@
5454
import org.elasticsearch.rest.RestStatus;
5555
import org.elasticsearch.search.SearchHit;
5656
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
57+
import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation;
5758
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
5859
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
5960
import org.elasticsearch.search.aggregations.metrics.MinAggregationBuilder;
6061
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
6162
import org.elasticsearch.search.aggregations.metrics.ValueCountAggregationBuilder;
63+
import org.elasticsearch.search.builder.SearchSourceBuilder;
6264
import org.junit.Before;
6365

6466
import java.util.Arrays;
@@ -70,6 +72,7 @@
7072
import java.util.Set;
7173

7274
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
75+
import static org.hamcrest.Matchers.closeTo;
7376
import static org.hamcrest.Matchers.either;
7477
import static org.hamcrest.Matchers.empty;
7578
import static org.hamcrest.Matchers.equalTo;
@@ -244,6 +247,33 @@ public void testPutStartAndGetRollupJob() throws Exception {
244247
}
245248
}
246249

250+
public void testSearch() throws Exception {
251+
testPutStartAndGetRollupJob();
252+
SearchRequest search = new SearchRequest(rollupIndex);
253+
search.source(new SearchSourceBuilder()
254+
.size(0)
255+
.aggregation(new AvgAggregationBuilder("avg").field("value")));
256+
SearchResponse response = highLevelClient().rollup().search(search, RequestOptions.DEFAULT);
257+
assertEquals(0, response.getFailedShards());
258+
assertEquals(0, response.getHits().getTotalHits().value);
259+
NumericMetricsAggregation.SingleValue avg = response.getAggregations().get("avg");
260+
assertThat(avg.value(), closeTo(sum / numDocs, 0.00000001));
261+
}
262+
263+
public void testSearchWithType() throws Exception {
264+
SearchRequest search = new SearchRequest(rollupIndex);
265+
search.types("a", "b", "c");
266+
search.source(new SearchSourceBuilder()
267+
.size(0)
268+
.aggregation(new AvgAggregationBuilder("avg").field("value")));
269+
try {
270+
highLevelClient().rollup().search(search, RequestOptions.DEFAULT);
271+
fail("types are not allowed but didn't fail");
272+
} catch (ValidationException e) {
273+
assertEquals("Validation Failed: 1: types are not allowed in rollup search;", e.getMessage());
274+
}
275+
}
276+
247277
public void testGetMissingRollupJob() throws Exception {
248278
GetRollupJobRequest getRollupJobRequest = new GetRollupJobRequest("missing");
249279
RollupClient rollupClient = highLevelClient().rollup();

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/RollupDocumentationIT.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.elasticsearch.action.bulk.BulkRequest;
2828
import org.elasticsearch.action.bulk.BulkResponse;
2929
import org.elasticsearch.action.index.IndexRequest;
30+
import org.elasticsearch.action.search.SearchRequest;
31+
import org.elasticsearch.action.search.SearchResponse;
3032
import org.elasticsearch.action.support.WriteRequest;
3133
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
3234
import org.elasticsearch.client.RequestOptions;
@@ -60,6 +62,9 @@
6062
import org.elasticsearch.common.unit.TimeValue;
6163
import org.elasticsearch.rest.RestStatus;
6264
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
65+
import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation;
66+
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
67+
import org.elasticsearch.search.builder.SearchSourceBuilder;
6368
import org.junit.Before;
6469

6570
import java.io.IOException;
@@ -72,6 +77,7 @@
7277
import java.util.concurrent.TimeUnit;
7378

7479
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
80+
import static org.hamcrest.Matchers.closeTo;
7581
import static org.hamcrest.Matchers.equalTo;
7682
import static org.hamcrest.Matchers.hasSize;
7783
import static org.hamcrest.Matchers.isOneOf;
@@ -89,7 +95,7 @@ public void setUpDocs() throws IOException {
8995
.field("timestamp", String.format(Locale.ROOT, "2018-01-01T00:%02d:00Z", i))
9096
.field("hostname", 0)
9197
.field("datacenter", 0)
92-
.field("temperature", 0)
98+
.field("temperature", i)
9399
.field("voltage", 0)
94100
.field("load", 0)
95101
.field("net_in", 0)
@@ -330,6 +336,56 @@ public void onFailure(Exception e) {
330336
assertTrue(latch.await(30L, TimeUnit.SECONDS));
331337
}
332338

339+
public void testSearch() throws Exception {
340+
// Setup a rollup index to query
341+
testCreateRollupJob();
342+
343+
RestHighLevelClient client = highLevelClient();
344+
345+
// tag::search-request
346+
SearchRequest request = new SearchRequest();
347+
request.source(new SearchSourceBuilder()
348+
.size(0)
349+
.aggregation(new MaxAggregationBuilder("max_temperature")
350+
.field("temperature")));
351+
// end::search-request
352+
353+
// tag::search-execute
354+
SearchResponse response =
355+
client.rollup().search(request, RequestOptions.DEFAULT);
356+
// end::search-execute
357+
358+
// tag::search-response
359+
NumericMetricsAggregation.SingleValue maxTemperature =
360+
response.getAggregations().get("max_temperature");
361+
assertThat(maxTemperature.value(), closeTo(49.0, .00001));
362+
// end::search-response
363+
364+
ActionListener<SearchResponse> listener;
365+
// tag::search-execute-listener
366+
listener = new ActionListener<SearchResponse>() {
367+
@Override
368+
public void onResponse(SearchResponse response) {
369+
// <1>
370+
}
371+
372+
@Override
373+
public void onFailure(Exception e) {
374+
// <2>
375+
}
376+
};
377+
// end::search-execute-listener
378+
379+
final CountDownLatch latch = new CountDownLatch(1);
380+
listener = new LatchedActionListener<>(listener, latch);
381+
382+
// tag::search-execute-async
383+
client.rollup().searchAsync(request, RequestOptions.DEFAULT, listener); // <1>
384+
// end::search-execute-async
385+
386+
assertTrue(latch.await(30L, TimeUnit.SECONDS));
387+
}
388+
333389
@SuppressWarnings("unused")
334390
public void testGetRollupCaps() throws Exception {
335391
RestHighLevelClient client = highLevelClient();
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--
2+
:api: search
3+
:request: SearchRequest
4+
:response: SearchResponse
5+
--
6+
7+
[id="{upid}-{api}"]
8+
=== Rollup Search API
9+
10+
The Rollup Search endpoint allows searching rolled-up data using the standard
11+
query DSL. The Rollup Search endpoint is needed because, internally,
12+
rolled-up documents utilize a different document structure than the original
13+
data. The Rollup Search endpoint rewrites standard query DSL into a format that
14+
matches the rollup documents, then takes the response and rewrites it back to
15+
what a client would expect given the original query.
16+
17+
[id="{upid}-{api}-request"]
18+
==== Request
19+
20+
Rollup Search uses the same +{request}+ that is used by the <<{mainid}-search>>
21+
but it is mostly for aggregations you should set the `size` to 0 and add
22+
aggregations like this:
23+
24+
["source","java",subs="attributes,callouts,macros"]
25+
--------------------------------------------------
26+
include-tagged::{doc-tests-file}[{api}-request]
27+
--------------------------------------------------
28+
29+
NOTE:: Rollup Search is limited in many ways because only some query elements
30+
can be translated into queries against the rollup indices. See the main
31+
{ref}/rollup-search.html[Rollup Search] documentation for more.
32+
33+
include::../execution.asciidoc[]
34+
35+
[id="{upid}-{api}-response"]
36+
==== Response
37+
38+
Rollup Search returns the same +{response}+ that is used by the
39+
<<{mainid}-search>> and everything can be accessed in exactly the same way.
40+
This will access the aggregation built by the example request above:
41+
42+
["source","java",subs="attributes,callouts,macros"]
43+
--------------------------------------------------
44+
include-tagged::{doc-tests-file}[{api}-response]
45+
--------------------------------------------------

docs/java-rest/high-level/supported-apis.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ The Java High Level REST Client supports the following Rollup APIs:
360360
* <<{upid}-rollup-stop-job>>
361361
* <<{upid}-rollup-delete-job>>
362362
* <<java-rest-high-x-pack-rollup-get-job>>
363+
* <<{upid}-search>>
363364
* <<{upid}-x-pack-rollup-get-rollup-caps>>
364365
* <<{upid}-x-pack-rollup-get-rollup-index-caps>>
365366

@@ -368,6 +369,7 @@ include::rollup/start_job.asciidoc[]
368369
include::rollup/stop_job.asciidoc[]
369370
include::rollup/delete_job.asciidoc[]
370371
include::rollup/get_job.asciidoc[]
372+
include::rollup/search.asciidoc[]
371373
include::rollup/get_rollup_caps.asciidoc[]
372374
include::rollup/get_rollup_index_caps.asciidoc[]
373375

docs/reference/rollup/apis/rollup-search.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ and rewrites it back to what a client would expect given the original query.
2626
indices.
2727

2828
Rules for the `index` parameter:
29+
2930
- At least one index/index-pattern must be specified. This can be either a rollup or non-rollup index. Omitting the index parameter,
3031
or using `_all`, is not permitted
3132
- Multiple non-rollup indices may be specified

0 commit comments

Comments
 (0)