From 14bfd51db0d1887b6c5f7235a1f535f7bd122603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 7 Sep 2018 10:43:39 +0200 Subject: [PATCH 1/8] Make Fuzziness class reject illegal values earlier The current java implementation of Fuzziness leaves a lot of room for initializing it with illegal values that either cause errors later when the queries reach the shards where they are executed or values that are silently ignored in favour of defaults. We should instead tighten the java implementation of the class so that we only accept supported values. Currently those are numeric values representing the edit distances 0, 1 and 2, optionally also as float or string, and the "AUTO" fuzziness, which can come in a cusomizable variant that allows specifying two value that define the positions in a term where the AUTO option increases the allowed edit distance. This change removes several redundant ways of object construction and adds input validation to the remaining ones. Java users should either use one of the predefined constants or use the static factory methods `fromEdits(int)` or `fromString(String)` to create instances of the class, while other ctors are hidden. This allows for instance control, e.g. returning one of the constants when creating instances from an integer value. Previously the class would accept any positive integer value and any float value, while in effect the maximum allowed edit distance was capped at 2 in practice. These values while throw an error now, as will any other String value other than "AUTO" that where previously accepted but led to numeric exceptions when the query was executed. --- .../elasticsearch/common/unit/Fuzziness.java | 149 +++++++-------- .../index/query/MatchQueryBuilder.java | 4 +- .../index/query/MultiMatchQueryBuilder.java | 10 +- .../index/search/QueryStringQueryParser.java | 12 +- .../common/unit/FuzzinessTests.java | 173 +++++++++++------- .../index/query/FuzzyQueryBuilderTests.java | 14 +- .../search/query/MultiMatchQueryIT.java | 3 +- .../test/AbstractQueryTestCase.java | 4 +- 8 files changed, 195 insertions(+), 174 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java b/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java index 6fc863ee9e4e6..eea53a9637abc 100644 --- a/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java +++ b/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java @@ -21,6 +21,7 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; @@ -40,40 +41,66 @@ public final class Fuzziness implements ToXContentFragment, Writeable { public static final String X_FIELD_NAME = "fuzziness"; - public static final Fuzziness ZERO = new Fuzziness(0); - public static final Fuzziness ONE = new Fuzziness(1); - public static final Fuzziness TWO = new Fuzziness(2); + public static final Fuzziness ZERO = new Fuzziness("0"); + public static final Fuzziness ONE = new Fuzziness("1"); + 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; + static final int DEFAULT_LOW_DISTANCE = 3; + 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) { - throw new IllegalArgumentException("Valid edit distances are [0, 1, 2] but was [" + fuzziness + "]"); - } - this.fuzziness = Integer.toString(fuzziness); + private Fuzziness(String fuzziness) { + this.fuzziness = fuzziness; } - private Fuzziness(String fuzziness) { - if (fuzziness == null || fuzziness.isEmpty()) { - throw new IllegalArgumentException("fuzziness can't be null!"); + /** + * Creates a {@link Fuzziness} instance from an edit distance. The value must be one of {@code [0, 1, 2]} + * Note: Using this method only makes sense if the field you are applying Fuzziness to is some sort of string. + * @throws IllegalArgumentException if the edit distance is not in [0, 1, 2] + */ + public static Fuzziness fromEdits(int edits) { + switch (edits) { + case 0: return Fuzziness.ZERO; + case 1: return Fuzziness.ONE; + case 2: return Fuzziness.TWO; + default: + throw new IllegalArgumentException("Valid edit distances are [0, 1, 2] but was [" + edits + "]"); } - 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 "); + /** + * Creates a {@link Fuzziness} instance from a String representation. This can + * either be an edit distance where the value must be one of + * {@code ["0", "1", "2"]} or "AUTO" for a fuzziness that depends on the term + * length. Using the "AUTO" fuzziness, you can optionally supply low and high + * distance arguments in the format {@code "AUTO:[low],[high]"}. See the query + * DSL documentation for more information about how these values affect the + * fuzziness value. + * Note: Using this method only makes sense if the field you + * are applying Fuzziness to is some sort of string. + */ + public static Fuzziness fromString(String fuzzinessString) { + if (Strings.isEmpty(fuzzinessString)) { + throw new IllegalArgumentException("fuzziness cannot be null or empty."); + } + String upperCase = fuzzinessString.toUpperCase(Locale.ROOT); + // check if it is one of the "AUTO" variants + if (upperCase.equals("AUTO")) { + return Fuzziness.AUTO; + } else if (upperCase.startsWith("AUTO:")) { + return parseCustomAuto(upperCase); + } else { + // should be a float or int representing a valid edit distance, otherwise throw error + try { + return fromEdits((int) Float.parseFloat(upperCase)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("fuzziness cannot be [" + fuzzinessString + "].", e); + } } - this.lowDistance = lowDistance; - this.highDistance = highDistance; } /** @@ -104,39 +131,23 @@ public void writeTo(StreamOutput out) throws IOException { } } - /** - * Creates a {@link Fuzziness} instance from an edit distance. The value must be one of {@code [0, 1, 2]} - * - * Note: Using this method only makes sense if the field you are applying Fuzziness to is some sort of string. - */ - public static Fuzziness fromEdits(int edits) { - return new Fuzziness(edits); - } - - public static Fuzziness build(Object fuzziness) { - if (fuzziness instanceof Fuzziness) { - return (Fuzziness) fuzziness; - } - 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(","); + private static Fuzziness parseCustomAuto(final String fuzzinessString) { + assert fuzzinessString.toUpperCase(Locale.ROOT).startsWith(AUTO.asString() + ":"); + String[] fuzzinessLimit = fuzzinessString.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); + if (lowerLimit < 0 || highLimit < 0 || lowerLimit > highLimit) { + throw new ElasticsearchParseException("fuzziness wrongly configured [{}]. Must be 0 < lower value <= higher value.", + fuzzinessString); + } + Fuzziness fuzziness = new Fuzziness("AUTO"); + fuzziness.lowDistance = lowerLimit; + fuzziness.highDistance = highLimit; + return fuzziness; } catch (NumberFormatException e) { - throw new ElasticsearchParseException("failed to parse [{}] as a \"auto:int,int\"", e, - string); + throw new ElasticsearchParseException("failed to parse [{}] as a \"auto:int,int\"", e, fuzzinessString); } } else { throw new ElasticsearchParseException("failed to find low and high distance values"); @@ -147,29 +158,9 @@ public static Fuzziness parse(XContentParser parser) throws IOException { XContentParser.Token token = parser.currentToken(); switch (token) { case VALUE_STRING: + return fromString(parser.text()); case VALUE_NUMBER: - 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); - switch (minimumSimilarity) { - case 0: - return ZERO; - case 1: - return ONE; - case 2: - return TWO; - default: - return build(fuzziness); - } - } catch (NumberFormatException ex) { - return build(fuzziness); - } - + return fromEdits(parser.intValue()); default: throw new IllegalArgumentException("Can't parse fuzziness on token: [" + token + "]"); } @@ -186,7 +177,7 @@ public int asDistance() { } public int asDistance(String text) { - if (this.equals(AUTO)) { //AUTO + if (this.fuzziness.equals("AUTO")) { final int len = termLen(text); if (len < lowDistance) { return 0; @@ -203,7 +194,7 @@ public float asFloat() { if (this.equals(AUTO) || isAutoWithCustomValues()) { return 1f; } - return Float.parseFloat(fuzziness.toString()); + return Float.parseFloat(fuzziness); } private int termLen(String text) { @@ -212,13 +203,13 @@ private int termLen(String text) { public String asString() { if (isAutoWithCustomValues()) { - return fuzziness.toString() + ":" + lowDistance + "," + highDistance; + return fuzziness + ":" + lowDistance + "," + highDistance; } - return fuzziness.toString(); + return fuzziness; } private boolean isAutoWithCustomValues() { - return fuzziness.startsWith("AUTO") && (lowDistance != DEFAULT_LOW_DISTANCE || + return fuzziness.equals("AUTO") && (lowDistance != DEFAULT_LOW_DISTANCE || highDistance != DEFAULT_HIGH_DISTANCE); } @@ -231,11 +222,13 @@ public boolean equals(Object obj) { return false; } Fuzziness other = (Fuzziness) obj; - return Objects.equals(fuzziness, other.fuzziness); + return Objects.equals(fuzziness, other.fuzziness) && + Objects.equals(lowDistance, other.lowDistance) && + Objects.equals(highDistance, other.highDistance); } @Override public int hashCode() { - return fuzziness.hashCode(); + return Objects.hash(fuzziness, lowDistance, highDistance); } } diff --git a/server/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java index dc2054eda4749..0a9eef7383f4c 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MatchQueryBuilder.java @@ -200,8 +200,8 @@ public String analyzer() { } /** Sets the fuzziness used when evaluated to a fuzzy query type. Defaults to "AUTO". */ - public MatchQueryBuilder fuzziness(Object fuzziness) { - this.fuzziness = Fuzziness.build(fuzziness); + public MatchQueryBuilder fuzziness(Fuzziness fuzziness) { + this.fuzziness = Objects.requireNonNull(fuzziness); return this; } diff --git a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java index 996e878dba85c..36705a6d36eb1 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java @@ -381,10 +381,8 @@ public int slop() { /** * Sets the fuzziness used when evaluated to a fuzzy query type. Defaults to "AUTO". */ - public MultiMatchQueryBuilder fuzziness(Object fuzziness) { - if (fuzziness != null) { - this.fuzziness = Fuzziness.build(fuzziness); - } + public MultiMatchQueryBuilder fuzziness(Fuzziness fuzziness) { + this.fuzziness = Objects.requireNonNull(fuzziness); return this; } @@ -722,7 +720,6 @@ public static MultiMatchQueryBuilder fromXContent(XContentParser parser) throws .type(type) .analyzer(analyzer) .cutoffFrequency(cutoffFrequency) - .fuzziness(fuzziness) .fuzzyRewrite(fuzzyRewrite) .useDisMax(useDisMax) .maxExpansions(maxExpansions) @@ -739,6 +736,9 @@ public static MultiMatchQueryBuilder fromXContent(XContentParser parser) throws if (lenient != null) { builder.lenient(lenient); } + if (fuzziness != null) { + builder.fuzziness(fuzziness); + } return builder; } diff --git a/server/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java b/server/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java index fa2fd033bee0d..80fe15467cb37 100644 --- a/server/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java +++ b/server/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java @@ -42,10 +42,10 @@ import org.apache.lucene.search.spans.SpanOrQuery; import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.unit.Fuzziness; +import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.FieldNamesFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -418,7 +418,7 @@ protected Query handleBareFuzzy(String field, Token fuzzySlop, String termImage) if (fuzzySlop.image.length() == 1) { return getFuzzyQuery(field, termImage, fuzziness.asDistance(termImage)); } - return getFuzzyQuery(field, termImage, Fuzziness.build(fuzzySlop.image.substring(1)).asFloat()); + return getFuzzyQuery(field, termImage, Fuzziness.fromString(fuzzySlop.image.substring(1)).asFloat()); } @Override @@ -429,7 +429,7 @@ protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) } List queries = new ArrayList<>(); for (Map.Entry entry : fields.entrySet()) { - Query q = getFuzzyQuerySingle(entry.getKey(), termStr, minSimilarity); + Query q = getFuzzyQuerySingle(entry.getKey(), termStr, (int) minSimilarity); assert q != null; queries.add(applyBoost(q, entry.getValue())); } @@ -442,7 +442,7 @@ protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) } } - private Query getFuzzyQuerySingle(String field, String termStr, float minSimilarity) throws ParseException { + private Query getFuzzyQuerySingle(String field, String termStr, int minSimilarity) throws ParseException { currentFieldType = context.fieldMapper(field); if (currentFieldType == null) { return newUnmappedFieldQuery(field); @@ -450,7 +450,7 @@ private Query getFuzzyQuerySingle(String field, String termStr, float minSimilar try { Analyzer normalizer = forceAnalyzer == null ? queryBuilder.context.getSearchAnalyzer(currentFieldType) : forceAnalyzer; BytesRef term = termStr == null ? null : normalizer.normalize(field, termStr); - return currentFieldType.fuzzyQuery(term, Fuzziness.fromEdits((int) minSimilarity), + return currentFieldType.fuzzyQuery(term, Fuzziness.fromEdits(minSimilarity), getFuzzyPrefixLength(), fuzzyMaxExpansions, fuzzyTranspositions); } catch (RuntimeException e) { if (lenient) { @@ -462,7 +462,7 @@ private Query getFuzzyQuerySingle(String field, String termStr, float minSimilar @Override protected Query newFuzzyQuery(Term term, float minimumSimilarity, int prefixLength) { - int numEdits = Fuzziness.build(minimumSimilarity).asDistance(term.text()); + int numEdits = Fuzziness.fromEdits((int) minimumSimilarity).asDistance(term.text()); FuzzyQuery query = new FuzzyQuery(term, numEdits, prefixLength, fuzzyMaxExpansions, fuzzyTranspositions); QueryParsers.setRewriteMethod(query, fuzzyRewriteMethod); diff --git a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java index 026c9a2e078a4..fa861a10f2abc 100644 --- a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java +++ b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.common.unit; +import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -32,37 +33,110 @@ import static org.hamcrest.CoreMatchers.sameInstance; public class FuzzinessTests extends ESTestCase { - public void testNumerics() { - String[] options = new String[]{"1.0", "1", "1.000000"}; - assertThat(Fuzziness.build(randomFrom(options)).asFloat(), equalTo(1f)); + + public void testNumericConstants() { + assertSame(Fuzziness.ZERO, Fuzziness.fromEdits(0)); + assertSame(Fuzziness.ZERO, Fuzziness.fromString("0")); + assertSame(Fuzziness.ZERO, Fuzziness.fromString("0.0")); + assertThat(Fuzziness.ZERO.asString(), equalTo("0")); + assertThat(Fuzziness.ZERO.asDistance(), equalTo(0)); + assertThat(Fuzziness.ZERO.asDistance(randomAlphaOfLengthBetween(0, randomIntBetween(1, 500))), equalTo(0)); + assertThat(Fuzziness.ZERO.asFloat(), equalTo(0.0f)); + + assertSame(Fuzziness.ONE, Fuzziness.fromEdits(1)); + assertSame(Fuzziness.ONE, Fuzziness.fromString("1")); + assertSame(Fuzziness.ONE, Fuzziness.fromString("1.0")); + assertThat(Fuzziness.ONE.asString(), equalTo("1")); + assertThat(Fuzziness.ONE.asDistance(), equalTo(1)); + assertThat(Fuzziness.ONE.asDistance(randomAlphaOfLengthBetween(0, randomIntBetween(1, 500))), equalTo(1)); + assertThat(Fuzziness.ONE.asFloat(), equalTo(1.0f)); + + assertSame(Fuzziness.TWO, Fuzziness.fromEdits(2)); + assertSame(Fuzziness.TWO, Fuzziness.fromString("2")); + assertSame(Fuzziness.TWO, Fuzziness.fromString("2.0")); + assertThat(Fuzziness.TWO.asString(), equalTo("2")); + assertThat(Fuzziness.TWO.asDistance(), equalTo(2)); + assertThat(Fuzziness.TWO.asDistance(randomAlphaOfLengthBetween(0, randomIntBetween(1, 500))), equalTo(2)); + assertThat(Fuzziness.TWO.asFloat(), equalTo(2.0f)); + } + + public void testAutoFuzziness() { + assertSame(Fuzziness.AUTO, Fuzziness.fromString("auto")); + assertSame(Fuzziness.AUTO, Fuzziness.fromString("AUTO")); + assertThat(Fuzziness.AUTO.asString(), equalTo("AUTO")); + assertThat(Fuzziness.AUTO.asDistance(), equalTo(1)); + assertThat(Fuzziness.AUTO.asDistance(randomAlphaOfLengthBetween(0, 2)), equalTo(0)); + assertThat(Fuzziness.AUTO.asDistance(randomAlphaOfLengthBetween(3, 5)), equalTo(1)); + assertThat(Fuzziness.AUTO.asDistance(randomAlphaOfLengthBetween(6, 100)), equalTo(2)); + assertThat(Fuzziness.AUTO.asFloat(), equalTo(1.0f)); + } + + public void testCustomAutoFuzziness() { + int lowDistance = randomIntBetween(1, 10); + int highDistance = randomIntBetween(lowDistance, 20); + String auto = randomFrom("auto", "AUTO"); + Fuzziness fuzziness = Fuzziness.fromString(auto + ":" + lowDistance + "," + highDistance); + assertNotSame(Fuzziness.AUTO, fuzziness); + if (lowDistance != Fuzziness.DEFAULT_LOW_DISTANCE || highDistance != Fuzziness.DEFAULT_HIGH_DISTANCE) { + assertThat(fuzziness.asString(), equalTo("AUTO:" + lowDistance + "," + highDistance)); + } + if (lowDistance > 5) { + assertThat(fuzziness.asDistance(), equalTo(0)); + } else if (highDistance > 5) { + assertThat(fuzziness.asDistance(), equalTo(1)); + } else { + assertThat(fuzziness.asDistance(), equalTo(2)); + } + assertThat(fuzziness.asDistance(randomAlphaOfLengthBetween(0, lowDistance - 1)), equalTo(0)); + if (lowDistance != highDistance) { + assertThat(fuzziness.asDistance(randomAlphaOfLengthBetween(lowDistance, highDistance - 1)), equalTo(1)); + } + assertThat(fuzziness.asDistance(randomAlphaOfLengthBetween(highDistance, 100)), equalTo(2)); + assertThat(fuzziness.asFloat(), equalTo(1.0f)); + } + + public void testFromEditsIllegalArgs() { + int illegalValue = randomValueOtherThanMany(i -> i >= 0 && i <= 2, () -> randomInt()); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromEdits(illegalValue)); + assertThat(e.getMessage(), equalTo("Valid edit distances are [0, 1, 2] but was [" + illegalValue + "]")); + } + + public void testFromStringIllegalArgs() { + Exception e = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString(null)); + assertThat(e.getMessage(), equalTo("fuzziness cannot be null or empty.")); + + e = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString("")); + assertThat(e.getMessage(), equalTo("fuzziness cannot be null or empty.")); + + e = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString("illegal")); + assertThat(e.getMessage(), equalTo("fuzziness cannot be [illegal].")); + + e = expectThrows(ElasticsearchParseException.class, () -> Fuzziness.fromString("AUTO:badFormat")); + assertThat(e.getMessage(), equalTo("failed to find low and high distance values")); } public void testParseFromXContent() throws IOException { final int iters = randomIntBetween(10, 50); for (int i = 0; i < iters; i++) { { - float floatValue = randomFloat(); + float floatValue = randomFrom(0.0f, 1.0f, 2.0f); XContentBuilder json = jsonBuilder().startObject() - .field(Fuzziness.X_FIELD_NAME, floatValue) + .field(Fuzziness.X_FIELD_NAME, randomBoolean() ? String.valueOf(floatValue) : floatValue) .endObject(); try (XContentParser parser = createParser(json)) { assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT)); assertThat(parser.nextToken(), equalTo(XContentParser.Token.FIELD_NAME)); - assertThat(parser.nextToken(), equalTo(XContentParser.Token.VALUE_NUMBER)); + assertThat(parser.nextToken(), + anyOf(equalTo(XContentParser.Token.VALUE_NUMBER), equalTo(XContentParser.Token.VALUE_STRING))); Fuzziness fuzziness = Fuzziness.parse(parser); assertThat(fuzziness.asFloat(), equalTo(floatValue)); assertThat(parser.nextToken(), equalTo(XContentParser.Token.END_OBJECT)); } } { - Integer intValue = frequently() ? randomIntBetween(0, 2) : randomIntBetween(0, 100); - Float floatRep = randomFloat(); - Number value = intValue; - if (randomBoolean()) { - value = Float.valueOf(floatRep += intValue); - } + int intValue = randomIntBetween(0, 2); XContentBuilder json = jsonBuilder().startObject() - .field(Fuzziness.X_FIELD_NAME, randomBoolean() ? value.toString() : value) + .field(Fuzziness.X_FIELD_NAME, randomBoolean() ? String.valueOf(intValue) : intValue) .endObject(); try (XContentParser parser = createParser(json)) { assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT)); @@ -70,24 +144,19 @@ public void testParseFromXContent() throws IOException { assertThat(parser.nextToken(), anyOf(equalTo(XContentParser.Token.VALUE_NUMBER), equalTo(XContentParser.Token.VALUE_STRING))); Fuzziness fuzziness = Fuzziness.parse(parser); - if (value.intValue() >= 1) { - assertThat(fuzziness.asDistance(), equalTo(Math.min(2, value.intValue()))); - } assertThat(parser.nextToken(), equalTo(XContentParser.Token.END_OBJECT)); - if (intValue.equals(value)) { - switch (intValue) { - case 1: - assertThat(fuzziness, sameInstance(Fuzziness.ONE)); - break; - case 2: - assertThat(fuzziness, sameInstance(Fuzziness.TWO)); - break; - case 0: - assertThat(fuzziness, sameInstance(Fuzziness.ZERO)); - break; - default: - break; - } + switch (intValue) { + case 1: + assertThat(fuzziness, sameInstance(Fuzziness.ONE)); + break; + case 2: + assertThat(fuzziness, sameInstance(Fuzziness.TWO)); + break; + case 0: + assertThat(fuzziness, sameInstance(Fuzziness.ZERO)); + break; + default: + break; } } } @@ -117,20 +186,6 @@ public void testParseFromXContent() throws IOException { } } } - - } - - public void testAuto() { - assertThat(Fuzziness.AUTO.asFloat(), equalTo(1f)); - } - - public void testAsDistance() { - final int iters = randomIntBetween(10, 50); - for (int i = 0; i < iters; i++) { - Integer integer = Integer.valueOf(randomIntBetween(0, 10)); - String value = "" + (randomBoolean() ? integer.intValue() : integer.floatValue()); - assertThat(Fuzziness.build(value).asDistance(), equalTo(Math.min(2, integer.intValue()))); - } } public void testSerialization() throws IOException { @@ -141,31 +196,15 @@ public void testSerialization() throws IOException { fuzziness = Fuzziness.fromEdits(randomIntBetween(0, 2)); deserializedFuzziness = doSerializeRoundtrip(fuzziness); assertEquals(fuzziness, deserializedFuzziness); - } - public void testSerializationDefaultAuto() throws IOException { - Fuzziness fuzziness = Fuzziness.AUTO; - Fuzziness deserializedFuzziness = doSerializeRoundtrip(fuzziness); - assertEquals(fuzziness, deserializedFuzziness); - assertEquals(fuzziness.asFloat(), deserializedFuzziness.asFloat(), 0f); - } + // custom AUTO + int lowDistance = randomIntBetween(1, 10); + int highDistance = randomIntBetween(lowDistance, 20); + fuzziness = Fuzziness.fromString("AUTO:" + lowDistance + "," + highDistance); - public void testSerializationCustomAuto() throws IOException { - String auto = "AUTO:4,7"; - XContentBuilder json = jsonBuilder().startObject() - .field(Fuzziness.X_FIELD_NAME, auto) - .endObject(); - - try (XContentParser parser = createParser(json)) { - assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT)); - assertThat(parser.nextToken(), equalTo(XContentParser.Token.FIELD_NAME)); - assertThat(parser.nextToken(), equalTo(XContentParser.Token.VALUE_STRING)); - Fuzziness fuzziness = Fuzziness.parse(parser); - - Fuzziness deserializedFuzziness = doSerializeRoundtrip(fuzziness); - assertEquals(fuzziness, deserializedFuzziness); - assertEquals(fuzziness.asString(), deserializedFuzziness.asString()); - } + deserializedFuzziness = doSerializeRoundtrip(fuzziness); + assertNotSame(fuzziness, deserializedFuzziness); + assertEquals(fuzziness, deserializedFuzziness); } private static Fuzziness doSerializeRoundtrip(Fuzziness in) throws IOException { diff --git a/server/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java index 0a0d567c7a468..d046f4f24d236 100644 --- a/server/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/FuzzyQueryBuilderTests.java @@ -33,7 +33,6 @@ import java.util.HashMap; import java.util.Map; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -95,15 +94,6 @@ public void testIllegalArguments() { assertEquals("query value cannot be null", e.getMessage()); } - public void testUnsupportedFuzzinessForStringType() throws IOException { - QueryShardContext context = createShardContext(); - context.setAllowUnmappedFields(true); - FuzzyQueryBuilder fuzzyQueryBuilder = new FuzzyQueryBuilder(STRING_FIELD_NAME, "text"); - fuzzyQueryBuilder.fuzziness(Fuzziness.build(randomFrom("a string which is not auto", "3h", "200s"))); - NumberFormatException e = expectThrows(NumberFormatException.class, () -> fuzzyQueryBuilder.toQuery(context)); - assertThat(e.getMessage(), containsString("For input string")); - } - public void testToQueryWithStringField() throws IOException { String query = "{\n" + " \"fuzzy\":{\n" + @@ -175,7 +165,7 @@ public void testToQueryWithStringFieldDefinedWrongFuzziness() throws IOException " }\n" + "}"; String msg2 = "fuzziness wrongly configured"; - IllegalArgumentException e2 = expectThrows(IllegalArgumentException.class, + ElasticsearchParseException e2 = expectThrows(ElasticsearchParseException.class, () -> parseQuery(queryHavingNegativeFuzzinessLowLimit).toQuery(createShardContext())); assertTrue(e2.getMessage() + " didn't contain: " + msg2 + " but: " + e.getMessage(), e.getMessage().contains (msg)); @@ -215,7 +205,7 @@ public void testToQueryWithNumericField() throws IOException { " \"fuzzy\":{\n" + " \"" + INT_FIELD_NAME + "\":{\n" + " \"value\":12,\n" + - " \"fuzziness\":5\n" + + " \"fuzziness\":2\n" + " }\n" + " }\n" + "}\n"; diff --git a/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java b/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java index 926839f2af22c..2c3f4011abbd1 100644 --- a/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java @@ -24,6 +24,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; @@ -693,7 +694,7 @@ public void testFuzzyFieldLevelBoosting() throws InterruptedException, Execution SearchResponse searchResponse = client().prepareSearch(idx) .setExplain(true) .setQuery(multiMatchQuery("foo").field("title", 100).field("body") - .fuzziness(0) + .fuzziness(Fuzziness.ZERO) ).get(); SearchHit[] hits = searchResponse.getHits().getHits(); assertNotEquals("both documents should be on different shards", hits[0].getShard().getShardId(), hits[1].getShard().getShardId()); diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java index cebd63dfa324c..8ce270271c79f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java @@ -702,11 +702,9 @@ protected static String getRandomRewriteMethod() { protected static Fuzziness randomFuzziness(String fieldName) { switch (fieldName) { case INT_FIELD_NAME: - return Fuzziness.build(randomIntBetween(3, 100)); case DOUBLE_FIELD_NAME: - return Fuzziness.build(1 + randomFloat() * 10); case DATE_FIELD_NAME: - return Fuzziness.build(randomTimeValue()); + return Fuzziness.fromEdits(randomIntBetween(0, 2)); default: if (randomBoolean()) { return Fuzziness.fromEdits(randomIntBetween(0, 2)); From 82546f2a515681de44c96161c442e1474863bf3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 28 Nov 2018 15:57:02 +0100 Subject: [PATCH 2/8] iter --- .../main/java/org/elasticsearch/common/unit/Fuzziness.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java b/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java index d12b1af0a1f3d..2170baba0d337 100644 --- a/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java +++ b/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java @@ -40,12 +40,14 @@ */ public final class Fuzziness implements ToXContentFragment, Writeable { - public static final String X_FIELD_NAME = "fuzziness"; + static final String X_FIELD_NAME = "fuzziness"; + public static final ParseField FIELD = new ParseField(X_FIELD_NAME); + public static final Fuzziness ZERO = new Fuzziness("0"); public static final Fuzziness ONE = new Fuzziness("1"); 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); + static final int DEFAULT_LOW_DISTANCE = 3; static final int DEFAULT_HIGH_DISTANCE = 6; From 6345af9b6c872b6fd47e0cea91b7ec322925a1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 29 Nov 2018 20:08:11 +0100 Subject: [PATCH 3/8] iter --- .../elasticsearch/common/unit/Fuzziness.java | 6 +++++- .../common/unit/FuzzinessTests.java | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java b/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java index 2170baba0d337..6f9bf8c588638 100644 --- a/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java +++ b/server/src/main/java/org/elasticsearch/common/unit/Fuzziness.java @@ -98,7 +98,11 @@ public static Fuzziness fromString(String fuzzinessString) { } else { // should be a float or int representing a valid edit distance, otherwise throw error try { - return fromEdits((int) Float.parseFloat(upperCase)); + float parsedFloat = Float.parseFloat(upperCase); + if (parsedFloat % 1 > 0) { + throw new IllegalArgumentException("fuzziness needs to be one of 0.0, 1.0 or 2.0 but was " + parsedFloat); + } + return fromEdits((int) parsedFloat); } catch (NumberFormatException e) { throw new IllegalArgumentException("fuzziness cannot be [" + fuzzinessString + "].", e); } diff --git a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java index c3d2d1f85799c..f5a3ee3f9cde5 100644 --- a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java +++ b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java @@ -34,6 +34,27 @@ public class FuzzinessTests extends ESTestCase { + public void testFromString() { + assertSame(Fuzziness.AUTO, Fuzziness.fromString("AUTO")); + assertSame(Fuzziness.AUTO, Fuzziness.fromString("auto")); + assertSame(Fuzziness.ZERO, Fuzziness.fromString("0")); + assertSame(Fuzziness.ZERO, Fuzziness.fromString("0.0")); + assertSame(Fuzziness.ONE, Fuzziness.fromString("1")); + assertSame(Fuzziness.ONE, Fuzziness.fromString("1.0")); + assertSame(Fuzziness.TWO, Fuzziness.fromString("2")); + assertSame(Fuzziness.TWO, Fuzziness.fromString("2.0")); + + // cases that should throw exceptions + IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString(null)); + assertEquals("fuzziness cannot be null or empty.", ex.getMessage()); + ex = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString("")); + assertEquals("fuzziness cannot be null or empty.", ex.getMessage()); + ex = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString("foo")); + assertEquals("fuzziness cannot be [foo].", ex.getMessage()); + ex = expectThrows(IllegalArgumentException.class, () -> Fuzziness.fromString("1.2")); + assertEquals("fuzziness needs to be one of 0.0, 1.0 or 2.0 but was 1.2", ex.getMessage()); + } + public void testNumericConstants() { assertSame(Fuzziness.ZERO, Fuzziness.fromEdits(0)); assertSame(Fuzziness.ZERO, Fuzziness.fromString("0")); From d53a57e064d8a323ad59320c0000ac593e4b94ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 30 Nov 2018 03:08:22 +0100 Subject: [PATCH 4/8] iter --- .../elasticsearch/index/query/QueryStringQueryBuilderTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java index 70f504516ec8a..6b974b1e073e0 100644 --- a/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java @@ -772,7 +772,7 @@ public void testToQueryFuzzyQueryAutoFuziness() throws Exception { } public void testFuzzyNumeric() throws Exception { - QueryStringQueryBuilder query = queryStringQuery("12~0.2").defaultField(INT_FIELD_NAME); + QueryStringQueryBuilder query = queryStringQuery("12~1.0").defaultField(INT_FIELD_NAME); QueryShardContext context = createShardContext(); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> query.toQuery(context)); From 93e2f056add4a65a4584bbfc850c90043bfa610e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Wed, 6 Mar 2019 11:15:19 +0100 Subject: [PATCH 5/8] iter --- .../elasticsearch/common/unit/FuzzinessTests.java | 10 +++++----- .../elasticsearch/search/query/SearchQueryIT.java | 12 +++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java index 44e1396e0fbf2..da6ae2986fa33 100644 --- a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java +++ b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java @@ -242,21 +242,21 @@ private static Fuzziness doSerializeRoundtrip(Fuzziness in) throws IOException { } public void testAsDistanceString() { - Fuzziness fuzziness = Fuzziness.build("0"); + Fuzziness fuzziness = Fuzziness.fromEdits(0); assertEquals(0, fuzziness.asDistance(randomAlphaOfLengthBetween(0, 10))); - fuzziness = Fuzziness.build("1"); + fuzziness = Fuzziness.fromEdits(1); assertEquals(1, fuzziness.asDistance(randomAlphaOfLengthBetween(0, 10))); - fuzziness = Fuzziness.build("2"); + fuzziness = Fuzziness.fromEdits(2); assertEquals(2, fuzziness.asDistance(randomAlphaOfLengthBetween(0, 10))); - fuzziness = Fuzziness.build("AUTO"); + fuzziness = Fuzziness.fromString("AUTO"); assertEquals(0, fuzziness.asDistance("")); assertEquals(0, fuzziness.asDistance("ab")); assertEquals(1, fuzziness.asDistance("abc")); assertEquals(1, fuzziness.asDistance("abcde")); assertEquals(2, fuzziness.asDistance("abcdef")); - fuzziness = Fuzziness.build("AUTO:5,7"); + fuzziness = Fuzziness.fromString("AUTO:5,7"); assertEquals(0, fuzziness.asDistance("")); assertEquals(0, fuzziness.asDistance("abcd")); assertEquals(1, fuzziness.asDistance("abcde")); diff --git a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java index 7007c7650f41b..5dc3874bcfa6d 100644 --- a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.time.DateFormatter; +import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; @@ -670,21 +671,22 @@ public void testMatchQueryFuzzy() throws Exception { indexRandom(true, client().prepareIndex("test", "_doc", "1").setSource("text", "Unit"), client().prepareIndex("test", "_doc", "2").setSource("text", "Unity")); - SearchResponse searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("0")).get(); + SearchResponse searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(0))) + .get(); assertHitCount(searchResponse, 0L); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("1")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromEdits(1))).get(); assertHitCount(searchResponse, 2L); assertSearchHits(searchResponse, "1", "2"); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("AUTO")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO"))).get(); assertHitCount(searchResponse, 2L); assertSearchHits(searchResponse, "1", "2"); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("AUTO:5,7")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.fromString("AUTO:5,7"))).get(); assertHitCount(searchResponse, 0L); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness("AUTO:5,7")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness(Fuzziness.fromString("AUTO:5,7"))).get(); assertHitCount(searchResponse, 1L); assertSearchHits(searchResponse, "2"); } From d250966f1a5ca6de72eeb8440cac33ae528edb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 4 Apr 2019 15:30:05 +0200 Subject: [PATCH 6/8] iter --- .../index/query/MatchBoolPrefixQueryBuilder.java | 4 ++-- .../elasticsearch/xpack/sql/querydsl/query/MatchQuery.java | 2 +- .../xpack/sql/querydsl/query/MultiMatchQuery.java | 2 +- .../xpack/sql/querydsl/query/QueryStringQuery.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java index 7f0c89f9df499..13132daab49d5 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java @@ -161,8 +161,8 @@ public String minimumShouldMatch() { } /** Sets the fuzziness used when evaluated to a fuzzy query type. Defaults to "AUTO". */ - public MatchBoolPrefixQueryBuilder fuzziness(Object fuzziness) { - this.fuzziness = Fuzziness.build(fuzziness); + public MatchBoolPrefixQueryBuilder fuzziness(Fuzziness fuzziness) { + this.fuzziness = fuzziness; return this; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java index 7bddacb86bf74..d9fac13e6a77d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java @@ -32,7 +32,7 @@ public class MatchQuery extends LeafQuery { appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); appliers.put("cutoff_frequency", (qb, s) -> qb.cutoffFrequency(Float.valueOf(s))); - appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.build(s))); + appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.fromString(s))); appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java index 2c6b47d7bdcc3..141dc4776a5ff 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java @@ -33,7 +33,7 @@ public class MultiMatchQuery extends LeafQuery { appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); appliers.put("cutoff_frequency", (qb, s) -> qb.cutoffFrequency(Float.valueOf(s))); - appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.build(s))); + appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.fromString(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java index a6d8ff2dbf5fc..94a9716b7cc8a 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java @@ -37,7 +37,7 @@ public class QueryStringQuery extends LeafQuery { appliers.put("default_operator", (qb, s) -> qb.defaultOperator(Operator.fromString(s))); appliers.put("enable_position_increments", (qb, s) -> qb.enablePositionIncrements(Booleans.parseBoolean(s))); appliers.put("escape", (qb, s) -> qb.escape(Booleans.parseBoolean(s))); - appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.build(s))); + appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.fromString(s))); appliers.put("fuzzy_max_expansions", (qb, s) -> qb.fuzzyMaxExpansions(Integer.valueOf(s))); appliers.put("fuzzy_prefix_length", (qb, s) -> qb.fuzzyPrefixLength(Integer.valueOf(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); From d63939546130d7cecd8102b5c9823269738792a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 4 Apr 2019 17:22:40 +0200 Subject: [PATCH 7/8] Adding migration notes for java API changes --- docs/reference/migration/migrate_8_0.asciidoc | 2 ++ .../migration/migrate_8_0/java.asciidoc | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 docs/reference/migration/migrate_8_0/java.asciidoc diff --git a/docs/reference/migration/migrate_8_0.asciidoc b/docs/reference/migration/migrate_8_0.asciidoc index 0c695a3b2bb47..55a5a621f664d 100644 --- a/docs/reference/migration/migrate_8_0.asciidoc +++ b/docs/reference/migration/migrate_8_0.asciidoc @@ -16,6 +16,7 @@ coming[8.0.0] * <> * <> * <> +* <> //NOTE: The notable-breaking-changes tagged regions are re-used in the //Installation and Upgrade Guide @@ -43,3 +44,4 @@ include::migrate_8_0/discovery.asciidoc[] include::migrate_8_0/mappings.asciidoc[] include::migrate_8_0/snapshots.asciidoc[] include::migrate_8_0/security.asciidoc[] +include::migrate_8_0/java.asciidoc[] diff --git a/docs/reference/migration/migrate_8_0/java.asciidoc b/docs/reference/migration/migrate_8_0/java.asciidoc new file mode 100644 index 0000000000000..589c6e7b43878 --- /dev/null +++ b/docs/reference/migration/migrate_8_0/java.asciidoc @@ -0,0 +1,20 @@ +[float] +[[breaking_80_java_changes]] +=== Java API changes + +[float] +==== Changes to Fuzziness + +To create `Fuzziness` instances, use the `fromString` and `fromEdits` method +instead of the `build` method that used to accept both Strings and numeric +values. Several fuzziness setters on query builders (e.g. +MatchQueryBuilder#fuzziness) now accept only a `Fuzziness`instance instead of +an Object. You should preferably use the available constants (e.g. +Fuzziness.ONE, Fuzziness.AUTO) or build your own instance using the above +mentioned factory methods. + +Fuzziness used to be lenient when it comes to parsing arbitrary numeric values +while silently truncating them to one of the three allowed edit distances 0, 1 +or 2. This leniency is now removed and the class will throw errors when trying +to construct an instance with another value (e.g. floats like 1.3 used to get +accepted but truncated to 1). You should use one of the allowed values. From 2dcfccca982b2fc4520238670369535d1e8db9ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Fri, 5 Apr 2019 00:21:02 +0200 Subject: [PATCH 8/8] iter --- x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec index 99aa07ec91f4d..cb410080e77bd 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec @@ -31,7 +31,7 @@ SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE QUERY('Man*', ' ; matchWithFuzziness -SELECT first_name, SCORE() FROM test_emp WHERE MATCH(first_name, 'geo', 'fuzziness=6'); +SELECT first_name, SCORE() FROM test_emp WHERE MATCH(first_name, 'geo', 'fuzziness=2'); first_name:s | SCORE():f ----------------+--------------- @@ -57,7 +57,7 @@ Shir |McClurg |8.210788 ; multiMatchWithFuzziness -SELECT first_name, last_name, SCORE() FROM test_emp WHERE MATCH('first_name^3,last_name^5', 'geo hir', 'fuzziness=5;operator=or') ORDER BY first_name; +SELECT first_name, last_name, SCORE() FROM test_emp WHERE MATCH('first_name^3,last_name^5', 'geo hir', 'fuzziness=2;operator=or') ORDER BY first_name; first_name:s | last_name:s | SCORE():f ----------------+-----------------+--------------- @@ -68,7 +68,7 @@ Uri |Lenart |4.105394 ; queryWithFuzziness -SELECT first_name, SCORE() FROM test_emp WHERE QUERY('geo~', 'fuzziness=5;default_field=first_name'); +SELECT first_name, SCORE() FROM test_emp WHERE QUERY('geo~', 'fuzziness=2;default_field=first_name'); first_name:s | SCORE():f ----------------+---------------