Improve error message if setting is not found
We can do better than just throwing an error when we don't find a setting. It's actually trivial to leverage lucenes slow LD StringDistance to find possible candiates for a setting to detect missspellings and suggest a possible setting. This commit adds error messages like: * `unknown setting [index.numbe_of_replica] did you mean [index.number_of_replicas]?` rather than just reporting the setting as unknown
This commit is contained in:
parent
8127a06b2e
commit
a0c68c281c
|
@ -19,7 +19,11 @@
|
|||
|
||||
package org.elasticsearch.common.settings;
|
||||
|
||||
import org.apache.lucene.search.spell.LevensteinDistance;
|
||||
import org.apache.lucene.util.ArrayUtil;
|
||||
import org.apache.lucene.util.CollectionUtil;
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
|
||||
|
@ -37,6 +41,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A basic setting service that can be used for per-index and per-cluster settings.
|
||||
|
@ -244,7 +249,21 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
public final void validate(String key, Settings settings) {
|
||||
Setting setting = get(key);
|
||||
if (setting == null) {
|
||||
throw new IllegalArgumentException("unknown setting [" + key + "]");
|
||||
LevensteinDistance ld = new LevensteinDistance();
|
||||
List<Tuple<Float, String>> scoredKeys = new ArrayList<>();
|
||||
for (String k : this.keySettings.keySet()) {
|
||||
float distance = ld.getDistance(key, k);
|
||||
if (distance > 0.7f) {
|
||||
scoredKeys.add(new Tuple<>(distance, k));
|
||||
}
|
||||
}
|
||||
CollectionUtil.timSort(scoredKeys, (a,b) -> b.v1().compareTo(a.v1()));
|
||||
String msg = "unknown setting [" + key + "]";
|
||||
List<String> keys = scoredKeys.stream().map((a) -> a.v2()).collect(Collectors.toList());
|
||||
if (keys.isEmpty() == false) {
|
||||
msg += " did you mean " + (keys.size() == 1 ? "[" + keys.get(0) + "]": "any of " + keys.toString()) + "?";
|
||||
}
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
setting.get(settings);
|
||||
}
|
||||
|
|
|
@ -187,6 +187,15 @@ public class ScopedSettingsTests extends ESTestCase {
|
|||
assertEquals("boom", copy.get(IndexModule.INDEX_STORE_TYPE_SETTING)); // test fallback to node settings
|
||||
}
|
||||
|
||||
public void testValidateWithSuggestion() {
|
||||
IndexScopedSettings settings = new IndexScopedSettings(
|
||||
Settings.EMPTY,
|
||||
IndexScopedSettings.BUILT_IN_INDEX_SETTINGS);
|
||||
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
|
||||
() -> settings.validate(Settings.builder().put("index.numbe_of_replica", "1").build()));
|
||||
assertEquals(iae.getMessage(), "unknown setting [index.numbe_of_replica] did you mean [index.number_of_replicas]?");
|
||||
}
|
||||
|
||||
public void testValidate() {
|
||||
IndexScopedSettings settings = new IndexScopedSettings(
|
||||
Settings.EMPTY,
|
||||
|
|
Loading…
Reference in New Issue