Skip to content

Commit e179fd1

Browse files
authored
Add support for rest_total_hits_as_int in watcher (#36035)
This change adds the support for rest_total_hits_as_int in the watcher search inputs. Setting this parameter in the request will transform the search response to contain the total hits as a number (instead of an object). Note that this parameter is currently a noop since #35849 is not merged. Closes #36008
1 parent c24be27 commit e179fd1

File tree

4 files changed

+300
-7
lines changed

4 files changed

+300
-7
lines changed

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/input/search/ExecutableSearchInput.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
3030
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
3131

32+
import java.util.Collections;
3233
import java.util.Map;
3334

3435
import static org.elasticsearch.xpack.watcher.input.search.SearchInput.TYPE;
@@ -37,11 +38,12 @@
3738
* An input that executes search and returns the search response as the initial payload
3839
*/
3940
public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchInput.Result> {
40-
4141
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.QUERY_THEN_FETCH;
4242

4343
private static final Logger logger = LogManager.getLogger(ExecutableSearchInput.class);
4444

45+
private static final Params EMPTY_PARAMS = new MapParams(Collections.emptyMap());
46+
4547
private final Client client;
4648
private final WatcherSearchTemplateService searchTemplateService;
4749
private final TimeValue timeout;
@@ -86,7 +88,13 @@ SearchInput.Result doExecute(WatchExecutionContext ctx, WatcherSearchTemplateReq
8688

8789
final Payload payload;
8890
if (input.getExtractKeys() != null) {
89-
BytesReference bytes = XContentHelper.toXContent(response, XContentType.JSON, false);
91+
Params params;
92+
if (request.isRestTotalHitsAsint()) {
93+
params = new MapParams(Collections.singletonMap("rest_total_hits_a_int", "true"));
94+
} else {
95+
params = EMPTY_PARAMS;
96+
}
97+
BytesReference bytes = XContentHelper.toXContent(response, XContentType.JSON, params, false);
9098
// EMPTY is safe here because we never use namedObject
9199
try (XContentParser parser = XContentHelper
92100
.createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, bytes)) {

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/support/search/WatcherSearchTemplateRequest.java

+36-5
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public class WatcherSearchTemplateRequest implements ToXContentObject {
4040
private final SearchType searchType;
4141
private final IndicesOptions indicesOptions;
4242
private final Script template;
43-
4443
private final BytesReference searchSource;
44+
private boolean restTotalHitsAsInt;
4545

4646
public WatcherSearchTemplateRequest(String[] indices, String[] types, SearchType searchType, IndicesOptions indicesOptions,
4747
BytesReference searchSource) {
@@ -72,6 +72,7 @@ public WatcherSearchTemplateRequest(WatcherSearchTemplateRequest original, Bytes
7272
this.indicesOptions = original.indicesOptions;
7373
this.searchSource = source;
7474
this.template = original.template;
75+
this.restTotalHitsAsInt = original.restTotalHitsAsInt;
7576
}
7677

7778
private WatcherSearchTemplateRequest(String[] indices, String[] types, SearchType searchType, IndicesOptions indicesOptions,
@@ -105,6 +106,19 @@ public IndicesOptions getIndicesOptions() {
105106
return indicesOptions;
106107
}
107108

109+
public boolean isRestTotalHitsAsint() {
110+
return restTotalHitsAsInt;
111+
}
112+
113+
/**
114+
* Indicates whether the total hits in the response should be
115+
* serialized as number (<code>true</code>) or as an object (<code>false</code>).
116+
* Defaults to false.
117+
*/
118+
public void setRestTotalHitsAsInt(boolean value) {
119+
this.restTotalHitsAsInt = restTotalHitsAsInt;
120+
}
121+
108122
public BytesReference getSearchSource() {
109123
return searchSource;
110124
}
@@ -129,6 +143,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
129143
if (types != null) {
130144
builder.array(TYPES_FIELD.getPreferredName(), types);
131145
}
146+
if (restTotalHitsAsInt) {
147+
builder.field(REST_TOTAL_HITS_AS_INT_FIELD.getPreferredName(), restTotalHitsAsInt);
148+
}
132149
if (searchSource != null && searchSource.length() > 0) {
133150
try (InputStream stream = searchSource.streamInput()) {
134151
builder.rawField(BODY_FIELD.getPreferredName(), stream);
@@ -167,6 +184,7 @@ public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, S
167184
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
168185
BytesReference searchSource = null;
169186
Script template = null;
187+
boolean totalHitsAsInt = false;
170188

171189
XContentParser.Token token;
172190
String currentFieldName = null;
@@ -263,10 +281,19 @@ public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, S
263281
types.addAll(Arrays.asList(Strings.delimitedListToStringArray(typesStr, ",", " \t")));
264282
} else if (SEARCH_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
265283
searchType = SearchType.fromString(parser.text().toLowerCase(Locale.ROOT));
284+
} else if (REST_TOTAL_HITS_AS_INT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
285+
totalHitsAsInt = parser.booleanValue();
266286
} else {
267287
throw new ElasticsearchParseException("could not read search request. unexpected string field [" +
268288
currentFieldName + "]");
269289
}
290+
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
291+
if (REST_TOTAL_HITS_AS_INT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
292+
totalHitsAsInt = parser.booleanValue();
293+
} else {
294+
throw new ElasticsearchParseException("could not read search request. unexpected boolean field [" +
295+
currentFieldName + "]");
296+
}
270297
} else {
271298
throw new ElasticsearchParseException("could not read search request. unexpected token [" + token + "]");
272299
}
@@ -276,8 +303,10 @@ public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, S
276303
searchSource = BytesArray.EMPTY;
277304
}
278305

279-
return new WatcherSearchTemplateRequest(indices.toArray(new String[0]), types.toArray(new String[0]), searchType,
280-
indicesOptions, searchSource, template);
306+
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(indices.toArray(new String[0]),
307+
types.toArray(new String[0]), searchType, indicesOptions, searchSource, template);
308+
request.setRestTotalHitsAsInt(totalHitsAsInt);
309+
return request;
281310
}
282311

283312
@Override
@@ -291,13 +320,14 @@ public boolean equals(Object o) {
291320
Objects.equals(searchType, other.searchType) &&
292321
Objects.equals(indicesOptions, other.indicesOptions) &&
293322
Objects.equals(searchSource, other.searchSource) &&
294-
Objects.equals(template, other.template);
323+
Objects.equals(template, other.template) &&
324+
Objects.equals(restTotalHitsAsInt, other.restTotalHitsAsInt);
295325

296326
}
297327

298328
@Override
299329
public int hashCode() {
300-
return Objects.hash(indices, types, searchType, indicesOptions, searchSource, template);
330+
return Objects.hash(indices, types, searchType, indicesOptions, searchSource, template, restTotalHitsAsInt);
301331
}
302332

303333
private static final ParseField INDICES_FIELD = new ParseField("indices");
@@ -309,6 +339,7 @@ public int hashCode() {
309339
private static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
310340
private static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");
311341
private static final ParseField TEMPLATE_FIELD = new ParseField("template");
342+
private static final ParseField REST_TOTAL_HITS_AS_INT_FIELD = new ParseField("rest_total_hits_as_int");
312343

313344
public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
314345
}

x-pack/qa/smoke-test-watcher/src/test/resources/rest-api-spec/test/mustache/30_search_input.yml

+126
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,129 @@ setup:
163163
- match: { "watch_record.result.input.search.request.body.query.bool.must.0.term.value": "val_2" }
164164
- match: { "watch_record.result.input.search.request.template.id": "search-template" }
165165
- match: { "watch_record.result.input.search.request.template.params.num": 2 }
166+
167+
---
168+
"Test search input mustache integration (using request body and rest_total_hits_as_int)":
169+
- skip:
170+
version: " - 6.99.99"
171+
reason: "rest_total_hits_as_int support was added in 7.0"
172+
- do:
173+
xpack.watcher.execute_watch:
174+
body: >
175+
{
176+
"trigger_data" : {
177+
"scheduled_time" : "2015-01-04T00:00:00"
178+
},
179+
"watch" : {
180+
"trigger" : { "schedule" : { "interval" : "10s" } },
181+
"actions" : {
182+
"dummy" : {
183+
"logging" : {
184+
"text" : "executed!"
185+
}
186+
}
187+
},
188+
"input" : {
189+
"search" : {
190+
"request" : {
191+
"indices" : "idx",
192+
"rest_total_hits_as_int": true,
193+
"body" : {
194+
"query" : {
195+
"bool" : {
196+
"filter" : [
197+
{
198+
"range" : {
199+
"date" : {
200+
"lte" : "{{ctx.trigger.scheduled_time}}",
201+
"gte" : "{{ctx.trigger.scheduled_time}}||-3d"
202+
}
203+
}
204+
}
205+
]
206+
}
207+
}
208+
}
209+
}
210+
}
211+
}
212+
}
213+
}
214+
- match: { "watch_record.result.input.type": "search" }
215+
- match: { "watch_record.result.input.status": "success" }
216+
- match: { "watch_record.result.input.payload.hits.total": 4 }
217+
# makes sure that the mustache template snippets have been resolved correctly:
218+
- match: { "watch_record.result.input.search.request.body.query.bool.filter.0.range.date.gte": "2015-01-04T00:00:00.000Z||-3d" }
219+
- match: { "watch_record.result.input.search.request.body.query.bool.filter.0.range.date.lte": "2015-01-04T00:00:00.000Z" }
220+
221+
---
222+
"Test search input mustache integration (using request template and rest_total_hits_as_int)":
223+
- skip:
224+
version: " - 6.99.99"
225+
reason: "rest_total_hits_as_int support was added in 7.0"
226+
227+
- do:
228+
put_script:
229+
id: "search-template"
230+
body: {
231+
"script": {
232+
"lang": "mustache",
233+
"source": {
234+
"query" : {
235+
"bool" : {
236+
"must" : [
237+
{
238+
"term" : {
239+
"value" : "val_{{num}}"
240+
}
241+
}
242+
]
243+
}
244+
}
245+
}
246+
}
247+
}
248+
- match: { acknowledged: true }
249+
250+
- do:
251+
xpack.watcher.execute_watch:
252+
body: >
253+
{
254+
"trigger_data" : {
255+
"scheduled_time" : "2015-01-04T00:00:00"
256+
},
257+
"watch" : {
258+
"trigger" : { "schedule" : { "interval" : "10s" } },
259+
"actions" : {
260+
"dummy" : {
261+
"logging" : {
262+
"text" : "executed!"
263+
}
264+
}
265+
},
266+
"input" : {
267+
"search" : {
268+
"request" : {
269+
"rest_total_hits_as_int": true,
270+
"indices" : "idx",
271+
"template" : {
272+
"id": "search-template",
273+
"params": {
274+
"num": 2
275+
}
276+
}
277+
}
278+
}
279+
}
280+
}
281+
}
282+
- match: { "watch_record.result.input.type": "search" }
283+
- match: { "watch_record.result.input.status": "success" }
284+
- match: { "watch_record.result.input.payload.hits.total": 1 }
285+
- match: { "watch_record.result.input.payload.hits.hits.0._id": "2" }
286+
# makes sure that the mustache template snippets have been resolved correctly:
287+
- match: { "watch_record.result.input.search.request.body.query.bool.must.0.term.value": "val_2" }
288+
- match: { "watch_record.result.input.search.request.template.id": "search-template" }
289+
- match: { "watch_record.result.input.search.request.template.params.num": 2 }
290+
291+

0 commit comments

Comments
 (0)