use of org.opensearch.ad.common.exception.ADValidationException in project anomaly-detection by opensearch-project.
the class AbstractAnomalyDetectorActionHandler method onSearchSingleEntityAdResponse.
protected void onSearchSingleEntityAdResponse(SearchResponse response, boolean indexingDryRun) throws IOException {
if (response.getHits().getTotalHits().value >= maxSingleEntityAnomalyDetectors) {
String errorMsgSingleEntity = String.format(Locale.ROOT, EXCEEDED_MAX_SINGLE_ENTITY_DETECTORS_PREFIX_MSG, maxSingleEntityAnomalyDetectors);
logger.error(errorMsgSingleEntity);
if (indexingDryRun) {
listener.onFailure(new ADValidationException(errorMsgSingleEntity, DetectorValidationIssueType.GENERAL_SETTINGS, ValidationAspect.DETECTOR));
return;
}
listener.onFailure(new IllegalArgumentException(errorMsgSingleEntity));
} else {
searchAdInputIndices(null, indexingDryRun);
}
}
use of org.opensearch.ad.common.exception.ADValidationException in project anomaly-detection by opensearch-project.
the class AbstractAnomalyDetectorActionHandler method validateAnomalyDetectorFeatures.
/**
* Validate config/syntax, and runtime error of detector features
* @param detectorId detector id
* @param indexingDryRun if false, then will eventually index detector; true, skip indexing detector
* @throws IOException when fail to parse feature aggregation
*/
// TODO: move this method to util class so that it can be re-usable for more use cases
// https://github.com/opensearch-project/anomaly-detection/issues/39
protected void validateAnomalyDetectorFeatures(String detectorId, boolean indexingDryRun) throws IOException {
if (anomalyDetector != null && (anomalyDetector.getFeatureAttributes() == null || anomalyDetector.getFeatureAttributes().isEmpty())) {
checkADNameExists(detectorId, indexingDryRun);
return;
}
// checking configuration/syntax error of detector features
String error = RestHandlerUtils.checkAnomalyDetectorFeaturesSyntax(anomalyDetector, maxAnomalyFeatures);
if (StringUtils.isNotBlank(error)) {
if (indexingDryRun) {
listener.onFailure(new ADValidationException(error, DetectorValidationIssueType.FEATURE_ATTRIBUTES, ValidationAspect.DETECTOR));
return;
}
listener.onFailure(new OpenSearchStatusException(error, RestStatus.BAD_REQUEST));
return;
}
// checking runtime error from feature query
ActionListener<MergeableList<Optional<double[]>>> validateFeatureQueriesListener = ActionListener.wrap(response -> {
checkADNameExists(detectorId, indexingDryRun);
}, exception -> {
listener.onFailure(new ADValidationException(exception.getMessage(), DetectorValidationIssueType.FEATURE_ATTRIBUTES, ValidationAspect.DETECTOR));
});
MultiResponsesDelegateActionListener<MergeableList<Optional<double[]>>> multiFeatureQueriesResponseListener = new MultiResponsesDelegateActionListener<MergeableList<Optional<double[]>>>(validateFeatureQueriesListener, anomalyDetector.getFeatureAttributes().size(), String.format(Locale.ROOT, CommonErrorMessages.VALIDATION_FEATURE_FAILURE, anomalyDetector.getName()), false);
for (Feature feature : anomalyDetector.getFeatureAttributes()) {
SearchSourceBuilder ssb = new SearchSourceBuilder().size(1).query(QueryBuilders.matchAllQuery());
AggregatorFactories.Builder internalAgg = parseAggregators(feature.getAggregation().toString(), xContentRegistry, feature.getId());
ssb.aggregation(internalAgg.getAggregatorFactories().iterator().next());
SearchRequest searchRequest = new SearchRequest().indices(anomalyDetector.getIndices().toArray(new String[0])).source(ssb);
client.search(searchRequest, ActionListener.wrap(response -> {
Optional<double[]> aggFeatureResult = searchFeatureDao.parseResponse(response, Arrays.asList(feature.getId()));
if (aggFeatureResult.isPresent()) {
multiFeatureQueriesResponseListener.onResponse(new MergeableList<Optional<double[]>>(new ArrayList<Optional<double[]>>(Arrays.asList(aggFeatureResult))));
} else {
String errorMessage = CommonErrorMessages.FEATURE_WITH_EMPTY_DATA_MSG + feature.getName();
logger.error(errorMessage);
multiFeatureQueriesResponseListener.onFailure(new OpenSearchStatusException(errorMessage, RestStatus.BAD_REQUEST));
}
}, e -> {
String errorMessage;
if (isExceptionCausedByInvalidQuery(e)) {
errorMessage = CommonErrorMessages.FEATURE_WITH_INVALID_QUERY_MSG + feature.getName();
} else {
errorMessage = CommonErrorMessages.UNKNOWN_SEARCH_QUERY_EXCEPTION_MSG + feature.getName();
}
logger.error(errorMessage, e);
multiFeatureQueriesResponseListener.onFailure(new OpenSearchStatusException(errorMessage, RestStatus.BAD_REQUEST, e));
}));
}
}
use of org.opensearch.ad.common.exception.ADValidationException in project anomaly-detection by opensearch-project.
the class ModelValidationActionHandler method processTopEntityResults.
private void processTopEntityResults(SearchResponse response, long latestTime) {
Histogram aggregate = checkBucketResultErrors(response);
if (aggregate == null) {
return;
}
double fullBucketRate = processBucketAggregationResults(aggregate);
if (fullBucketRate < CONFIG_BUCKET_MINIMUM_SUCCESS_RATE) {
listener.onFailure(new ADValidationException(CommonErrorMessages.CATEGORY_FIELD_TOO_SPARSE, DetectorValidationIssueType.CATEGORY, ValidationAspect.MODEL));
} else {
try {
checkFeatureQueryDelegate(latestTime);
} catch (Exception ex) {
logger.error(ex);
listener.onFailure(ex);
}
}
}
use of org.opensearch.ad.common.exception.ADValidationException in project anomaly-detection by opensearch-project.
the class AbstractAnomalyDetectorActionHandler method onSearchAdInputIndicesResponse.
protected void onSearchAdInputIndicesResponse(SearchResponse response, String detectorId, boolean indexingDryRun) throws IOException {
if (response.getHits().getTotalHits().value == 0) {
String errorMsg = NO_DOCS_IN_USER_INDEX_MSG + Arrays.toString(anomalyDetector.getIndices().toArray(new String[0]));
logger.error(errorMsg);
if (indexingDryRun) {
listener.onFailure(new ADValidationException(errorMsg, DetectorValidationIssueType.INDICES, ValidationAspect.DETECTOR));
return;
}
listener.onFailure(new IllegalArgumentException(errorMsg));
} else {
validateAnomalyDetectorFeatures(detectorId, indexingDryRun);
}
}
use of org.opensearch.ad.common.exception.ADValidationException in project anomaly-detection by opensearch-project.
the class AbstractAnomalyDetectorActionHandler method validateCategoricalField.
@SuppressWarnings("unchecked")
protected void validateCategoricalField(String detectorId, boolean indexingDryRun) {
List<String> categoryField = anomalyDetector.getCategoryField();
if (categoryField == null) {
searchAdInputIndices(detectorId, indexingDryRun);
return;
}
// we only support a certain number of categorical field
// If there is more fields than required, AnomalyDetector's constructor
// throws ADValidationException before reaching this line
int maxCategoryFields = NumericSetting.maxCategoricalFields();
if (categoryField.size() > maxCategoryFields) {
listener.onFailure(new ADValidationException(CommonErrorMessages.getTooManyCategoricalFieldErr(maxCategoryFields), DetectorValidationIssueType.CATEGORY, ValidationAspect.DETECTOR));
return;
}
String categoryField0 = categoryField.get(0);
GetFieldMappingsRequest getMappingsRequest = new GetFieldMappingsRequest();
getMappingsRequest.indices(anomalyDetector.getIndices().toArray(new String[0])).fields(categoryField.toArray(new String[0]));
getMappingsRequest.indicesOptions(IndicesOptions.strictExpand());
ActionListener<GetFieldMappingsResponse> mappingsListener = ActionListener.wrap(getMappingsResponse -> {
// example getMappingsResponse:
// GetFieldMappingsResponse{mappings={server-metrics={_doc={service=FieldMappingMetadata{fullName='service',
// source=org.opensearch.common.bytes.BytesArray@7ba87dbd}}}}}
// for nested field, it would be
// GetFieldMappingsResponse{mappings={server-metrics={_doc={host_nest.host2=FieldMappingMetadata{fullName='host_nest.host2',
// source=org.opensearch.common.bytes.BytesArray@8fb4de08}}}}}
boolean foundField = false;
// Review why the change from FieldMappingMetadata to GetFieldMappingsResponse.FieldMappingMetadata
Map<String, Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetadata>>> mappingsByIndex = getMappingsResponse.mappings();
for (Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetadata>> mappingsByType : mappingsByIndex.values()) {
for (Map<String, GetFieldMappingsResponse.FieldMappingMetadata> mappingsByField : mappingsByType.values()) {
for (Map.Entry<String, GetFieldMappingsResponse.FieldMappingMetadata> field2Metadata : mappingsByField.entrySet()) {
// example output:
// host_nest.host2=FieldMappingMetadata{fullName='host_nest.host2',
// source=org.opensearch.common.bytes.BytesArray@8fb4de08}
// Review why the change from FieldMappingMetadata to GetFieldMappingsResponse.FieldMappingMetadata
GetFieldMappingsResponse.FieldMappingMetadata fieldMetadata = field2Metadata.getValue();
if (fieldMetadata != null) {
// sourceAsMap returns sth like {host2={type=keyword}} with host2 being a nested field
Map<String, Object> fieldMap = fieldMetadata.sourceAsMap();
if (fieldMap != null) {
for (Object type : fieldMap.values()) {
if (type != null && type instanceof Map) {
foundField = true;
Map<String, Object> metadataMap = (Map<String, Object>) type;
String typeName = (String) metadataMap.get(CommonName.TYPE);
if (!typeName.equals(CommonName.KEYWORD_TYPE) && !typeName.equals(CommonName.IP_TYPE)) {
listener.onFailure(new ADValidationException(CATEGORICAL_FIELD_TYPE_ERR_MSG, DetectorValidationIssueType.CATEGORY, ValidationAspect.DETECTOR));
return;
}
}
}
}
}
}
}
}
if (foundField == false) {
listener.onFailure(new ADValidationException(String.format(Locale.ROOT, CATEGORY_NOT_FOUND_ERR_MSG, categoryField0), DetectorValidationIssueType.CATEGORY, ValidationAspect.DETECTOR));
return;
}
searchAdInputIndices(detectorId, indexingDryRun);
}, error -> {
String message = String.format(Locale.ROOT, "Fail to get the index mapping of %s", anomalyDetector.getIndices());
logger.error(message, error);
listener.onFailure(new IllegalArgumentException(message));
});
client.execute(GetFieldMappingsAction.INSTANCE, getMappingsRequest, mappingsListener);
}
Aggregations