Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Model Type Validation to Validate API ("non-blocker") #384

Merged
merged 13 commits into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ List<String> jacocoExclusions = [
'org.opensearch.ad.AnomalyDetectorPlugin',
'org.opensearch.ad.settings.AnomalyDetectorSettings',

//TODO: Add more cases for model validation API, both UT and IT
//TODO: add more test cases later for these package
'org.opensearch.ad.model.*',
'org.opensearch.ad.rest.*',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@

import org.opensearch.ad.model.AnomalyDetector;
import org.opensearch.ad.model.DetectorValidationIssueType;
import org.opensearch.ad.model.IntervalTimeConfiguration;
import org.opensearch.ad.model.ValidationAspect;

public class ADValidationException extends AnomalyDetectionException {
private final DetectorValidationIssueType type;
private final ValidationAspect aspect;
private final IntervalTimeConfiguration intervalSuggestion;

public DetectorValidationIssueType getType() {
return type;
Expand All @@ -27,14 +29,34 @@ public ValidationAspect getAspect() {
return aspect;
}

public IntervalTimeConfiguration getIntervalSuggestion() {
return intervalSuggestion;
}

public ADValidationException(String message, DetectorValidationIssueType type, ValidationAspect aspect) {
this(message, null, type, aspect);
this(message, null, type, aspect, null);
}

public ADValidationException(
String message,
DetectorValidationIssueType type,
ValidationAspect aspect,
IntervalTimeConfiguration intervalSuggestion
) {
this(message, null, type, aspect, intervalSuggestion);
}

public ADValidationException(String message, Throwable cause, DetectorValidationIssueType type, ValidationAspect aspect) {
public ADValidationException(
String message,
Throwable cause,
DetectorValidationIssueType type,
ValidationAspect aspect,
IntervalTimeConfiguration intervalSuggestion
) {
super(AnomalyDetector.NO_ID, message, cause);
this.type = type;
this.aspect = aspect;
this.intervalSuggestion = intervalSuggestion;
}

@Override
Expand All @@ -51,6 +73,12 @@ public String toString() {
sb.append(aspect.getName());
}

if (intervalSuggestion != null) {
sb.append(" interval suggestion: ");
sb.append(intervalSuggestion.getInterval());
sb.append(intervalSuggestion.getUnit());
}

return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public static String getTooManyCategoricalFieldErr(int limit) {
public static String INVALID_DETECTOR_NAME =
"Valid characters for detector name are a-z, A-Z, 0-9, -(hyphen), _(underscore) and .(period)";
public static String DUPLICATE_FEATURE_AGGREGATION_NAMES = "Detector has duplicate feature aggregation query names: ";
public static String INVALID_TIMESTAMP = "Timestamp field: (%s) must be of type date";
public static String NON_EXISTENT_TIMESTAMP = "Timestamp field: (%s) is not found in index mapping";

public static String FAIL_TO_GET_DETECTOR = "Fail to get detector";
public static String FAIL_TO_GET_DETECTOR_INFO = "Fail to get detector info";
Expand All @@ -103,4 +105,31 @@ public static String getTooManyCategoricalFieldErr(int limit) {
public static String INVALID_DETECTOR_NAME_SIZE = "Name should be shortened. The maximum limit is "
+ MAX_DETECTOR_NAME_SIZE
+ " characters.";

public static String WINDOW_DELAY_REC =
"Latest seen data point is at least %d minutes ago, consider changing window delay to at least %d minutes.";
public static String TIME_FIELD_NOT_ENOUGH_HISTORICAL_DATA =
"There isn't enough historical data found with current timefield selected.";
public static String DETECTOR_INTERVAL_REC =
"The selected detector interval might collect sparse data. Consider changing interval length too: ";
public static String RAW_DATA_TOO_SPARSE =
"Source index data is potentially too sparse for model training. Consider changing interval length or ingesting more data";
public static String MODEL_VALIDATION_FAILED_UNEXPECTEDLY = "Model validation experienced issues completing.";
public static String FILTER_QUERY_TOO_SPARSE = "Data is too sparse after data filter is applied. Consider changing the data filter";
public static String CATEGORY_FIELD_TOO_SPARSE =
"Data is most likely too sparse with the given category fields. Consider revising category field/s or ingesting more data ";
public static String CATEGORY_FIELD_NO_DATA =
"No entity was found with the given categorical fields. Consider revising category field/s or ingesting more data";
public static String FEATURE_QUERY_TOO_SPARSE =
"Data is most likely too sparse when given feature queries are applied. Consider revising feature queries.";
public static String TIMEOUT_ON_INTERVAL_REC = "Timed out getting interval recommendation";

// Modifying message for FEATURE below may break the parseADValidationException method of ValidateAnomalyDetectorTransportAction
public static final String FEATURE_INVALID_MSG_PREFIX = "Feature has an invalid query";
public static final String FEATURE_WITH_EMPTY_DATA_MSG = FEATURE_INVALID_MSG_PREFIX + " returning empty aggregated data: ";
public static final String FEATURE_WITH_INVALID_QUERY_MSG = FEATURE_INVALID_MSG_PREFIX + " causing a runtime exception: ";
public static final String UNKNOWN_SEARCH_QUERY_EXCEPTION_MSG =
"Feature has an unknown exception caught while executing the feature query: ";
public static final String VALIDATION_FEATURE_FAILURE = "Validation failed for feature(s) of detector %s";

}
4 changes: 3 additions & 1 deletion src/main/java/org/opensearch/ad/constant/CommonName.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public class CommonName {
public static final String TYPE = "type";
public static final String KEYWORD_TYPE = "keyword";
public static final String IP_TYPE = "ip";
public static final String DATE_TYPE = "date";

// used for updating mapping
public static final String SCHEMA_VERSION_FIELD = "schema_version";
Expand Down Expand Up @@ -134,7 +135,8 @@ public class CommonName {
// Validation
// ======================================
// detector validation aspect
public static final String DETECTOR = "detector";
public static final String DETECTOR_ASPECT = "detector";
public static final String MODEL_ASPECT = "model";

// ======================================
// Used for custom AD result index
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ public class AnomalyDetector implements Writeable, ToXContentObject {
public static final String TYPE = "_doc";
public static final String QUERY_PARAM_PERIOD_START = "period_start";
public static final String QUERY_PARAM_PERIOD_END = "period_end";
public static final String PARSING_ISSUE = "query_parsing";
public static final String GENERAL_SETTINGS = "general_settings";

public static final String NAME_FIELD = "name";
Expand All @@ -95,6 +94,8 @@ public class AnomalyDetector implements Writeable, ToXContentObject {
public static final String USER_FIELD = "user";
public static final String DETECTOR_TYPE_FIELD = "detector_type";
public static final String RESULT_INDEX_FIELD = "result_index";
public static final String AGGREGATION = "aggregation_issue";
public static final String TIMEOUT = "timeout";
@Deprecated
public static final String DETECTION_DATE_RANGE_FIELD = "detection_date_range";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class DetectorValidationIssue implements ToXContentObject, Writeable {
private final DetectorValidationIssueType type;
private final String message;
private Map<String, String> subIssues;
private String suggestion;
private IntervalTimeConfiguration intervalSuggestion;

public ValidationAspect getAspect() {
return aspect;
Expand All @@ -59,22 +59,22 @@ public Map<String, String> getSubIssues() {
return subIssues;
}

public String getSuggestion() {
return suggestion;
public IntervalTimeConfiguration getIntervalSuggestion() {
return intervalSuggestion;
}

public DetectorValidationIssue(
ValidationAspect aspect,
DetectorValidationIssueType type,
String message,
Map<String, String> subIssues,
String suggestion
IntervalTimeConfiguration intervalSuggestion
) {
this.aspect = aspect;
this.type = type;
this.message = message;
this.subIssues = subIssues;
this.suggestion = suggestion;
this.intervalSuggestion = intervalSuggestion;
}

public DetectorValidationIssue(ValidationAspect aspect, DetectorValidationIssueType type, String message) {
Expand All @@ -89,7 +89,7 @@ public DetectorValidationIssue(StreamInput input) throws IOException {
subIssues = input.readMap(StreamInput::readString, StreamInput::readString);
}
if (input.readBoolean()) {
suggestion = input.readString();
intervalSuggestion = IntervalTimeConfiguration.readFrom(input);
}
}

Expand All @@ -104,9 +104,9 @@ public void writeTo(StreamOutput out) throws IOException {
} else {
out.writeBoolean(false);
}
if (suggestion != null) {
if (intervalSuggestion != null) {
out.writeBoolean(true);
out.writeGenericValue(suggestion);
intervalSuggestion.writeTo(out);
} else {
out.writeBoolean(false);
}
Expand All @@ -123,8 +123,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
}
subIssuesBuilder.endObject();
}
if (suggestion != null) {
xContentBuilder.field(SUGGESTED_FIELD_NAME, suggestion);
if (intervalSuggestion != null) {
xContentBuilder.field(SUGGESTED_FIELD_NAME, intervalSuggestion);
}

return xContentBuilder.endObject().endObject();
Expand All @@ -140,7 +140,7 @@ public boolean equals(Object o) {
return Objects.equal(getAspect(), anotherIssue.getAspect())
&& Objects.equal(getMessage(), anotherIssue.getMessage())
&& Objects.equal(getSubIssues(), anotherIssue.getSubIssues())
&& Objects.equal(getSuggestion(), anotherIssue.getSuggestion())
&& Objects.equal(getIntervalSuggestion(), anotherIssue.getIntervalSuggestion())
&& Objects.equal(getType(), anotherIssue.getType());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ public enum DetectorValidationIssueType implements Name {
FILTER_QUERY(AnomalyDetector.FILTER_QUERY_FIELD),
WINDOW_DELAY(AnomalyDetector.WINDOW_DELAY_FIELD),
GENERAL_SETTINGS(AnomalyDetector.GENERAL_SETTINGS),
RESULT_INDEX(AnomalyDetector.RESULT_INDEX_FIELD);
RESULT_INDEX(AnomalyDetector.RESULT_INDEX_FIELD),
TIMEOUT(AnomalyDetector.TIMEOUT),
AGGREGATION(AnomalyDetector.AGGREGATION); // this is a unique case where aggregation failed due to an issue in core but
// don't want to throw exception

private String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ public static IntervalTimeConfiguration readFrom(StreamInput input) throws IOExc
return new IntervalTimeConfiguration(input);
}

public static long getIntervalInMinute(IntervalTimeConfiguration interval) {
if (interval.getUnit() == ChronoUnit.SECONDS) {
return interval.getInterval() / 60;
}
return interval.getInterval();
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeLong(this.interval);
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/org/opensearch/ad/model/ValidationAspect.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
* </ul>
*/
public enum ValidationAspect implements Name {
DETECTOR(CommonName.DETECTOR);
DETECTOR(CommonName.DETECTOR_ASPECT),
MODEL(CommonName.MODEL_ASPECT);

private String name;

Expand All @@ -48,8 +49,10 @@ public String getName() {

public static ValidationAspect getName(String name) {
switch (name) {
case CommonName.DETECTOR:
case CommonName.DETECTOR_ASPECT:
return DETECTOR;
case CommonName.MODEL_ASPECT:
return MODEL;
default:
throw new IllegalArgumentException("Unsupported validation aspects");
}
Expand Down
Loading