Skip to content

Commit 4d6f8ba

Browse files
ruai0511Lingxi Chen
andauthored
[rule based autotagging] Add Create Rule API Logic (#17792)
Signed-off-by: Ruirui Zhang <[email protected]> --------- Co-authored-by: Lingxi Chen <[email protected]>
1 parent bee0749 commit 4d6f8ba

File tree

46 files changed

+1534
-253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1534
-253
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
99
- [Rule based auto-tagging] Add get rule API ([#17336](https://github.com/opensearch-project/OpenSearch/pull/17336))
1010
- [Rule based auto-tagging] Add Delete Rule API ([#18184](https://github.com/opensearch-project/OpenSearch/pull/18184))
1111
- Add paginated wlm/stats API ([#17638](https://github.com/opensearch-project/OpenSearch/pull/17638))
12+
- [Rule based auto-tagging] Add Create rule API ([#17792](https://github.com/opensearch-project/OpenSearch/pull/17792))
1213
- Implement parallel shard refresh behind cluster settings ([#17782](https://github.com/opensearch-project/OpenSearch/pull/17782))
1314
- Bump OpenSearch Core main branch to 3.0.0 ([#18039](https://github.com/opensearch-project/OpenSearch/pull/18039))
1415
- [Rule based Auto-tagging] Add wlm `ActionFilter` ([#17791](https://github.com/opensearch-project/OpenSearch/pull/17791))

modules/autotagging-commons/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* compatible open source license.
77
*/
88

9-
109
opensearchplugin {
1110
name = "rule-framework"
1211
description = 'OpenSearch Rule Framework plugin'
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.rule;
10+
11+
import org.opensearch.action.ActionRequest;
12+
import org.opensearch.action.ActionRequestValidationException;
13+
import org.opensearch.core.common.io.stream.StreamInput;
14+
import org.opensearch.core.common.io.stream.StreamOutput;
15+
import org.opensearch.rule.autotagging.Rule;
16+
17+
import java.io.IOException;
18+
19+
/**
20+
* A request for create Rule
21+
* Example request:
22+
* curl -X PUT "localhost:9200/_rules/{featureType}/" -H 'Content-Type: application/json' -d '
23+
* {
24+
* "description": "description1",
25+
* "attribute_name": ["log*", "event*"],
26+
* "feature_type": "poOiU851RwyLYvV5lbvv5w"
27+
* }'
28+
* @opensearch.experimental
29+
*/
30+
public class CreateRuleRequest extends ActionRequest {
31+
private final Rule rule;
32+
33+
/**
34+
* constructor for CreateRuleRequest
35+
* @param rule the rule to create
36+
*/
37+
public CreateRuleRequest(Rule rule) {
38+
this.rule = rule;
39+
}
40+
41+
/**
42+
* Constructs a CreateRuleRequest from a StreamInput for deserialization
43+
* @param in - The {@link StreamInput} instance to read from.
44+
*/
45+
public CreateRuleRequest(StreamInput in) throws IOException {
46+
super(in);
47+
rule = new Rule(in);
48+
}
49+
50+
@Override
51+
public ActionRequestValidationException validate() {
52+
try {
53+
rule.getFeatureType().getFeatureValueValidator().validate(rule.getFeatureValue());
54+
return null;
55+
} catch (Exception e) {
56+
ActionRequestValidationException validationException = new ActionRequestValidationException();
57+
validationException.addValidationError("Validation failed: " + e.getMessage());
58+
return validationException;
59+
}
60+
}
61+
62+
@Override
63+
public void writeTo(StreamOutput out) throws IOException {
64+
super.writeTo(out);
65+
rule.writeTo(out);
66+
}
67+
68+
/**
69+
* rule getter
70+
*/
71+
public Rule getRule() {
72+
return rule;
73+
}
74+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.rule;
10+
11+
import org.opensearch.core.action.ActionResponse;
12+
import org.opensearch.core.common.io.stream.StreamInput;
13+
import org.opensearch.core.common.io.stream.StreamOutput;
14+
import org.opensearch.core.xcontent.ToXContent;
15+
import org.opensearch.core.xcontent.ToXContentObject;
16+
import org.opensearch.core.xcontent.XContentBuilder;
17+
import org.opensearch.rule.autotagging.Rule;
18+
19+
import java.io.IOException;
20+
import java.util.Map;
21+
22+
import static org.opensearch.rule.autotagging.Rule._ID_STRING;
23+
24+
/**
25+
* Response for the create API for Rule
26+
* Example response:
27+
* {
28+
* "_id":"wi6VApYBoX5wstmtU_8l",
29+
* "description":"description1",
30+
* "index_pattern":["log*", "uvent*"],
31+
* "workload_group":"poOiU851RwyLYvV5lbvv5w",
32+
* "updated_at":"2025-04-04T20:54:22.406Z"
33+
* }
34+
* @opensearch.experimental
35+
*/
36+
public class CreateRuleResponse extends ActionResponse implements ToXContent, ToXContentObject {
37+
private final String _id;
38+
private final Rule rule;
39+
40+
/**
41+
* contructor for CreateRuleResponse
42+
* @param id - the id for the rule created
43+
* @param rule - the rule created
44+
*/
45+
public CreateRuleResponse(String id, final Rule rule) {
46+
this._id = id;
47+
this.rule = rule;
48+
}
49+
50+
/**
51+
* Constructs a CreateRuleResponse from a StreamInput for deserialization
52+
* @param in - The {@link StreamInput} instance to read from.
53+
*/
54+
public CreateRuleResponse(StreamInput in) throws IOException {
55+
_id = in.readString();
56+
rule = new Rule(in);
57+
}
58+
59+
@Override
60+
public void writeTo(StreamOutput out) throws IOException {
61+
out.writeString(_id);
62+
rule.writeTo(out);
63+
}
64+
65+
@Override
66+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
67+
return rule.toXContent(builder, new MapParams(Map.of(_ID_STRING, _id)));
68+
}
69+
70+
/**
71+
* rule getter
72+
*/
73+
public Rule getRule() {
74+
return rule;
75+
}
76+
}

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/GetRuleRequest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@
2626
/**
2727
* A request for get Rule
2828
* Example Request:
29-
* The endpoint "localhost:9200/_wlm/rule" is specific to the Workload Management feature to manage rules
30-
* curl -X GET "localhost:9200/_wlm/rule" - get all rules
31-
* curl -X GET "localhost:9200/_wlm/rule/{_id}" - get single rule by id
32-
* curl -X GET "localhost:9200/_wlm/rule?index_pattern=a,b" - get all rules containing attribute index_pattern as a or b
29+
* curl -X GET "localhost:9200/_rules/{featureType}/" - get all rules for {featureType}
30+
* curl -X GET "localhost:9200/_rules/{featureType}/{_id}" - get single rule by id
31+
* curl -X GET "localhost:9200/_rules/{featureType}?index_pattern=a,b" - get all rules containing attribute index_pattern as a or b for {featureType}
3332
* @opensearch.experimental
3433
*/
3534
@ExperimentalApi

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/GetRuleResponse.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
* "rules": [
3030
* {
3131
* "_id": "z1MJApUB0zgMcDmz-UQq",
32-
* "description": "Rule for tagging query_group_id to index123"
32+
* "description": "Rule for tagging workload_group_id to index123"
3333
* "index_pattern": ["index123"],
34-
* "query_group": "query_group_id",
34+
* "workload_group": "workload_group_id",
3535
* "updated_at": "2025-02-14T01:19:22.589Z"
3636
* },
3737
* ...

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/RulePersistenceService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
*/
1818
public interface RulePersistenceService {
1919

20+
/**
21+
* Create rules based on the provided request.
22+
* @param request The request containing the details for creating the rule.
23+
* @param listener The listener that will handle the response or failure.
24+
*/
25+
void createRule(CreateRuleRequest request, ActionListener<CreateRuleResponse> listener);
26+
2027
/**
2128
* Get rules based on the provided request.
2229
* @param request The request containing the details for retrieving the rule.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.rule;
10+
11+
import org.opensearch.core.action.ActionListener;
12+
13+
/**
14+
* Interface that handles rule routing logic
15+
* @opensearch.experimental
16+
*/
17+
public interface RuleRoutingService {
18+
19+
/**
20+
* Handles a create rule request by routing it to the appropriate node.
21+
* @param request the create rule request
22+
* @param listener listener to handle the final response
23+
*/
24+
void handleCreateRuleRequest(CreateRuleRequest request, ActionListener<CreateRuleResponse> listener);
25+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.rule;
10+
11+
import org.opensearch.common.annotation.ExperimentalApi;
12+
import org.opensearch.rule.autotagging.Attribute;
13+
import org.opensearch.rule.autotagging.Rule;
14+
15+
import java.util.Collections;
16+
import java.util.Map;
17+
import java.util.Optional;
18+
import java.util.Set;
19+
20+
/**
21+
* Utility class for operations related to {@link Rule} objects.
22+
* @opensearch.experimental
23+
*/
24+
@ExperimentalApi
25+
public class RuleUtils {
26+
27+
/**
28+
* constructor for RuleUtils
29+
*/
30+
public RuleUtils() {}
31+
32+
/**
33+
* Checks if a duplicate rule exists and returns its id.
34+
* Two rules are considered to be duplicate when meeting all the criteria below
35+
* 1. They have the same feature type
36+
* 2. They have the exact same attributes
37+
* 3. For each attribute, the sets of values must intersect — i.e., at least one common value must exist
38+
* between the current rule and the one being checked.
39+
*
40+
* @param rule The rule to be validated against ruleMap.
41+
* @param ruleMap This map contains existing rules to be checked
42+
*/
43+
public static Optional<String> getDuplicateRuleId(Rule rule, Map<String, Rule> ruleMap) {
44+
Map<Attribute, Set<String>> targetAttributeMap = rule.getAttributeMap();
45+
for (Map.Entry<String, Rule> entry : ruleMap.entrySet()) {
46+
Rule currRule = entry.getValue();
47+
Map<Attribute, Set<String>> existingAttributeMap = currRule.getAttributeMap();
48+
49+
if (rule.getFeatureType() != currRule.getFeatureType() || targetAttributeMap.size() != existingAttributeMap.size()) {
50+
continue;
51+
}
52+
boolean allAttributesIntersect = true;
53+
for (Attribute attribute : targetAttributeMap.keySet()) {
54+
Set<String> targetAttributeValues = targetAttributeMap.get(attribute);
55+
Set<String> existingAttributeValues = existingAttributeMap.get(attribute);
56+
if (existingAttributeValues == null || Collections.disjoint(targetAttributeValues, existingAttributeValues)) {
57+
allAttributesIntersect = false;
58+
break;
59+
}
60+
}
61+
if (allAttributesIntersect) {
62+
return Optional.of(entry.getKey());
63+
}
64+
}
65+
return Optional.empty();
66+
}
67+
}

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/autotagging/AutoTaggingRegistry.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ private static void validateFeatureType(FeatureType featureType) {
5959
"Feature type name " + name + " should not be null, empty or have more than " + MAX_FEATURE_TYPE_NAME_LENGTH + "characters"
6060
);
6161
}
62+
if (featureType.getFeatureValueValidator() == null) {
63+
throw new IllegalStateException("FeatureValueValidator is not defined for feature type " + name);
64+
}
6265
}
6366

6467
/**

modules/autotagging-commons/common/src/main/java/org/opensearch/rule/autotagging/FeatureType.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717

1818
/**
1919
* Represents a feature type within the auto-tagging feature. Feature types define different categories of
20-
* characteristics that can be used for tagging and classification. Implementations of this interface are
21-
* responsible for registering feature types in {@link AutoTaggingRegistry}. Implementations must ensure that
20+
* characteristics that can be used for tagging and classification. Implementations must ensure that
2221
* feature types are uniquely identifiable by their class and name.
2322
*
2423
* Implementers should follow these guidelines:
2524
* Feature types should be singletons and managed centrally to avoid duplicates.
26-
* {@link #registerFeatureType()} must be called during initialization to ensure the feature type is available.
2725
*
2826
* @opensearch.experimental
2927
*/
@@ -49,6 +47,16 @@ public interface FeatureType extends Writeable {
4947
*/
5048
Map<String, Attribute> getAllowedAttributesRegistry();
5149

50+
/**
51+
* returns the validator for feature value
52+
*/
53+
default FeatureValueValidator getFeatureValueValidator() {
54+
return new FeatureValueValidator() {
55+
@Override
56+
public void validate(String featureValue) {}
57+
};
58+
}
59+
5260
/**
5361
* returns max attribute values
5462
* @return
@@ -65,11 +73,6 @@ default int getMaxCharLengthPerAttributeValue() {
6573
return DEFAULT_MAX_ATTRIBUTE_VALUE_LENGTH;
6674
}
6775

68-
/**
69-
* makes the feature type usable and available to framework plugin
70-
*/
71-
void registerFeatureType();
72-
7376
/**
7477
* checks the validity of the input attribute
7578
* @param attribute
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.rule.autotagging;
10+
11+
/**
12+
* Interface for validating a feature value against pre-defined values (such as
13+
* values from the index, cluster state, etc.) for a specific feature type.
14+
* @opensearch.experimental
15+
*/
16+
public interface FeatureValueValidator {
17+
/**
18+
* Validates the given feature value.
19+
* @param featureValue the value to validate
20+
*/
21+
void validate(String featureValue);
22+
}

0 commit comments

Comments
 (0)