diff --git a/server/src/main/java/org/elasticsearch/TransportVersion.java b/server/src/main/java/org/elasticsearch/TransportVersion.java index 10641cae54e6d..4e0c28ea396fd 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..7bb5106403d2d 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("esql_ixed_index_like")); + assertThat( + ise.getMessage(), + is( + "Unknown transport version [esql_ixed_index_like]. " + + "Did you mean [esql_fixed_index_like]? " + + "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'." + ) + ); + } }