Skip to content

Commit ddf9ebc

Browse files
[Synonym] Return Empty RuleSet (#131032)
* Adding filter agg to include empty synonym sets * Adding featurespecification for synonyms * linting * Update docs/changelog/131032.yaml * update changelog * Adding synonym feature into module-info * sorted the expected response --------- Co-authored-by: Elastic Machine <[email protected]>
1 parent 9e054f5 commit ddf9ebc

File tree

6 files changed

+90
-6
lines changed

6 files changed

+90
-6
lines changed

docs/changelog/131032.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 131032
2+
summary: "Fix: `GET _synonyms` returns synonyms with empty rules"
3+
area: Relevance
4+
type: bug
5+
issues: []

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/synonyms/40_synonyms_sets_get.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,33 @@ setup:
157157

158158
- match:
159159
count: 12
160+
161+
---
162+
"Return empty rule set":
163+
- requires:
164+
cluster_features: [ synonyms_set.get.return_empty_synonym_sets ]
165+
reason: "synonyms_set get api return empty synonym sets"
166+
167+
- do:
168+
synonyms.put_synonym:
169+
id: empty-synonyms
170+
body:
171+
synonyms_set: []
172+
173+
- do:
174+
synonyms.get_synonyms_sets: {}
175+
176+
- match:
177+
count: 4
178+
179+
- match:
180+
results:
181+
- synonyms_set: "empty-synonyms"
182+
count: 0
183+
- synonyms_set: "test-synonyms-1"
184+
count: 3
185+
- synonyms_set: "test-synonyms-2"
186+
count: 1
187+
- synonyms_set: "test-synonyms-3"
188+
count: 2
189+

server/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@
429429
org.elasticsearch.index.mapper.MapperFeatures,
430430
org.elasticsearch.index.IndexFeatures,
431431
org.elasticsearch.search.SearchFeatures,
432+
org.elasticsearch.synonyms.SynonymFeatures,
432433
org.elasticsearch.script.ScriptFeatures,
433434
org.elasticsearch.search.retriever.RetrieversFeatures,
434435
org.elasticsearch.action.admin.cluster.stats.ClusterStatsFeatures;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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.synonyms;
11+
12+
import org.elasticsearch.features.FeatureSpecification;
13+
import org.elasticsearch.features.NodeFeature;
14+
15+
import java.util.Set;
16+
17+
public class SynonymFeatures implements FeatureSpecification {
18+
private static final NodeFeature RETURN_EMPTY_SYNONYM_SETS = new NodeFeature("synonyms_set.get.return_empty_synonym_sets");
19+
20+
@Override
21+
public Set<NodeFeature> getTestFeatures() {
22+
return Set.of(RETURN_EMPTY_SYNONYM_SETS);
23+
}
24+
}

server/src/main/java/org/elasticsearch/synonyms/SynonymsManagementAPIService.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
import org.elasticsearch.indices.SystemIndexDescriptor;
5151
import org.elasticsearch.rest.RestStatus;
5252
import org.elasticsearch.search.aggregations.BucketOrder;
53+
import org.elasticsearch.search.aggregations.bucket.filter.Filters;
54+
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregationBuilder;
55+
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator;
5356
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
5457
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
5558
import org.elasticsearch.search.builder.SearchSourceBuilder;
@@ -94,6 +97,8 @@ public class SynonymsManagementAPIService {
9497
private static final int MAX_SYNONYMS_SETS = 10_000;
9598
private static final String SYNONYM_RULE_ID_FIELD = SynonymRule.ID_FIELD.getPreferredName();
9699
private static final String SYNONYM_SETS_AGG_NAME = "synonym_sets_aggr";
100+
private static final String RULE_COUNT_AGG_NAME = "rule_count";
101+
private static final String RULE_COUNT_FILTER_KEY = "synonym_rules";
97102
private static final int SYNONYMS_INDEX_MAPPINGS_VERSION = 1;
98103
public static final int INDEX_SEARCHABLE_TIMEOUT_SECONDS = 30;
99104
private final int maxSynonymsSets;
@@ -185,27 +190,45 @@ private static XContentBuilder mappings() {
185190
}
186191
}
187192

193+
/**
194+
* Returns all synonym sets with their rule counts, including empty synonym sets.
195+
* @param from The index of the first synonym set to return
196+
* @param size The number of synonym sets to return
197+
* @param listener The listener to return the synonym sets to
198+
*/
188199
public void getSynonymsSets(int from, int size, ActionListener<PagedResult<SynonymSetSummary>> listener) {
200+
BoolQueryBuilder synonymSetQuery = QueryBuilders.boolQuery()
201+
.should(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_SET_OBJECT_TYPE))
202+
.should(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
203+
.minimumShouldMatch(1);
204+
205+
// Aggregation query to count only synonym rules (excluding synonym set objects)
206+
FiltersAggregationBuilder ruleCountAggregation = new FiltersAggregationBuilder(
207+
RULE_COUNT_AGG_NAME,
208+
new FiltersAggregator.KeyedFilter(RULE_COUNT_FILTER_KEY, QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
209+
);
210+
189211
client.prepareSearch(SYNONYMS_ALIAS_NAME)
190212
.setSize(0)
191213
// Retrieves aggregated synonym rules for each synonym set, excluding the synonym set object type
192-
.setQuery(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
214+
.setQuery(synonymSetQuery)
193215
.addAggregation(
194216
new TermsAggregationBuilder(SYNONYM_SETS_AGG_NAME).field(SYNONYMS_SET_FIELD)
195217
.order(BucketOrder.key(true))
196218
.size(maxSynonymsSets)
219+
.subAggregation(ruleCountAggregation)
197220
)
198221
.setPreference(Preference.LOCAL.type())
199222
.execute(new ActionListener<>() {
200223
@Override
201224
public void onResponse(SearchResponse searchResponse) {
202225
Terms termsAggregation = searchResponse.getAggregations().get(SYNONYM_SETS_AGG_NAME);
203226
List<? extends Terms.Bucket> buckets = termsAggregation.getBuckets();
204-
SynonymSetSummary[] synonymSetSummaries = buckets.stream()
205-
.skip(from)
206-
.limit(size)
207-
.map(bucket -> new SynonymSetSummary(bucket.getDocCount(), bucket.getKeyAsString()))
208-
.toArray(SynonymSetSummary[]::new);
227+
SynonymSetSummary[] synonymSetSummaries = buckets.stream().skip(from).limit(size).map(bucket -> {
228+
Filters ruleCountFilters = bucket.getAggregations().get(RULE_COUNT_AGG_NAME);
229+
Filters.Bucket ruleCountBucket = ruleCountFilters.getBucketByKey(RULE_COUNT_FILTER_KEY);
230+
return new SynonymSetSummary(ruleCountBucket.getDocCount(), bucket.getKeyAsString());
231+
}).toArray(SynonymSetSummary[]::new);
209232

210233
listener.onResponse(new PagedResult<>(buckets.size(), synonymSetSummaries));
211234
}

server/src/main/resources/META-INF/services/org.elasticsearch.features.FeatureSpecification

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ org.elasticsearch.rest.action.admin.cluster.GetSnapshotsFeatures
1414
org.elasticsearch.index.IndexFeatures
1515
org.elasticsearch.index.mapper.MapperFeatures
1616
org.elasticsearch.search.SearchFeatures
17+
org.elasticsearch.synonyms.SynonymFeatures
1718
org.elasticsearch.search.retriever.RetrieversFeatures
1819
org.elasticsearch.script.ScriptFeatures
1920
org.elasticsearch.cluster.routing.RoutingFeatures

0 commit comments

Comments
 (0)