Skip to content

Commit a6a06d1

Browse files
committed
POC rule retriever
1 parent 8cf2cb3 commit a6a06d1

File tree

3 files changed

+155
-1
lines changed

3 files changed

+155
-1
lines changed

x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.elasticsearch.common.settings.SettingsFilter;
2020
import org.elasticsearch.features.NodeFeature;
2121
import org.elasticsearch.indices.SystemIndexDescriptor;
22+
import org.elasticsearch.license.License;
23+
import org.elasticsearch.license.LicensedFeature;
2224
import org.elasticsearch.license.XPackLicenseState;
2325
import org.elasticsearch.logging.LogManager;
2426
import org.elasticsearch.logging.Logger;
@@ -28,6 +30,7 @@
2830
import org.elasticsearch.plugins.SystemIndexPlugin;
2931
import org.elasticsearch.rest.RestController;
3032
import org.elasticsearch.rest.RestHandler;
33+
import org.elasticsearch.xcontent.ParseField;
3134
import org.elasticsearch.xpack.application.analytics.AnalyticsTemplateRegistry;
3235
import org.elasticsearch.xpack.application.analytics.action.DeleteAnalyticsCollectionAction;
3336
import org.elasticsearch.xpack.application.analytics.action.GetAnalyticsCollectionAction;
@@ -175,6 +178,7 @@
175178
import org.elasticsearch.xpack.application.rules.action.TransportPutQueryRuleAction;
176179
import org.elasticsearch.xpack.application.rules.action.TransportPutQueryRulesetAction;
177180
import org.elasticsearch.xpack.application.rules.action.TransportTestQueryRulesetAction;
181+
import org.elasticsearch.xpack.application.rules.retriever.QueryRuleRetrieverBuilder;
178182
import org.elasticsearch.xpack.application.search.SearchApplicationIndexService;
179183
import org.elasticsearch.xpack.application.search.action.DeleteSearchApplicationAction;
180184
import org.elasticsearch.xpack.application.search.action.GetSearchApplicationAction;
@@ -237,6 +241,12 @@ protected XPackLicenseState getLicenseState() {
237241
return XPackPlugin.getSharedLicenseState();
238242
}
239243

244+
public static final LicensedFeature.Momentary QUERY_RULES_RETRIEVER_FEATURE = LicensedFeature.momentary(
245+
null,
246+
"rule-retriever",
247+
License.OperationMode.ENTERPRISE
248+
);
249+
240250
@Override
241251
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
242252
var usageAction = new ActionHandler<>(XPackUsageFeatureAction.ENTERPRISE_SEARCH, EnterpriseSearchUsageTransportAction.class);
@@ -506,4 +516,9 @@ public List<QuerySpec<?>> getQueries() {
506516
new QuerySpec<>(RuleQueryBuilder.NAME, RuleQueryBuilder::new, p -> RuleQueryBuilder.fromXContent(p, getLicenseState()))
507517
);
508518
}
519+
520+
@Override
521+
public List<RetrieverSpec<?>> getRetrievers() {
522+
return List.of(new RetrieverSpec<>(new ParseField(QueryRuleRetrieverBuilder.NAME), QueryRuleRetrieverBuilder::fromXContent));
523+
}
509524
}

x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearchFeatures.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.elasticsearch.features.NodeFeature;
1313
import org.elasticsearch.xpack.application.analytics.AnalyticsTemplateRegistry;
1414
import org.elasticsearch.xpack.application.connector.ConnectorTemplateRegistry;
15+
import org.elasticsearch.xpack.application.rules.retriever.QueryRuleRetrieverBuilder;
1516

1617
import java.util.Map;
1718
import java.util.Set;
@@ -22,7 +23,7 @@ public class EnterpriseSearchFeatures implements FeatureSpecification {
2223

2324
@Override
2425
public Set<NodeFeature> getFeatures() {
25-
return Set.of(QUERY_RULES_TEST_API);
26+
return Set.of(QUERY_RULES_TEST_API, QueryRuleRetrieverBuilder.QUERY_RULE_RETRIEVERS_SUPPORTED);
2627
}
2728

2829
@Override
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.xpack.application.rules.retriever;
11+
12+
import org.elasticsearch.common.ParsingException;
13+
import org.elasticsearch.features.NodeFeature;
14+
import org.elasticsearch.index.query.QueryBuilder;
15+
import org.elasticsearch.license.LicenseUtils;
16+
import org.elasticsearch.search.builder.SearchSourceBuilder;
17+
import org.elasticsearch.search.retriever.RetrieverBuilder;
18+
import org.elasticsearch.search.retriever.RetrieverParserContext;
19+
import org.elasticsearch.xcontent.ConstructingObjectParser;
20+
import org.elasticsearch.xcontent.ParseField;
21+
import org.elasticsearch.xcontent.XContentBuilder;
22+
import org.elasticsearch.xcontent.XContentParser;
23+
import org.elasticsearch.xpack.application.EnterpriseSearch;
24+
import org.elasticsearch.xpack.application.rules.RuleQueryBuilder;
25+
import org.elasticsearch.xpack.core.XPackPlugin;
26+
27+
import java.io.IOException;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.Objects;
31+
32+
import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg;
33+
34+
/**
35+
* A query rule retriever applies query rules defined in one or more rulesets to the underlying retriever.
36+
*/
37+
public final class QueryRuleRetrieverBuilder extends RetrieverBuilder {
38+
39+
public static final String NAME = "rule";
40+
public static final NodeFeature QUERY_RULE_RETRIEVERS_SUPPORTED = new NodeFeature("query_rule_retriever_supported");
41+
42+
public static final ParseField RULESET_IDS_FIELD = new ParseField("ruleset_ids");
43+
public static final ParseField MATCH_CRITERIA_FIELD = new ParseField("match_criteria");
44+
public static final ParseField RETRIEVER_FIELD = new ParseField("retriever");
45+
46+
@SuppressWarnings("unchecked")
47+
public static final ConstructingObjectParser<QueryRuleRetrieverBuilder, RetrieverParserContext> PARSER = new ConstructingObjectParser<>(
48+
"rule",
49+
args -> {
50+
List<String> rulesetIds = (List<String>) args[0];
51+
Map<String, Object> matchCriteria = (Map<String, Object>) args[1];
52+
RetrieverBuilder retrieverBuilder = (RetrieverBuilder) args[2];
53+
return new QueryRuleRetrieverBuilder(rulesetIds, matchCriteria, retrieverBuilder);
54+
}
55+
);
56+
57+
static {
58+
PARSER.declareStringArray(constructorArg(), RULESET_IDS_FIELD);
59+
PARSER.declareObject(constructorArg(), (p, c) -> p.map(), MATCH_CRITERIA_FIELD);
60+
PARSER.declareNamedObject(constructorArg(), (p, c, n) -> p.namedObject(RetrieverBuilder.class, n, c), RETRIEVER_FIELD);
61+
RetrieverBuilder.declareBaseParserFields(NAME, PARSER);
62+
}
63+
64+
public static QueryRuleRetrieverBuilder fromXContent(XContentParser parser, RetrieverParserContext context) throws IOException {
65+
if (context.clusterSupportsFeature(QUERY_RULE_RETRIEVERS_SUPPORTED) == false) {
66+
throw new ParsingException(parser.getTokenLocation(), "unknown retriever [" + NAME + "]");
67+
}
68+
if (EnterpriseSearch.QUERY_RULES_RETRIEVER_FEATURE.check(XPackPlugin.getSharedLicenseState()) == false) {
69+
throw LicenseUtils.newComplianceException("Query Rules");
70+
}
71+
try {
72+
return PARSER.apply(parser, context);
73+
} catch (Exception e) {
74+
throw new ParsingException(parser.getTokenLocation(), e.getMessage(), e);
75+
}
76+
}
77+
78+
private final List<String> rulesetIds;
79+
private final Map<String, Object> matchCriteria;
80+
private final RetrieverBuilder retrieverBuilder;
81+
82+
public QueryRuleRetrieverBuilder(List<String> rulesetIds, Map<String, Object> matchCriteria, RetrieverBuilder retrieverBuilder) {
83+
this.rulesetIds = rulesetIds;
84+
this.matchCriteria = matchCriteria;
85+
this.retrieverBuilder = retrieverBuilder;
86+
}
87+
88+
@Override
89+
public String getName() {
90+
return NAME;
91+
}
92+
93+
@Override
94+
public QueryBuilder topDocsQuery() {
95+
assert rankDocs != null : "{rankDocs} should have been materialized at this point";
96+
97+
// TODO is this correct?
98+
return retrieverBuilder.topDocsQuery();
99+
}
100+
101+
@Override
102+
public void extractToSearchSourceBuilder(SearchSourceBuilder searchSourceBuilder, boolean compoundUsed) {
103+
104+
// TODO throw if compoundUsed is true?
105+
106+
QueryBuilder organicQuery = retrieverBuilder.topDocsQuery();
107+
QueryBuilder queryBuilder = new RuleQueryBuilder(organicQuery, matchCriteria, rulesetIds);
108+
109+
searchSourceBuilder.query(queryBuilder);
110+
}
111+
112+
@Override
113+
public void doToXContent(XContentBuilder builder, Params params) throws IOException {
114+
builder.array(RULESET_IDS_FIELD.getPreferredName(), rulesetIds.toArray());
115+
builder.startObject(MATCH_CRITERIA_FIELD.getPreferredName());
116+
builder.mapContents(matchCriteria);
117+
builder.endObject();
118+
builder.startObject("retriever");
119+
builder.startObject();
120+
builder.field(retrieverBuilder.getName());
121+
retrieverBuilder.toXContent(builder, params);
122+
builder.endObject();
123+
builder.endObject();
124+
}
125+
126+
@Override
127+
public boolean doEquals(Object o) {
128+
QueryRuleRetrieverBuilder that = (QueryRuleRetrieverBuilder) o;
129+
return Objects.equals(rulesetIds, that.rulesetIds)
130+
&& Objects.equals(matchCriteria, that.matchCriteria)
131+
&& Objects.equals(retrieverBuilder, that.retrieverBuilder);
132+
}
133+
134+
@Override
135+
public int doHashCode() {
136+
return Objects.hash(rulesetIds, matchCriteria, retrieverBuilder);
137+
}
138+
}

0 commit comments

Comments
 (0)