Browse Source

Deprecate types in explain requests. (#35611)

The following updates were made:
- Add a new untyped endpoint `{index}/_explain/{id}`.
- Add deprecation warnings to Rest*Action, plus tests in Rest*ActionTests.
- For each REST yml test, make sure there is one version without types, and another legacy version that retains types (called *_with_types.yml).
- Deprecate relevant methods on the Java HLRC requests/ responses.
- Update documentation (for both the REST API and Java HLRC).
Julie Tibshirani 6 years ago
parent
commit
87831051dc
21 changed files with 450 additions and 112 deletions
  1. 4 2
      client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java
  2. 18 9
      client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java
  3. 25 25
      client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java
  4. 33 35
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java
  5. 7 8
      docs/java-rest/high-level/search/explain.asciidoc
  6. 2 2
      docs/reference/search/explain.asciidoc
  7. 0 1
      modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/40_query_string.yml
  8. 9 0
      qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/FullClusterRestartIT.java
  9. 2 3
      rest-api-spec/src/main/resources/rest-api-spec/api/explain.json
  10. 7 6
      rest-api-spec/src/main/resources/rest-api-spec/test/explain/10_basic.yml
  11. 65 0
      rest-api-spec/src/main/resources/rest-api-spec/test/explain/11_basic_with_types.yml
  12. 12 8
      rest-api-spec/src/main/resources/rest-api-spec/test/explain/20_source_filtering.yml
  13. 44 0
      rest-api-spec/src/main/resources/rest-api-spec/test/explain/21_source_filtering_with_types.yml
  14. 6 7
      rest-api-spec/src/main/resources/rest-api-spec/test/explain/30_query_string.yml
  15. 70 0
      rest-api-spec/src/main/resources/rest-api-spec/test/explain/31_query_string_with_types.yml
  16. 23 1
      server/src/main/java/org/elasticsearch/action/explain/ExplainRequest.java
  17. 4 0
      server/src/main/java/org/elasticsearch/action/explain/ExplainResponse.java
  18. 19 1
      server/src/main/java/org/elasticsearch/rest/action/search/RestExplainAction.java
  19. 49 0
      server/src/test/java/org/elasticsearch/rest/action/search/RestExplainActionTests.java
  20. 47 0
      test/framework/src/main/java/org/elasticsearch/test/rest/TypesRemovalWarningsHandler.java
  21. 4 4
      x-pack/plugin/security/src/test/java/org/elasticsearch/integration/IndexPrivilegeTests.java

+ 4 - 2
client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

@@ -462,8 +462,10 @@ final class RequestConverters {
     }
 
     static Request explain(ExplainRequest explainRequest) throws IOException {
-        Request request = new Request(HttpGet.METHOD_NAME,
-            endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain"));
+        String endpoint = explainRequest.isTypeless()
+            ? endpoint(explainRequest.index(), "_explain", explainRequest.id())
+            : endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain");
+        Request request = new Request(HttpGet.METHOD_NAME, endpoint);
 
         Params params = new Params(request);
         params.withStoredFields(explainRequest.storedFields());

+ 18 - 9
client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java

@@ -1225,10 +1225,9 @@ public class RequestConvertersTests extends ESTestCase {
 
     public void testExplain() throws IOException {
         String index = randomAlphaOfLengthBetween(3, 10);
-        String type = randomAlphaOfLengthBetween(3, 10);
         String id = randomAlphaOfLengthBetween(3, 10);
 
-        ExplainRequest explainRequest = new ExplainRequest(index, type, id);
+        ExplainRequest explainRequest = new ExplainRequest(index, id);
         explainRequest.query(QueryBuilders.termQuery(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)));
 
         Map<String, String> expectedParams = new HashMap<>();
@@ -1254,18 +1253,28 @@ public class RequestConvertersTests extends ESTestCase {
         }
 
         Request request = RequestConverters.explain(explainRequest);
-        StringJoiner endpoint = new StringJoiner("/", "/", "");
-        endpoint.add(index)
-            .add(type)
-            .add(id)
-            .add("_explain");
-
         assertEquals(HttpGet.METHOD_NAME, request.getMethod());
-        assertEquals(endpoint.toString(), request.getEndpoint());
+        assertEquals("/" + index + "/_explain/" + id, request.getEndpoint());
+
         assertEquals(expectedParams, request.getParameters());
         assertToXContentBody(explainRequest, request.getEntity());
     }
 
+    public void testExplainWithType() throws IOException {
+        String index = randomAlphaOfLengthBetween(3, 10);
+        String type = randomAlphaOfLengthBetween(3, 10);
+        String id = randomAlphaOfLengthBetween(3, 10);
+
+        ExplainRequest explainRequest = new ExplainRequest(index, type, id);
+        explainRequest.query(QueryBuilders.termQuery(randomAlphaOfLengthBetween(3, 10), randomAlphaOfLengthBetween(3, 10)));
+
+        Request request = RequestConverters.explain(explainRequest);
+        assertEquals(HttpGet.METHOD_NAME, request.getMethod());
+        assertEquals("/" + index + "/" + type + "/" + id + "/_explain", request.getEndpoint());
+
+        assertToXContentBody(explainRequest, request.getEntity());
+    }
+
     public void testTermVectors() throws IOException {
         String index = randomAlphaOfLengthBetween(3, 10);
         String id = randomAlphaOfLengthBetween(3, 10);

+ 25 - 25
client/rest-high-level/src/test/java/org/elasticsearch/client/SearchIT.java

@@ -118,10 +118,10 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
         }
 
         {
-            Request doc1 = new Request(HttpPut.METHOD_NAME, "/index1/doc/1");
+            Request doc1 = new Request(HttpPut.METHOD_NAME, "/index1/_doc/1");
             doc1.setJsonEntity("{\"field\":\"value1\", \"rating\": 7}");
             client().performRequest(doc1);
-            Request doc2 = new Request(HttpPut.METHOD_NAME, "/index1/doc/2");
+            Request doc2 = new Request(HttpPut.METHOD_NAME, "/index1/_doc/2");
             doc2.setJsonEntity("{\"field\":\"value2\"}");
             client().performRequest(doc2);
         }
@@ -131,7 +131,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             create.setJsonEntity(
                 "{" +
                 "  \"mappings\": {" +
-                "    \"doc\": {" +
+                "    \"_doc\": {" +
                 "      \"properties\": {" +
                 "        \"rating\": {" +
                 "          \"type\":  \"keyword\"" +
@@ -141,19 +141,19 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
                 "  }" +
                 "}");
             client().performRequest(create);
-            Request doc3 = new Request(HttpPut.METHOD_NAME, "/index2/doc/3");
+            Request doc3 = new Request(HttpPut.METHOD_NAME, "/index2/_doc/3");
             doc3.setJsonEntity("{\"field\":\"value1\", \"rating\": \"good\"}");
             client().performRequest(doc3);
-            Request doc4 = new Request(HttpPut.METHOD_NAME, "/index2/doc/4");
+            Request doc4 = new Request(HttpPut.METHOD_NAME, "/index2/_doc/4");
             doc4.setJsonEntity("{\"field\":\"value2\"}");
             client().performRequest(doc4);
         }
 
         {
-            Request doc5 = new Request(HttpPut.METHOD_NAME, "/index3/doc/5");
+            Request doc5 = new Request(HttpPut.METHOD_NAME, "/index3/_doc/5");
             doc5.setJsonEntity("{\"field\":\"value1\"}");
             client().performRequest(doc5);
-            Request doc6 = new Request(HttpPut.METHOD_NAME, "/index3/doc/6");
+            Request doc6 = new Request(HttpPut.METHOD_NAME, "/index3/_doc/6");
             doc6.setJsonEntity("{\"field\":\"value2\"}");
             client().performRequest(doc6);
         }
@@ -163,7 +163,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             create.setJsonEntity(
                     "{" +
                     "  \"mappings\": {" +
-                    "    \"doc\": {" +
+                    "    \"_doc\": {" +
                     "      \"properties\": {" +
                     "        \"field1\": {" +
                     "          \"type\":  \"keyword\"," +
@@ -178,7 +178,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
                     "  }" +
                     "}");
             client().performRequest(create);
-            Request doc1 = new Request(HttpPut.METHOD_NAME, "/index4/doc/1");
+            Request doc1 = new Request(HttpPut.METHOD_NAME, "/index4/_doc/1");
             doc1.setJsonEntity("{\"field1\":\"value1\", \"field2\":\"value2\"}");
             client().performRequest(doc1);
 
@@ -1010,13 +1010,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
 
     public void testExplain() throws IOException {
         {
-            ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index1", "1");
             explainRequest.query(QueryBuilders.matchAllQuery());
 
             ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
 
             assertThat(explainResponse.getIndex(), equalTo("index1"));
-            assertThat(explainResponse.getType(), equalTo("doc"));
+            assertThat(explainResponse.getType(), equalTo("_doc"));
             assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
             assertTrue(explainResponse.isExists());
             assertTrue(explainResponse.isMatch());
@@ -1025,13 +1025,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             assertNull(explainResponse.getGetResult());
         }
         {
-            ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index1", "1");
             explainRequest.query(QueryBuilders.termQuery("field", "value1"));
 
             ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
 
             assertThat(explainResponse.getIndex(), equalTo("index1"));
-            assertThat(explainResponse.getType(), equalTo("doc"));
+            assertThat(explainResponse.getType(), equalTo("_doc"));
             assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
             assertTrue(explainResponse.isExists());
             assertTrue(explainResponse.isMatch());
@@ -1040,13 +1040,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             assertNull(explainResponse.getGetResult());
         }
         {
-            ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index1", "1");
             explainRequest.query(QueryBuilders.termQuery("field", "value2"));
 
             ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
 
             assertThat(explainResponse.getIndex(), equalTo("index1"));
-            assertThat(explainResponse.getType(), equalTo("doc"));
+            assertThat(explainResponse.getType(), equalTo("_doc"));
             assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
             assertTrue(explainResponse.isExists());
             assertFalse(explainResponse.isMatch());
@@ -1054,7 +1054,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             assertNull(explainResponse.getGetResult());
         }
         {
-            ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index1", "1");
             explainRequest.query(QueryBuilders.boolQuery()
                 .must(QueryBuilders.termQuery("field", "value1"))
                 .must(QueryBuilders.termQuery("field", "value2")));
@@ -1062,7 +1062,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
 
             assertThat(explainResponse.getIndex(), equalTo("index1"));
-            assertThat(explainResponse.getType(), equalTo("doc"));
+            assertThat(explainResponse.getType(), equalTo("_doc"));
             assertThat(Integer.valueOf(explainResponse.getId()), equalTo(1));
             assertTrue(explainResponse.isExists());
             assertFalse(explainResponse.isMatch());
@@ -1074,7 +1074,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
 
     public void testExplainNonExistent() throws IOException {
         {
-            ExplainRequest explainRequest = new ExplainRequest("non_existent_index", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("non_existent_index", "1");
             explainRequest.query(QueryBuilders.matchQuery("field", "value"));
             ElasticsearchException exception = expectThrows(ElasticsearchException.class,
                 () -> execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync));
@@ -1084,13 +1084,13 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
                 containsString("Elasticsearch exception [type=index_not_found_exception, reason=no such index [non_existent_index]]"));
         }
         {
-            ExplainRequest explainRequest = new ExplainRequest("index1", "doc", "999");
+            ExplainRequest explainRequest = new ExplainRequest("index1", "999");
             explainRequest.query(QueryBuilders.matchQuery("field", "value1"));
 
             ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);
 
             assertThat(explainResponse.getIndex(), equalTo("index1"));
-            assertThat(explainResponse.getType(), equalTo("doc"));
+            assertThat(explainResponse.getType(), equalTo("_doc"));
             assertThat(explainResponse.getId(), equalTo("999"));
             assertFalse(explainResponse.isExists());
             assertFalse(explainResponse.isMatch());
@@ -1101,7 +1101,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
 
     public void testExplainWithStoredFields() throws IOException {
         {
-            ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index4", "1");
             explainRequest.query(QueryBuilders.matchAllQuery());
             explainRequest.storedFields(new String[]{"field1"});
 
@@ -1117,7 +1117,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             assertTrue(explainResponse.getGetResult().isSourceEmpty());
         }
         {
-            ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index4", "1");
             explainRequest.query(QueryBuilders.matchAllQuery());
             explainRequest.storedFields(new String[]{"field1", "field2"});
 
@@ -1137,7 +1137,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
 
     public void testExplainWithFetchSource() throws IOException {
         {
-            ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index4", "1");
             explainRequest.query(QueryBuilders.matchAllQuery());
             explainRequest.fetchSourceContext(new FetchSourceContext(true, new String[]{"field1"}, null));
 
@@ -1151,7 +1151,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
             assertThat(explainResponse.getGetResult().getSource(), equalTo(Collections.singletonMap("field1", "value1")));
         }
         {
-            ExplainRequest explainRequest = new ExplainRequest("index4", "doc", "1");
+            ExplainRequest explainRequest = new ExplainRequest("index4", "1");
             explainRequest.query(QueryBuilders.matchAllQuery());
             explainRequest.fetchSourceContext(new FetchSourceContext(true, null, new String[] {"field2"}));
 
@@ -1167,7 +1167,7 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
     }
 
     public void testExplainWithAliasFilter() throws IOException {
-        ExplainRequest explainRequest = new ExplainRequest("alias4", "doc", "1");
+        ExplainRequest explainRequest = new ExplainRequest("alias4", "1");
         explainRequest.query(QueryBuilders.matchAllQuery());
 
         ExplainResponse explainResponse = execute(explainRequest, highLevelClient()::explain, highLevelClient()::explainAsync);

+ 33 - 35
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java

@@ -305,11 +305,11 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
         RestHighLevelClient client = highLevelClient();
         {
             BulkRequest request = new BulkRequest();
-            request.add(new IndexRequest("posts", "doc", "1")
+            request.add(new IndexRequest("posts", "_doc", "1")
                     .source(XContentType.JSON, "company", "Elastic", "age", 20));
-            request.add(new IndexRequest("posts", "doc", "2")
+            request.add(new IndexRequest("posts", "_doc", "2")
                     .source(XContentType.JSON, "company", "Elastic", "age", 30));
-            request.add(new IndexRequest("posts", "doc", "3")
+            request.add(new IndexRequest("posts", "_doc", "3")
                     .source(XContentType.JSON, "company", "Elastic", "age", 40));
             request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
             BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
@@ -381,10 +381,10 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
         RestHighLevelClient client = highLevelClient();
         {
             BulkRequest request = new BulkRequest();
-            request.add(new IndexRequest("posts", "doc", "1").source(XContentType.JSON, "user", "kimchy"));
-            request.add(new IndexRequest("posts", "doc", "2").source(XContentType.JSON, "user", "javanna"));
-            request.add(new IndexRequest("posts", "doc", "3").source(XContentType.JSON, "user", "tlrx"));
-            request.add(new IndexRequest("posts", "doc", "4").source(XContentType.JSON, "user", "cbuescher"));
+            request.add(new IndexRequest("posts", "_doc", "1").source(XContentType.JSON, "user", "kimchy"));
+            request.add(new IndexRequest("posts", "_doc", "2").source(XContentType.JSON, "user", "javanna"));
+            request.add(new IndexRequest("posts", "_doc", "3").source(XContentType.JSON, "user", "tlrx"));
+            request.add(new IndexRequest("posts", "_doc", "4").source(XContentType.JSON, "user", "cbuescher"));
             request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
             BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
             assertSame(RestStatus.OK, bulkResponse.status());
@@ -424,13 +424,13 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
         RestHighLevelClient client = highLevelClient();
         {
             BulkRequest request = new BulkRequest();
-            request.add(new IndexRequest("posts", "doc", "1")
+            request.add(new IndexRequest("posts", "_doc", "1")
                     .source(XContentType.JSON, "title", "In which order are my Elasticsearch queries executed?", "user",
                             Arrays.asList("kimchy", "luca"), "innerObject", Collections.singletonMap("key", "value")));
-            request.add(new IndexRequest("posts", "doc", "2")
+            request.add(new IndexRequest("posts", "_doc", "2")
                     .source(XContentType.JSON, "title", "Current status and upcoming changes in Elasticsearch", "user",
                             Arrays.asList("kimchy", "christoph"), "innerObject", Collections.singletonMap("key", "value")));
-            request.add(new IndexRequest("posts", "doc", "3")
+            request.add(new IndexRequest("posts", "_doc", "3")
                     .source(XContentType.JSON, "title", "The Future of Federated Search in Elasticsearch", "user",
                             Arrays.asList("kimchy", "tanguy"), "innerObject", Collections.singletonMap("key", "value")));
             request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
@@ -487,7 +487,7 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
     public void testSearchRequestProfiling() throws IOException {
         RestHighLevelClient client = highLevelClient();
         {
-            IndexRequest request = new IndexRequest("posts", "doc", "1")
+            IndexRequest request = new IndexRequest("posts", "_doc", "1")
                     .source(XContentType.JSON, "tags", "elasticsearch", "comments", 123);
             request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
             IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
@@ -559,11 +559,11 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
         RestHighLevelClient client = highLevelClient();
         {
             BulkRequest request = new BulkRequest();
-            request.add(new IndexRequest("posts", "doc", "1")
+            request.add(new IndexRequest("posts", "_doc", "1")
                     .source(XContentType.JSON, "title", "In which order are my Elasticsearch queries executed?"));
-            request.add(new IndexRequest("posts", "doc", "2")
+            request.add(new IndexRequest("posts", "_doc", "2")
                     .source(XContentType.JSON, "title", "Current status and upcoming changes in Elasticsearch"));
-            request.add(new IndexRequest("posts", "doc", "3")
+            request.add(new IndexRequest("posts", "_doc", "3")
                     .source(XContentType.JSON, "title", "The Future of Federated Search in Elasticsearch"));
             request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
             BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
@@ -983,7 +983,7 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
         RestHighLevelClient client = highLevelClient();
 
         // tag::explain-request
-        ExplainRequest request = new ExplainRequest("contributors", "doc", "1");
+        ExplainRequest request = new ExplainRequest("contributors", "1");
         request.query(QueryBuilders.termQuery("user", "tanguy"));
         // end::explain-request
 
@@ -1009,16 +1009,14 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
 
         // tag::explain-response
         String index = response.getIndex(); // <1>
-        String type = response.getType(); // <2>
-        String id = response.getId(); // <3>
-        boolean exists = response.isExists(); // <4>
-        boolean match = response.isMatch(); // <5>
-        boolean hasExplanation = response.hasExplanation(); // <6>
-        Explanation explanation = response.getExplanation(); // <7>
-        GetResult getResult = response.getGetResult(); // <8>
+        String id = response.getId(); // <2>
+        boolean exists = response.isExists(); // <3>
+        boolean match = response.isMatch(); // <4>
+        boolean hasExplanation = response.hasExplanation(); // <5>
+        Explanation explanation = response.getExplanation(); // <6>
+        GetResult getResult = response.getGetResult(); // <7>
         // end::explain-response
         assertThat(index, equalTo("contributors"));
-        assertThat(type, equalTo("doc"));
         assertThat(id, equalTo("1"));
         assertTrue(exists);
         assertTrue(match);
@@ -1251,29 +1249,29 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
 
     private void indexSearchTestData() throws IOException {
         CreateIndexRequest authorsRequest = new CreateIndexRequest("authors")
-            .mapping("doc", "user", "type=keyword,doc_values=false");
+            .mapping("_doc", "user", "type=keyword,doc_values=false");
         CreateIndexResponse authorsResponse = highLevelClient().indices().create(authorsRequest, RequestOptions.DEFAULT);
         assertTrue(authorsResponse.isAcknowledged());
 
         CreateIndexRequest reviewersRequest = new CreateIndexRequest("contributors")
-            .mapping("doc", "user", "type=keyword,store=true");
+            .mapping("_doc", "user", "type=keyword,store=true");
         CreateIndexResponse reviewersResponse = highLevelClient().indices().create(reviewersRequest, RequestOptions.DEFAULT);
         assertTrue(reviewersResponse.isAcknowledged());
 
         BulkRequest bulkRequest = new BulkRequest();
-        bulkRequest.add(new IndexRequest("posts", "doc", "1")
+        bulkRequest.add(new IndexRequest("posts", "_doc", "1")
                 .source(XContentType.JSON, "title", "In which order are my Elasticsearch queries executed?", "user",
                         Arrays.asList("kimchy", "luca"), "innerObject", Collections.singletonMap("key", "value")));
-        bulkRequest.add(new IndexRequest("posts", "doc", "2")
+        bulkRequest.add(new IndexRequest("posts", "_doc", "2")
                 .source(XContentType.JSON, "title", "Current status and upcoming changes in Elasticsearch", "user",
                         Arrays.asList("kimchy", "christoph"), "innerObject", Collections.singletonMap("key", "value")));
-        bulkRequest.add(new IndexRequest("posts", "doc", "3")
+        bulkRequest.add(new IndexRequest("posts", "_doc", "3")
                 .source(XContentType.JSON, "title", "The Future of Federated Search in Elasticsearch", "user",
                         Arrays.asList("kimchy", "tanguy"), "innerObject", Collections.singletonMap("key", "value")));
 
-        bulkRequest.add(new IndexRequest("authors", "doc", "1")
+        bulkRequest.add(new IndexRequest("authors", "_doc", "1")
             .source(XContentType.JSON, "user", "kimchy"));
-        bulkRequest.add(new IndexRequest("contributors", "doc", "1")
+        bulkRequest.add(new IndexRequest("contributors", "_doc", "1")
             .source(XContentType.JSON, "user", "tanguy"));
 
 
@@ -1370,22 +1368,22 @@ public class SearchDocumentationIT extends ESRestHighLevelClientTestCase {
 
     private static void indexCountTestData() throws IOException {
         CreateIndexRequest authorsRequest = new CreateIndexRequest("author")
-            .mapping("doc", "user", "type=keyword,doc_values=false");
+            .mapping("_doc", "user", "type=keyword,doc_values=false");
         CreateIndexResponse authorsResponse = highLevelClient().indices().create(authorsRequest, RequestOptions.DEFAULT);
         assertTrue(authorsResponse.isAcknowledged());
 
         BulkRequest bulkRequest = new BulkRequest();
-        bulkRequest.add(new IndexRequest("blog", "doc", "1")
+        bulkRequest.add(new IndexRequest("blog", "_doc", "1")
             .source(XContentType.JSON, "title", "Doubling Down on Open?", "user",
                 Collections.singletonList("kimchy"), "innerObject", Collections.singletonMap("key", "value")));
-        bulkRequest.add(new IndexRequest("blog", "doc", "2")
+        bulkRequest.add(new IndexRequest("blog", "_doc", "2")
             .source(XContentType.JSON, "title", "Swiftype Joins Forces with Elastic", "user",
                 Arrays.asList("kimchy", "matt"), "innerObject", Collections.singletonMap("key", "value")));
-        bulkRequest.add(new IndexRequest("blog", "doc", "3")
+        bulkRequest.add(new IndexRequest("blog", "_doc", "3")
             .source(XContentType.JSON, "title", "On Net Neutrality", "user",
                 Arrays.asList("tyler", "kimchy"), "innerObject", Collections.singletonMap("key", "value")));
 
-        bulkRequest.add(new IndexRequest("author", "doc", "1")
+        bulkRequest.add(new IndexRequest("author", "_doc", "1")
             .source(XContentType.JSON, "user", "kimchy"));
 
 

+ 7 - 8
docs/java-rest/high-level/search/explain.asciidoc

@@ -7,7 +7,7 @@ This can give useful feedback whether a document matches or didn’t match a spe
 [[java-rest-high-explain-request]]
 ==== Explain Request
 
-An `ExplainRequest` expects an `index`, a `type` and an `id` to specify a certain document,
+An `ExplainRequest` expects an `index` and an `id` to specify a certain document,
 and a query represented by `QueryBuilder` to run against it (the way of <<java-rest-high-query-builders, building queries>>).
 
 ["source","java",subs="attributes,callouts,macros"]
@@ -92,15 +92,14 @@ The `ExplainResponse` contains the following information:
 include-tagged::{doc-tests}/SearchDocumentationIT.java[explain-response]
 --------------------------------------------------
 <1> The index name of the explained document.
-<2> The type name of the explained document.
-<3> The id of the explained document.
-<4> Indicates whether or not the explained document exists.
-<5> Indicates whether or not there is a match between the explained document and
+<2> The id of the explained document.
+<3> Indicates whether or not the explained document exists.
+<4> Indicates whether or not there is a match between the explained document and
 the provided query (the `match` is retrieved from the lucene `Explanation` behind the scenes
 if the lucene `Explanation` models a match, it returns `true`, otherwise it returns `false`).
-<6> Indicates whether or not there exists a lucene `Explanation` for this request.
-<7> Get the lucene `Explanation` object if there exists.
-<8> Get the `GetResult` object if the `_source` or the stored fields are retrieved.
+<5> Indicates whether or not there exists a lucene `Explanation` for this request.
+<6> Get the lucene `Explanation` object if there exists.
+<7> Get the `GetResult` object if the `_source` or the stored fields are retrieved.
 
 The `GetResult` contains two maps internally to store the fetched `_source` and stored fields.
 You can use the following methods to get them:

+ 2 - 2
docs/reference/search/explain.asciidoc

@@ -14,7 +14,7 @@ Full query example:
 
 [source,js]
 --------------------------------------------------
-GET /twitter/_doc/0/_explain
+GET /twitter/_explain/0
 {
       "query" : {
         "match" : { "message" : "elasticsearch" }
@@ -108,7 +108,7 @@ explain api:
 
 [source,js]
 --------------------------------------------------
-GET /twitter/_doc/0/_explain?q=message:search
+GET /twitter/_explain/0?q=message:search
 --------------------------------------------------
 // CONSOLE
 // TEST[setup:twitter]

+ 0 - 1
modules/analysis-common/src/test/resources/rest-api-spec/test/search.query/40_query_string.yml

@@ -43,7 +43,6 @@
   - do:
       explain:
         index:  test
-        type:   test
         id:     1
         q: field:bars
         analyzer: snowball

+ 9 - 0
qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/FullClusterRestartIT.java

@@ -22,9 +22,11 @@ package org.elasticsearch.upgrades;
 import org.apache.http.util.EntityUtils;
 import org.elasticsearch.Version;
 import org.elasticsearch.client.Request;
+import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
 import org.elasticsearch.client.RestClient;
+import org.elasticsearch.test.rest.TypesRemovalWarningsHandler;
 import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.common.Booleans;
 import org.elasticsearch.common.CheckedFunction;
@@ -80,6 +82,7 @@ public class FullClusterRestartIT extends AbstractFullClusterRestartTestCase {
         index = getTestName().toLowerCase(Locale.ROOT);
     }
 
+
     public void testSearch() throws Exception {
         int count;
         if (isRunningAgainstOldCluster()) {
@@ -567,8 +570,14 @@ public class FullClusterRestartIT extends AbstractFullClusterRestartTestCase {
         assertNotNull(stringValue);
         String type = (String) bestHit.get("_type");
         String id = (String) bestHit.get("_id");
+
         Request explanationRequest = new Request("GET", "/" + index + "/" + type + "/" + id + "/_explain");
         explanationRequest.setJsonEntity("{ \"query\": { \"match_all\" : {} }}");
+
+        RequestOptions.Builder explanationOptions = RequestOptions.DEFAULT.toBuilder();
+        explanationOptions.setWarningsHandler(TypesRemovalWarningsHandler.INSTANCE);
+        explanationRequest.setOptions(explanationOptions);
+
         String explanation = toStr(client().performRequest(explanationRequest));
         assertFalse("Could not find payload boost in explanation\n" + explanation, explanation.contains("payloadBoost"));
 

+ 2 - 3
rest-api-spec/src/main/resources/rest-api-spec/api/explain.json

@@ -3,8 +3,8 @@
     "documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/search-explain.html",
     "methods": ["GET", "POST"],
     "url": {
-      "path": "/{index}/{type}/{id}/_explain",
-      "paths": ["/{index}/{type}/{id}/_explain"],
+      "path": "/{index}/_explain/{id}",
+      "paths": ["/{index}/_explain/{id}", "/{index}/{type}/{id}/_explain"],
       "parts": {
         "id": {
           "type" : "string",
@@ -18,7 +18,6 @@
         },
         "type": {
           "type" : "string",
-          "required" : true,
           "description" : "The type of the document"
         }
       },

+ 7 - 6
rest-api-spec/src/main/resources/rest-api-spec/test/explain/10_basic.yml

@@ -1,4 +1,8 @@
 setup:
+  - skip:
+      version: " - 6.99.99"
+      reason: types are required in requests before 7.0.0
+
   - do:
       indices.create:
           index: test_1
@@ -10,7 +14,7 @@ setup:
   - do:
       index:
           index:  test_1
-          type:   test
+          type:   _doc
           id:     id_1
           body:   { foo: bar, title: howdy }
 
@@ -23,7 +27,6 @@ setup:
   - do:
       explain:
           index:  test_1
-          type:   test
           id:     id_1
           body:
             query:
@@ -32,7 +35,7 @@ setup:
   - is_true: matched
   - match: { explanation.value: 1 }
   - match: { _index: test_1 }
-  - match: { _type: test }
+  - match: { _type: _doc }
   - match: { _id: id_1 }
 
 ---
@@ -41,7 +44,6 @@ setup:
   - do:
       explain:
           index:  alias_1
-          type:   test
           id:     id_1
           body:
             query:
@@ -50,7 +52,7 @@ setup:
   - is_true: matched
   - match: { explanation.value: 1 }
   - match: { _index: test_1 }
-  - match: { _type: test }
+  - match: { _type: _doc }
   - match: { _id: id_1 }
 
 ---
@@ -59,7 +61,6 @@ setup:
       catch: bad_request
       explain:
           index:  test_1
-          type:   test
           id:     id_1
           body:
             match_all: {}

+ 65 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/explain/11_basic_with_types.yml

@@ -0,0 +1,65 @@
+setup:
+  - do:
+      indices.create:
+          index: test_1
+          body:
+              aliases:
+                alias_1:
+                  "filter" : { "term" : { "foo" : "bar"} }
+
+  - do:
+      index:
+          index:  test_1
+          type:   test
+          id:     id_1
+          body:   { foo: bar, title: howdy }
+
+  - do:
+      indices.refresh: {}
+
+---
+"Basic explain":
+
+  - do:
+      explain:
+          index:  test_1
+          type:   test
+          id:     id_1
+          body:
+            query:
+              match_all: {}
+
+  - is_true: matched
+  - match: { explanation.value: 1 }
+  - match: { _index: test_1 }
+  - match: { _type: test }
+  - match: { _id: id_1 }
+
+---
+"Basic explain with alias":
+
+  - do:
+      explain:
+          index:  alias_1
+          type:   test
+          id:     id_1
+          body:
+            query:
+              match_all: {}
+
+  - is_true: matched
+  - match: { explanation.value: 1 }
+  - match: { _index: test_1 }
+  - match: { _type: test }
+  - match: { _id: id_1 }
+
+---
+"Explain body without query element":
+  - do:
+      catch: bad_request
+      explain:
+          index:  test_1
+          id:     id_1
+          body:
+            match_all: {}
+

+ 12 - 8
rest-api-spec/src/main/resources/rest-api-spec/test/explain/20_source_filtering.yml

@@ -1,9 +1,13 @@
 ---
 "Source filtering":
+  - skip:
+      version: " - 6.99.99"
+      reason: types are required in requests before 7.0.0
+
   - do:
       index:
         index:  test_1
-        type:   test
+        type:   _doc
         id:     1
         body:   { "include": { "field1": "v1", "field2": "v2" }, "count": 1 }
   - do:
@@ -11,34 +15,34 @@
         index: test_1
 
   - do:
-      explain: { index:  test_1, type: test, id: 1, _source: false, body: { query: { match_all: {}} } }
+      explain: { index:  test_1, id: 1, _source: false, body: { query: { match_all: {}} } }
   - match:   { _index:   test_1  }
-  - match:   { _type:    test    }
+  - match:   { _type:    _doc    }
   - match:   { _id:      "1"       }
   - is_false:  get._source
 
   - do:
-      explain: { index:  test_1, type: test, id: 1, _source: true, body: { query: { match_all: {}} } }
+      explain: { index:  test_1, id: 1, _source: true, body: { query: { match_all: {}} } }
   - match:  { get._source.include.field1: v1 }
 
   - do:
-      explain: { index:  test_1, type: test, id: 1, _source: include.field1, body: { query: { match_all: {}} } }
+      explain: { index:  test_1, id: 1, _source: include.field1, body: { query: { match_all: {}} } }
   - match:  { get._source.include.field1: v1 }
   - is_false: get._source.include.field2
 
   - do:
-      explain: { index:  test_1, type: test, id: 1, _source_includes: include.field1, body: { query: { match_all: {}} } }
+      explain: { index:  test_1, id: 1, _source_includes: include.field1, body: { query: { match_all: {}} } }
   - match:  { get._source.include.field1: v1 }
   - is_false: get._source.include.field2
 
   - do:
-      explain: { index:  test_1, type: test, id: 1, _source_includes: "include.field1,include.field2", body: { query: { match_all: {}} } }
+      explain: { index:  test_1, id: 1, _source_includes: "include.field1,include.field2", body: { query: { match_all: {}} } }
   - match:  { get._source.include.field1: v1 }
   - match:  { get._source.include.field2: v2 }
   - is_false: get._source.count
 
   - do:
-      explain: { index:  test_1, type: test, id: 1, _source_includes: include, _source_excludes: "*.field2", body: { query: { match_all: {}} } }
+      explain: { index:  test_1, id: 1, _source_includes: include, _source_excludes: "*.field2", body: { query: { match_all: {}} } }
   - match:  { get._source.include.field1: v1 }
   - is_false: get._source.include.field2
   - is_false: get._source.count

+ 44 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/explain/21_source_filtering_with_types.yml

@@ -0,0 +1,44 @@
+---
+"Source filtering":
+  - do:
+      index:
+        index:  test_1
+        type:   test
+        id:     1
+        body:   { "include": { "field1": "v1", "field2": "v2" }, "count": 1 }
+  - do:
+      indices.refresh:
+        index: test_1
+
+  - do:
+      explain: { index:  test_1, type: test, id: 1, _source: false, body: { query: { match_all: {}} } }
+  - match:   { _index:   test_1  }
+  - match:   { _type:    test    }
+  - match:   { _id:      "1"       }
+  - is_false:  get._source
+
+  - do:
+      explain: { index:  test_1, type: test, id: 1, _source: true, body: { query: { match_all: {}} } }
+  - match:  { get._source.include.field1: v1 }
+
+  - do:
+      explain: { index:  test_1, type: test, id: 1, _source: include.field1, body: { query: { match_all: {}} } }
+  - match:  { get._source.include.field1: v1 }
+  - is_false: get._source.include.field2
+
+  - do:
+      explain: { index:  test_1, type: test, id: 1, _source_includes: include.field1, body: { query: { match_all: {}} } }
+  - match:  { get._source.include.field1: v1 }
+  - is_false: get._source.include.field2
+
+  - do:
+      explain: { index:  test_1, type: test, id: 1, _source_includes: "include.field1,include.field2", body: { query: { match_all: {}} } }
+  - match:  { get._source.include.field1: v1 }
+  - match:  { get._source.include.field2: v2 }
+  - is_false: get._source.count
+
+  - do:
+      explain: { index:  test_1, type: test, id: 1, _source_includes: include, _source_excludes: "*.field2", body: { query: { match_all: {}} } }
+  - match:  { get._source.include.field1: v1 }
+  - is_false: get._source.include.field2
+  - is_false: get._source.count

+ 6 - 7
rest-api-spec/src/main/resources/rest-api-spec/test/explain/30_query_string.yml

@@ -1,11 +1,15 @@
 ---
 "explain with query_string parameters":
+  - skip:
+      version: " - 6.99.99"
+      reason: types are required in requests before 7.0.0
+
   - do:
       indices.create:
           index:  test
           body:
             mappings:
-              test:
+              _doc:
                 properties:
                   number:
                     type: integer
@@ -13,7 +17,7 @@
   - do:
       index:
           index:  test
-          type:   test
+          type:   _doc
           id:     1
           body:   { field: foo bar}
 
@@ -24,7 +28,6 @@
   - do:
       explain:
         index:  test
-        type:   test
         id:     1
         q: bar
         df: field
@@ -34,7 +37,6 @@
   - do:
       explain:
         index:  test
-        type:   test
         id:     1
         q: field:foo field:xyz
 
@@ -43,7 +45,6 @@
   - do:
       explain:
         index:  test
-        type:   test
         id:     1
         q: field:foo field:xyz
         default_operator: AND
@@ -53,7 +54,6 @@
   - do:
       explain:
         index:  test
-        type:   test
         id:     1
         q: field:BA*
 
@@ -62,7 +62,6 @@
   - do:
       explain:
         index:  test
-        type:   test
         id:     1
         q: number:foo
         lenient: true

+ 70 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/explain/31_query_string_with_types.yml

@@ -0,0 +1,70 @@
+---
+"explain with query_string parameters":
+  - do:
+      indices.create:
+          index:  test
+          body:
+            mappings:
+              test:
+                properties:
+                  number:
+                    type: integer
+
+  - do:
+      index:
+          index:  test
+          type:   test
+          id:     1
+          body:   { field: foo bar}
+
+  - do:
+      indices.refresh:
+        index: [test]
+
+  - do:
+      explain:
+        index:  test
+        type:   test
+        id:     1
+        q: bar
+        df: field
+
+  - is_true: matched
+
+  - do:
+      explain:
+        index:  test
+        type:   test
+        id:     1
+        q: field:foo field:xyz
+
+  - is_true: matched
+
+  - do:
+      explain:
+        index:  test
+        type:   test
+        id:     1
+        q: field:foo field:xyz
+        default_operator: AND
+
+  - is_false: matched
+
+  - do:
+      explain:
+        index:  test
+        type:   test
+        id:     1
+        q: field:BA*
+
+  - is_true: matched
+
+  - do:
+      explain:
+        index:  test
+        type:   test
+        id:     1
+        q: number:foo
+        lenient: true
+
+  - is_false: matched

+ 23 - 1
server/src/main/java/org/elasticsearch/action/explain/ExplainRequest.java

@@ -28,6 +28,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
 import org.elasticsearch.search.internal.AliasFilter;
@@ -43,7 +44,7 @@ public class ExplainRequest extends SingleShardRequest<ExplainRequest> implement
 
     private static final ParseField QUERY_FIELD = new ParseField("query");
 
-    private String type = "_all";
+    private String type = MapperService.SINGLE_MAPPING_NAME;
     private String id;
     private String routing;
     private String preference;
@@ -58,21 +59,42 @@ public class ExplainRequest extends SingleShardRequest<ExplainRequest> implement
     public ExplainRequest() {
     }
 
+    /**
+     * @deprecated Types are in the process of being removed. Use {@link ExplainRequest(String, String) instead.}
+     */
+    @Deprecated
     public ExplainRequest(String index, String type, String id) {
         this.index = index;
         this.type = type;
         this.id = id;
     }
 
+    public ExplainRequest(String index, String id) {
+        this.index = index;
+        this.id = id;
+    }
+
+    /**
+     * @deprecated Types are in the process of being removed.
+     */
+    @Deprecated
     public String type() {
         return type;
     }
 
+    /**
+     * @deprecated Types are in the process of being removed.
+     */
+    @Deprecated
     public ExplainRequest type(String type) {
         this.type = type;
         return this;
     }
 
+    public boolean isTypeless() {
+        return type == null || type.equals(MapperService.SINGLE_MAPPING_NAME);
+    }
+
     public String id() {
         return id;
     }

+ 4 - 0
server/src/main/java/org/elasticsearch/action/explain/ExplainResponse.java

@@ -84,6 +84,10 @@ public class ExplainResponse extends ActionResponse implements StatusToXContentO
         return index;
     }
 
+    /**
+     * @deprecated Types are in the process of being removed.
+     */
+    @Deprecated
     public String getType() {
         return type;
     }

+ 19 - 1
server/src/main/java/org/elasticsearch/rest/action/search/RestExplainAction.java

@@ -19,9 +19,11 @@
 
 package org.elasticsearch.rest.action.search;
 
+import org.apache.logging.log4j.LogManager;
 import org.elasticsearch.action.explain.ExplainRequest;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.logging.DeprecationLogger;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.rest.BaseRestHandler;
@@ -40,10 +42,17 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
  * Rest action for computing a score explanation for specific documents.
  */
 public class RestExplainAction extends BaseRestHandler {
+    private static final DeprecationLogger deprecationLogger = new DeprecationLogger(
+        LogManager.getLogger(RestExplainAction.class));
+    static final String TYPES_DEPRECATION_MESSAGE = "[types removal] " +
+        "Specifying a type in explain requests is deprecated.";
+
     public RestExplainAction(Settings settings, RestController controller) {
         super(settings);
         controller.registerHandler(GET, "/{index}/{type}/{id}/_explain", this);
         controller.registerHandler(POST, "/{index}/{type}/{id}/_explain", this);
+        controller.registerHandler(GET, "/{index}/_explain/{id}", this);
+        controller.registerHandler(POST, "/{index}/_explain/{id}", this);
     }
 
     @Override
@@ -53,7 +62,16 @@ public class RestExplainAction extends BaseRestHandler {
 
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
-        final ExplainRequest explainRequest = new ExplainRequest(request.param("index"), request.param("type"), request.param("id"));
+        ExplainRequest explainRequest;
+        if (request.hasParam("type")) {
+            deprecationLogger.deprecated(TYPES_DEPRECATION_MESSAGE);
+            explainRequest = new ExplainRequest(request.param("index"),
+                request.param("type"),
+                request.param("id"));
+        } else {
+            explainRequest = new ExplainRequest(request.param("index"), request.param("id"));
+        }
+
         explainRequest.parent(request.param("parent"));
         explainRequest.routing(request.param("routing"));
         explainRequest.preference(request.param("preference"));

+ 49 - 0
server/src/test/java/org/elasticsearch/rest/action/search/RestExplainActionTests.java

@@ -0,0 +1,49 @@
+/*
+ * 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.rest.action.search;
+
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.rest.RestRequest;
+import org.elasticsearch.rest.action.RestActionTestCase;
+import org.elasticsearch.test.rest.FakeRestRequest;
+import org.junit.Before;
+
+public class RestExplainActionTests extends RestActionTestCase {
+
+    @Before
+    public void setUpAction() {
+        new RestExplainAction(Settings.EMPTY, controller());
+    }
+
+    public void testTypeInPath() {
+        RestRequest deprecatedRequest = new FakeRestRequest.Builder(xContentRegistry())
+            .withMethod(RestRequest.Method.GET)
+            .withPath("/some_index/some_type/some_id/_explain")
+            .build();
+        dispatchRequest(deprecatedRequest);
+        assertWarnings(RestExplainAction.TYPES_DEPRECATION_MESSAGE);
+
+        RestRequest validRequest = new FakeRestRequest.Builder(xContentRegistry())
+            .withMethod(RestRequest.Method.GET)
+            .withPath("/some_index/_explain/some_id")
+            .build();
+        dispatchRequest(validRequest);
+    }
+}

+ 47 - 0
test/framework/src/main/java/org/elasticsearch/test/rest/TypesRemovalWarningsHandler.java

@@ -0,0 +1,47 @@
+/*
+ * 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.test.rest;
+
+import org.elasticsearch.client.WarningsHandler;
+
+import java.util.List;
+
+/**
+ * An implementation of {@link WarningsHandler} that ignores warnings related to types removal,
+ * but fails the request on all other warnings.
+ */
+public class TypesRemovalWarningsHandler implements WarningsHandler {
+
+    public static final TypesRemovalWarningsHandler INSTANCE = new TypesRemovalWarningsHandler();
+
+    private TypesRemovalWarningsHandler() {
+        // Prevent instantiation.
+    }
+
+    @Override
+    public boolean warningsShouldFailRequest(List<String> warnings) {
+        for (String warning : warnings) {
+            if (warning.startsWith("[types removal]") == false) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 4 - 4
x-pack/plugin/security/src/test/java/org/elasticsearch/integration/IndexPrivilegeTests.java

@@ -491,14 +491,14 @@ public class IndexPrivilegeTests extends AbstractPrivilegeTestCase {
                     assertAccessIsAllowed(user, "GET", "/" + index + "/_count");
                     assertAccessIsAllowed("admin", "GET", "/" + index + "/_search");
                     assertAccessIsAllowed("admin", "GET", "/" + index + "/foo/1");
-                    assertAccessIsAllowed(user, "GET", "/" + index + "/foo/1/_explain", "{ \"query\" : { \"match_all\" : {} } }");
-                    assertAccessIsAllowed(user, "GET", "/" + index + "/foo/1/_termvectors");
+                    assertAccessIsAllowed(user, "GET", "/" + index + "/_explain/1", "{ \"query\" : { \"match_all\" : {} } }");
+                    assertAccessIsAllowed(user, "GET", "/" + index + "/_termvectors/1");
                     assertUserIsAllowed(user, "search", index);
                 } else {
                     assertAccessIsDenied(user, "GET", "/" + index + "/_count");
                     assertAccessIsDenied(user, "GET", "/" + index + "/_search");
-                    assertAccessIsDenied(user, "GET", "/" + index + "/foo/1/_explain", "{ \"query\" : { \"match_all\" : {} } }");
-                    assertAccessIsDenied(user, "GET", "/" + index + "/foo/1/_termvectors");
+                    assertAccessIsDenied(user, "GET", "/" + index + "/_explain/1", "{ \"query\" : { \"match_all\" : {} } }");
+                    assertAccessIsDenied(user, "GET", "/" + index + "/_termvectors/1");
                     assertUserIsDenied(user, "search", index);
                 }
                 break;