Skip to content

Commit b508ee7

Browse files
Auto-reload synonym analyzers on synonyms updates (#96886)
Synonym Management API project On changes of synonyms in a synonym set, auto-reload analyzers. Note that currently all updateable analyzers will be reloaded, even those that are not relevant for a synonyms set being updated.
1 parent cd3f84c commit b508ee7

File tree

12 files changed

+321
-31
lines changed

12 files changed

+321
-31
lines changed

modules/analysis-common/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ esplugin {
1919

2020
restResources {
2121
restApi {
22-
include '_common', 'indices', 'index', 'cluster', 'search', 'nodes', 'bulk', 'termvectors', 'explain', 'count', 'synonyms.put'
22+
include '_common', 'indices', 'index', 'cluster', 'search', 'nodes', 'bulk', 'termvectors', 'explain', 'count', 'synonyms.put', 'synonyms.delete'
2323
}
2424
}
2525

modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/70_synonyms_from_index.yml

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
---
2-
"Load synonyms from index for an analyzer":
2+
"Load and auto-reload synonyms from index for an analyzer":
33
- skip:
44
version: " - 8.8.99"
55
reason: Loading synonyms from index is introduced in 8.9.0
6+
7+
# Create a new synonyms set
68
- do:
79
synonyms.put:
810
synonyms_set: set1
911
body:
1012
synonyms_set:
1113
- synonyms: "hello, hi"
1214
- synonyms: "bye => goodbye"
13-
1415
- match: { result: "created" }
1516

17+
# Create an index with synonym_filter that uses that synonyms set
1618
- do:
1719
indices.create:
1820
index: my_index
@@ -46,6 +48,7 @@
4648
- '{"index": {"_index": "my_index", "_id": "2"}}'
4749
- '{"my_field": "goodbye"}'
4850

51+
# Confirm that synonyms from the synonyms set are used
4952
- do:
5053
search:
5154
index: my_index
@@ -66,6 +69,71 @@
6669
query: bye
6770
- match: { hits.total.value: 1 }
6871

72+
# Update synonyms and check reload status
73+
- do:
74+
synonyms.put:
75+
synonyms_set: set1
76+
body:
77+
synonyms_set:
78+
- synonyms: "hello, salute"
79+
- synonyms: "ciao => goodbye"
80+
- match: { result: "updated" }
81+
- gte: { reload_analyzers_details._shards.total: 1 }
82+
- match: { reload_analyzers_details.reload_details.0.index: "my_index" }
83+
- match: { reload_analyzers_details.reload_details.0.reloaded_analyzers.0 : "my_analyzer" }
84+
85+
# Confirm that the index analyzers are reloaded
86+
- do:
87+
search:
88+
index: my_index
89+
body:
90+
query:
91+
match:
92+
my_field:
93+
query: salute
94+
- match: { hits.total.value: 1 }
95+
96+
- do:
97+
search:
98+
index: my_index
99+
body:
100+
query:
101+
match:
102+
my_field:
103+
query: ciao
104+
- match: { hits.total.value: 1 }
105+
106+
# Delete the synonyms set and confirm failed reload analyzers details
107+
- do:
108+
synonyms.delete:
109+
synonyms_set: set1
110+
111+
- match:
112+
acknowledged: true
113+
- gte: { reload_analyzers_details._shards.failed: 1 }
114+
- match: { reload_analyzers_details._shards.failures.0.index: "my_index" }
115+
- match: { reload_analyzers_details._shards.failures.0.reason.reason: "Synonym set [set1] not found" }
116+
117+
# Confirm that the index analyzers are not reloaded and still using old synonyms
118+
- do:
119+
search:
120+
index: my_index
121+
body:
122+
query:
123+
match:
124+
my_field:
125+
query: salute
126+
- match: { hits.total.value: 1 }
127+
128+
- do:
129+
search:
130+
index: my_index
131+
body:
132+
query:
133+
match:
134+
my_field:
135+
query: ciao
136+
- match: { hits.total.value: 1 }
69137

70138
---
71139
"Fail loading synonyms from index if synonyms_set doesn't exist":

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/synonyms/10_synonyms_put.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
id: "test-id"
1414

1515
- match: { result: "created" }
16+
- match: { reload_analyzers_details._shards.total: 0 }
17+
- length: { reload_analyzers_details.reload_details: 0 }
1618

1719
- do:
1820
synonyms.put:
@@ -22,6 +24,8 @@
2224
- synonyms: "other, another"
2325

2426
- match: { result: "updated" }
27+
- match: { reload_analyzers_details._shards.total: 0 }
28+
- length: { reload_analyzers_details.reload_details: 0 }
2529

2630
---
2731
"Validation fails tests":

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/synonyms/30_synonyms_delete.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ setup:
2020

2121
- match:
2222
acknowledged: true
23+
- match: { reload_analyzers_details._shards.total: 0 }
24+
- length: { reload_analyzers_details.reload_details: 0 }
2325

2426
- do:
2527
catch: missing

server/src/main/java/org/elasticsearch/action/synonyms/DeleteSynonymsAction.java

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,28 @@
1111
import org.apache.logging.log4j.util.Strings;
1212
import org.elasticsearch.action.ActionRequest;
1313
import org.elasticsearch.action.ActionRequestValidationException;
14+
import org.elasticsearch.action.ActionResponse;
1415
import org.elasticsearch.action.ActionType;
16+
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse;
1517
import org.elasticsearch.action.support.master.AcknowledgedResponse;
1618
import org.elasticsearch.common.io.stream.StreamInput;
1719
import org.elasticsearch.common.io.stream.StreamOutput;
20+
import org.elasticsearch.xcontent.ToXContent;
21+
import org.elasticsearch.xcontent.ToXContentObject;
22+
import org.elasticsearch.xcontent.XContentBuilder;
1823

1924
import java.io.IOException;
2025
import java.util.Objects;
2126

22-
public class DeleteSynonymsAction extends ActionType<AcknowledgedResponse> {
27+
import static org.elasticsearch.action.support.master.AcknowledgedResponse.ACKNOWLEDGED_KEY;
28+
29+
public class DeleteSynonymsAction extends ActionType<DeleteSynonymsAction.Response> {
2330

2431
public static final DeleteSynonymsAction INSTANCE = new DeleteSynonymsAction();
2532
public static final String NAME = "cluster:admin/synonyms/delete";
2633

2734
public DeleteSynonymsAction() {
28-
super(NAME, AcknowledgedResponse::readFrom);
35+
super(NAME, Response::new);
2936
}
3037

3138
public static class Request extends ActionRequest {
@@ -71,4 +78,56 @@ public int hashCode() {
7178
return Objects.hash(synonymsSetId);
7279
}
7380
}
81+
82+
public static class Response extends ActionResponse implements ToXContentObject {
83+
84+
private final AcknowledgedResponse acknowledgedResponse;
85+
private final ReloadAnalyzersResponse reloadAnalyzersResponse;
86+
87+
public Response(StreamInput in) throws IOException {
88+
super(in);
89+
this.acknowledgedResponse = AcknowledgedResponse.readFrom(in);
90+
this.reloadAnalyzersResponse = new ReloadAnalyzersResponse(in);
91+
}
92+
93+
public Response(AcknowledgedResponse acknowledgedResponse, ReloadAnalyzersResponse reloadAnalyzersResponse) {
94+
super();
95+
Objects.requireNonNull(acknowledgedResponse, "Acknowledge response must not be null");
96+
Objects.requireNonNull(reloadAnalyzersResponse, "Reload analyzers response must not be null");
97+
this.acknowledgedResponse = acknowledgedResponse;
98+
this.reloadAnalyzersResponse = reloadAnalyzersResponse;
99+
}
100+
101+
@Override
102+
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
103+
builder.startObject();
104+
{
105+
builder.field(ACKNOWLEDGED_KEY, acknowledgedResponse.isAcknowledged());
106+
builder.field("reload_analyzers_details");
107+
reloadAnalyzersResponse.toXContent(builder, params);
108+
}
109+
builder.endObject();
110+
return builder;
111+
}
112+
113+
@Override
114+
public void writeTo(StreamOutput out) throws IOException {
115+
acknowledgedResponse.writeTo(out);
116+
reloadAnalyzersResponse.writeTo(out);
117+
}
118+
119+
@Override
120+
public boolean equals(Object o) {
121+
if (this == o) return true;
122+
if (o == null || getClass() != o.getClass()) return false;
123+
Response response = (Response) o;
124+
return Objects.equals(acknowledgedResponse, response.acknowledgedResponse)
125+
&& Objects.equals(reloadAnalyzersResponse, response.reloadAnalyzersResponse);
126+
}
127+
128+
@Override
129+
public int hashCode() {
130+
return Objects.hash(acknowledgedResponse, reloadAnalyzersResponse);
131+
}
132+
}
74133
}

server/src/main/java/org/elasticsearch/action/synonyms/PutSynonymsAction.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.action.ActionResponse;
1414
import org.elasticsearch.action.ActionType;
1515
import org.elasticsearch.action.ValidateActions;
16+
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse;
1617
import org.elasticsearch.common.Strings;
1718
import org.elasticsearch.common.bytes.BytesReference;
1819
import org.elasticsearch.common.io.stream.StreamInput;
@@ -22,6 +23,7 @@
2223
import org.elasticsearch.rest.RestStatus;
2324
import org.elasticsearch.synonyms.SynonymRule;
2425
import org.elasticsearch.synonyms.SynonymsManagementAPIService;
26+
import org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus;
2527
import org.elasticsearch.xcontent.ConstructingObjectParser;
2628
import org.elasticsearch.xcontent.ParseField;
2729
import org.elasticsearch.xcontent.ToXContent;
@@ -121,36 +123,42 @@ public int hashCode() {
121123

122124
public static class Response extends ActionResponse implements StatusToXContentObject {
123125

124-
private final SynonymsManagementAPIService.UpdateSynonymsResult result;
126+
private final UpdateSynonymsResultStatus updateStatus;
127+
private final ReloadAnalyzersResponse reloadAnalyzersResponse;
125128

126129
public Response(StreamInput in) throws IOException {
127130
super(in);
128-
this.result = in.readEnum((SynonymsManagementAPIService.UpdateSynonymsResult.class));
131+
this.updateStatus = in.readEnum(UpdateSynonymsResultStatus.class);
132+
this.reloadAnalyzersResponse = new ReloadAnalyzersResponse(in);
129133
}
130134

131-
public Response(SynonymsManagementAPIService.UpdateSynonymsResult result) {
135+
public Response(UpdateSynonymsResultStatus updateStatus, ReloadAnalyzersResponse reloadAnalyzersResponse) {
132136
super();
133-
Objects.requireNonNull(result, "Result must not be null");
134-
this.result = result;
137+
Objects.requireNonNull(updateStatus, "Update status must not be null");
138+
Objects.requireNonNull(updateStatus, "Reload analyzers response must not be null");
139+
this.updateStatus = updateStatus;
140+
this.reloadAnalyzersResponse = reloadAnalyzersResponse;
135141
}
136142

137143
@Override
138144
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
139145
builder.startObject();
140-
builder.field("result", result.name().toLowerCase(Locale.ENGLISH));
146+
builder.field("result", updateStatus.name().toLowerCase(Locale.ENGLISH));
147+
builder.field("reload_analyzers_details");
148+
reloadAnalyzersResponse.toXContent(builder, params);
141149
builder.endObject();
142-
143150
return builder;
144151
}
145152

146153
@Override
147154
public void writeTo(StreamOutput out) throws IOException {
148-
out.writeEnum(result);
155+
out.writeEnum(updateStatus);
156+
reloadAnalyzersResponse.writeTo(out);
149157
}
150158

151159
@Override
152160
public RestStatus status() {
153-
return switch (result) {
161+
return switch (updateStatus) {
154162
case CREATED -> RestStatus.CREATED;
155163
default -> RestStatus.OK;
156164
};
@@ -161,12 +169,12 @@ public boolean equals(Object o) {
161169
if (this == o) return true;
162170
if (o == null || getClass() != o.getClass()) return false;
163171
Response response = (Response) o;
164-
return result == response.result;
172+
return updateStatus == response.updateStatus && Objects.equals(reloadAnalyzersResponse, response.reloadAnalyzersResponse);
165173
}
166174

167175
@Override
168176
public int hashCode() {
169-
return Objects.hash(result);
177+
return Objects.hash(updateStatus, reloadAnalyzersResponse);
170178
}
171179
}
172180
}

server/src/main/java/org/elasticsearch/action/synonyms/TransportDeleteSynonymsAction.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@
1111
import org.elasticsearch.action.ActionListener;
1212
import org.elasticsearch.action.support.ActionFilters;
1313
import org.elasticsearch.action.support.HandledTransportAction;
14-
import org.elasticsearch.action.support.master.AcknowledgedResponse;
1514
import org.elasticsearch.client.internal.Client;
1615
import org.elasticsearch.common.inject.Inject;
1716
import org.elasticsearch.synonyms.SynonymsManagementAPIService;
1817
import org.elasticsearch.tasks.Task;
1918
import org.elasticsearch.transport.TransportService;
2019

21-
public class TransportDeleteSynonymsAction extends HandledTransportAction<DeleteSynonymsAction.Request, AcknowledgedResponse> {
20+
public class TransportDeleteSynonymsAction extends HandledTransportAction<DeleteSynonymsAction.Request, DeleteSynonymsAction.Response> {
2221

2322
private final SynonymsManagementAPIService synonymsManagementAPIService;
2423

@@ -30,7 +29,10 @@ public TransportDeleteSynonymsAction(TransportService transportService, ActionFi
3029
}
3130

3231
@Override
33-
protected void doExecute(Task task, DeleteSynonymsAction.Request request, ActionListener<AcknowledgedResponse> listener) {
34-
synonymsManagementAPIService.deleteSynonymsSet(request.synonymsSetId(), listener);
32+
protected void doExecute(Task task, DeleteSynonymsAction.Request request, ActionListener<DeleteSynonymsAction.Response> listener) {
33+
synonymsManagementAPIService.deleteSynonymsSet(
34+
request.synonymsSetId(),
35+
listener.map(dr -> new DeleteSynonymsAction.Response(dr.acknowledgedResponse(), dr.reloadAnalyzersResponse()))
36+
);
3537
}
3638
}

server/src/main/java/org/elasticsearch/action/synonyms/TransportPutSynonymsAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ protected void doExecute(Task task, PutSynonymsAction.Request request, ActionLis
3333
synonymsManagementAPIService.putSynonymsSet(
3434
request.synonymsSetId(),
3535
request.synonymRules(),
36-
listener.map(PutSynonymsAction.Response::new)
36+
listener.map(ur -> new PutSynonymsAction.Response(ur.updateStatus(), ur.reloadAnalyzersResponse()))
3737
);
3838
}
3939
}

0 commit comments

Comments
 (0)