EQL: HLRC documentation (#80979)

This commit is contained in:
Andrei Stefan 2021-12-08 13:56:25 +02:00 committed by GitHub
parent 3798ff6df5
commit eeb39f8499
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 247 additions and 6 deletions

View file

@ -10,6 +10,7 @@ package org.elasticsearch.client.eql;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.get.GetResult;
@ -25,6 +26,7 @@ import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -144,16 +146,19 @@ public class EqlSearchResponse {
static final String INDEX = GetResult._INDEX;
static final String ID = GetResult._ID;
static final String SOURCE = SourceFieldMapper.NAME;
static final String FIELDS = "fields";
}
private static final ParseField INDEX = new ParseField(Fields.INDEX);
private static final ParseField ID = new ParseField(Fields.ID);
private static final ParseField SOURCE = new ParseField(Fields.SOURCE);
private static final ParseField FIELDS = new ParseField(Fields.FIELDS);
@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<Event, Void> PARSER = new ConstructingObjectParser<>(
"eql/search_response_event",
true,
args -> new Event((String) args[0], (String) args[1], (BytesReference) args[2])
args -> new Event((String) args[0], (String) args[1], (BytesReference) args[2], (Map<String, DocumentField>) args[3])
);
static {
@ -165,19 +170,35 @@ public class EqlSearchResponse {
return BytesReference.bytes(builder);
}
}, SOURCE);
PARSER.declareObject(optionalConstructorArg(), (p, c) -> {
Map<String, DocumentField> fields = new HashMap<>();
while (p.nextToken() != XContentParser.Token.END_OBJECT) {
DocumentField field = DocumentField.fromXContent(p);
fields.put(field.getName(), field);
}
return fields;
}, FIELDS);
}
private final String index;
private final String id;
private final BytesReference source;
private Map<String, Object> sourceAsMap;
private final Map<String, DocumentField> fetchFields;
@Deprecated
public Event(String index, String id, BytesReference source) {
this(index, id, source, null);
}
private Event(String index, String id, BytesReference source, Map<String, DocumentField> fetchFields) {
this.index = index;
this.id = id;
this.source = source;
this.fetchFields = fetchFields;
}
@Deprecated
public static Event fromXContent(XContentParser parser) throws IOException {
return PARSER.apply(parser, null);
}
@ -194,6 +215,10 @@ public class EqlSearchResponse {
return source;
}
public Map<String, DocumentField> fetchFields() {
return fetchFields;
}
public Map<String, Object> sourceAsMap() {
if (source == null) {
return null;
@ -208,7 +233,7 @@ public class EqlSearchResponse {
@Override
public int hashCode() {
return Objects.hash(index, id, source);
return Objects.hash(index, id, source, fetchFields);
}
@Override
@ -222,7 +247,10 @@ public class EqlSearchResponse {
}
EqlSearchResponse.Event other = (EqlSearchResponse.Event) obj;
return Objects.equals(index, other.index) && Objects.equals(id, other.id) && Objects.equals(source, other.source);
return Objects.equals(index, other.index)
&& Objects.equals(id, other.id)
&& Objects.equals(source, other.source)
&& Objects.equals(fetchFields, other.fetchFields);
}
}
@ -262,11 +290,13 @@ public class EqlSearchResponse {
private final List<Object> joinKeys;
private final List<Event> events;
@Deprecated
public Sequence(List<Object> joinKeys, List<Event> events) {
this.joinKeys = joinKeys == null ? Collections.emptyList() : joinKeys;
this.events = events == null ? Collections.emptyList() : events;
}
@Deprecated
public static Sequence fromXContent(XContentParser parser) {
return PARSER.apply(parser, null);
}
@ -311,6 +341,7 @@ public class EqlSearchResponse {
static final String SEQUENCES = "sequences";
}
@Deprecated
public Hits(@Nullable List<Event> events, @Nullable List<Sequence> sequences, @Nullable TotalHits totalHits) {
this.events = events;
this.sequences = sequences;
@ -345,6 +376,7 @@ public class EqlSearchResponse {
);
}
@Deprecated
public static Hits fromXContent(XContentParser parser) throws IOException {
return PARSER.parse(parser, null);
}

View file

@ -0,0 +1,122 @@
/*
* 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.documentation;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.WarningsHandler;
import org.elasticsearch.client.eql.EqlSearchRequest;
import org.elasticsearch.client.eql.EqlSearchResponse;
import org.elasticsearch.client.eql.EqlSearchResponse.Event;
import org.elasticsearch.client.eql.EqlSearchResponse.Hits;
import org.elasticsearch.client.eql.EqlSearchResponse.Sequence;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.FieldAndFormat;
import org.junit.Before;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.xcontent.XContentType.JSON;
/**
* Documentation for EQL APIs in the high level java client.
* Code wrapped in {@code tag} and {@code end} tags is included in the docs.
*/
@SuppressWarnings("removal")
public class EqlDocumentationIT extends ESRestHighLevelClientTestCase {
@Before
void setUpIndex() throws IOException {
String index = "my-index";
CreateIndexResponse createIndexResponse = highLevelClient().indices().create(new CreateIndexRequest(index), RequestOptions.DEFAULT);
assertTrue(createIndexResponse.isAcknowledged());
BulkRequest bulk = new BulkRequest(index).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
bulk.add(new IndexRequest().source(JSON, "event_category", "process", "timestamp", "2021-11-23T00:00:00Z", "tie", 1, "host", "A"));
bulk.add(new IndexRequest().source(JSON, "event_category", "process", "timestamp", "2021-11-23T00:00:00Z", "tie", 2, "host", "B"));
bulk.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
highLevelClient().bulk(bulk, RequestOptions.DEFAULT);
}
public void testEqlSearch() throws Exception {
RestHighLevelClient client = highLevelClient();
// tag::eql-search-request
String indices = "my-index"; // <1>
String query = "any where true"; // <2>
EqlSearchRequest request = new EqlSearchRequest(indices, query);
// end::eql-search-request
// tag::eql-search-request-arguments
request.eventCategoryField("event_category"); // <1>
request.fetchSize(50); // <2>
request.size(15); // <3>
request.tiebreakerField("tie"); // <4>
request.timestampField("timestamp"); // <5>
request.filter(QueryBuilders.matchAllQuery()); // <6>
request.resultPosition("head"); // <7>
List<FieldAndFormat> fields = new ArrayList<>();
fields.add(new FieldAndFormat("hostname", null));
request.fetchFields(fields); // <8>
IndicesOptions op = IndicesOptions.fromOptions(true, true, true, false);
request.indicesOptions(op); // <9>
Map<String, Object> settings = new HashMap<>();
settings.put("type", "keyword");
settings.put("script", "emit(doc['host.keyword'].value)");
Map<String, Object> field = new HashMap<>();
field.put("hostname", settings);
request.runtimeMappings(field); // <10>
request.waitForCompletionTimeout(TimeValue.timeValueMinutes(1)); // <11>
request.keepOnCompletion(true); // <12>
request.keepAlive(TimeValue.timeValueHours(12)); // <13>
// end::eql-search-request-arguments
// Ignore warning about ignore_throttled being deprecated
RequestOptions options = RequestOptions.DEFAULT.toBuilder().setWarningsHandler(WarningsHandler.PERMISSIVE).build();
// tag::eql-search-response
EqlSearchResponse response = client.eql().search(request, options);
response.id(); // <1>
response.isPartial(); // <2>
response.isRunning(); // <3>
response.isTimeout(); // <4>
response.took(); // <5>
Hits hits = response.hits(); // <6>
hits.totalHits(); // <7>
List<Event> events = hits.events(); // <8>
List<Sequence> sequences = hits.sequences(); // <9>
Map<String, Object> event = events.get(0).sourceAsMap();
Map<String, DocumentField> fetchField = events.get(0).fetchFields();
fetchField.get("hostname").getValues(); // <10>
// end::eql-search-response
assertFalse(response.isPartial());
assertFalse(response.isRunning());
assertFalse(response.isTimeout());
assertEquals(2, hits.totalHits().value);
assertEquals(2, events.size());
assertNull(sequences);
assertEquals(1, fetchField.size());
assertEquals(1, fetchField.get("hostname").getValues().size());
assertEquals("A", fetchField.get("hostname").getValues().get(0));
}
}

View file

@ -93,9 +93,10 @@ public class EqlSearchResponseTests extends AbstractResponseTestCase<
Map<String, DocumentField> fetchFields = new HashMap<>();
int fieldsCount = randomIntBetween(0, 5);
for (int j = 0; j < fieldsCount; j++) {
fetchFields.put(randomAlphaOfLength(10), randomDocumentField(xType).v1());
DocumentField doc = randomDocumentField(xType).v2();
fetchFields.put(doc.getName(), doc);
}
if (fetchFields.isEmpty() && randomBoolean()) {
if (fetchFields.isEmpty()) {
fetchFields = null;
}
hits.add(
@ -262,7 +263,10 @@ public class EqlSearchResponseTests extends AbstractResponseTestCase<
) {
assertThat(serverEvents.size(), equalTo(clientEvents.size()));
for (int j = 0; j < serverEvents.size(); j++) {
assertThat(SourceLookup.sourceAsMap(serverEvents.get(j).source()), is(clientEvents.get(j).sourceAsMap()));
org.elasticsearch.xpack.eql.action.EqlSearchResponse.Event serverEvent = serverEvents.get(j);
EqlSearchResponse.Event clientEvent = clientEvents.get(j);
assertThat(SourceLookup.sourceAsMap(serverEvent.source()), is(clientEvent.sourceAsMap()));
assertThat(serverEvent.fetchFields(), equalTo(clientEvent.fetchFields()));
}
}
}