Skip to content

Commit eb17128

Browse files
authored
[ML] Add Detector config classes to protocol library (#32495)
This commit adds the Detector class and its dependencies to the X-Pack protocol library used by the high level REST client. (Future commits will add the remaining config classes, plus results and stats classes.) These classes: - Are immutable, with builders, but the builders do no validation beyond null checks - Are convertible to and from X-Content, but NOT wire transportable - Have lenient parsers to maximize compatibility across versions - Have the same class names, member names and getter/setter names as the corresponding classes in X-Pack core to ease migration for transport client users - Don't reproduce all the methods that do calculations or transformations that the the corresponding classes in X-Pack core have
1 parent 937dcfd commit eb17128

File tree

17 files changed

+1870
-0
lines changed

17 files changed

+1870
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.protocol.xpack.ml.job.config;
20+
21+
import org.elasticsearch.common.Strings;
22+
23+
public final class DefaultDetectorDescription {
24+
private static final String BY_TOKEN = " by ";
25+
private static final String OVER_TOKEN = " over ";
26+
27+
private static final String USE_NULL_OPTION = " usenull=";
28+
private static final String PARTITION_FIELD_OPTION = " partitionfield=";
29+
private static final String EXCLUDE_FREQUENT_OPTION = " excludefrequent=";
30+
31+
private DefaultDetectorDescription() {
32+
}
33+
34+
/**
35+
* Returns the default description for the given {@code detector}
36+
*
37+
* @param detector the {@code Detector} for which a default description is requested
38+
* @return the default description
39+
*/
40+
public static String of(Detector detector) {
41+
StringBuilder sb = new StringBuilder();
42+
appendOn(detector, sb);
43+
return sb.toString();
44+
}
45+
46+
/**
47+
* Appends to the given {@code StringBuilder} the default description
48+
* for the given {@code detector}
49+
*
50+
* @param detector the {@code Detector} for which a default description is requested
51+
* @param sb the {@code StringBuilder} to append to
52+
*/
53+
public static void appendOn(Detector detector, StringBuilder sb) {
54+
if (isNotNullOrEmpty(detector.getFunction().getFullName())) {
55+
sb.append(detector.getFunction());
56+
if (isNotNullOrEmpty(detector.getFieldName())) {
57+
sb.append('(').append(quoteField(detector.getFieldName()))
58+
.append(')');
59+
}
60+
} else if (isNotNullOrEmpty(detector.getFieldName())) {
61+
sb.append(quoteField(detector.getFieldName()));
62+
}
63+
64+
if (isNotNullOrEmpty(detector.getByFieldName())) {
65+
sb.append(BY_TOKEN).append(quoteField(detector.getByFieldName()));
66+
}
67+
68+
if (isNotNullOrEmpty(detector.getOverFieldName())) {
69+
sb.append(OVER_TOKEN).append(quoteField(detector.getOverFieldName()));
70+
}
71+
72+
if (detector.isUseNull()) {
73+
sb.append(USE_NULL_OPTION).append(detector.isUseNull());
74+
}
75+
76+
if (isNotNullOrEmpty(detector.getPartitionFieldName())) {
77+
sb.append(PARTITION_FIELD_OPTION).append(quoteField(detector.getPartitionFieldName()));
78+
}
79+
80+
if (detector.getExcludeFrequent() != null) {
81+
sb.append(EXCLUDE_FREQUENT_OPTION).append(detector.getExcludeFrequent());
82+
}
83+
}
84+
85+
private static String quoteField(String field) {
86+
if (field.matches("\\w*")) {
87+
return field;
88+
} else {
89+
return "\"" + field.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
90+
}
91+
}
92+
93+
private static boolean isNotNullOrEmpty(String arg) {
94+
return !Strings.isNullOrEmpty(arg);
95+
}
96+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.protocol.xpack.ml.job.config;
20+
21+
import org.elasticsearch.common.ParseField;
22+
import org.elasticsearch.common.xcontent.ObjectParser;
23+
import org.elasticsearch.common.xcontent.ToXContentObject;
24+
import org.elasticsearch.common.xcontent.XContentBuilder;
25+
26+
import java.io.IOException;
27+
import java.util.Arrays;
28+
import java.util.Collections;
29+
import java.util.EnumSet;
30+
import java.util.List;
31+
import java.util.Objects;
32+
33+
public class DetectionRule implements ToXContentObject {
34+
35+
public static final ParseField DETECTION_RULE_FIELD = new ParseField("detection_rule");
36+
public static final ParseField ACTIONS_FIELD = new ParseField("actions");
37+
public static final ParseField SCOPE_FIELD = new ParseField("scope");
38+
public static final ParseField CONDITIONS_FIELD = new ParseField("conditions");
39+
40+
public static final ObjectParser<Builder, Void> PARSER =
41+
new ObjectParser<>(DETECTION_RULE_FIELD.getPreferredName(), true, Builder::new);;
42+
43+
static {
44+
PARSER.declareStringArray(Builder::setActions, ACTIONS_FIELD);
45+
PARSER.declareObject(Builder::setScope, RuleScope.parser(), SCOPE_FIELD);
46+
PARSER.declareObjectArray(Builder::setConditions, RuleCondition.PARSER, CONDITIONS_FIELD);
47+
}
48+
49+
private final EnumSet<RuleAction> actions;
50+
private final RuleScope scope;
51+
private final List<RuleCondition> conditions;
52+
53+
private DetectionRule(EnumSet<RuleAction> actions, RuleScope scope, List<RuleCondition> conditions) {
54+
this.actions = Objects.requireNonNull(actions);
55+
this.scope = Objects.requireNonNull(scope);
56+
this.conditions = Collections.unmodifiableList(conditions);
57+
}
58+
59+
@Override
60+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
61+
builder.startObject();
62+
builder.field(ACTIONS_FIELD.getPreferredName(), actions);
63+
if (scope.isEmpty() == false) {
64+
builder.field(SCOPE_FIELD.getPreferredName(), scope);
65+
}
66+
if (conditions.isEmpty() == false) {
67+
builder.field(CONDITIONS_FIELD.getPreferredName(), conditions);
68+
}
69+
builder.endObject();
70+
return builder;
71+
}
72+
73+
public EnumSet<RuleAction> getActions() {
74+
return actions;
75+
}
76+
77+
public RuleScope getScope() {
78+
return scope;
79+
}
80+
81+
public List<RuleCondition> getConditions() {
82+
return conditions;
83+
}
84+
85+
@Override
86+
public boolean equals(Object obj) {
87+
if (this == obj) {
88+
return true;
89+
}
90+
91+
if (obj instanceof DetectionRule == false) {
92+
return false;
93+
}
94+
95+
DetectionRule other = (DetectionRule) obj;
96+
return Objects.equals(actions, other.actions)
97+
&& Objects.equals(scope, other.scope)
98+
&& Objects.equals(conditions, other.conditions);
99+
}
100+
101+
@Override
102+
public int hashCode() {
103+
return Objects.hash(actions, scope, conditions);
104+
}
105+
106+
public static class Builder {
107+
private EnumSet<RuleAction> actions = EnumSet.of(RuleAction.SKIP_RESULT);
108+
private RuleScope scope = new RuleScope();
109+
private List<RuleCondition> conditions = Collections.emptyList();
110+
111+
public Builder(RuleScope.Builder scope) {
112+
this.scope = scope.build();
113+
}
114+
115+
public Builder(List<RuleCondition> conditions) {
116+
this.conditions = Objects.requireNonNull(conditions);
117+
}
118+
119+
Builder() {
120+
}
121+
122+
public Builder setActions(List<String> actions) {
123+
this.actions.clear();
124+
actions.stream().map(RuleAction::fromString).forEach(this.actions::add);
125+
return this;
126+
}
127+
128+
public Builder setActions(EnumSet<RuleAction> actions) {
129+
this.actions = Objects.requireNonNull(actions, ACTIONS_FIELD.getPreferredName());
130+
return this;
131+
}
132+
133+
public Builder setActions(RuleAction... actions) {
134+
this.actions.clear();
135+
Arrays.stream(actions).forEach(this.actions::add);
136+
return this;
137+
}
138+
139+
public Builder setScope(RuleScope scope) {
140+
this.scope = Objects.requireNonNull(scope);
141+
return this;
142+
}
143+
144+
public Builder setConditions(List<RuleCondition> conditions) {
145+
this.conditions = Objects.requireNonNull(conditions);
146+
return this;
147+
}
148+
149+
public DetectionRule build() {
150+
return new DetectionRule(actions, scope, conditions);
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)