Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased 3.x]
### Added
- [Rule based auto-tagging] Add get rule API ([#17336](https://github.com/opensearch-project/OpenSearch/pull/17336))
- Add multi-threaded writer support in pull-based ingestion ([#17912](https://github.com/opensearch-project/OpenSearch/pull/17912))
- Unset discovery nodes for every transport node actions request ([#17682](https://github.com/opensearch-project/OpenSearch/pull/17682))

Expand Down
21 changes: 21 additions & 0 deletions modules/autotagging-commons/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/


opensearchplugin {
description = 'OpenSearch Rule Framework plugin'
classname = 'org.opensearch.rule.RuleFrameworkPlugin'
}

dependencies {
api project("spi")
api project("common")
testImplementation(project(":test:framework")) {
exclude group: 'org.opensearch', module: 'opensearch-core'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@
* compatible open source license.
*/

apply plugin: 'opensearch.build'
apply plugin: 'opensearch.publish'

description = 'OpenSearch Rule framework common constructs which spi and module shares'

dependencies {
api 'org.apache.commons:commons-collections4:4.4'
api project(":server")
implementation project(":libs:opensearch-common")
compileOnly project(":server")

testImplementation(project(":test:framework")) {
exclude group: 'org.opensearch', module: 'opensearch-core'
}
}


tasks.named("dependencyLicenses").configure {
mapping from: /commons-collections.*/, to: 'commons-collections'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.action.ActionRequest;
import org.opensearch.action.ActionRequestValidationException;
import org.opensearch.common.annotation.ExperimentalApi;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.rule.autotagging.Attribute;
import org.opensearch.rule.autotagging.FeatureType;
import org.opensearch.rule.autotagging.Rule;
import org.opensearch.rule.autotagging.RuleValidator;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* A request for get Rule
* Example Request:
* The endpoint "localhost:9200/_wlm/rule" is specific to the Workload Management feature to manage rules
* curl -X GET "localhost:9200/_wlm/rule" - get all rules
* curl -X GET "localhost:9200/_wlm/rule/{_id}" - get single rule by id
* curl -X GET "localhost:9200/_wlm/rule?index_pattern=a,b" - get all rules containing attribute index_pattern as a or b
* @opensearch.experimental
*/
@ExperimentalApi
public class GetRuleRequest extends ActionRequest {
private final String id;
private final Map<Attribute, Set<String>> attributeFilters;
private final String searchAfter;
private final FeatureType featureType;

/**
* Constructor for GetRuleRequest
* @param id - Rule id to get
* @param attributeFilters - Rules will be filtered based on the attribute-value mappings.
* @param searchAfter - The sort value used for pagination.
* @param featureType - The feature type related to rule.
*/
public GetRuleRequest(String id, Map<Attribute, Set<String>> attributeFilters, String searchAfter, FeatureType featureType) {
this.id = id;
this.attributeFilters = attributeFilters;
this.searchAfter = searchAfter;
this.featureType = featureType;
}

/**
* Constructs a GetRuleRequest from a StreamInput for deserialization.
* @param in - The {@link StreamInput} instance to read from.
*/
public GetRuleRequest(StreamInput in) throws IOException {
super(in);
id = in.readOptionalString();
featureType = FeatureType.from(in);
attributeFilters = in.readMap(i -> Attribute.from(i, featureType), i -> new HashSet<>(i.readStringList()));
searchAfter = in.readOptionalString();
}

@Override
public ActionRequestValidationException validate() {
if (RuleValidator.isEmpty(id)) {
throw new IllegalArgumentException(Rule._ID_STRING + " cannot be empty.");
}
if (RuleValidator.isEmpty(searchAfter)) {
throw new IllegalArgumentException("search_after cannot be empty.");
}
return null;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeOptionalString(id);
featureType.writeTo(out);
out.writeMap(attributeFilters, (output, attribute) -> attribute.writeTo(output), StreamOutput::writeStringCollection);
out.writeOptionalString(searchAfter);
}

/**
* id getter
*/
public String getId() {
return id;
}

/**
* attributeFilters getter
*/
public Map<Attribute, Set<String>> getAttributeFilters() {
return attributeFilters;
}

/**
* searchAfter getter
*/
public String getSearchAfter() {
return searchAfter;
}

/**
* FeatureType getter
* @return
*/
public FeatureType getFeatureType() {
return featureType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.common.annotation.ExperimentalApi;
import org.opensearch.core.action.ActionResponse;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.rule.autotagging.Rule;

import java.io.IOException;
import java.util.Map;

import static org.opensearch.rule.autotagging.Rule._ID_STRING;

/**
* Response for the get API for Rule.
* Example response:
* {
* "rules": [
* {
* "_id": "z1MJApUB0zgMcDmz-UQq",
* "description": "Rule for tagging query_group_id to index123"
* "index_pattern": ["index123"],
* "query_group": "query_group_id",
* "updated_at": "2025-02-14T01:19:22.589Z"
* },
* ...
* ],
* "search_after": ["z1MJApUB0zgMcDmz-UQq"]
* }
* @opensearch.experimental
*/
@ExperimentalApi
public class GetRuleResponse extends ActionResponse implements ToXContent, ToXContentObject {
private final Map<String, Rule> rules;
private final String searchAfter;

/**
* Constructor for GetRuleResponse
* @param rules - Rules get from the request
* @param searchAfter - The sort value used for pagination.
*/
public GetRuleResponse(final Map<String, Rule> rules, String searchAfter) {
this.rules = rules;
this.searchAfter = searchAfter;
}

/**
* Constructs a GetRuleResponse from a StreamInput for deserialization
* @param in - The {@link StreamInput} instance to read from.
*/
public GetRuleResponse(StreamInput in) throws IOException {
this(in.readMap(StreamInput::readString, Rule::new), in.readOptionalString());
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeMap(rules, StreamOutput::writeString, (outStream, rule) -> rule.writeTo(outStream));
out.writeOptionalString(searchAfter);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startArray("rules");
for (Map.Entry<String, Rule> entry : rules.entrySet()) {
entry.getValue().toXContent(builder, new MapParams(Map.of(_ID_STRING, entry.getKey())));
}
builder.endArray();
if (searchAfter != null && !searchAfter.isEmpty()) {
builder.field("search_after", new Object[] { searchAfter });
}
builder.endObject();
return builder;
}

/**
* rules getter
*/
public Map<String, Rule> getRules() {
return rules;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.rule.autotagging.Attribute;

/**
* Generic Rule attributes that features can use out of the use by using the lib.
* @opensearch.experimental
*/
public enum RuleAttribute implements Attribute {
/**
* Represents the index_pattern attribute in RuleAttribute
*/
INDEX_PATTERN("index_pattern");

private final String name;

RuleAttribute(String name) {
this.name = name;
validateAttribute();
}

@Override
public String getName() {
return name;
}

/**
* Retrieves the RuleAttribute from a name string
* @param name - attribute name
*/
public static RuleAttribute fromName(String name) {
for (RuleAttribute attr : RuleAttribute.values()) {
if (attr.getName().equals(name)) {
return attr;
}
}
throw new IllegalArgumentException("Unknown RuleAttribute: " + name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.common.annotation.ExperimentalApi;
import org.opensearch.rule.autotagging.Rule;

/**
* Interface to parse various string representation of Rule entity
* clients can use/implement as per their choice of storage for the Rule
*/
@ExperimentalApi
public interface RuleEntityParser {
/**
* Parses the src string into {@link Rule} object
* @param src String representation of Rule, it could be a XContentObject or something else based on
* where and how it is stored
* @return Rule
*/
Rule parse(String src);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.core.action.ActionListener;

/**
* Interface for a service that handles rule persistence CRUD operations.
* @opensearch.experimental
*/
public interface RulePersistenceService {

/**
* Get rules based on the provided request.
* @param request The request containing the details for retrieving the rule.
* @param listener The listener that will handle the response or failure.
*/
void getRule(GetRuleRequest request, ActionListener<GetRuleResponse> listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.rule;

import org.opensearch.common.annotation.ExperimentalApi;

/**
* This interface is responsible for creating query objects which storage layer can use
* to query the backend
* @param <T>
*/
@ExperimentalApi
public interface RuleQueryMapper<T> {
/**
* This method translates the {@link GetRuleRequest} to a storage engine specific query object
* @param request
* @return
*/
T from(GetRuleRequest request);
}
Loading
Loading