Synonyms API - Add refresh parameter to check synonyms index and reload analyzers (#126935)

* Add timeout to SynonymsManagementAPIService put synonyms

* Remove replicas 0, as that may impact serverless

* Add timeout to put synonyms action, fix tests

* Fix number of replicas

* Remove cluster.health checks for synonyms index

* Revert debugging

* Add integration test for timeouts

* Use TimeValue instead of an int

* Add YAML tests and REST API specs

* Fix a validation bug in put synonym rule

* Spotless

* Update docs/changelog/126314.yaml

* Remove unnecessary checks for null

* Fix equals / HashCode

* Checks that timeout is passed correctly to the check health method

* Use correctly the default timeout

* spotless

* Add monitor cluster privilege to internal synonyms user

* [CI] Auto commit changes from spotless

* Add capabilities to avoid failing on bwc tests

* Replace timeout for refresh param

* Add param to specs

* Add YAML tests

* Fix changelog

* [CI] Auto commit changes from spotless

* Use BWC serialization tests

* Fix bug in test parser

* Spotless

* Delete doesn't need reloading 🤦 removing it

* Revert "Delete doesn't need reloading 🤦 removing it"

This reverts commit 9c8e0b62be.

* [CI] Auto commit changes from spotless

* Fix refresh for delete synonym rule

* Fix tests

* Update docs/changelog/126935.yaml

* Add reload analyzers test

* reload_analyzers is not available on serverless

---------

Co-authored-by: elasticsearchmachine <infra-root+elasticsearchmachine@elastic.co>
This commit is contained in:
Carlos Delgado 2025-04-22 17:23:06 +02:00 committed by GitHub
parent 2ea04a9fed
commit 4d4b962fd1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 719 additions and 191 deletions

View file

@ -0,0 +1,6 @@
pr: 126314
summary: Add refresh to synonyms put / delete APIs to wait for synonyms to be accessible and reload analyzers
area: Analysis
type: bug
issues:
- 121441

View file

@ -0,0 +1,6 @@
pr: 126935
summary: Synonyms API - Add refresh parameter to check synonyms index and reload analyzers
area: Analysis
type: enhancement
issues:
- 121441

View file

@ -33,6 +33,12 @@
} }
} }
] ]
},
"params": {
"refresh": {
"type": "boolean",
"description": "Refresh search analyzers to update synonyms"
}
} }
} }
} }

View file

@ -30,6 +30,12 @@
} }
] ]
}, },
"params": {
"refresh": {
"type": "boolean",
"description": "Refresh search analyzers to update synonyms"
}
},
"body": { "body": {
"description": "Synonyms set rules", "description": "Synonyms set rules",
"required": true "required": true

View file

@ -34,6 +34,12 @@
} }
] ]
}, },
"params": {
"refresh": {
"type": "boolean",
"description": "Refresh search analyzers to update synonyms"
}
},
"body": { "body": {
"description": "Synonym rule", "description": "Synonym rule",
"required": true "required": true

View file

@ -15,11 +15,6 @@ setup:
- match: { result: "created" } - match: { result: "created" }
- do:
cluster.health:
index: .synonyms
wait_for_status: green
- do: - do:
synonyms.get_synonym: synonyms.get_synonym:
id: test-update-synonyms id: test-update-synonyms
@ -63,11 +58,6 @@ setup:
- match: { result: "created" } - match: { result: "created" }
- do:
cluster.health:
index: .synonyms
wait_for_status: green
- do: - do:
synonyms.get_synonym: synonyms.get_synonym:
id: test-empty-synonyms id: test-empty-synonyms
@ -75,6 +65,31 @@ setup:
- match: { count: 0 } - match: { count: 0 }
- match: { synonyms_set: [] } - match: { synonyms_set: [] }
---
"Refresh can be specified":
- requires:
test_runner_features: [ capabilities ]
capabilities:
- method: PUT
path: /_synonyms/{rule_id}
capabilities: [ synonyms_refresh_param ]
reason: "synonyms refresh param capability needed"
- do:
synonyms.put_synonym:
id: test-update-synonyms
refresh: false
body:
synonyms_set:
- synonyms: "hello, hi"
- synonyms: "bye => goodbye"
id: "test-id"
- match: { result: "created" }
# Reload analyzers info is not included
- not_exists: reload_analyzers_details
--- ---
"Validation fails tests": "Validation fails tests":
- do: - do:
@ -116,3 +131,4 @@ setup:
body: body:
synonyms_set: synonyms_set:
- synonyms: "bye, goodbye, " - synonyms: "bye, goodbye, "

View file

@ -11,12 +11,6 @@ setup:
synonyms_set: synonyms_set:
synonyms: "foo => bar, baz" synonyms: "foo => bar, baz"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
- do: - do:
indices.create: indices.create:
index: test_index index: test_index
@ -372,13 +366,6 @@ setup:
synonyms_set: synonyms_set:
synonyms: "foo => bar, baz" synonyms: "foo => bar, baz"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
- do: - do:
indices.stats: { index: test_index } indices.stats: { index: test_index }
@ -441,12 +428,6 @@ setup:
synonyms_set: synonyms_set:
synonyms: "foo => bar, baz" synonyms: "foo => bar, baz"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
- do: - do:
# Warning issued in previous versions # Warning issued in previous versions
allowed_warnings: allowed_warnings:

View file

@ -14,12 +14,6 @@ setup:
- synonyms: "test => check" - synonyms: "test => check"
id: "test-id-3" id: "test-id-3"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
--- ---
"Get synonyms set": "Get synonyms set":
- do: - do:

View file

@ -12,12 +12,6 @@ setup:
- synonyms: "bye => goodbye" - synonyms: "bye => goodbye"
id: "test-id-2" id: "test-id-2"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
--- ---
"Delete synonyms set": "Delete synonyms set":
- do: - do:
@ -77,7 +71,6 @@ setup:
settings: settings:
index: index:
number_of_shards: 1 number_of_shards: 1
number_of_replicas: 0
analysis: analysis:
filter: filter:
my_synonym_filter: my_synonym_filter:

View file

@ -10,12 +10,6 @@ setup:
- synonyms: "hello, hi" - synonyms: "hello, hi"
- synonyms: "goodbye, bye" - synonyms: "goodbye, bye"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
- do: - do:
synonyms.put_synonym: synonyms.put_synonym:
id: test-synonyms-1 id: test-synonyms-1

View file

@ -14,12 +14,6 @@ setup:
- synonyms: "test => check" - synonyms: "test => check"
id: "test-id-3" id: "test-id-3"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
--- ---
"Update a synonyms rule": "Update a synonyms rule":
- do: - do:
@ -85,3 +79,68 @@ setup:
rule_id: "test-id-0" rule_id: "test-id-0"
body: body:
synonyms: "i-phone, iphone" synonyms: "i-phone, iphone"
---
"Refresh can be specified":
- requires:
test_runner_features: [ capabilities ]
capabilities:
- method: PUT
path: /_synonyms/{rule_id}
capabilities: [ synonyms_refresh_param ]
reason: "synonyms refresh param capability needed"
- do:
synonyms.put_synonym_rule:
refresh: false
set_id: "test-synonyms"
rule_id: "test-id-2"
body:
synonyms: "bye, goodbye, seeya"
- match: { result: "updated" }
# Reload analyzers info is not included
- not_exists: reload_analyzers_details
---
"Validation failure tests":
- do:
catch: /\[synonyms\] field can't be empty/
synonyms.put_synonym_rule:
set_id: "test-synonyms"
rule_id: "test-id-0"
body:
synonyms: ""
- do:
catch: /More than one explicit mapping specified in the same synonyms rule/
synonyms.put_synonym_rule:
set_id: "test-synonyms"
rule_id: "test-id-0"
body:
synonyms: "bye => => goodbye"
- do:
catch: /Incorrect syntax for \[synonyms\]/
synonyms.put_synonym_rule:
set_id: "test-synonyms"
rule_id: "test-id-0"
body:
synonyms: " => goodbye"
- do:
catch: /Incorrect syntax for \[synonyms\]/
synonyms.put_synonym_rule:
set_id: "test-synonyms"
rule_id: "test-id-0"
body:
synonyms: "bye => "
- do:
catch: /Incorrect syntax for \[synonyms\]/
synonyms.put_synonym_rule:
set_id: "test-synonyms"
rule_id: "test-id-0"
body:
synonyms: "bye, goodbye, "

View file

@ -14,12 +14,6 @@ setup:
- synonyms: "test => check" - synonyms: "test => check"
id: "test-id-3" id: "test-id-3"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
--- ---
"Get a synonym rule": "Get a synonym rule":
- do: - do:

View file

@ -14,12 +14,6 @@ setup:
- synonyms: "test => check" - synonyms: "test => check"
id: "test-id-3" id: "test-id-3"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
--- ---
"Delete synonym rule": "Delete synonym rule":
- do: - do:
@ -50,6 +44,27 @@ setup:
- synonyms: "test => check" - synonyms: "test => check"
id: "test-id-3" id: "test-id-3"
---
"Refresh can be specified":
- requires:
test_runner_features: [ capabilities ]
capabilities:
- method: PUT
path: /_synonyms/{rule_id}
capabilities: [ synonyms_refresh_param ]
reason: "synonyms refresh param capability needed"
- do:
synonyms.delete_synonym_rule:
set_id: test-synonyms
rule_id: test-id-2
refresh: false
- match: { result: "deleted" }
# Reload analyzers info is not included
- not_exists: reload_analyzers_details
--- ---
"Delete synonym rule - missing synonym set": "Delete synonym rule - missing synonym set":
- do: - do:

View file

@ -13,12 +13,6 @@ setup:
- synonyms: "bye => goodbye" - synonyms: "bye => goodbye"
id: "synonym-rule-2" id: "synonym-rule-2"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
# Create an index with synonym_filter that uses that synonyms set # Create an index with synonym_filter that uses that synonyms set
- do: - do:
indices.create: indices.create:

View file

@ -14,12 +14,6 @@ setup:
- synonyms: "bye => goodbye" - synonyms: "bye => goodbye"
id: "synonym-rule-2" id: "synonym-rule-2"
# This is to ensure that all index shards (write and read) are available. In serverless this can take some time.
- do:
cluster.health:
index: .synonyms
wait_for_status: green
# Create synonyms synonyms_set2 # Create synonyms synonyms_set2
- do: - do:
synonyms.put_synonym: synonyms.put_synonym:
@ -156,3 +150,62 @@ setup:
my_field: my_field:
query: salute query: salute
- match: { hits.total.value: 0 } - match: { hits.total.value: 0 }
---
"Reload analyzers with refresh false":
- requires:
test_runner_features: [ capabilities ]
capabilities:
- method: PUT
path: /_synonyms/{rule_id}
capabilities: [ synonyms_refresh_param ]
reason: "synonyms refresh param capability needed"
- do:
synonyms.put_synonym:
id: synonyms_set1
refresh: false
body:
synonyms_set:
- synonyms: "hello, salute"
- match: { result: "updated" }
- not_exists: reload_analyzers_details
# Confirm that the index analyzers are not reloaded for my_index1
- do:
search:
index: my_index1
body:
query:
match:
my_field:
query: salute
- match: { hits.total.value: 0 }
# Reloading analyzers makes synonyms refresh
- do:
synonyms.put_synonym:
id: synonyms_set1
refresh: true
body:
synonyms_set:
- synonyms: "hello, salute"
- synonyms: "ciao => goodbye"
- match: { result: "updated" }
- gt: { reload_analyzers_details._shards.total: 0 }
- gt: { reload_analyzers_details._shards.successful: 0 }
- length: { reload_analyzers_details.reload_details: 1 }
- do:
search:
index: my_index1
body:
query:
match:
my_field:
query: salute
- match: { hits.total.value: 1 }

View file

@ -11,8 +11,12 @@ package org.elasticsearch.synonyms;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.index.mapper.extras.MapperExtrasPlugin; import org.elasticsearch.index.mapper.extras.MapperExtrasPlugin;
import org.elasticsearch.indices.IndexCreationException;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.reindex.ReindexPlugin; import org.elasticsearch.reindex.ReindexPlugin;
import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase;
@ -50,14 +54,20 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
public void testCreateManySynonyms() throws Exception { public void testCreateManySynonyms() throws Exception {
CountDownLatch putLatch = new CountDownLatch(1); CountDownLatch putLatch = new CountDownLatch(1);
String synonymSetId = randomIdentifier(); String synonymSetId = randomIdentifier();
boolean refresh = randomBoolean();
int rulesNumber = randomIntBetween(maxSynonymSets / 2, maxSynonymSets); int rulesNumber = randomIntBetween(maxSynonymSets / 2, maxSynonymSets);
synonymsManagementAPIService.putSynonymsSet(synonymSetId, randomSynonymsSet(rulesNumber, rulesNumber), new ActionListener<>() { synonymsManagementAPIService.putSynonymsSet(
synonymSetId,
randomSynonymsSet(rulesNumber, rulesNumber),
refresh,
new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
assertEquals( assertEquals(
SynonymsManagementAPIService.UpdateSynonymsResultStatus.CREATED, SynonymsManagementAPIService.UpdateSynonymsResultStatus.CREATED,
synonymsReloadResult.synonymsOperationResult() synonymsReloadResult.synonymsOperationResult()
); );
assertEquals(refresh, synonymsReloadResult.reloadAnalyzersResponse() != null);
putLatch.countDown(); putLatch.countDown();
} }
@ -65,7 +75,8 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
public void onFailure(Exception e) { public void onFailure(Exception e) {
fail(e); fail(e);
} }
}); }
);
putLatch.await(5, TimeUnit.SECONDS); putLatch.await(5, TimeUnit.SECONDS);
@ -95,6 +106,7 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
synonymsManagementAPIService.putSynonymsSet( synonymsManagementAPIService.putSynonymsSet(
randomIdentifier(), randomIdentifier(),
randomSynonymsSet(maxSynonymSets + 1, maxSynonymSets * 2), randomSynonymsSet(maxSynonymSets + 1, maxSynonymSets * 2),
randomBoolean(),
new ActionListener<>() { new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
@ -120,14 +132,18 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
int rulesToUpdate = randomIntBetween(1, 10); int rulesToUpdate = randomIntBetween(1, 10);
int synonymsToCreate = maxSynonymSets - rulesToUpdate; int synonymsToCreate = maxSynonymSets - rulesToUpdate;
String synonymSetId = randomIdentifier(); String synonymSetId = randomIdentifier();
synonymsManagementAPIService.putSynonymsSet(synonymSetId, randomSynonymsSet(synonymsToCreate), new ActionListener<>() { synonymsManagementAPIService.putSynonymsSet(
synonymSetId,
randomSynonymsSet(synonymsToCreate),
randomBoolean(),
new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
// Create as many rules as should fail // Create as many rules as should fail
SynonymRule[] rules = randomSynonymsSet(atLeast(rulesToUpdate + 1)); SynonymRule[] rules = randomSynonymsSet(atLeast(rulesToUpdate + 1));
CountDownLatch updatedRulesLatch = new CountDownLatch(rulesToUpdate); CountDownLatch updatedRulesLatch = new CountDownLatch(rulesToUpdate);
for (int i = 0; i < rulesToUpdate; i++) { for (int i = 0; i < rulesToUpdate; i++) {
synonymsManagementAPIService.putSynonymRule(synonymSetId, rules[i], new ActionListener<>() { synonymsManagementAPIService.putSynonymRule(synonymSetId, rules[i], true, new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
updatedRulesLatch.countDown(); updatedRulesLatch.countDown();
@ -153,6 +169,7 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
// Error here // Error here
synonymSetId, synonymSetId,
rules[i], rules[i],
randomBoolean(),
new ActionListener<>() { new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
@ -180,7 +197,8 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
public void onFailure(Exception e) { public void onFailure(Exception e) {
fail(e); fail(e);
} }
}); }
);
latch.await(5, TimeUnit.SECONDS); latch.await(5, TimeUnit.SECONDS);
} }
@ -189,13 +207,14 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
String synonymSetId = randomIdentifier(); String synonymSetId = randomIdentifier();
SynonymRule[] synonymsSet = randomSynonymsSet(maxSynonymSets, maxSynonymSets); SynonymRule[] synonymsSet = randomSynonymsSet(maxSynonymSets, maxSynonymSets);
synonymsManagementAPIService.putSynonymsSet(synonymSetId, synonymsSet, new ActionListener<>() { synonymsManagementAPIService.putSynonymsSet(synonymSetId, synonymsSet, true, new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
// Updating a rule fails // Updating a rule fails
synonymsManagementAPIService.putSynonymRule( synonymsManagementAPIService.putSynonymRule(
synonymSetId, synonymSetId,
synonymsSet[randomIntBetween(0, maxSynonymSets - 1)], synonymsSet[randomIntBetween(0, maxSynonymSets - 1)],
randomBoolean(),
new ActionListener<>() { new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
@ -224,11 +243,11 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
String synonymSetId = randomIdentifier(); String synonymSetId = randomIdentifier();
String ruleId = randomIdentifier(); String ruleId = randomIdentifier();
SynonymRule[] synonymsSet = randomSynonymsSet(maxSynonymSets, maxSynonymSets); SynonymRule[] synonymsSet = randomSynonymsSet(maxSynonymSets, maxSynonymSets);
synonymsManagementAPIService.putSynonymsSet(synonymSetId, synonymsSet, new ActionListener<>() { synonymsManagementAPIService.putSynonymsSet(synonymSetId, synonymsSet, randomBoolean(), new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
// Updating a rule fails // Updating a rule fails
synonymsManagementAPIService.putSynonymRule(synonymSetId, randomSynonymRule(ruleId), new ActionListener<>() { synonymsManagementAPIService.putSynonymRule(synonymSetId, randomSynonymRule(ruleId), true, new ActionListener<>() {
@Override @Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) { public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
fail("Should not create a new rule that does not exist when at max capacity"); fail("Should not create a new rule that does not exist when at max capacity");
@ -289,4 +308,113 @@ public class SynonymsManagementAPIServiceIT extends ESIntegTestCase {
readLatch.await(5, TimeUnit.SECONDS); readLatch.await(5, TimeUnit.SECONDS);
verify(logger).warn(anyString(), eq(synonymSetId)); verify(logger).warn(anyString(), eq(synonymSetId));
} }
public void testCreateSynonymsWithYellowSynonymsIndex() throws Exception {
// Override health method check to simulate a timeout in checking the synonyms index
synonymsManagementAPIService = new SynonymsManagementAPIService(client()) {
@Override
void checkSynonymsIndexHealth(ActionListener<ClusterHealthResponse> listener) {
ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).build();
ClusterHealthResponse response = new ClusterHealthResponse(
randomIdentifier(),
new String[] { SynonymsManagementAPIService.SYNONYMS_INDEX_CONCRETE_NAME },
clusterState
);
response.setTimedOut(true);
listener.onResponse(response);
}
};
// Create a rule fails
CountDownLatch putLatch = new CountDownLatch(1);
String synonymSetId = randomIdentifier();
synonymsManagementAPIService.putSynonymsSet(synonymSetId, randomSynonymsSet(1, 1), true, new ActionListener<>() {
@Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
fail("Shouldn't have been able to create synonyms with refresh in synonyms index health");
}
@Override
public void onFailure(Exception e) {
// Expected
assertTrue(e instanceof IndexCreationException);
assertTrue(e.getMessage().contains("synonyms index [.synonyms] is not searchable"));
putLatch.countDown();
}
});
putLatch.await(5, TimeUnit.SECONDS);
// Update a rule fails
CountDownLatch updateLatch = new CountDownLatch(1);
String synonymRuleId = randomIdentifier();
synonymsManagementAPIService.putSynonymRule(synonymSetId, randomSynonymRule(synonymRuleId), true, new ActionListener<>() {
@Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
fail("Shouldn't have been able to update synonyms with refresh in synonyms index health");
}
@Override
public void onFailure(Exception e) {
// Expected
assertTrue(e instanceof IndexCreationException);
assertTrue(e.getMessage().contains("synonyms index [.synonyms] is not searchable"));
updateLatch.countDown();
}
});
updateLatch.await(5, TimeUnit.SECONDS);
// Delete a rule does not fail
CountDownLatch deleteLatch = new CountDownLatch(1);
synonymsManagementAPIService.deleteSynonymRule(synonymSetId, synonymRuleId, true, new ActionListener<>() {
@Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
updateLatch.countDown();
}
@Override
public void onFailure(Exception e) {
// Expected
fail("Should have been able to delete a synonym rule");
}
});
deleteLatch.await(5, TimeUnit.SECONDS);
// But, we can still create a synonyms set without refresh
CountDownLatch putNoRefreshLatch = new CountDownLatch(1);
synonymsManagementAPIService.putSynonymsSet(synonymSetId, randomSynonymsSet(1, 1), false, new ActionListener<>() {
@Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
// Expected
putLatch.countDown();
}
@Override
public void onFailure(Exception e) {
fail(e);
}
});
putNoRefreshLatch.await(5, TimeUnit.SECONDS);
// Same for update
CountDownLatch putRuleNoRefreshLatch = new CountDownLatch(1);
synonymsManagementAPIService.putSynonymRule(synonymSetId, randomSynonymRule(synonymRuleId), false, new ActionListener<>() {
@Override
public void onResponse(SynonymsManagementAPIService.SynonymsReloadResult synonymsReloadResult) {
// Expected
putRuleNoRefreshLatch.countDown();
}
@Override
public void onFailure(Exception e) {
fail(e);
}
});
putRuleNoRefreshLatch.await(5, TimeUnit.SECONDS);
}
} }

View file

@ -232,6 +232,7 @@ public class TransportVersions {
public static final TransportVersion BATCHED_QUERY_EXECUTION_DELAYABLE_WRITABLE = def(9_057_0_00); public static final TransportVersion BATCHED_QUERY_EXECUTION_DELAYABLE_WRITABLE = def(9_057_0_00);
public static final TransportVersion SEARCH_INCREMENTAL_TOP_DOCS_NULL = def(9_058_0_00); public static final TransportVersion SEARCH_INCREMENTAL_TOP_DOCS_NULL = def(9_058_0_00);
public static final TransportVersion COMPRESS_DELAYABLE_WRITEABLE = def(9_059_0_00); public static final TransportVersion COMPRESS_DELAYABLE_WRITEABLE = def(9_059_0_00);
public static final TransportVersion SYNONYMS_REFRESH_PARAM = def(9_060_0_00);
/* /*
* STOP! READ THIS FIRST! No, really, * STOP! READ THIS FIRST! No, really,

View file

@ -9,6 +9,7 @@
package org.elasticsearch.action.synonyms; package org.elasticsearch.action.synonyms;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionType; import org.elasticsearch.action.ActionType;
@ -31,18 +32,24 @@ public class DeleteSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
public static class Request extends ActionRequest { public static class Request extends ActionRequest {
private final String synonymsSetId; private final String synonymsSetId;
private final String synonymRuleId; private final String synonymRuleId;
private final boolean refresh;
public Request(StreamInput in) throws IOException { public Request(StreamInput in) throws IOException {
super(in); super(in);
this.synonymsSetId = in.readString(); this.synonymsSetId = in.readString();
this.synonymRuleId = in.readString(); this.synonymRuleId = in.readString();
if (in.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
this.refresh = in.readBoolean();
} else {
this.refresh = true;
}
} }
public Request(String synonymsSetId, String synonymRuleId) { public Request(String synonymsSetId, String synonymRuleId, boolean refresh) {
this.synonymsSetId = synonymsSetId; this.synonymsSetId = synonymsSetId;
this.synonymRuleId = synonymRuleId; this.synonymRuleId = synonymRuleId;
this.refresh = refresh;
} }
@Override @Override
@ -63,6 +70,9 @@ public class DeleteSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
super.writeTo(out); super.writeTo(out);
out.writeString(synonymsSetId); out.writeString(synonymsSetId);
out.writeString(synonymRuleId); out.writeString(synonymRuleId);
if (out.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
out.writeBoolean(refresh);
}
} }
public String synonymsSetId() { public String synonymsSetId() {
@ -73,6 +83,10 @@ public class DeleteSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
return synonymRuleId; return synonymRuleId;
} }
public boolean refresh() {
return refresh;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View file

@ -9,6 +9,7 @@
package org.elasticsearch.action.synonyms; package org.elasticsearch.action.synonyms;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionType; import org.elasticsearch.action.ActionType;
@ -40,8 +41,8 @@ public class PutSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
public static class Request extends ActionRequest { public static class Request extends ActionRequest {
private final String synonymsSetId; private final String synonymsSetId;
private final SynonymRule synonymRule; private final SynonymRule synonymRule;
private final boolean refresh;
public static final ParseField SYNONYMS_FIELD = new ParseField(SynonymsManagementAPIService.SYNONYMS_FIELD); public static final ParseField SYNONYMS_FIELD = new ParseField(SynonymsManagementAPIService.SYNONYMS_FIELD);
private static final ConstructingObjectParser<SynonymRule, String> PARSER = new ConstructingObjectParser<>( private static final ConstructingObjectParser<SynonymRule, String> PARSER = new ConstructingObjectParser<>(
@ -58,20 +59,28 @@ public class PutSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
super(in); super(in);
this.synonymsSetId = in.readString(); this.synonymsSetId = in.readString();
this.synonymRule = new SynonymRule(in); this.synonymRule = new SynonymRule(in);
if (in.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
this.refresh = in.readBoolean();
} else {
this.refresh = true;
}
} }
public Request(String synonymsSetId, String synonymRuleId, BytesReference content, XContentType contentType) throws IOException { public Request(String synonymsSetId, String synonymRuleId, boolean refresh, BytesReference content, XContentType contentType)
throws IOException {
this.synonymsSetId = synonymsSetId; this.synonymsSetId = synonymsSetId;
try (XContentParser parser = XContentHelper.createParser(XContentParserConfiguration.EMPTY, content, contentType)) { try (XContentParser parser = XContentHelper.createParser(XContentParserConfiguration.EMPTY, content, contentType)) {
this.synonymRule = PARSER.apply(parser, synonymRuleId); this.synonymRule = PARSER.apply(parser, synonymRuleId);
} catch (Exception e) { } catch (Exception e) {
throw new IllegalArgumentException("Failed to parse: " + content.utf8ToString(), e); throw new IllegalArgumentException("Failed to parse: " + content.utf8ToString(), e);
} }
this.refresh = refresh;
} }
Request(String synonymsSetId, SynonymRule synonymRule) { Request(String synonymsSetId, SynonymRule synonymRule, boolean refresh) {
this.synonymsSetId = synonymsSetId; this.synonymsSetId = synonymsSetId;
this.synonymRule = synonymRule; this.synonymRule = synonymRule;
this.refresh = refresh;
} }
@Override @Override
@ -96,6 +105,9 @@ public class PutSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
super.writeTo(out); super.writeTo(out);
out.writeString(synonymsSetId); out.writeString(synonymsSetId);
synonymRule.writeTo(out); synonymRule.writeTo(out);
if (out.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
out.writeBoolean(refresh);
}
} }
public String synonymsSetId() { public String synonymsSetId() {
@ -106,17 +118,23 @@ public class PutSynonymRuleAction extends ActionType<SynonymUpdateResponse> {
return synonymRule; return synonymRule;
} }
public boolean refresh() {
return refresh;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
Request request = (Request) o; Request request = (Request) o;
return Objects.equals(synonymsSetId, request.synonymsSetId) && Objects.equals(synonymRule, request.synonymRule); return Objects.equals(refresh, request.refresh)
&& Objects.equals(synonymsSetId, request.synonymsSetId)
&& Objects.equals(synonymRule, request.synonymRule);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(synonymsSetId, synonymRule); return Objects.hash(synonymsSetId, synonymRule, refresh);
} }
} }
} }

View file

@ -9,6 +9,7 @@
package org.elasticsearch.action.synonyms; package org.elasticsearch.action.synonyms;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ActionType; import org.elasticsearch.action.ActionType;
@ -43,6 +44,7 @@ public class PutSynonymsAction extends ActionType<SynonymUpdateResponse> {
public static class Request extends ActionRequest { public static class Request extends ActionRequest {
private final String synonymsSetId; private final String synonymsSetId;
private final SynonymRule[] synonymRules; private final SynonymRule[] synonymRules;
private final boolean refresh;
public static final ParseField SYNONYMS_SET_FIELD = new ParseField(SynonymsManagementAPIService.SYNONYMS_SET_FIELD); public static final ParseField SYNONYMS_SET_FIELD = new ParseField(SynonymsManagementAPIService.SYNONYMS_SET_FIELD);
private static final ConstructingObjectParser<SynonymRule[], Void> PARSER = new ConstructingObjectParser<>("synonyms_set", args -> { private static final ConstructingObjectParser<SynonymRule[], Void> PARSER = new ConstructingObjectParser<>("synonyms_set", args -> {
@ -59,10 +61,16 @@ public class PutSynonymsAction extends ActionType<SynonymUpdateResponse> {
super(in); super(in);
this.synonymsSetId = in.readString(); this.synonymsSetId = in.readString();
this.synonymRules = in.readArray(SynonymRule::new, SynonymRule[]::new); this.synonymRules = in.readArray(SynonymRule::new, SynonymRule[]::new);
if (in.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
this.refresh = in.readBoolean();
} else {
this.refresh = false;
}
} }
public Request(String synonymsSetId, BytesReference content, XContentType contentType) throws IOException { public Request(String synonymsSetId, boolean refresh, BytesReference content, XContentType contentType) throws IOException {
this.synonymsSetId = synonymsSetId; this.synonymsSetId = synonymsSetId;
this.refresh = refresh;
try (XContentParser parser = XContentHelper.createParser(XContentParserConfiguration.EMPTY, content, contentType)) { try (XContentParser parser = XContentHelper.createParser(XContentParserConfiguration.EMPTY, content, contentType)) {
this.synonymRules = PARSER.apply(parser, null); this.synonymRules = PARSER.apply(parser, null);
} catch (Exception e) { } catch (Exception e) {
@ -70,9 +78,10 @@ public class PutSynonymsAction extends ActionType<SynonymUpdateResponse> {
} }
} }
Request(String synonymsSetId, SynonymRule[] synonymRules) { Request(String synonymsSetId, SynonymRule[] synonymRules, boolean refresh) {
this.synonymsSetId = synonymsSetId; this.synonymsSetId = synonymsSetId;
this.synonymRules = synonymRules; this.synonymRules = synonymRules;
this.refresh = refresh;
} }
@Override @Override
@ -95,12 +104,19 @@ public class PutSynonymsAction extends ActionType<SynonymUpdateResponse> {
super.writeTo(out); super.writeTo(out);
out.writeString(synonymsSetId); out.writeString(synonymsSetId);
out.writeArray(synonymRules); out.writeArray(synonymRules);
if (out.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
out.writeBoolean(refresh);
}
} }
public String synonymsSetId() { public String synonymsSetId() {
return synonymsSetId; return synonymsSetId;
} }
public boolean refresh() {
return refresh;
}
public SynonymRule[] synonymRules() { public SynonymRule[] synonymRules() {
return synonymRules; return synonymRules;
} }
@ -110,12 +126,14 @@ public class PutSynonymsAction extends ActionType<SynonymUpdateResponse> {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
Request request = (Request) o; Request request = (Request) o;
return Objects.equals(synonymsSetId, request.synonymsSetId) && Arrays.equals(synonymRules, request.synonymRules); return Objects.equals(refresh, request.refresh)
&& Objects.equals(synonymsSetId, request.synonymsSetId)
&& Arrays.equals(synonymRules, request.synonymRules);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(synonymsSetId, Arrays.hashCode(synonymRules)); return Objects.hash(synonymsSetId, Arrays.hashCode(synonymRules), refresh);
} }
} }
} }

View file

@ -9,6 +9,7 @@
package org.elasticsearch.action.synonyms; package org.elasticsearch.action.synonyms;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse; import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
@ -20,25 +21,34 @@ import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
public class SynonymUpdateResponse extends ActionResponse implements ToXContentObject { public class SynonymUpdateResponse extends ActionResponse implements ToXContentObject {
public static final String RESULT_FIELD = "result";
public static final String RELOAD_ANALYZERS_DETAILS_FIELD = "reload_analyzers_details";
static final ReloadAnalyzersResponse EMPTY_RELOAD_ANALYZER_RESPONSE = new ReloadAnalyzersResponse(0, 0, 0, List.of(), Map.of());
private final UpdateSynonymsResultStatus updateStatus; private final UpdateSynonymsResultStatus updateStatus;
private final ReloadAnalyzersResponse reloadAnalyzersResponse; private final ReloadAnalyzersResponse reloadAnalyzersResponse;
public SynonymUpdateResponse(StreamInput in) throws IOException { public SynonymUpdateResponse(StreamInput in) throws IOException {
this.updateStatus = in.readEnum(UpdateSynonymsResultStatus.class); this.updateStatus = in.readEnum(UpdateSynonymsResultStatus.class);
if (in.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
this.reloadAnalyzersResponse = in.readOptionalWriteable(ReloadAnalyzersResponse::new);
} else {
this.reloadAnalyzersResponse = new ReloadAnalyzersResponse(in); this.reloadAnalyzersResponse = new ReloadAnalyzersResponse(in);
} }
}
public SynonymUpdateResponse(SynonymsReloadResult synonymsReloadResult) { public SynonymUpdateResponse(SynonymsReloadResult synonymsReloadResult) {
super(); super();
UpdateSynonymsResultStatus updateStatus = synonymsReloadResult.synonymsOperationResult(); UpdateSynonymsResultStatus updateStatus = synonymsReloadResult.synonymsOperationResult();
Objects.requireNonNull(updateStatus, "Update status must not be null"); Objects.requireNonNull(updateStatus, "Update status must not be null");
ReloadAnalyzersResponse reloadResponse = synonymsReloadResult.reloadAnalyzersResponse(); ReloadAnalyzersResponse reloadResponse = synonymsReloadResult.reloadAnalyzersResponse();
Objects.requireNonNull(reloadResponse, "Reload analyzers response must not be null");
this.updateStatus = updateStatus; this.updateStatus = updateStatus;
this.reloadAnalyzersResponse = reloadResponse; this.reloadAnalyzersResponse = reloadResponse;
@ -48,10 +58,12 @@ public class SynonymUpdateResponse extends ActionResponse implements ToXContentO
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(); builder.startObject();
{ {
builder.field("result", updateStatus.name().toLowerCase(Locale.ENGLISH)); builder.field(RESULT_FIELD, updateStatus.name().toLowerCase(Locale.ENGLISH));
builder.field("reload_analyzers_details"); if (reloadAnalyzersResponse != null) {
builder.field(RELOAD_ANALYZERS_DETAILS_FIELD);
reloadAnalyzersResponse.toXContent(builder, params); reloadAnalyzersResponse.toXContent(builder, params);
} }
}
builder.endObject(); builder.endObject();
return builder; return builder;
@ -60,8 +72,17 @@ public class SynonymUpdateResponse extends ActionResponse implements ToXContentO
@Override @Override
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
out.writeEnum(updateStatus); out.writeEnum(updateStatus);
if (out.getTransportVersion().onOrAfter(TransportVersions.SYNONYMS_REFRESH_PARAM)) {
out.writeOptionalWriteable(reloadAnalyzersResponse);
} else {
if (reloadAnalyzersResponse == null) {
// Nulls will be written as empty reload analyzer responses for older versions
EMPTY_RELOAD_ANALYZER_RESPONSE.writeTo(out);
} else {
reloadAnalyzersResponse.writeTo(out); reloadAnalyzersResponse.writeTo(out);
} }
}
}
public RestStatus status() { public RestStatus status() {
return switch (updateStatus) { return switch (updateStatus) {
@ -70,6 +91,14 @@ public class SynonymUpdateResponse extends ActionResponse implements ToXContentO
}; };
} }
UpdateSynonymsResultStatus updateStatus() {
return updateStatus;
}
ReloadAnalyzersResponse reloadAnalyzersResponse() {
return reloadAnalyzersResponse;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View file

@ -41,6 +41,7 @@ public class TransportDeleteSynonymRuleAction extends HandledTransportAction<Del
synonymsManagementAPIService.deleteSynonymRule( synonymsManagementAPIService.deleteSynonymRule(
request.synonymsSetId(), request.synonymsSetId(),
request.synonymRuleId(), request.synonymRuleId(),
request.refresh(),
listener.map(SynonymUpdateResponse::new) listener.map(SynonymUpdateResponse::new)
); );
} }

View file

@ -41,8 +41,8 @@ public class TransportPutSynonymRuleAction extends HandledTransportAction<PutSyn
synonymsManagementAPIService.putSynonymRule( synonymsManagementAPIService.putSynonymRule(
request.synonymsSetId(), request.synonymsSetId(),
request.synonymRule(), request.synonymRule(),
listener.map(updateResponse -> new SynonymUpdateResponse(updateResponse)) request.refresh(),
listener.map(SynonymUpdateResponse::new)
); );
} }
} }

View file

@ -35,6 +35,7 @@ public class TransportPutSynonymsAction extends HandledTransportAction<PutSynony
synonymsManagementAPIService.putSynonymsSet( synonymsManagementAPIService.putSynonymsSet(
request.synonymsSetId(), request.synonymsSetId(),
request.synonymRules(), request.synonymRules(),
request.refresh(),
listener.map(SynonymUpdateResponse::new) listener.map(SynonymUpdateResponse::new)
); );
} }

View file

@ -19,6 +19,7 @@ import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.elasticsearch.rest.RestRequest.Method.DELETE; import static org.elasticsearch.rest.RestRequest.Method.DELETE;
@ -39,8 +40,14 @@ public class RestDeleteSynonymRuleAction extends BaseRestHandler {
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException { protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
DeleteSynonymRuleAction.Request request = new DeleteSynonymRuleAction.Request( DeleteSynonymRuleAction.Request request = new DeleteSynonymRuleAction.Request(
restRequest.param("synonymsSet"), restRequest.param("synonymsSet"),
restRequest.param("synonymRuleId") restRequest.param("synonymRuleId"),
restRequest.paramAsBoolean("refresh", true)
); );
return channel -> client.execute(DeleteSynonymRuleAction.INSTANCE, request, new RestToXContentListener<>(channel)); return channel -> client.execute(DeleteSynonymRuleAction.INSTANCE, request, new RestToXContentListener<>(channel));
} }
@Override
public Set<String> supportedCapabilities() {
return SynonymCapabilities.CAPABILITIES;
}
} }

View file

@ -20,6 +20,7 @@ import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.elasticsearch.rest.RestRequest.Method.PUT; import static org.elasticsearch.rest.RestRequest.Method.PUT;
@ -41,6 +42,7 @@ public class RestPutSynonymRuleAction extends BaseRestHandler {
PutSynonymRuleAction.Request request = new PutSynonymRuleAction.Request( PutSynonymRuleAction.Request request = new PutSynonymRuleAction.Request(
restRequest.param("synonymsSet"), restRequest.param("synonymsSet"),
restRequest.param("synonymRuleId"), restRequest.param("synonymRuleId"),
restRequest.paramAsBoolean("refresh", true),
restRequest.content(), restRequest.content(),
restRequest.getXContentType() restRequest.getXContentType()
); );
@ -50,4 +52,9 @@ public class RestPutSynonymRuleAction extends BaseRestHandler {
new RestToXContentListener<>(channel, SynonymUpdateResponse::status, r -> null) new RestToXContentListener<>(channel, SynonymUpdateResponse::status, r -> null)
); );
} }
@Override
public Set<String> supportedCapabilities() {
return SynonymCapabilities.CAPABILITIES;
}
} }

View file

@ -20,6 +20,7 @@ import org.elasticsearch.rest.action.RestToXContentListener;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.elasticsearch.rest.RestRequest.Method.PUT; import static org.elasticsearch.rest.RestRequest.Method.PUT;
@ -40,6 +41,7 @@ public class RestPutSynonymsAction extends BaseRestHandler {
protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException { protected RestChannelConsumer prepareRequest(RestRequest restRequest, NodeClient client) throws IOException {
PutSynonymsAction.Request request = new PutSynonymsAction.Request( PutSynonymsAction.Request request = new PutSynonymsAction.Request(
restRequest.param("synonymsSet"), restRequest.param("synonymsSet"),
restRequest.paramAsBoolean("refresh", true),
restRequest.content(), restRequest.content(),
restRequest.getXContentType() restRequest.getXContentType()
); );
@ -49,4 +51,9 @@ public class RestPutSynonymsAction extends BaseRestHandler {
new RestToXContentListener<>(channel, SynonymUpdateResponse::status, r -> null) new RestToXContentListener<>(channel, SynonymUpdateResponse::status, r -> null)
); );
} }
@Override
public Set<String> supportedCapabilities() {
return SynonymCapabilities.CAPABILITIES;
}
} }

View file

@ -0,0 +1,26 @@
/*
* 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.rest.action.synonyms;
import java.util.Set;
/**
* A {@link Set} of "capabilities" supported by the {@link RestPutSynonymsAction} and {@link RestPutSynonymRuleAction}.
*/
public final class SynonymCapabilities {
private SynonymCapabilities() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
private static final String SYNONYMS_REFRESH_PARAM = "synonyms_refresh_param";
public static final Set<String> CAPABILITIES = Set.of(SYNONYMS_REFRESH_PARAM);
}

View file

@ -19,6 +19,9 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.DelegatingActionListener; import org.elasticsearch.action.DelegatingActionListener;
import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.TransportClusterHealthAction;
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersRequest; import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersRequest;
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse; import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse;
import org.elasticsearch.action.admin.indices.analyze.TransportReloadAnalyzersAction; import org.elasticsearch.action.admin.indices.analyze.TransportReloadAnalyzersAction;
@ -36,12 +39,14 @@ import org.elasticsearch.client.internal.OriginSettingClient;
import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.routing.Preference; import org.elasticsearch.cluster.routing.Preference;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryAction; import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.indices.IndexCreationException;
import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.SystemIndexDescriptor;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.BucketOrder;
@ -72,7 +77,7 @@ public class SynonymsManagementAPIService {
private static final String SYNONYMS_INDEX_NAME_PATTERN = ".synonyms-*"; private static final String SYNONYMS_INDEX_NAME_PATTERN = ".synonyms-*";
private static final int SYNONYMS_INDEX_FORMAT = 2; private static final int SYNONYMS_INDEX_FORMAT = 2;
private static final String SYNONYMS_INDEX_CONCRETE_NAME = ".synonyms-" + SYNONYMS_INDEX_FORMAT; static final String SYNONYMS_INDEX_CONCRETE_NAME = ".synonyms-" + SYNONYMS_INDEX_FORMAT;
private static final String SYNONYMS_ALIAS_NAME = ".synonyms"; private static final String SYNONYMS_ALIAS_NAME = ".synonyms";
public static final String SYNONYMS_FEATURE_NAME = "synonyms"; public static final String SYNONYMS_FEATURE_NAME = "synonyms";
// Stores the synonym set the rule belongs to // Stores the synonym set the rule belongs to
@ -90,6 +95,7 @@ public class SynonymsManagementAPIService {
private static final String SYNONYM_RULE_ID_FIELD = SynonymRule.ID_FIELD.getPreferredName(); 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 SYNONYM_SETS_AGG_NAME = "synonym_sets_aggr";
private static final int SYNONYMS_INDEX_MAPPINGS_VERSION = 1; private static final int SYNONYMS_INDEX_MAPPINGS_VERSION = 1;
public static final int INDEX_SEARCHABLE_TIMEOUT_SECONDS = 30;
private final int maxSynonymsSets; private final int maxSynonymsSets;
// Package private for testing // Package private for testing
@ -301,7 +307,12 @@ public class SynonymsManagementAPIService {
}); });
} }
public void putSynonymsSet(String synonymSetId, SynonymRule[] synonymsSet, ActionListener<SynonymsReloadResult> listener) { public void putSynonymsSet(
String synonymSetId,
SynonymRule[] synonymsSet,
boolean refresh,
ActionListener<SynonymsReloadResult> listener
) {
if (synonymsSet.length > maxSynonymsSets) { if (synonymsSet.length > maxSynonymsSets) {
listener.onFailure( listener.onFailure(
new IllegalArgumentException("The number of synonyms rules in a synonym set cannot exceed " + maxSynonymsSets) new IllegalArgumentException("The number of synonyms rules in a synonym set cannot exceed " + maxSynonymsSets)
@ -343,7 +354,13 @@ public class SynonymsManagementAPIService {
? UpdateSynonymsResultStatus.CREATED ? UpdateSynonymsResultStatus.CREATED
: UpdateSynonymsResultStatus.UPDATED; : UpdateSynonymsResultStatus.UPDATED;
reloadAnalyzers(synonymSetId, false, bulkInsertResponseListener, updateSynonymsResultStatus); checkIndexSearchableAndReloadAnalyzers(
synonymSetId,
refresh,
false,
updateSynonymsResultStatus,
bulkInsertResponseListener
);
}) })
); );
})); }));
@ -366,7 +383,12 @@ public class SynonymsManagementAPIService {
bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).execute(listener); bulkRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).execute(listener);
} }
public void putSynonymRule(String synonymsSetId, SynonymRule synonymRule, ActionListener<SynonymsReloadResult> listener) { public void putSynonymRule(
String synonymsSetId,
SynonymRule synonymRule,
boolean refresh,
ActionListener<SynonymsReloadResult> listener
) {
checkSynonymSetExists(synonymsSetId, listener.delegateFailureAndWrap((l1, obj) -> { checkSynonymSetExists(synonymsSetId, listener.delegateFailureAndWrap((l1, obj) -> {
// Count synonym rules to check if we're at maximum // Count synonym rules to check if we're at maximum
BoolQueryBuilder queryFilter = QueryBuilders.boolQuery() BoolQueryBuilder queryFilter = QueryBuilders.boolQuery()
@ -388,14 +410,18 @@ public class SynonymsManagementAPIService {
new IllegalArgumentException("The number of synonym rules in a synonyms set cannot exceed " + maxSynonymsSets) new IllegalArgumentException("The number of synonym rules in a synonyms set cannot exceed " + maxSynonymsSets)
); );
} else { } else {
indexSynonymRule(synonymsSetId, synonymRule, searchListener); indexSynonymRule(synonymsSetId, synonymRule, refresh, searchListener);
} }
})); }));
})); }));
} }
private void indexSynonymRule(String synonymsSetId, SynonymRule synonymRule, ActionListener<SynonymsReloadResult> listener) private void indexSynonymRule(
throws IOException { String synonymsSetId,
SynonymRule synonymRule,
boolean refresh,
ActionListener<SynonymsReloadResult> listener
) throws IOException {
IndexRequest indexRequest = createSynonymRuleIndexRequest(synonymsSetId, synonymRule).setRefreshPolicy( IndexRequest indexRequest = createSynonymRuleIndexRequest(synonymsSetId, synonymRule).setRefreshPolicy(
WriteRequest.RefreshPolicy.IMMEDIATE WriteRequest.RefreshPolicy.IMMEDIATE
); );
@ -404,7 +430,7 @@ public class SynonymsManagementAPIService {
? UpdateSynonymsResultStatus.CREATED ? UpdateSynonymsResultStatus.CREATED
: UpdateSynonymsResultStatus.UPDATED; : UpdateSynonymsResultStatus.UPDATED;
reloadAnalyzers(synonymsSetId, false, l2, updateStatus); checkIndexSearchableAndReloadAnalyzers(synonymsSetId, refresh, false, updateStatus, l2);
})); }));
} }
@ -424,7 +450,12 @@ public class SynonymsManagementAPIService {
); );
} }
public void deleteSynonymRule(String synonymsSetId, String synonymRuleId, ActionListener<SynonymsReloadResult> listener) { public void deleteSynonymRule(
String synonymsSetId,
String synonymRuleId,
boolean refresh,
ActionListener<SynonymsReloadResult> listener
) {
client.prepareDelete(SYNONYMS_ALIAS_NAME, internalSynonymRuleId(synonymsSetId, synonymRuleId)) client.prepareDelete(SYNONYMS_ALIAS_NAME, internalSynonymRuleId(synonymsSetId, synonymRuleId))
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.execute(new DelegatingIndexNotFoundActionListener<>(synonymsSetId, listener, (l, deleteResponse) -> { .execute(new DelegatingIndexNotFoundActionListener<>(synonymsSetId, listener, (l, deleteResponse) -> {
@ -443,7 +474,11 @@ public class SynonymsManagementAPIService {
return; return;
} }
reloadAnalyzers(synonymsSetId, false, listener, UpdateSynonymsResultStatus.DELETED); if (refresh) {
reloadAnalyzers(synonymsSetId, false, UpdateSynonymsResultStatus.DELETED, listener);
} else {
listener.onResponse(new SynonymsReloadResult(UpdateSynonymsResultStatus.DELETED, null));
}
})); }));
} }
@ -501,7 +536,7 @@ public class SynonymsManagementAPIService {
public void deleteSynonymsSet(String synonymSetId, ActionListener<AcknowledgedResponse> listener) { public void deleteSynonymsSet(String synonymSetId, ActionListener<AcknowledgedResponse> listener) {
// Previews reloading the resource to understand its usage on indices // Previews reloading the resource to understand its usage on indices
reloadAnalyzers(synonymSetId, true, listener.delegateFailure((reloadListener, reloadResult) -> { reloadAnalyzers(synonymSetId, true, null, listener.delegateFailure((reloadListener, reloadResult) -> {
Map<String, ReloadAnalyzersResponse.ReloadDetails> reloadDetails = reloadResult.reloadAnalyzersResponse.getReloadDetails(); Map<String, ReloadAnalyzersResponse.ReloadDetails> reloadDetails = reloadResult.reloadAnalyzersResponse.getReloadDetails();
if (reloadDetails.isEmpty() == false) { if (reloadDetails.isEmpty() == false) {
Set<String> indices = reloadDetails.entrySet() Set<String> indices = reloadDetails.entrySet()
@ -538,14 +573,48 @@ public class SynonymsManagementAPIService {
deleteObjectsListener.onResponse(AcknowledgedResponse.of(true)); deleteObjectsListener.onResponse(AcknowledgedResponse.of(true));
})); }));
}), null); }));
} }
private <T> void reloadAnalyzers( private <T> void checkIndexSearchableAndReloadAnalyzers(
String synonymSetId,
boolean refresh,
boolean preview,
UpdateSynonymsResultStatus synonymsOperationResult,
ActionListener<SynonymsReloadResult> listener
) {
if (refresh == false) {
// If not refreshing, we don't need to reload analyzers
listener.onResponse(new SynonymsReloadResult(synonymsOperationResult, null));
return;
}
// Check synonyms index is searchable before reloading, to ensure analyzers are able to load the changed information
checkSynonymsIndexHealth(listener.delegateFailure((l, response) -> {
if (response.isTimedOut()) {
l.onFailure(
new IndexCreationException(
"synonyms index ["
+ SYNONYMS_ALIAS_NAME
+ "] is not searchable. "
+ response.getActiveShardsPercent()
+ "% shards are active",
null
)
);
return;
}
reloadAnalyzers(synonymSetId, preview, synonymsOperationResult, listener);
}));
}
private void reloadAnalyzers(
String synonymSetId, String synonymSetId,
boolean preview, boolean preview,
ActionListener<SynonymsReloadResult> listener, UpdateSynonymsResultStatus synonymsOperationResult,
UpdateSynonymsResultStatus synonymsOperationResult ActionListener<SynonymsReloadResult> listener
) { ) {
// auto-reload all reloadable analyzers (currently only those that use updateable synonym or keyword_marker filters) // auto-reload all reloadable analyzers (currently only those that use updateable synonym or keyword_marker filters)
ReloadAnalyzersRequest reloadAnalyzersRequest = new ReloadAnalyzersRequest(synonymSetId, preview, "*"); ReloadAnalyzersRequest reloadAnalyzersRequest = new ReloadAnalyzersRequest(synonymSetId, preview, "*");
@ -556,13 +625,23 @@ public class SynonymsManagementAPIService {
); );
} }
// Allows checking failures in tests
void checkSynonymsIndexHealth(ActionListener<ClusterHealthResponse> listener) {
ClusterHealthRequest healthRequest = new ClusterHealthRequest(
TimeValue.timeValueSeconds(INDEX_SEARCHABLE_TIMEOUT_SECONDS),
SYNONYMS_ALIAS_NAME
).waitForGreenStatus();
client.execute(TransportClusterHealthAction.TYPE, healthRequest, listener);
}
// Retrieves the internal synonym rule ID to store it in the index. As the same synonym rule ID // Retrieves the internal synonym rule ID to store it in the index. As the same synonym rule ID
// can be used in different synonym sets, we prefix the ID with the synonym set to avoid collisions // can be used in different synonym sets, we prefix the ID with the synonym set to avoid collisions
private static String internalSynonymRuleId(String synonymsSetId, String synonymRuleId) { private static String internalSynonymRuleId(String synonymsSetId, String synonymRuleId) {
return synonymsSetId + SYNONYM_RULE_ID_SEPARATOR + synonymRuleId; return synonymsSetId + SYNONYM_RULE_ID_SEPARATOR + synonymRuleId;
} }
static Settings settings() { private static Settings settings() {
return Settings.builder() return Settings.builder()
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-1") .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-1")

View file

@ -31,7 +31,7 @@ import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg
public class ReloadAnalyzersResponseTests extends AbstractBroadcastResponseTestCase<ReloadAnalyzersResponse> { public class ReloadAnalyzersResponseTests extends AbstractBroadcastResponseTestCase<ReloadAnalyzersResponse> {
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({ "unchecked" })
private static final ConstructingObjectParser<ReloadAnalyzersResponse, Void> PARSER = new ConstructingObjectParser<>( public static final ConstructingObjectParser<ReloadAnalyzersResponse, Void> PARSER = new ConstructingObjectParser<>(
"reload_analyzer", "reload_analyzer",
true, true,
arg -> { arg -> {
@ -67,8 +67,8 @@ public class ReloadAnalyzersResponseTests extends AbstractBroadcastResponseTestC
declareBroadcastFields(PARSER); declareBroadcastFields(PARSER);
PARSER.declareObjectArray(constructorArg(), ENTRY_PARSER, ReloadAnalyzersResponse.RELOAD_DETAILS_FIELD); PARSER.declareObjectArray(constructorArg(), ENTRY_PARSER, ReloadAnalyzersResponse.RELOAD_DETAILS_FIELD);
ENTRY_PARSER.declareString(constructorArg(), ReloadAnalyzersResponse.INDEX_FIELD); ENTRY_PARSER.declareString(constructorArg(), ReloadAnalyzersResponse.INDEX_FIELD);
ENTRY_PARSER.declareStringArray(constructorArg(), ReloadAnalyzersResponse.RELOADED_ANALYZERS_FIELD);
ENTRY_PARSER.declareStringArray(constructorArg(), ReloadAnalyzersResponse.RELOADED_NODE_IDS_FIELD); ENTRY_PARSER.declareStringArray(constructorArg(), ReloadAnalyzersResponse.RELOADED_NODE_IDS_FIELD);
ENTRY_PARSER.declareStringArray(constructorArg(), ReloadAnalyzersResponse.RELOADED_ANALYZERS_FIELD);
} }
@Override @Override

View file

@ -23,7 +23,7 @@ public class DeleteSynonymRuleActionRequestSerializingTests extends AbstractWire
@Override @Override
protected DeleteSynonymRuleAction.Request createTestInstance() { protected DeleteSynonymRuleAction.Request createTestInstance() {
return new DeleteSynonymRuleAction.Request(randomIdentifier(), randomIdentifier()); return new DeleteSynonymRuleAction.Request(randomIdentifier(), randomIdentifier(), randomBoolean());
} }
@Override @Override

View file

@ -22,7 +22,7 @@ public class PutSynonymRuleActionRequestSerializingTests extends AbstractWireSer
@Override @Override
protected PutSynonymRuleAction.Request createTestInstance() { protected PutSynonymRuleAction.Request createTestInstance() {
return new PutSynonymRuleAction.Request(randomIdentifier(), SynonymsTestUtils.randomSynonymRule()); return new PutSynonymRuleAction.Request(randomIdentifier(), SynonymsTestUtils.randomSynonymRule(), randomBoolean());
} }
@Override @Override

View file

@ -25,7 +25,7 @@ public class PutSynonymsActionRequestSerializingTests extends AbstractWireSerial
@Override @Override
protected PutSynonymsAction.Request createTestInstance() { protected PutSynonymsAction.Request createTestInstance() {
return new PutSynonymsAction.Request(randomIdentifier(), randomSynonymsSet()); return new PutSynonymsAction.Request(randomIdentifier(), randomSynonymsSet(), randomBoolean());
} }
@Override @Override

View file

@ -9,23 +9,54 @@
package org.elasticsearch.action.synonyms; package org.elasticsearch.action.synonyms;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse; import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponse;
import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponseTests; import org.elasticsearch.action.admin.indices.analyze.ReloadAnalyzersResponseTests;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.synonyms.SynonymsManagementAPIService;
import org.elasticsearch.synonyms.SynonymsManagementAPIService.SynonymsReloadResult; import org.elasticsearch.synonyms.SynonymsManagementAPIService.SynonymsReloadResult;
import org.elasticsearch.test.AbstractWireSerializingTestCase; import org.elasticsearch.test.AbstractBWCSerializationTestCase;
import org.elasticsearch.xcontent.ConstructingObjectParser;
import org.elasticsearch.xcontent.ParseField;
import org.elasticsearch.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.action.synonyms.SynonymUpdateResponse.EMPTY_RELOAD_ANALYZER_RESPONSE;
import static org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus.CREATED; import static org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus.CREATED;
import static org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus.DELETED; import static org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus.DELETED;
import static org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus.UPDATED; import static org.elasticsearch.synonyms.SynonymsManagementAPIService.UpdateSynonymsResultStatus.UPDATED;
import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg;
public class SynonymUpdateResponseSerializingTests extends AbstractWireSerializingTestCase<SynonymUpdateResponse> { public class SynonymUpdateResponseSerializingTests extends AbstractBWCSerializationTestCase<SynonymUpdateResponse> {
private static final ConstructingObjectParser<SynonymUpdateResponse, Void> PARSER = new ConstructingObjectParser<>(
"synonyms_update_response",
true,
arg -> {
SynonymsManagementAPIService.UpdateSynonymsResultStatus status = SynonymsManagementAPIService.UpdateSynonymsResultStatus
.valueOf(((String) arg[0]).toUpperCase(Locale.ROOT));
ReloadAnalyzersResponse reloadAnalyzersResponse = (ReloadAnalyzersResponse) arg[1];
return new SynonymUpdateResponse(new SynonymsReloadResult(status, reloadAnalyzersResponse));
}
);
static {
PARSER.declareString(constructorArg(), new ParseField(SynonymUpdateResponse.RESULT_FIELD));
PARSER.declareObjectOrNull(
optionalConstructorArg(),
(p, c) -> ReloadAnalyzersResponseTests.PARSER.parse(p, null),
null,
new ParseField(SynonymUpdateResponse.RELOAD_ANALYZERS_DETAILS_FIELD)
);
}
@Override @Override
protected Writeable.Reader<SynonymUpdateResponse> instanceReader() { protected Writeable.Reader<SynonymUpdateResponse> instanceReader() {
@ -34,9 +65,22 @@ public class SynonymUpdateResponseSerializingTests extends AbstractWireSerializi
@Override @Override
protected SynonymUpdateResponse createTestInstance() { protected SynonymUpdateResponse createTestInstance() {
return createTestInstance(randomBoolean());
}
private SynonymUpdateResponse createTestInstance(boolean includeReloadInfo) {
ReloadAnalyzersResponse reloadAnalyzersResponse = null;
if (includeReloadInfo) {
Map<String, ReloadAnalyzersResponse.ReloadDetails> reloadedIndicesDetails = ReloadAnalyzersResponseTests Map<String, ReloadAnalyzersResponse.ReloadDetails> reloadedIndicesDetails = ReloadAnalyzersResponseTests
.createRandomReloadDetails(); .createRandomReloadDetails();
ReloadAnalyzersResponse reloadAnalyzersResponse = new ReloadAnalyzersResponse(10, 10, 0, null, reloadedIndicesDetails); reloadAnalyzersResponse = new ReloadAnalyzersResponse(
randomIntBetween(0, 10),
randomIntBetween(0, 10),
randomIntBetween(0, 5),
null,
reloadedIndicesDetails
);
}
return new SynonymUpdateResponse(new SynonymsReloadResult(randomFrom(CREATED, UPDATED, DELETED), reloadAnalyzersResponse)); return new SynonymUpdateResponse(new SynonymsReloadResult(randomFrom(CREATED, UPDATED, DELETED), reloadAnalyzersResponse));
} }
@ -45,6 +89,17 @@ public class SynonymUpdateResponseSerializingTests extends AbstractWireSerializi
return randomValueOtherThan(instance, this::createTestInstance); return randomValueOtherThan(instance, this::createTestInstance);
} }
@Override
protected SynonymUpdateResponse mutateInstanceForVersion(SynonymUpdateResponse instance, TransportVersion version) {
if (version.before(TransportVersions.SYNONYMS_REFRESH_PARAM) && instance.reloadAnalyzersResponse() == null) {
// Nulls will be written as empty reload analyzer responses for older versions
return new SynonymUpdateResponse(new SynonymsReloadResult(instance.updateStatus(), EMPTY_RELOAD_ANALYZER_RESPONSE));
}
return instance;
}
public void testToXContent() throws IOException { public void testToXContent() throws IOException {
Map<String, ReloadAnalyzersResponse.ReloadDetails> reloadedIndicesNodes = Collections.singletonMap( Map<String, ReloadAnalyzersResponse.ReloadDetails> reloadedIndicesNodes = Collections.singletonMap(
"index", "index",
@ -73,4 +128,18 @@ public class SynonymUpdateResponseSerializingTests extends AbstractWireSerializi
} }
}"""), output); }"""), output);
} }
public void testToXContentWithNoReloadResult() throws IOException {
SynonymUpdateResponse response = new SynonymUpdateResponse(new SynonymsReloadResult(CREATED, null));
String output = Strings.toString(response);
assertEquals(XContentHelper.stripWhitespace("""
{
"result": "created"
}"""), output);
}
@Override
protected SynonymUpdateResponse doParseInstance(XContentParser parser) throws IOException {
return PARSER.apply(parser, null);
}
} }

View file

@ -282,7 +282,7 @@ public class InternalUsers {
UsernamesField.SYNONYMS_USER_NAME, UsernamesField.SYNONYMS_USER_NAME,
new RoleDescriptor( new RoleDescriptor(
UsernamesField.SYNONYMS_ROLE_NAME, UsernamesField.SYNONYMS_ROLE_NAME,
null, new String[] { "monitor" },
new RoleDescriptor.IndicesPrivileges[] { new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".synonyms*").privileges("all").allowRestrictedIndices(true).build(), RoleDescriptor.IndicesPrivileges.builder().indices(".synonyms*").privileges("all").allowRestrictedIndices(true).build(),
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges(TransportReloadAnalyzersAction.TYPE.name()).build(), }, RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges(TransportReloadAnalyzersAction.TYPE.name()).build(), },