Skip to content

Commit 83cd314

Browse files
[Synonym] Return Empty RuleSet (elastic#131032) (elastic#131089)
* 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 08a0247 commit 83cd314

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
@@ -178,3 +178,33 @@ teardown:
178178

179179
- match:
180180
count: 12
181+
182+
---
183+
"Return empty rule set":
184+
- requires:
185+
cluster_features: [ synonyms_set.get.return_empty_synonym_sets ]
186+
reason: "synonyms_set get api return empty synonym sets"
187+
188+
- do:
189+
synonyms.put_synonym:
190+
id: empty-synonyms
191+
body:
192+
synonyms_set: []
193+
194+
- do:
195+
synonyms.get_synonyms_sets: {}
196+
197+
- match:
198+
count: 4
199+
200+
- match:
201+
results:
202+
- synonyms_set: "empty-synonyms"
203+
count: 0
204+
- synonyms_set: "test-synonyms-1"
205+
count: 3
206+
- synonyms_set: "test-synonyms-2"
207+
count: 1
208+
- synonyms_set: "test-synonyms-3"
209+
count: 2
210+

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@
433433
org.elasticsearch.index.IndexFeatures,
434434
org.elasticsearch.ingest.IngestGeoIpFeatures,
435435
org.elasticsearch.search.SearchFeatures,
436+
org.elasticsearch.synonyms.SynonymFeatures,
436437
org.elasticsearch.script.ScriptFeatures,
437438
org.elasticsearch.search.retriever.RetrieversFeatures,
438439
org.elasticsearch.reservedstate.service.FileSettingsFeatures,
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

@@ -180,27 +185,45 @@ private static XContentBuilder mappings() {
180185
}
181186
}
182187

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

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

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
@@ -20,6 +20,7 @@ org.elasticsearch.index.IndexFeatures
2020
org.elasticsearch.index.mapper.MapperFeatures
2121
org.elasticsearch.ingest.IngestGeoIpFeatures
2222
org.elasticsearch.search.SearchFeatures
23+
org.elasticsearch.synonyms.SynonymFeatures
2324
org.elasticsearch.search.retriever.RetrieversFeatures
2425
org.elasticsearch.script.ScriptFeatures
2526
org.elasticsearch.reservedstate.service.FileSettingsFeatures

0 commit comments

Comments
 (0)