فهرست منبع

Add "did you mean" to ObjectParser (#50938)

Check it out:
```
$ curl -u elastic:password -HContent-Type:application/json -XPOST localhost:9200/test/_update/foo?pretty -d'{
  "dac": {}
}'

{
  "error" : {
    "root_cause" : [
      {
        "type" : "x_content_parse_exception",
        "reason" : "[2:3] [UpdateRequest] unknown field [dac] did you mean [doc]?"
      }
    ],
    "type" : "x_content_parse_exception",
    "reason" : "[2:3] [UpdateRequest] unknown field [dac] did you mean [doc]?"
  },
  "status" : 400
}
```

The tricky thing about implementing this is that x-content doesn't
depend on Lucene. So this works by creating an extension point for the
error message using SPI. Elasticsearch's server module provides the
"spell checking" implementation.
Nik Everett 5 سال پیش
والد
کامیت
5da5f44de4
25فایلهای تغییر یافته به همراه240 افزوده شده و 43 حذف شده
  1. 67 0
      libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ErrorOnUnknown.java
  2. 11 11
      libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java
  3. 1 1
      libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java
  4. 0 1
      modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java
  5. 13 0
      rest-api-spec/src/main/resources/rest-api-spec/test/update/90_error.yml
  6. 71 0
      server/src/main/java/org/elasticsearch/common/xcontent/SuggestingErrorOnUnknown.java
  7. 1 0
      server/src/main/resources/META-INF/services/org.elasticsearch.common.xcontent.ErrorOnUnknown
  8. 1 1
      server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestTests.java
  9. 2 2
      server/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java
  10. 46 0
      server/src/test/java/org/elasticsearch/common/xcontent/SuggestingErrorOnUnknownTests.java
  11. 6 6
      server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilderTests.java
  12. 1 1
      server/src/test/java/org/elasticsearch/search/rescore/QueryRescorerBuilderTests.java
  13. 1 1
      server/src/test/java/org/elasticsearch/search/sort/FieldSortBuilderTests.java
  14. 2 2
      server/src/test/java/org/elasticsearch/search/sort/ScriptSortBuilderTests.java
  15. 1 1
      server/src/test/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorTests.java
  16. 1 1
      test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java
  17. 1 1
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java
  18. 2 2
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/dataframe/DataFrameAnalyticsConfigTests.java
  19. 1 1
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/JobTests.java
  20. 1 1
      x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/process/ProcessResultsParserTests.java
  21. 1 1
      x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java
  22. 4 4
      x-pack/plugin/src/test/resources/rest-api-spec/test/ml/data_frame_analytics_crud.yml
  23. 1 1
      x-pack/plugin/src/test/resources/rest-api-spec/test/ml/datafeeds_crud.yml
  24. 1 1
      x-pack/plugin/src/test/resources/rest-api-spec/test/rollup/put_job.yml
  25. 3 3
      x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/text/TextTemplateTests.java

+ 67 - 0
libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ErrorOnUnknown.java

@@ -0,0 +1,67 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.xcontent;
+
+import java.util.ServiceLoader;
+
+/**
+ * Extension point to customize the error message for unknown fields. We expect
+ * Elasticsearch to plug a fancy implementation that uses Lucene's spelling
+ * correction infrastructure to suggest corrections.
+ */
+public interface ErrorOnUnknown {
+    /**
+     * The implementation of this interface that was loaded from SPI.
+     */
+    ErrorOnUnknown IMPLEMENTATION = findImplementation();
+
+    /**
+     * Build the error message to use when {@link ObjectParser} encounters an unknown field.
+     * @param parserName the name of the thing we're parsing
+     * @param unknownField the field that we couldn't recognize
+     * @param candidates the possible fields
+     */
+    String errorMessage(String parserName, String unknownField, Iterable<String> candidates);
+
+    /**
+     * Priority that this error message handler should be used.
+     */
+    int priority();
+
+    private static ErrorOnUnknown findImplementation() {
+        ErrorOnUnknown best = new ErrorOnUnknown() {
+            @Override
+            public String errorMessage(String parserName, String unknownField, Iterable<String> candidates) {
+                return "[" + parserName + "] unknown field [" + unknownField + "]";
+            }
+
+            @Override
+            public int priority() {
+                return Integer.MIN_VALUE;
+            }
+        };
+        for (ErrorOnUnknown c : ServiceLoader.load(ErrorOnUnknown.class)) {
+            if (best.priority() < c.priority()) {
+                best = c;
+            }
+        }
+        return best;
+    }
+}

+ 11 - 11
libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java

@@ -81,18 +81,17 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
     }
 
     private interface UnknownFieldParser<Value, Context> {
-
-        void acceptUnknownField(String parserName, String field, XContentLocation location, XContentParser parser,
-                                Value value, Context context) throws IOException;
+        void acceptUnknownField(ObjectParser<Value, Context> objectParser, String field, XContentLocation location, XContentParser parser,
+                Value value, Context context) throws IOException;
     }
 
     private static <Value, Context> UnknownFieldParser<Value, Context> ignoreUnknown() {
-      return (n, f, l, p, v, c) -> p.skipChildren();
+      return (op, f, l, p, v, c) -> p.skipChildren();
     }
 
     private static <Value, Context> UnknownFieldParser<Value, Context> errorOnUnknown() {
-        return (n, f, l, p, v, c) -> {
-            throw new XContentParseException(l, "[" + n + "] unknown field [" + f + "], parser not found");
+        return (op, f, l, p, v, c) -> {
+            throw new XContentParseException(l, ErrorOnUnknown.IMPLEMENTATION.errorMessage(op.name, f, op.fieldParserMap.keySet()));
         };
     }
 
@@ -104,7 +103,7 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
     }
 
     private static <Value, Context> UnknownFieldParser<Value, Context> consumeUnknownField(UnknownFieldConsumer<Value> consumer) {
-        return (parserName, field, location, parser, value, context) -> {
+        return (objectParser, field, location, parser, value, context) -> {
             XContentParser.Token t = parser.currentToken();
             switch (t) {
                 case VALUE_STRING:
@@ -127,7 +126,7 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
                     break;
                 default:
                     throw new XContentParseException(parser.getTokenLocation(),
-                        "[" + parserName + "] cannot parse field [" + field + "] with value type [" + t + "]");
+                        "[" + objectParser.name + "] cannot parse field [" + field + "] with value type [" + t + "]");
             }
         };
     }
@@ -136,12 +135,13 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
         Class<Category> categoryClass,
         BiConsumer<Value, ? super Category> consumer
     ) {
-        return (parserName, field, location, parser, value, context) -> {
+        return (objectParser, field, location, parser, value, context) -> {
             Category o;
             try {
                 o = parser.namedObject(categoryClass, field, context);
             } catch (NamedObjectNotFoundException e) {
-                throw new XContentParseException(location, "[" + parserName  + "] " + e.getBareMessage(), e);
+                // TODO It'd be lovely if we could the options here but we don't have the right stuff plumbed through. We'll get to it!
+                throw new XContentParseException(location, "[" + objectParser.name  + "] " + e.getBareMessage(), e);
             }
             consumer.accept(value, o);
         };
@@ -278,7 +278,7 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
                     throw new XContentParseException(parser.getTokenLocation(), "[" + name  + "] no field found");
                 }
                 if (fieldParser == null) {
-                    unknownFieldParser.acceptUnknownField(name, currentFieldName, currentPosition, parser, value, context);
+                    unknownFieldParser.acceptUnknownField(this, currentFieldName, currentPosition, parser, value, context);
                 } else {
                     fieldParser.assertSupports(name, parser, currentFieldName);
                     parseSub(parser, fieldParser, currentFieldName, value, context);

+ 1 - 1
libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java

@@ -206,7 +206,7 @@ public class ObjectParserTests extends ESTestCase {
         {
             XContentParser parser = createParser(JsonXContent.jsonXContent, "{\"not_supported_field\" : \"foo\"}");
             XContentParseException ex = expectThrows(XContentParseException.class, () -> objectParser.parse(parser, s, null));
-            assertEquals(ex.getMessage(), "[1:2] [the_parser] unknown field [not_supported_field], parser not found");
+            assertEquals(ex.getMessage(), "[1:2] [the_parser] unknown field [not_supported_field]");
         }
     }
 

+ 0 - 1
modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java

@@ -143,7 +143,6 @@ public class RatedRequestsTests extends ESTestCase {
                 exception = exception.getCause();
             }
             assertThat(exception.getMessage(), containsString("unknown field"));
-            assertThat(exception.getMessage(), containsString("parser not found"));
         }
     }
 

+ 13 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/update/90_error.yml

@@ -0,0 +1,13 @@
+---
+'Misspelled fields get "did you mean"':
+  - skip:
+      version: " - 7.99.99"
+      reason: Implemented in 8.0
+  - do:
+      catch: /\[UpdateRequest\] unknown field \[dac\] did you mean \[doc\]\?/
+      update:
+          index: test
+          id:    1
+          body:
+            dac:    { foo: baz }
+            upsert: { foo: bar }

+ 71 - 0
server/src/main/java/org/elasticsearch/common/xcontent/SuggestingErrorOnUnknown.java

@@ -0,0 +1,71 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.xcontent;
+
+import org.apache.lucene.search.spell.LevenshteinDistance;
+import org.apache.lucene.util.CollectionUtil;
+import org.elasticsearch.common.collect.Tuple;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import static java.util.stream.Collectors.toList;
+
+public class SuggestingErrorOnUnknown implements ErrorOnUnknown {
+    @Override
+    public String errorMessage(String parserName, String unknownField, Iterable<String> candidates) {
+        String message = String.format(Locale.ROOT, "[%s] unknown field [%s]", parserName, unknownField);
+        // TODO it'd be nice to combine this with BaseRestHandler's implementation.
+        LevenshteinDistance ld = new LevenshteinDistance();
+        final List<Tuple<Float, String>> scored = new ArrayList<>();
+        for (String candidate : candidates) {
+            float distance = ld.getDistance(unknownField, candidate);
+            if (distance > 0.5f) {
+                scored.add(new Tuple<>(distance, candidate));
+            }
+        }
+        if (scored.isEmpty()) {
+            return message;
+        }
+        CollectionUtil.timSort(scored, (a, b) -> {
+            // sort by distance in reverse order, then parameter name for equal distances
+            int compare = a.v1().compareTo(b.v1());
+            if (compare != 0) {
+                return -compare;
+            }
+            return a.v2().compareTo(b.v2());
+        });
+        List<String> keys = scored.stream().map(Tuple::v2).collect(toList());
+        StringBuilder builder = new StringBuilder(message).append(" did you mean ");
+        if (keys.size() == 1) {
+            builder.append("[").append(keys.get(0)).append("]");
+        } else {
+            builder.append("any of ").append(keys.toString());
+        }
+        builder.append("?");
+        return builder.toString();
+    }
+
+    @Override
+    public int priority() {
+        return 0;
+    }
+}

+ 1 - 0
server/src/main/resources/META-INF/services/org.elasticsearch.common.xcontent.ErrorOnUnknown

@@ -0,0 +1 @@
+org.elasticsearch.common.xcontent.SuggestingErrorOnUnknown

+ 1 - 1
server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestTests.java

@@ -56,7 +56,7 @@ public class ClusterUpdateSettingsRequestTests extends ESTestCase {
             XContentParseException iae = expectThrows(XContentParseException.class,
                     () -> ClusterUpdateSettingsRequest.fromXContent(createParser(xContentType.xContent(), mutated)));
             assertThat(iae.getMessage(),
-                    containsString("[cluster_update_settings_request] unknown field [" + unsupportedField + "], parser not found"));
+                    containsString("[cluster_update_settings_request] unknown field [" + unsupportedField + "]"));
         } else {
             try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) {
                 ClusterUpdateSettingsRequest parsedRequest = ClusterUpdateSettingsRequest.fromXContent(parser);

+ 2 - 2
server/src/test/java/org/elasticsearch/action/update/UpdateRequestTests.java

@@ -288,7 +288,7 @@ public class UpdateRequestTests extends ESTestCase {
                 .endObject());
 
         XContentParseException ex = expectThrows(XContentParseException.class, () -> request.fromXContent(contentParser));
-        assertEquals("[1:2] [UpdateRequest] unknown field [unknown_field], parser not found", ex.getMessage());
+        assertEquals("[1:2] [UpdateRequest] unknown field [unknown_field]", ex.getMessage());
 
         UpdateRequest request2 = new UpdateRequest("test", "1");
         XContentParser unknownObject = createParser(XContentFactory.jsonBuilder()
@@ -299,7 +299,7 @@ public class UpdateRequestTests extends ESTestCase {
                     .endObject()
                 .endObject());
         ex = expectThrows(XContentParseException.class, () -> request2.fromXContent(unknownObject));
-        assertEquals("[1:76] [UpdateRequest] unknown field [params], parser not found", ex.getMessage());
+        assertEquals("[1:76] [UpdateRequest] unknown field [params]", ex.getMessage());
     }
 
     public void testFetchSourceParsing() throws Exception {

+ 46 - 0
server/src/test/java/org/elasticsearch/common/xcontent/SuggestingErrorOnUnknownTests.java

@@ -0,0 +1,46 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.common.xcontent;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.util.Arrays;
+
+import static org.hamcrest.Matchers.equalTo;
+
+public class SuggestingErrorOnUnknownTests extends ESTestCase {
+    private String errorMessage(String unknownField, String... candidates) {
+        return new SuggestingErrorOnUnknown().errorMessage("test", unknownField, Arrays.asList(candidates));
+    }
+
+    public void testNoCandidates() {
+        assertThat(errorMessage("foo"), equalTo("[test] unknown field [foo]"));
+    }
+    public void testBadCandidates() {
+        assertThat(errorMessage("foo", "bar", "baz"), equalTo("[test] unknown field [foo]"));
+    }
+    public void testOneCandidate() {
+        assertThat(errorMessage("foo", "bar", "fop"), equalTo("[test] unknown field [foo] did you mean [fop]?"));
+    }
+    public void testManyCandidate() {
+        assertThat(errorMessage("foo", "bar", "fop", "fou", "baz"),
+                equalTo("[test] unknown field [foo] did you mean any of [fop, fou]?"));
+    }
+}

+ 6 - 6
server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilderTests.java

@@ -163,7 +163,7 @@ public class HighlightBuilderTests extends ESTestCase {
             XContentParseException e = expectParseThrows(XContentParseException.class, "{\n" +
                     "    \"bad_fieldname\" : [ \"field1\" 1 \"field2\" ]\n" +
                     "}\n");
-            assertEquals("[2:5] [highlight] unknown field [bad_fieldname], parser not found", e.getMessage());
+            assertEquals("[2:5] [highlight] unknown field [bad_fieldname]", e.getMessage());
         }
 
         {
@@ -176,7 +176,7 @@ public class HighlightBuilderTests extends ESTestCase {
                     "}\n");
             assertThat(e.getMessage(), containsString("[highlight] failed to parse field [fields]"));
             assertThat(e.getCause().getMessage(), containsString("[fields] failed to parse field [body]"));
-            assertEquals("[4:9] [highlight_field] unknown field [bad_fieldname], parser not found", e.getCause().getCause().getMessage());
+            assertEquals("[4:9] [highlight_field] unknown field [bad_fieldname]", e.getCause().getCause().getMessage());
         }
     }
 
@@ -194,7 +194,7 @@ public class HighlightBuilderTests extends ESTestCase {
             XContentParseException e = expectParseThrows(XContentParseException.class, "{\n" +
                     "    \"bad_fieldname\" : \"value\"\n" +
                     "}\n");
-            assertEquals("[2:5] [highlight] unknown field [bad_fieldname], parser not found", e.getMessage());
+            assertEquals("[2:5] [highlight] unknown field [bad_fieldname]", e.getMessage());
         }
 
         {
@@ -207,7 +207,7 @@ public class HighlightBuilderTests extends ESTestCase {
                     "}\n");
             assertThat(e.getMessage(), containsString("[highlight] failed to parse field [fields]"));
             assertThat(e.getCause().getMessage(), containsString("[fields] failed to parse field [body]"));
-            assertEquals("[4:9] [highlight_field] unknown field [bad_fieldname], parser not found", e.getCause().getCause().getMessage());
+            assertEquals("[4:9] [highlight_field] unknown field [bad_fieldname]", e.getCause().getCause().getMessage());
         }
     }
 
@@ -219,7 +219,7 @@ public class HighlightBuilderTests extends ESTestCase {
             XContentParseException e = expectParseThrows(XContentParseException.class, "{\n" +
                     "    \"bad_fieldname\" :  { \"field\" : \"value\" }\n \n" +
                     "}\n");
-            assertEquals("[2:5] [highlight] unknown field [bad_fieldname], parser not found", e.getMessage());
+            assertEquals("[2:5] [highlight] unknown field [bad_fieldname]", e.getMessage());
         }
 
         {
@@ -232,7 +232,7 @@ public class HighlightBuilderTests extends ESTestCase {
                     "}\n");
             assertThat(e.getMessage(), containsString("[highlight] failed to parse field [fields]"));
             assertThat(e.getCause().getMessage(), containsString("[fields] failed to parse field [body]"));
-            assertEquals("[4:9] [highlight_field] unknown field [bad_fieldname], parser not found", e.getCause().getCause().getMessage());
+            assertEquals("[4:9] [highlight_field] unknown field [bad_fieldname]", e.getCause().getCause().getMessage());
         }
     }
 

+ 1 - 1
server/src/test/java/org/elasticsearch/search/rescore/QueryRescorerBuilderTests.java

@@ -254,7 +254,7 @@ public class QueryRescorerBuilderTests extends ESTestCase {
             "}\n";
         try (XContentParser parser = createParser(rescoreElement)) {
             XContentParseException e = expectThrows(XContentParseException.class, () -> RescorerBuilder.parseFromXContent(parser));
-            assertEquals("[3:17] [query] unknown field [bad_fieldname], parser not found", e.getMessage());
+            assertEquals("[3:17] [query] unknown field [bad_fieldname]", e.getMessage());
         }
 
         rescoreElement = "{\n" +

+ 1 - 1
server/src/test/java/org/elasticsearch/search/sort/FieldSortBuilderTests.java

@@ -316,7 +316,7 @@ public class FieldSortBuilderTests extends AbstractSortTestCase<FieldSortBuilder
             parser.nextToken();
 
             XContentParseException e = expectThrows(XContentParseException.class, () -> FieldSortBuilder.fromXContent(parser, ""));
-            assertEquals("[1:18] [field_sort] unknown field [reverse], parser not found", e.getMessage());
+            assertEquals("[1:18] [field_sort] unknown field [reverse]", e.getMessage());
         }
     }
 

+ 2 - 2
server/src/test/java/org/elasticsearch/search/sort/ScriptSortBuilderTests.java

@@ -228,7 +228,7 @@ public class ScriptSortBuilderTests extends AbstractSortTestCase<ScriptSortBuild
             parser.nextToken();
 
             XContentParseException e = expectThrows(XContentParseException.class, () -> ScriptSortBuilder.fromXContent(parser, null));
-            assertEquals("[1:15] [_script] unknown field [bad_field], parser not found", e.getMessage());
+            assertEquals("[1:15] [_script] unknown field [bad_field]", e.getMessage());
         }
     }
 
@@ -241,7 +241,7 @@ public class ScriptSortBuilderTests extends AbstractSortTestCase<ScriptSortBuild
             parser.nextToken();
 
             XContentParseException e = expectThrows(XContentParseException.class, () -> ScriptSortBuilder.fromXContent(parser, null));
-            assertEquals("[1:15] [_script] unknown field [bad_field], parser not found", e.getMessage());
+            assertEquals("[1:15] [_script] unknown field [bad_field]", e.getMessage());
         }
     }
 

+ 1 - 1
server/src/test/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorTests.java

@@ -172,7 +172,7 @@ public class DirectCandidateGeneratorTests extends ESTestCase {
         // test unknown field
         directGenerator = "{ \"unknown_param\" : \"f1\" }";
         assertIllegalXContent(directGenerator, IllegalArgumentException.class,
-                "[direct_generator] unknown field [unknown_param], parser not found");
+                "[direct_generator] unknown field [unknown_param]");
 
         // test bad value for field (e.g. size expects an int)
         directGenerator = "{ \"size\" : \"xxl\" }";

+ 1 - 1
test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java

@@ -189,7 +189,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
                 if (expectedException == false) {
                     throw new AssertionError("unexpected exception when parsing query:\n" + testQuery, e);
                 }
-                assertThat(e.getMessage(), containsString("unknown field [newField], parser not found"));
+                assertThat(e.getMessage(), containsString("unknown field [newField]"));
             }
         }
     }

+ 1 - 1
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java

@@ -274,7 +274,7 @@ public class DatafeedConfigTests extends AbstractSerializingTestCase<DatafeedCon
                 .createParser(xContentRegistry(), DeprecationHandler.THROW_UNSUPPORTED_OPERATION, FUTURE_DATAFEED);
         XContentParseException e = expectThrows(XContentParseException.class,
                 () -> DatafeedConfig.STRICT_PARSER.apply(parser, null).build());
-        assertEquals("[6:5] [datafeed_config] unknown field [tomorrows_technology_today], parser not found", e.getMessage());
+        assertEquals("[6:5] [datafeed_config] unknown field [tomorrows_technology_today]", e.getMessage());
     }
 
     public void testPastQueryConfigParse() throws IOException {

+ 2 - 2
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/dataframe/DataFrameAnalyticsConfigTests.java

@@ -328,7 +328,7 @@ public class DataFrameAnalyticsConfigTests extends AbstractSerializingTestCase<D
                  XContentFactory.xContent(XContentType.JSON).createParser(
                      xContentRegistry(), DeprecationHandler.THROW_UNSUPPORTED_OPERATION, json)) {
             Exception e = expectThrows(IllegalArgumentException.class, () -> DataFrameAnalyticsConfig.STRICT_PARSER.apply(parser, null));
-            assertThat(e.getMessage(), containsString("unknown field [create_time], parser not found"));
+            assertThat(e.getMessage(), containsString("unknown field [create_time]"));
         }
     }
 
@@ -343,7 +343,7 @@ public class DataFrameAnalyticsConfigTests extends AbstractSerializingTestCase<D
                  XContentFactory.xContent(XContentType.JSON).createParser(
                      xContentRegistry(), DeprecationHandler.THROW_UNSUPPORTED_OPERATION, json)) {
             Exception e = expectThrows(IllegalArgumentException.class, () -> DataFrameAnalyticsConfig.STRICT_PARSER.apply(parser, null));
-            assertThat(e.getMessage(), containsString("unknown field [version], parser not found"));
+            assertThat(e.getMessage(), containsString("unknown field [version]"));
         }
     }
 

+ 1 - 1
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/JobTests.java

@@ -81,7 +81,7 @@ public class JobTests extends AbstractSerializingTestCase<Job> {
                 .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, FUTURE_JOB);
         XContentParseException e = expectThrows(XContentParseException.class,
                 () -> Job.STRICT_PARSER.apply(parser, null).build());
-        assertEquals("[4:5] [job_details] unknown field [tomorrows_technology_today], parser not found", e.getMessage());
+        assertEquals("[4:5] [job_details] unknown field [tomorrows_technology_today]", e.getMessage());
     }
 
     public void testFutureMetadataParse() throws IOException {

+ 1 - 1
x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/process/ProcessResultsParserTests.java

@@ -41,7 +41,7 @@ public class ProcessResultsParserTests extends ESTestCase {
             XContentParseException e = expectThrows(XContentParseException.class,
                 () -> parser.parseResults(inputStream).forEachRemaining(a -> {
                 }));
-            assertEquals("[1:3] [test_result] unknown field [unknown], parser not found", e.getMessage());
+            assertEquals("[1:3] [test_result] unknown field [unknown]", e.getMessage());
         }
     }
 

+ 1 - 1
x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java

@@ -350,7 +350,7 @@ public abstract class RestSqlTestCase extends BaseRestSqlTestCase implements Err
         expectBadRequest(() -> {
                 client().performRequest(request);
                 return Collections.emptyMap();
-            }, containsString("unknown field [columnar], parser not found"));
+            }, containsString("unknown field [columnar]"));
     }
 
     public static void expectBadRequest(CheckedSupplier<Map<String, Object>, Exception> code, Matcher<String> errorMessageMatcher) {

+ 4 - 4
x-pack/plugin/src/test/resources/rest-api-spec/test/ml/data_frame_analytics_crud.yml

@@ -87,7 +87,7 @@ setup:
 ---
 "Test put config with security headers in the body":
   - do:
-      catch: /unknown field \[headers\], parser not found/
+      catch: /unknown field \[headers\]/
       ml.put_data_frame_analytics:
         id: "data_frame_with_header"
         body:  >
@@ -107,7 +107,7 @@ setup:
 "Test put config with create_time in the body":
 
   - do:
-      catch: /unknown field \[create_time\], parser not found/
+      catch: /unknown field \[create_time\]/
       ml.put_data_frame_analytics:
         id: "data_frame_with_create_time"
         body: >
@@ -126,7 +126,7 @@ setup:
 "Test put config with version in the body":
 
   - do:
-      catch: /unknown field \[version\], parser not found/
+      catch: /unknown field \[version\]/
       ml.put_data_frame_analytics:
         id: "data_frame_with_version"
         body: >
@@ -443,7 +443,7 @@ setup:
 "Test put config with unknown top level field":
 
   - do:
-      catch: /unknown field \[unknown_field\], parser not found/
+      catch: /unknown field \[unknown_field\]/
       ml.put_data_frame_analytics:
         id: "unknown_field"
         body: >

+ 1 - 1
x-pack/plugin/src/test/resources/rest-api-spec/test/ml/datafeeds_crud.yml

@@ -86,7 +86,7 @@ setup:
 ---
 "Test put datafeed with security headers in the body":
   - do:
-      catch: /unknown field \[headers\], parser not found/
+      catch: /unknown field \[headers\]/
       ml.put_datafeed:
         datafeed_id: test-datafeed-1
         body:  >

+ 1 - 1
x-pack/plugin/src/test/resources/rest-api-spec/test/rollup/put_job.yml

@@ -170,7 +170,7 @@ setup:
 "Try to include headers":
 
   - do:
-      catch: /unknown field \[headers\], parser not found/
+      catch: /unknown field \[headers\]/
       headers:
         Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser
       rollup.put_job:

+ 3 - 3
x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/text/TextTemplateTests.java

@@ -196,7 +196,7 @@ public class TextTemplateTests extends ESTestCase {
             TextTemplate.parse(parser);
             fail("expected parse exception when encountering an unknown field");
         } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), containsString("[script] unknown field [unknown_field], parser not found"));
+            assertThat(e.getMessage(), containsString("[script] unknown field [unknown_field]"));
         }
     }
 
@@ -210,7 +210,7 @@ public class TextTemplateTests extends ESTestCase {
         XContentParser parser = createParser(JsonXContent.jsonXContent, bytes);
         parser.nextToken();
         XContentParseException ex = expectThrows(XContentParseException.class, () -> TextTemplate.parse(parser));
-        assertEquals("[1:2] [script] unknown field [template], parser not found", ex.getMessage());
+        assertEquals("[1:2] [script] unknown field [template]", ex.getMessage());
     }
 
     public void testParserInvalidMissingText() throws Exception {
@@ -222,7 +222,7 @@ public class TextTemplateTests extends ESTestCase {
         XContentParser parser = createParser(JsonXContent.jsonXContent, bytes);
         parser.nextToken();
         XContentParseException ex = expectThrows(XContentParseException.class, () -> TextTemplate.parse(parser));
-        assertEquals("[1:2] [script] unknown field [type], parser not found", ex.getMessage());
+        assertEquals("[1:2] [script] unknown field [type]", ex.getMessage());
     }
 
     public void testNullObject() throws Exception {