|
@@ -18,6 +18,8 @@
|
|
|
*/
|
|
|
package org.elasticsearch.common.unit;
|
|
|
|
|
|
+import org.elasticsearch.ElasticsearchParseException;
|
|
|
+import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.common.ParseField;
|
|
|
import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
@@ -43,8 +45,12 @@ public final class Fuzziness implements ToXContentFragment, Writeable {
|
|
|
public static final Fuzziness TWO = new Fuzziness(2);
|
|
|
public static final Fuzziness AUTO = new Fuzziness("AUTO");
|
|
|
public static final ParseField FIELD = new ParseField(X_FIELD_NAME);
|
|
|
+ private static final int DEFAULT_LOW_DISTANCE = 3;
|
|
|
+ private static final int DEFAULT_HIGH_DISTANCE = 6;
|
|
|
|
|
|
private final String fuzziness;
|
|
|
+ private int lowDistance = DEFAULT_LOW_DISTANCE;
|
|
|
+ private int highDistance = DEFAULT_HIGH_DISTANCE;
|
|
|
|
|
|
private Fuzziness(int fuzziness) {
|
|
|
if (fuzziness != 0 && fuzziness != 1 && fuzziness != 2) {
|
|
@@ -54,22 +60,48 @@ public final class Fuzziness implements ToXContentFragment, Writeable {
|
|
|
}
|
|
|
|
|
|
private Fuzziness(String fuzziness) {
|
|
|
- if (fuzziness == null) {
|
|
|
+ if (fuzziness == null || fuzziness.isEmpty()) {
|
|
|
throw new IllegalArgumentException("fuzziness can't be null!");
|
|
|
}
|
|
|
this.fuzziness = fuzziness.toUpperCase(Locale.ROOT);
|
|
|
}
|
|
|
|
|
|
+ private Fuzziness(String fuzziness, int lowDistance, int highDistance) {
|
|
|
+ this(fuzziness);
|
|
|
+ if (lowDistance < 0 || highDistance < 0 || lowDistance > highDistance) {
|
|
|
+ throw new IllegalArgumentException("fuzziness wrongly configured, must be: lowDistance > 0, highDistance" +
|
|
|
+ " > 0 and lowDistance <= highDistance ");
|
|
|
+ }
|
|
|
+ this.lowDistance = lowDistance;
|
|
|
+ this.highDistance = highDistance;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Read from a stream.
|
|
|
*/
|
|
|
public Fuzziness(StreamInput in) throws IOException {
|
|
|
fuzziness = in.readString();
|
|
|
+ if (in.getVersion().onOrAfter(Version.V_6_1_0) && in.readBoolean()) {
|
|
|
+ lowDistance = in.readVInt();
|
|
|
+ highDistance = in.readVInt();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void writeTo(StreamOutput out) throws IOException {
|
|
|
out.writeString(fuzziness);
|
|
|
+ if (out.getVersion().onOrAfter(Version.V_6_1_0)) {
|
|
|
+ // we cannot serialize the low/high bounds since the other node does not know about them.
|
|
|
+ // This is a best-effort to not fail queries in case the cluster is being upgraded and users
|
|
|
+ // start using features that are not available on all nodes.
|
|
|
+ if (isAutoWithCustomValues()) {
|
|
|
+ out.writeBoolean(true);
|
|
|
+ out.writeVInt(lowDistance);
|
|
|
+ out.writeVInt(highDistance);
|
|
|
+ } else {
|
|
|
+ out.writeBoolean(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -88,10 +120,29 @@ public final class Fuzziness implements ToXContentFragment, Writeable {
|
|
|
String string = fuzziness.toString();
|
|
|
if (AUTO.asString().equalsIgnoreCase(string)) {
|
|
|
return AUTO;
|
|
|
+ } else if (string.toUpperCase(Locale.ROOT).startsWith(AUTO.asString() + ":")) {
|
|
|
+ return parseCustomAuto(string);
|
|
|
}
|
|
|
return new Fuzziness(string);
|
|
|
}
|
|
|
|
|
|
+ private static Fuzziness parseCustomAuto( final String string) {
|
|
|
+ assert string.toUpperCase(Locale.ROOT).startsWith(AUTO.asString() + ":");
|
|
|
+ String[] fuzzinessLimit = string.substring(AUTO.asString().length() + 1).split(",");
|
|
|
+ if (fuzzinessLimit.length == 2) {
|
|
|
+ try {
|
|
|
+ int lowerLimit = Integer.parseInt(fuzzinessLimit[0]);
|
|
|
+ int highLimit = Integer.parseInt(fuzzinessLimit[1]);
|
|
|
+ return new Fuzziness("AUTO", lowerLimit, highLimit);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new ElasticsearchParseException("failed to parse [{}] as a \"auto:int,int\"", e,
|
|
|
+ string);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new ElasticsearchParseException("failed to find low and high distance values");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public static Fuzziness parse(XContentParser parser) throws IOException {
|
|
|
XContentParser.Token token = parser.currentToken();
|
|
|
switch (token) {
|
|
@@ -100,6 +151,8 @@ public final class Fuzziness implements ToXContentFragment, Writeable {
|
|
|
final String fuzziness = parser.text();
|
|
|
if (AUTO.asString().equalsIgnoreCase(fuzziness)) {
|
|
|
return AUTO;
|
|
|
+ } else if (fuzziness.toUpperCase(Locale.ROOT).startsWith(AUTO.asString() + ":")) {
|
|
|
+ return parseCustomAuto(fuzziness);
|
|
|
}
|
|
|
try {
|
|
|
final int minimumSimilarity = Integer.parseInt(fuzziness);
|
|
@@ -135,19 +188,19 @@ public final class Fuzziness implements ToXContentFragment, Writeable {
|
|
|
public int asDistance(String text) {
|
|
|
if (this.equals(AUTO)) { //AUTO
|
|
|
final int len = termLen(text);
|
|
|
- if (len <= 2) {
|
|
|
+ if (len < lowDistance) {
|
|
|
return 0;
|
|
|
- } else if (len > 5) {
|
|
|
- return 2;
|
|
|
- } else {
|
|
|
+ } else if (len < highDistance) {
|
|
|
return 1;
|
|
|
+ } else {
|
|
|
+ return 2;
|
|
|
}
|
|
|
}
|
|
|
return Math.min(2, (int) asFloat());
|
|
|
}
|
|
|
|
|
|
public float asFloat() {
|
|
|
- if (this.equals(AUTO)) {
|
|
|
+ if (this.equals(AUTO) || isAutoWithCustomValues()) {
|
|
|
return 1f;
|
|
|
}
|
|
|
return Float.parseFloat(fuzziness.toString());
|
|
@@ -158,9 +211,17 @@ public final class Fuzziness implements ToXContentFragment, Writeable {
|
|
|
}
|
|
|
|
|
|
public String asString() {
|
|
|
+ if (isAutoWithCustomValues()) {
|
|
|
+ return fuzziness.toString() + ":" + lowDistance + "," + highDistance;
|
|
|
+ }
|
|
|
return fuzziness.toString();
|
|
|
}
|
|
|
|
|
|
+ private boolean isAutoWithCustomValues() {
|
|
|
+ return fuzziness.startsWith("AUTO") && (lowDistance != DEFAULT_LOW_DISTANCE ||
|
|
|
+ highDistance != DEFAULT_HIGH_DISTANCE);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public boolean equals(Object obj) {
|
|
|
if (this == obj) {
|