diff --git a/server/src/main/java/org/elasticsearch/TransportVersion.java b/server/src/main/java/org/elasticsearch/TransportVersion.java index f130f7d62e29b..831891595cb00 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersion.java +++ b/server/src/main/java/org/elasticsearch/TransportVersion.java @@ -9,9 +9,11 @@ package org.elasticsearch; +import org.apache.lucene.search.spell.LevenshteinDistance; import org.elasticsearch.common.VersionId; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.Tuple; import org.elasticsearch.internal.VersionExtension; import org.elasticsearch.plugins.ExtensionLoader; @@ -234,12 +236,29 @@ public static TransportVersion fromId(int id) { * This will only return the latest known referable transport version for a given name and not its * patch versions. Patch versions are constructed as a linked list internally and may be found by * cycling through them in a loop using {@link TransportVersion#nextPatchVersion()}. - * */ public static TransportVersion fromName(String name) { TransportVersion known = VersionsHolder.ALL_VERSIONS_BY_NAME.get(name); if (known == null) { - throw new IllegalStateException("unknown transport version [" + name + "]"); + LevenshteinDistance ld = new LevenshteinDistance(); + List> scoredNames = new ArrayList<>(); + for (String key : VersionsHolder.ALL_VERSIONS_BY_NAME.keySet()) { + float distance = ld.getDistance(name, key); + if (distance > 0.7f) { + scoredNames.add(new Tuple<>(distance, key)); + } + } + StringBuilder message = new StringBuilder("Unknown transport version ["); + message.append(name); + message.append("]."); + if (scoredNames.isEmpty() == false) { + List names = scoredNames.stream().map(Tuple::v2).toList(); + message.append(" Did you mean "); + message.append(names); + message.append("?"); + } + message.append(" If this is a new transport version, run './gradle generateTransportVersion'."); + throw new IllegalStateException(message.toString()); } return known; } diff --git a/server/src/test/java/org/elasticsearch/TransportVersionTests.java b/server/src/test/java/org/elasticsearch/TransportVersionTests.java index e51ca0c553fb6..bfbe1d31133aa 100644 --- a/server/src/test/java/org/elasticsearch/TransportVersionTests.java +++ b/server/src/test/java/org/elasticsearch/TransportVersionTests.java @@ -391,4 +391,25 @@ public void testComment() { ); assertThat(new TransportVersion(null, 1000000, null).supports(test3), is(true)); } + + public void testMoreLikeThis() { + IllegalStateException ise = expectThrows(IllegalStateException.class, () -> TransportVersion.fromName("to_child_lock_join_query")); + assertThat( + ise.getMessage(), + is( + "Unknown transport version [to_child_lock_join_query]. " + + "Did you mean [to_child_block_join_query]? " + + "If this is a new transport version, run './gradle generateTransportVersion'." + ) + ); + + ise = expectThrows(IllegalStateException.class, () -> TransportVersion.fromName("brand_new_version_unrelated_to_others")); + assertThat( + ise.getMessage(), + is( + "Unknown transport version [brand_new_version_unrelated_to_others]. " + + "If this is a new transport version, run './gradle generateTransportVersion'." + ) + ); + } }