Skip to content
Merged
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
5 changes: 5 additions & 0 deletions docs/changelog/131032.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 131032
summary: "Fix: `GET _synonyms` returns synonyms with empty rules"
area: Relevance
type: bug
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,33 @@ setup:

- match:
count: 12

---
"Return empty rule set":
- requires:
cluster_features: [ synonyms_set.get.return_empty_synonym_sets ]
reason: "synonyms_set get api return empty synonym sets"

- do:
synonyms.put_synonym:
id: empty-synonyms
body:
synonyms_set: []

- do:
synonyms.get_synonyms_sets: {}

- match:
count: 4

- match:
results:
- synonyms_set: "empty-synonyms"
count: 0
- synonyms_set: "test-synonyms-1"
count: 3
- synonyms_set: "test-synonyms-2"
count: 1
- synonyms_set: "test-synonyms-3"
count: 2

1 change: 1 addition & 0 deletions server/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@
org.elasticsearch.index.mapper.MapperFeatures,
org.elasticsearch.index.IndexFeatures,
org.elasticsearch.search.SearchFeatures,
org.elasticsearch.synonyms.SynonymFeatures,
org.elasticsearch.script.ScriptFeatures,
org.elasticsearch.search.retriever.RetrieversFeatures,
org.elasticsearch.action.admin.cluster.stats.ClusterStatsFeatures;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.synonyms;

import org.elasticsearch.features.FeatureSpecification;
import org.elasticsearch.features.NodeFeature;

import java.util.Set;

public class SynonymFeatures implements FeatureSpecification {
private static final NodeFeature RETURN_EMPTY_SYNONYM_SETS = new NodeFeature("synonyms_set.get.return_empty_synonym_sets");

@Override
public Set<NodeFeature> getTestFeatures() {
return Set.of(RETURN_EMPTY_SYNONYM_SETS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
import org.elasticsearch.indices.SystemIndexDescriptor;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.filter.Filters;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
Expand Down Expand Up @@ -89,6 +92,8 @@ public class SynonymsManagementAPIService {
private static final int MAX_SYNONYMS_SETS = 10_000;
private static final String SYNONYM_RULE_ID_FIELD = SynonymRule.ID_FIELD.getPreferredName();
private static final String SYNONYM_SETS_AGG_NAME = "synonym_sets_aggr";
private static final String RULE_COUNT_AGG_NAME = "rule_count";
private static final String RULE_COUNT_FILTER_KEY = "synonym_rules";
private static final int SYNONYMS_INDEX_MAPPINGS_VERSION = 1;
private final int maxSynonymsSets;

Expand Down Expand Up @@ -179,27 +184,45 @@ private static XContentBuilder mappings() {
}
}

/**
* Returns all synonym sets with their rule counts, including empty synonym sets.
* @param from The index of the first synonym set to return
* @param size The number of synonym sets to return
* @param listener The listener to return the synonym sets to
*/
public void getSynonymsSets(int from, int size, ActionListener<PagedResult<SynonymSetSummary>> listener) {
BoolQueryBuilder synonymSetQuery = QueryBuilders.boolQuery()
.should(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_SET_OBJECT_TYPE))
.should(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
.minimumShouldMatch(1);

// Aggregation query to count only synonym rules (excluding synonym set objects)
FiltersAggregationBuilder ruleCountAggregation = new FiltersAggregationBuilder(
RULE_COUNT_AGG_NAME,
new FiltersAggregator.KeyedFilter(RULE_COUNT_FILTER_KEY, QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
);

client.prepareSearch(SYNONYMS_ALIAS_NAME)
.setSize(0)
// Retrieves aggregated synonym rules for each synonym set, excluding the synonym set object type
.setQuery(QueryBuilders.termQuery(OBJECT_TYPE_FIELD, SYNONYM_RULE_OBJECT_TYPE))
.setQuery(synonymSetQuery)
.addAggregation(
new TermsAggregationBuilder(SYNONYM_SETS_AGG_NAME).field(SYNONYMS_SET_FIELD)
.order(BucketOrder.key(true))
.size(maxSynonymsSets)
.subAggregation(ruleCountAggregation)
)
.setPreference(Preference.LOCAL.type())
.execute(new ActionListener<>() {
@Override
public void onResponse(SearchResponse searchResponse) {
Terms termsAggregation = searchResponse.getAggregations().get(SYNONYM_SETS_AGG_NAME);
List<? extends Terms.Bucket> buckets = termsAggregation.getBuckets();
SynonymSetSummary[] synonymSetSummaries = buckets.stream()
.skip(from)
.limit(size)
.map(bucket -> new SynonymSetSummary(bucket.getDocCount(), bucket.getKeyAsString()))
.toArray(SynonymSetSummary[]::new);
SynonymSetSummary[] synonymSetSummaries = buckets.stream().skip(from).limit(size).map(bucket -> {
Filters ruleCountFilters = bucket.getAggregations().get(RULE_COUNT_AGG_NAME);
Filters.Bucket ruleCountBucket = ruleCountFilters.getBucketByKey(RULE_COUNT_FILTER_KEY);
return new SynonymSetSummary(ruleCountBucket.getDocCount(), bucket.getKeyAsString());
}).toArray(SynonymSetSummary[]::new);

listener.onResponse(new PagedResult<>(buckets.size(), synonymSetSummaries));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures
org.elasticsearch.index.IndexFeatures
org.elasticsearch.index.mapper.MapperFeatures
org.elasticsearch.search.SearchFeatures
org.elasticsearch.synonyms.SynonymFeatures
org.elasticsearch.search.retriever.RetrieversFeatures
org.elasticsearch.script.ScriptFeatures
org.elasticsearch.cluster.routing.RoutingFeatures
Expand Down