Skip to content

Commit 20af856

Browse files
authored
[7.x] EQL: Adds an ability to execute an asynchronous EQL search (#58192)
Adds async support to EQL searches Closes #49638 Co-authored-by: James Rodewig [email protected]
1 parent c7ba79b commit 20af856

File tree

78 files changed

+4245
-901
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+4245
-901
lines changed

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.elasticsearch.action.support.IndicesOptions;
2323
import org.elasticsearch.client.Validatable;
24+
import org.elasticsearch.common.unit.TimeValue;
2425
import org.elasticsearch.common.xcontent.ToXContent;
2526
import org.elasticsearch.common.xcontent.ToXContentObject;
2627
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -45,6 +46,11 @@ public class EqlSearchRequest implements Validatable, ToXContentObject {
4546
private String query;
4647
private String tiebreakerField;
4748

49+
// Async settings
50+
private TimeValue waitForCompletionTimeout;
51+
private boolean keepOnCompletion;
52+
private TimeValue keepAlive;
53+
4854
static final String KEY_FILTER = "filter";
4955
static final String KEY_TIMESTAMP_FIELD = "timestamp_field";
5056
static final String KEY_TIEBREAKER_FIELD = "tiebreaker_field";
@@ -53,6 +59,9 @@ public class EqlSearchRequest implements Validatable, ToXContentObject {
5359
static final String KEY_SIZE = "size";
5460
static final String KEY_SEARCH_AFTER = "search_after";
5561
static final String KEY_QUERY = "query";
62+
static final String KEY_WAIT_FOR_COMPLETION_TIMEOUT = "wait_for_completion_timeout";
63+
static final String KEY_KEEP_ALIVE = "keep_alive";
64+
static final String KEY_KEEP_ON_COMPLETION = "keep_on_completion";
5665

5766
public EqlSearchRequest(String indices, String query) {
5867
indices(indices);
@@ -80,6 +89,13 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par
8089
}
8190

8291
builder.field(KEY_QUERY, query);
92+
if (waitForCompletionTimeout != null) {
93+
builder.field(KEY_WAIT_FOR_COMPLETION_TIMEOUT, waitForCompletionTimeout);
94+
}
95+
if (keepAlive != null) {
96+
builder.field(KEY_KEEP_ALIVE, keepAlive);
97+
}
98+
builder.field(KEY_KEEP_ON_COMPLETION, keepOnCompletion);
8399
builder.endObject();
84100
return builder;
85101
}
@@ -181,6 +197,32 @@ public EqlSearchRequest query(String query) {
181197
return this;
182198
}
183199

200+
public TimeValue waitForCompletionTimeout() {
201+
return waitForCompletionTimeout;
202+
}
203+
204+
public EqlSearchRequest waitForCompletionTimeout(TimeValue waitForCompletionTimeout) {
205+
this.waitForCompletionTimeout = waitForCompletionTimeout;
206+
return this;
207+
}
208+
209+
public Boolean keepOnCompletion() {
210+
return keepOnCompletion;
211+
}
212+
213+
public void keepOnCompletion(Boolean keepOnCompletion) {
214+
this.keepOnCompletion = keepOnCompletion;
215+
}
216+
217+
public TimeValue keepAlive() {
218+
return keepAlive;
219+
}
220+
221+
public EqlSearchRequest keepAlive(TimeValue keepAlive) {
222+
this.keepAlive = keepAlive;
223+
return this;
224+
}
225+
184226
@Override
185227
public boolean equals(Object o) {
186228
if (this == o) {
@@ -199,7 +241,10 @@ public boolean equals(Object o) {
199241
Objects.equals(eventCategoryField, that.eventCategoryField) &&
200242
Objects.equals(implicitJoinKeyField, that.implicitJoinKeyField) &&
201243
Objects.equals(searchAfterBuilder, that.searchAfterBuilder) &&
202-
Objects.equals(query, that.query);
244+
Objects.equals(query, that.query) &&
245+
Objects.equals(waitForCompletionTimeout, that.waitForCompletionTimeout) &&
246+
Objects.equals(keepAlive, that.keepAlive) &&
247+
Objects.equals(keepOnCompletion, that.keepOnCompletion);
203248
}
204249

205250
@Override
@@ -214,7 +259,10 @@ public int hashCode() {
214259
eventCategoryField,
215260
implicitJoinKeyField,
216261
searchAfterBuilder,
217-
query);
262+
query,
263+
waitForCompletionTimeout,
264+
keepAlive,
265+
keepOnCompletion);
218266
}
219267

220268
public String[] indices() {

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

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.common.Nullable;
2424
import org.elasticsearch.common.ParseField;
2525
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
26+
import org.elasticsearch.common.xcontent.InstantiatingObjectParser;
2627
import org.elasticsearch.common.xcontent.XContentParser;
2728
import org.elasticsearch.search.SearchHit;
2829
import org.elasticsearch.search.SearchHits;
@@ -32,43 +33,56 @@
3233
import java.util.List;
3334
import java.util.Objects;
3435

36+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
37+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
38+
3539
public class EqlSearchResponse {
3640

3741
private final Hits hits;
3842
private final long tookInMillis;
3943
private final boolean isTimeout;
44+
private final String asyncExecutionId;
45+
private final boolean isRunning;
46+
private final boolean isPartial;
4047

4148
private static final class Fields {
4249
static final String TOOK = "took";
4350
static final String TIMED_OUT = "timed_out";
4451
static final String HITS = "hits";
52+
static final String ID = "id";
53+
static final String IS_RUNNING = "is_running";
54+
static final String IS_PARTIAL = "is_partial";
4555
}
4656

4757
private static final ParseField TOOK = new ParseField(Fields.TOOK);
4858
private static final ParseField TIMED_OUT = new ParseField(Fields.TIMED_OUT);
4959
private static final ParseField HITS = new ParseField(Fields.HITS);
60+
private static final ParseField ID = new ParseField(Fields.ID);
61+
private static final ParseField IS_RUNNING = new ParseField(Fields.IS_RUNNING);
62+
private static final ParseField IS_PARTIAL = new ParseField(Fields.IS_PARTIAL);
5063

51-
private static final ConstructingObjectParser<EqlSearchResponse, Void> PARSER =
52-
new ConstructingObjectParser<>("eql/search_response", true,
53-
args -> {
54-
int i = 0;
55-
Hits hits = (Hits) args[i++];
56-
Long took = (Long) args[i++];
57-
Boolean timeout = (Boolean) args[i];
58-
return new EqlSearchResponse(hits, took, timeout);
59-
});
60-
64+
private static final InstantiatingObjectParser<EqlSearchResponse, Void> PARSER;
6165
static {
62-
PARSER.declareObject(ConstructingObjectParser.constructorArg(), (p, c) -> Hits.fromXContent(p), HITS);
63-
PARSER.declareLong(ConstructingObjectParser.constructorArg(), TOOK);
64-
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), TIMED_OUT);
66+
InstantiatingObjectParser.Builder<EqlSearchResponse, Void> parser =
67+
InstantiatingObjectParser.builder("eql/search_response", true, EqlSearchResponse.class);
68+
parser.declareObject(constructorArg(), (p, c) -> Hits.fromXContent(p), HITS);
69+
parser.declareLong(constructorArg(), TOOK);
70+
parser.declareBoolean(constructorArg(), TIMED_OUT);
71+
parser.declareString(optionalConstructorArg(), ID);
72+
parser.declareBoolean(constructorArg(), IS_RUNNING);
73+
parser.declareBoolean(constructorArg(), IS_PARTIAL);
74+
PARSER = parser.build();
6575
}
6676

67-
public EqlSearchResponse(Hits hits, long tookInMillis, boolean isTimeout) {
77+
public EqlSearchResponse(Hits hits, long tookInMillis, boolean isTimeout, String asyncExecutionId,
78+
boolean isRunning, boolean isPartial) {
6879
super();
6980
this.hits = hits == null ? Hits.EMPTY : hits;
7081
this.tookInMillis = tookInMillis;
7182
this.isTimeout = isTimeout;
83+
this.asyncExecutionId = asyncExecutionId;
84+
this.isRunning = isRunning;
85+
this.isPartial = isPartial;
7286
}
7387

7488
public static EqlSearchResponse fromXContent(XContentParser parser) {
@@ -87,6 +101,18 @@ public Hits hits() {
87101
return hits;
88102
}
89103

104+
public String id() {
105+
return asyncExecutionId;
106+
}
107+
108+
public boolean isRunning() {
109+
return isRunning;
110+
}
111+
112+
public boolean isPartial() {
113+
return isPartial;
114+
}
115+
90116
@Override
91117
public boolean equals(Object o) {
92118
if (this == o) {

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,12 @@ public static org.elasticsearch.xpack.eql.action.EqlSearchResponse createRandomE
5757
if (randomBoolean()) {
5858
hits = new org.elasticsearch.xpack.eql.action.EqlSearchResponse.Hits(randomEvents(), null, null, totalHits);
5959
}
60-
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean());
60+
if (randomBoolean()) {
61+
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean());
62+
} else {
63+
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean(),
64+
randomAlphaOfLength(10), randomBoolean(), randomBoolean());
65+
}
6166
}
6267

6368
public static org.elasticsearch.xpack.eql.action.EqlSearchResponse createRandomSequencesResponse(TotalHits totalHits) {
@@ -77,7 +82,12 @@ public static org.elasticsearch.xpack.eql.action.EqlSearchResponse createRandomS
7782
if (randomBoolean()) {
7883
hits = new org.elasticsearch.xpack.eql.action.EqlSearchResponse.Hits(null, seq, null, totalHits);
7984
}
80-
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean());
85+
if (randomBoolean()) {
86+
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean());
87+
} else {
88+
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean(),
89+
randomAlphaOfLength(10), randomBoolean(), randomBoolean());
90+
}
8191
}
8292

8393
public static org.elasticsearch.xpack.eql.action.EqlSearchResponse createRandomCountResponse(TotalHits totalHits) {
@@ -97,7 +107,12 @@ public static org.elasticsearch.xpack.eql.action.EqlSearchResponse createRandomC
97107
if (randomBoolean()) {
98108
hits = new org.elasticsearch.xpack.eql.action.EqlSearchResponse.Hits(null, null, cn, totalHits);
99109
}
100-
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean());
110+
if (randomBoolean()) {
111+
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean());
112+
} else {
113+
return new org.elasticsearch.xpack.eql.action.EqlSearchResponse(hits, randomIntBetween(0, 1001), randomBoolean(),
114+
randomAlphaOfLength(10), randomBoolean(), randomBoolean());
115+
}
101116
}
102117

103118
public static org.elasticsearch.xpack.eql.action.EqlSearchResponse createRandomInstance(TotalHits totalHits) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[role="xpack"]
2+
[testenv="basic"]
3+
4+
[[delete-async-eql-search-api]]
5+
=== Delete async EQL search API
6+
++++
7+
<titleabbrev>Delete async EQL search</titleabbrev>
8+
++++
9+
10+
dev::[]
11+
12+
Deletes an <<eql-search-async,async EQL search>> or a
13+
<<eql-search-store-sync-eql-search,stored synchronous EQL search>>. The API also
14+
deletes results for the search.
15+
16+
[source,console]
17+
----
18+
DELETE /_eql/search/FkpMRkJGS1gzVDRlM3g4ZzMyRGlLbkEaTXlJZHdNT09TU2VTZVBoNDM3cFZMUToxMDM=
19+
----
20+
// TEST[skip: no access to search ID]
21+
22+
[[delete-async-eql-search-api-request]]
23+
==== {api-request-title}
24+
25+
`DELETE /_eql/search/<search_id>`
26+
27+
[[delete-async-eql-search-api-prereqs]]
28+
==== {api-prereq-title}
29+
30+
See <<eql-requirements,EQL requirements>>.
31+
32+
[[delete-async-eql-search-api-limitations]]
33+
===== Limitations
34+
35+
See <<eql-limitations,EQL limitations>>.
36+
37+
[[delete-async-eql-search-api-path-params]]
38+
==== {api-path-parms-title}
39+
40+
`<search_id>`::
41+
(Required, string)
42+
Identifier for the search to delete.
43+
+
44+
A search ID is provided in the <<eql-search-api,EQL search API>>'s response for
45+
an <<eql-search-async,async search>>. A search ID is also provided if the
46+
request's <<eql-search-api-keep-on-completion,`keep_on_completion`>> parameter
47+
is `true`.

0 commit comments

Comments
 (0)