Skip to content

Commit a3d20da

Browse files
[Synonym] Return Empty RuleSet (elastic#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 0b4293d commit a3d20da

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
@@ -163,3 +163,33 @@ setup:
163163

164164
- match:
165165
count: 12
166+
167+
---
168+
"Return empty rule set":
169+
- requires:
170+
cluster_features: [ synonyms_set.get.return_empty_synonym_sets ]
171+
reason: "synonyms_set get api return empty synonym sets"
172+
173+
- do:
174+
synonyms.put_synonym:
175+
id: empty-synonyms
176+
body:
177+
synonyms_set: []
178+
179+
- do:
180+
synonyms.get_synonyms_sets: {}
181+
182+
- match:
183+
count: 4
184+
185+
- match:
186+
results:
187+
- synonyms_set: "empty-synonyms"
188+
count: 0
189+
- synonyms_set: "test-synonyms-1"
190+
count: 3
191+
- synonyms_set: "test-synonyms-2"
192+
count: 1
193+
- synonyms_set: "test-synonyms-3"
194+
count: 2
195+

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@
430430
org.elasticsearch.index.mapper.MapperFeatures,
431431
org.elasticsearch.index.IndexFeatures,
432432
org.elasticsearch.search.SearchFeatures,
433+
org.elasticsearch.synonyms.SynonymFeatures,
433434
org.elasticsearch.script.ScriptFeatures,
434435
org.elasticsearch.search.retriever.RetrieversFeatures,
435436
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
@@ -45,6 +45,9 @@
4545
import org.elasticsearch.indices.SystemIndexDescriptor;
4646
import org.elasticsearch.rest.RestStatus;
4747
import org.elasticsearch.search.aggregations.BucketOrder;
48+
import org.elasticsearch.search.aggregations.bucket.filter.Filters;
49+
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregationBuilder;
50+
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator;
4851
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
4952
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
5053
import org.elasticsearch.search.builder.SearchSourceBuilder;
@@ -89,6 +92,8 @@ public class SynonymsManagementAPIService {
8992
private static final int MAX_SYNONYMS_SETS = 10_000;
9093
private static final String SYNONYM_RULE_ID_FIELD = SynonymRule.ID_FIELD.getPreferredName();
9194
private static final String SYNONYM_SETS_AGG_NAME = "synonym_sets_aggr";
95+
private static final String RULE_COUNT_AGG_NAME = "rule_count";
96+
private static final String RULE_COUNT_FILTER_KEY = "synonym_rules";
9297
private static final int SYNONYMS_INDEX_MAPPINGS_VERSION = 1;
9398
private final int maxSynonymsSets;
9499

@@ -179,27 +184,45 @@ private static XContentBuilder mappings() {
179184
}
180185
}
181186

187+
/**
188+
* Returns all synonym sets with their rule counts, including empty synonym sets.
189+
* @param from The index of the first synonym set to return
190+
* @param size The number of synonym sets to return
191+
* @param listener The listener to return the synonym sets to
192+
*/
182193
public void getSynonymsSets(int from, int size, ActionListener<PagedResult<SynonymSetSummary>> listener) {
194+
BoolQueryBuilder synonymSetQuery = QueryBuilders.boolQuery()
195+
.should(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_SET_OBJECT_TYPE))
196+
.should(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
197+
.minimumShouldMatch(1);
198+
199+
// Aggregation query to count only synonym rules (excluding synonym set objects)
200+
FiltersAggregationBuilder ruleCountAggregation = new FiltersAggregationBuilder(
201+
RULE_COUNT_AGG_NAME,
202+
new FiltersAggregator.KeyedFilter(RULE_COUNT_FILTER_KEY, QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
203+
);
204+
183205
client.prepareSearch(SYNONYMS_ALIAS_NAME)
184206
.setSize(0)
185207
// Retrieves aggregated synonym rules for each synonym set, excluding the synonym set object type
186-
.setQuery(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
208+
.setQuery(synonymSetQuery)
187209
.addAggregation(
188210
new TermsAggregationBuilder(SYNONYM_SETS_AGG_NAME).field(SYNONYMS_SET_FIELD)
189211
.order(BucketOrder.key(true))
190212
.size(maxSynonymsSets)
213+
.subAggregation(ruleCountAggregation)
191214
)
192215
.setPreference(Preference.LOCAL.type())
193216
.execute(new ActionListener<>() {
194217
@Override
195218
public void onResponse(SearchResponse searchResponse) {
196219
Terms termsAggregation = searchResponse.getAggregations().get(SYNONYM_SETS_AGG_NAME);
197220
List<? extends Terms.Bucket> buckets = termsAggregation.getBuckets();
198-
SynonymSetSummary[] synonymSetSummaries = buckets.stream()
199-
.skip(from)
200-
.limit(size)
201-
.map(bucket -> new SynonymSetSummary(bucket.getDocCount(), bucket.getKeyAsString()))
202-
.toArray(SynonymSetSummary[]::new);
221+
SynonymSetSummary[] synonymSetSummaries = buckets.stream().skip(from).limit(size).map(bucket -> {
222+
Filters ruleCountFilters = bucket.getAggregations().get(RULE_COUNT_AGG_NAME);
223+
Filters.Bucket ruleCountBucket = ruleCountFilters.getBucketByKey(RULE_COUNT_FILTER_KEY);
224+
return new SynonymSetSummary(ruleCountBucket.getDocCount(), bucket.getKeyAsString());
225+
}).toArray(SynonymSetSummary[]::new);
203226

204227
listener.onResponse(new PagedResult<>(buckets.size(), synonymSetSummaries));
205228
}

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
@@ -13,6 +13,7 @@ org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures
1313
org.elasticsearch.index.IndexFeatures
1414
org.elasticsearch.index.mapper.MapperFeatures
1515
org.elasticsearch.search.SearchFeatures
16+
org.elasticsearch.synonyms.SynonymFeatures
1617
org.elasticsearch.search.retriever.RetrieversFeatures
1718
org.elasticsearch.script.ScriptFeatures
1819
org.elasticsearch.cluster.routing.RoutingFeatures

0 commit comments

Comments
 (0)