mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-23 13:26:02 +00:00
[ML] Add description to ML filters (#31330)
This adds a `description` to ML filters in order to allow users to describe their filters in a human readable form which is also editable (filter updates to be added shortly).
This commit is contained in:
parent
f7a0cafe55
commit
9b293275af
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.core.ml.job.config;
|
package org.elasticsearch.xpack.core.ml.job.config;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
@ -30,6 +31,7 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||||||
|
|
||||||
public static final ParseField TYPE = new ParseField("type");
|
public static final ParseField TYPE = new ParseField("type");
|
||||||
public static final ParseField ID = new ParseField("filter_id");
|
public static final ParseField ID = new ParseField("filter_id");
|
||||||
|
public static final ParseField DESCRIPTION = new ParseField("description");
|
||||||
public static final ParseField ITEMS = new ParseField("items");
|
public static final ParseField ITEMS = new ParseField("items");
|
||||||
|
|
||||||
// For QueryPage
|
// For QueryPage
|
||||||
@ -43,27 +45,38 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||||||
|
|
||||||
parser.declareString((builder, s) -> {}, TYPE);
|
parser.declareString((builder, s) -> {}, TYPE);
|
||||||
parser.declareString(Builder::setId, ID);
|
parser.declareString(Builder::setId, ID);
|
||||||
|
parser.declareStringOrNull(Builder::setDescription, DESCRIPTION);
|
||||||
parser.declareStringArray(Builder::setItems, ITEMS);
|
parser.declareStringArray(Builder::setItems, ITEMS);
|
||||||
|
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
|
private final String description;
|
||||||
private final List<String> items;
|
private final List<String> items;
|
||||||
|
|
||||||
public MlFilter(String id, List<String> items) {
|
public MlFilter(String id, String description, List<String> items) {
|
||||||
this.id = Objects.requireNonNull(id, ID.getPreferredName() + " must not be null");
|
this.id = Objects.requireNonNull(id, ID.getPreferredName() + " must not be null");
|
||||||
|
this.description = description;
|
||||||
this.items = Objects.requireNonNull(items, ITEMS.getPreferredName() + " must not be null");
|
this.items = Objects.requireNonNull(items, ITEMS.getPreferredName() + " must not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
public MlFilter(StreamInput in) throws IOException {
|
public MlFilter(StreamInput in) throws IOException {
|
||||||
id = in.readString();
|
id = in.readString();
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_6_4_0)) {
|
||||||
|
description = in.readOptionalString();
|
||||||
|
} else {
|
||||||
|
description = null;
|
||||||
|
}
|
||||||
items = Arrays.asList(in.readStringArray());
|
items = Arrays.asList(in.readStringArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
out.writeString(id);
|
out.writeString(id);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_6_4_0)) {
|
||||||
|
out.writeOptionalString(description);
|
||||||
|
}
|
||||||
out.writeStringArray(items.toArray(new String[items.size()]));
|
out.writeStringArray(items.toArray(new String[items.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +84,9 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
builder.field(ID.getPreferredName(), id);
|
builder.field(ID.getPreferredName(), id);
|
||||||
|
if (description != null) {
|
||||||
|
builder.field(DESCRIPTION.getPreferredName(), description);
|
||||||
|
}
|
||||||
builder.field(ITEMS.getPreferredName(), items);
|
builder.field(ITEMS.getPreferredName(), items);
|
||||||
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
|
if (params.paramAsBoolean(MlMetaIndex.INCLUDE_TYPE_KEY, false)) {
|
||||||
builder.field(TYPE.getPreferredName(), FILTER_TYPE);
|
builder.field(TYPE.getPreferredName(), FILTER_TYPE);
|
||||||
@ -83,6 +99,10 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getItems() {
|
public List<String> getItems() {
|
||||||
return new ArrayList<>(items);
|
return new ArrayList<>(items);
|
||||||
}
|
}
|
||||||
@ -98,12 +118,12 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MlFilter other = (MlFilter) obj;
|
MlFilter other = (MlFilter) obj;
|
||||||
return id.equals(other.id) && items.equals(other.items);
|
return id.equals(other.id) && Objects.equals(description, other.description) && items.equals(other.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, items);
|
return Objects.hash(id, description, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String documentId() {
|
public String documentId() {
|
||||||
@ -114,30 +134,45 @@ public class MlFilter implements ToXContentObject, Writeable {
|
|||||||
return DOCUMENT_ID_PREFIX + filterId;
|
return DOCUMENT_ID_PREFIX + filterId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Builder builder(String filterId) {
|
||||||
|
return new Builder().setId(filterId);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
|
private String description;
|
||||||
private List<String> items = Collections.emptyList();
|
private List<String> items = Collections.emptyList();
|
||||||
|
|
||||||
|
private Builder() {}
|
||||||
|
|
||||||
public Builder setId(String id) {
|
public Builder setId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder() {}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setItems(List<String> items) {
|
public Builder setItems(List<String> items) {
|
||||||
this.items = items;
|
this.items = items;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setItems(String... items) {
|
||||||
|
this.items = Arrays.asList(items);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MlFilter build() {
|
public MlFilter build() {
|
||||||
return new MlFilter(id, items);
|
return new MlFilter(id, description, items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ import org.elasticsearch.test.AbstractStreamableTestCase;
|
|||||||
import org.elasticsearch.xpack.core.ml.action.GetFiltersAction.Response;
|
import org.elasticsearch.xpack.core.ml.action.GetFiltersAction.Response;
|
||||||
import org.elasticsearch.xpack.core.ml.action.util.QueryPage;
|
import org.elasticsearch.xpack.core.ml.action.util.QueryPage;
|
||||||
import org.elasticsearch.xpack.core.ml.job.config.MlFilter;
|
import org.elasticsearch.xpack.core.ml.job.config.MlFilter;
|
||||||
|
import org.elasticsearch.xpack.core.ml.job.config.MlFilterTests;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@ -17,9 +18,7 @@ public class GetFiltersActionResponseTests extends AbstractStreamableTestCase<Ge
|
|||||||
@Override
|
@Override
|
||||||
protected Response createTestInstance() {
|
protected Response createTestInstance() {
|
||||||
final QueryPage<MlFilter> result;
|
final QueryPage<MlFilter> result;
|
||||||
|
MlFilter doc = MlFilterTests.createRandom();
|
||||||
MlFilter doc = new MlFilter(
|
|
||||||
randomAlphaOfLengthBetween(1, 20), Collections.singletonList(randomAlphaOfLengthBetween(1, 20)));
|
|
||||||
result = new QueryPage<>(Collections.singletonList(doc), 1, MlFilter.RESULTS_FIELD);
|
result = new QueryPage<>(Collections.singletonList(doc), 1, MlFilter.RESULTS_FIELD);
|
||||||
return new Response(result);
|
return new Response(result);
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,7 @@ package org.elasticsearch.xpack.core.ml.action;
|
|||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||||
import org.elasticsearch.xpack.core.ml.action.PutFilterAction.Request;
|
import org.elasticsearch.xpack.core.ml.action.PutFilterAction.Request;
|
||||||
import org.elasticsearch.xpack.core.ml.job.config.MlFilter;
|
import org.elasticsearch.xpack.core.ml.job.config.MlFilterTests;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PutFilterActionRequestTests extends AbstractStreamableXContentTestCase<Request> {
|
public class PutFilterActionRequestTests extends AbstractStreamableXContentTestCase<Request> {
|
||||||
|
|
||||||
@ -19,13 +16,7 @@ public class PutFilterActionRequestTests extends AbstractStreamableXContentTestC
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Request createTestInstance() {
|
protected Request createTestInstance() {
|
||||||
int size = randomInt(10);
|
return new PutFilterAction.Request(MlFilterTests.createRandom(filterId));
|
||||||
List<String> items = new ArrayList<>(size);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
items.add(randomAlphaOfLengthBetween(1, 20));
|
|
||||||
}
|
|
||||||
MlFilter filter = new MlFilter(filterId, items);
|
|
||||||
return new PutFilterAction.Request(filter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -42,5 +33,4 @@ public class PutFilterActionRequestTests extends AbstractStreamableXContentTestC
|
|||||||
protected Request doParseInstance(XContentParser parser) {
|
protected Request doParseInstance(XContentParser parser) {
|
||||||
return PutFilterAction.Request.parseRequest(filterId, parser);
|
return PutFilterAction.Request.parseRequest(filterId, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,25 @@ public class MlFilterTests extends AbstractSerializingTestCase<MlFilter> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MlFilter createTestInstance() {
|
protected MlFilter createTestInstance() {
|
||||||
|
return createRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MlFilter createRandom() {
|
||||||
|
return createRandom(randomAlphaOfLengthBetween(1, 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MlFilter createRandom(String filterId) {
|
||||||
|
String description = null;
|
||||||
|
if (randomBoolean()) {
|
||||||
|
description = randomAlphaOfLength(20);
|
||||||
|
}
|
||||||
|
|
||||||
int size = randomInt(10);
|
int size = randomInt(10);
|
||||||
List<String> items = new ArrayList<>(size);
|
List<String> items = new ArrayList<>(size);
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
items.add(randomAlphaOfLengthBetween(1, 20));
|
items.add(randomAlphaOfLengthBetween(1, 20));
|
||||||
}
|
}
|
||||||
return new MlFilter(randomAlphaOfLengthBetween(1, 20), items);
|
return new MlFilter(filterId, description, items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -45,13 +58,13 @@ public class MlFilterTests extends AbstractSerializingTestCase<MlFilter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNullId() {
|
public void testNullId() {
|
||||||
NullPointerException ex = expectThrows(NullPointerException.class, () -> new MlFilter(null, Collections.emptyList()));
|
NullPointerException ex = expectThrows(NullPointerException.class, () -> new MlFilter(null, "", Collections.emptyList()));
|
||||||
assertEquals(MlFilter.ID.getPreferredName() + " must not be null", ex.getMessage());
|
assertEquals(MlFilter.ID.getPreferredName() + " must not be null", ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullItems() {
|
public void testNullItems() {
|
||||||
NullPointerException ex =
|
NullPointerException ex =
|
||||||
expectThrows(NullPointerException.class, () -> new MlFilter(randomAlphaOfLengthBetween(1, 20), null));
|
expectThrows(NullPointerException.class, () -> new MlFilter(randomAlphaOfLengthBetween(1, 20), "", null));
|
||||||
assertEquals(MlFilter.ITEMS.getPreferredName() + " must not be null", ex.getMessage());
|
assertEquals(MlFilter.ITEMS.getPreferredName() + " must not be null", ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,8 +385,8 @@ public class JobProviderIT extends MlSingleNodeTestCase {
|
|||||||
indexScheduledEvents(events);
|
indexScheduledEvents(events);
|
||||||
|
|
||||||
List<MlFilter> filters = new ArrayList<>();
|
List<MlFilter> filters = new ArrayList<>();
|
||||||
filters.add(new MlFilter("fruit", Arrays.asList("apple", "pear")));
|
filters.add(MlFilter.builder("fruit").setItems("apple", "pear").build());
|
||||||
filters.add(new MlFilter("tea", Arrays.asList("green", "builders")));
|
filters.add(MlFilter.builder("tea").setItems("green", "builders").build());
|
||||||
indexFilters(filters);
|
indexFilters(filters);
|
||||||
|
|
||||||
DataCounts earliestCounts = DataCountsTests.createTestInstance(jobId);
|
DataCounts earliestCounts = DataCountsTests.createTestInstance(jobId);
|
||||||
|
@ -210,7 +210,7 @@ public class JobManagerTests extends ESTestCase {
|
|||||||
|
|
||||||
JobManager jobManager = createJobManager();
|
JobManager jobManager = createJobManager();
|
||||||
|
|
||||||
MlFilter filter = new MlFilter("foo_filter", Arrays.asList("a", "b"));
|
MlFilter filter = MlFilter.builder("foo_filter").setItems("a", "b").build();
|
||||||
|
|
||||||
jobManager.updateProcessOnFilterChanged(filter);
|
jobManager.updateProcessOnFilterChanged(filter);
|
||||||
|
|
||||||
|
@ -207,8 +207,8 @@ public class ControlMsgToProcessWriterTests extends ESTestCase {
|
|||||||
public void testWriteUpdateFiltersMessage() throws IOException {
|
public void testWriteUpdateFiltersMessage() throws IOException {
|
||||||
ControlMsgToProcessWriter writer = new ControlMsgToProcessWriter(lengthEncodedWriter, 2);
|
ControlMsgToProcessWriter writer = new ControlMsgToProcessWriter(lengthEncodedWriter, 2);
|
||||||
|
|
||||||
MlFilter filter1 = new MlFilter("filter_1", Arrays.asList("a"));
|
MlFilter filter1 = MlFilter.builder("filter_1").setItems("a").build();
|
||||||
MlFilter filter2 = new MlFilter("filter_2", Arrays.asList("b", "c"));
|
MlFilter filter2 = MlFilter.builder("filter_2").setItems("b", "c").build();
|
||||||
|
|
||||||
writer.writeUpdateFiltersMessage(Arrays.asList(filter1, filter2));
|
writer.writeUpdateFiltersMessage(Arrays.asList(filter1, filter2));
|
||||||
|
|
||||||
|
@ -220,8 +220,8 @@ public class FieldConfigWriterTests extends ESTestCase {
|
|||||||
AnalysisConfig.Builder builder = new AnalysisConfig.Builder(Collections.singletonList(d));
|
AnalysisConfig.Builder builder = new AnalysisConfig.Builder(Collections.singletonList(d));
|
||||||
analysisConfig = builder.build();
|
analysisConfig = builder.build();
|
||||||
|
|
||||||
filters.add(new MlFilter("filter_1", Arrays.asList("a", "b")));
|
filters.add(MlFilter.builder("filter_1").setItems("a", "b").build());
|
||||||
filters.add(new MlFilter("filter_2", Arrays.asList("c", "d")));
|
filters.add(MlFilter.builder("filter_2").setItems("c", "d").build());
|
||||||
writer = mock(OutputStreamWriter.class);
|
writer = mock(OutputStreamWriter.class);
|
||||||
|
|
||||||
createFieldConfigWriter().write();
|
createFieldConfigWriter().write();
|
||||||
|
@ -10,7 +10,6 @@ import org.elasticsearch.xpack.core.ml.job.config.MlFilter;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -28,8 +27,8 @@ public class MlFilterWriterTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testWrite() throws IOException {
|
public void testWrite() throws IOException {
|
||||||
List<MlFilter> filters = new ArrayList<>();
|
List<MlFilter> filters = new ArrayList<>();
|
||||||
filters.add(new MlFilter("filter_1", Arrays.asList("a", "b")));
|
filters.add(MlFilter.builder("filter_1").setItems("a", "b").build());
|
||||||
filters.add(new MlFilter("filter_2", Arrays.asList("c", "d")));
|
filters.add(MlFilter.builder("filter_2").setItems("c", "d").build());
|
||||||
|
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
new MlFilterWriter(filters, buffer).write();
|
new MlFilterWriter(filters, buffer).write();
|
||||||
|
@ -32,6 +32,7 @@ setup:
|
|||||||
filter_id: filter-foo2
|
filter_id: filter-foo2
|
||||||
body: >
|
body: >
|
||||||
{
|
{
|
||||||
|
"description": "This filter has a description",
|
||||||
"items": ["123", "lmnop"]
|
"items": ["123", "lmnop"]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +77,7 @@ setup:
|
|||||||
- match:
|
- match:
|
||||||
filters.1:
|
filters.1:
|
||||||
filter_id: "filter-foo2"
|
filter_id: "filter-foo2"
|
||||||
|
description: "This filter has a description"
|
||||||
items: ["123", "lmnop"]
|
items: ["123", "lmnop"]
|
||||||
|
|
||||||
- do:
|
- do:
|
||||||
|
@ -120,7 +120,7 @@ public class DetectionRulesIT extends MlNativeAutodetectIntegTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testScope() throws Exception {
|
public void testScope() throws Exception {
|
||||||
MlFilter safeIps = new MlFilter("safe_ips", Arrays.asList("111.111.111.111", "222.222.222.222"));
|
MlFilter safeIps = MlFilter.builder("safe_ips").setItems("111.111.111.111", "222.222.222.222").build();
|
||||||
assertThat(putMlFilter(safeIps), is(true));
|
assertThat(putMlFilter(safeIps), is(true));
|
||||||
|
|
||||||
DetectionRule rule = new DetectionRule.Builder(RuleScope.builder().include("ip", "safe_ips")).build();
|
DetectionRule rule = new DetectionRule.Builder(RuleScope.builder().include("ip", "safe_ips")).build();
|
||||||
@ -178,7 +178,7 @@ public class DetectionRulesIT extends MlNativeAutodetectIntegTestCase {
|
|||||||
assertThat(records.get(0).getOverFieldValue(), equalTo("333.333.333.333"));
|
assertThat(records.get(0).getOverFieldValue(), equalTo("333.333.333.333"));
|
||||||
|
|
||||||
// Now let's update the filter
|
// Now let's update the filter
|
||||||
MlFilter updatedFilter = new MlFilter(safeIps.getId(), Collections.singletonList("333.333.333.333"));
|
MlFilter updatedFilter = MlFilter.builder(safeIps.getId()).setItems("333.333.333.333").build();
|
||||||
assertThat(putMlFilter(updatedFilter), is(true));
|
assertThat(putMlFilter(updatedFilter), is(true));
|
||||||
|
|
||||||
// Wait until the notification that the process was updated is indexed
|
// Wait until the notification that the process was updated is indexed
|
||||||
@ -229,7 +229,7 @@ public class DetectionRulesIT extends MlNativeAutodetectIntegTestCase {
|
|||||||
public void testScopeAndCondition() throws IOException {
|
public void testScopeAndCondition() throws IOException {
|
||||||
// We have 2 IPs and they're both safe-listed.
|
// We have 2 IPs and they're both safe-listed.
|
||||||
List<String> ips = Arrays.asList("111.111.111.111", "222.222.222.222");
|
List<String> ips = Arrays.asList("111.111.111.111", "222.222.222.222");
|
||||||
MlFilter safeIps = new MlFilter("safe_ips", ips);
|
MlFilter safeIps = MlFilter.builder("safe_ips").setItems(ips).build();
|
||||||
assertThat(putMlFilter(safeIps), is(true));
|
assertThat(putMlFilter(safeIps), is(true));
|
||||||
|
|
||||||
// Ignore if ip in safe list AND actual < 10.
|
// Ignore if ip in safe list AND actual < 10.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user