浏览代码

HLRC for _mtermvectors (#35266)

relates to #27205
Mayya Sharipova 7 年之前
父节点
当前提交
aaeb47d1f7

+ 8 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java

@@ -50,6 +50,7 @@ import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.action.support.WriteRequest;
 import org.elasticsearch.action.update.UpdateRequest;
 import org.elasticsearch.client.core.CountRequest;
+import org.elasticsearch.client.core.MultiTermVectorsRequest;
 import org.elasticsearch.client.core.TermVectorsRequest;
 import org.elasticsearch.client.security.RefreshPolicy;
 import org.elasticsearch.cluster.health.ClusterHealthStatus;
@@ -626,6 +627,13 @@ final class RequestConverters {
         return request;
     }
 
+    static Request mtermVectors(MultiTermVectorsRequest mtvrequest) throws IOException {
+        String endpoint = "_mtermvectors";
+        Request request = new Request(HttpGet.METHOD_NAME, endpoint);
+        request.setEntity(createEntity(mtvrequest, REQUEST_BODY_CONTENT_TYPE));
+        return request;
+    }
+
     static Request getScript(GetStoredScriptRequest getStoredScriptRequest) {
         String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(getStoredScriptRequest.id()).build();
         Request request = new Request(HttpGet.METHOD_NAME, endpoint);

+ 33 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

@@ -60,6 +60,8 @@ import org.elasticsearch.client.core.CountRequest;
 import org.elasticsearch.client.core.CountResponse;
 import org.elasticsearch.client.core.TermVectorsResponse;
 import org.elasticsearch.client.core.TermVectorsRequest;
+import org.elasticsearch.client.core.MultiTermVectorsRequest;
+import org.elasticsearch.client.core.MultiTermVectorsResponse;
 import org.elasticsearch.client.tasks.TaskSubmissionResponse;
 import org.elasticsearch.common.CheckedConsumer;
 import org.elasticsearch.common.CheckedFunction;
@@ -1158,6 +1160,37 @@ public class RestHighLevelClient implements Closeable {
     }
 
 
+    /**
+     * Calls the Multi Term Vectors API
+     *
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-termvectors.html">Multi Term Vectors API
+     * on elastic.co</a>
+     *
+     * @param request   the request
+     * @param options   the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     */
+    public final MultiTermVectorsResponse mtermvectors(MultiTermVectorsRequest request, RequestOptions options) throws IOException {
+        return performRequestAndParseEntity(
+            request, RequestConverters::mtermVectors, options, MultiTermVectorsResponse::fromXContent, emptySet());
+    }
+
+
+    /**
+     * Asynchronously calls the Multi Term Vectors API
+     *
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-termvectors.html">Multi Term Vectors API
+     * on elastic.co</a>
+     * @param request the request
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @param listener the listener to be notified upon request completion
+     */
+    public final void mtermvectorsAsync(MultiTermVectorsRequest request, RequestOptions options,
+            ActionListener<MultiTermVectorsResponse> listener) {
+        performRequestAsyncAndParseEntity(
+            request, RequestConverters::mtermVectors, options, MultiTermVectorsResponse::fromXContent, listener, emptySet());
+    }
+
+
     /**
      * Executes a request using the Ranking Evaluation API.
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-rank-eval.html">Ranking Evaluation API

+ 77 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/core/MultiTermVectorsRequest.java

@@ -0,0 +1,77 @@
+/*
+ * 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.client.core;
+
+import org.elasticsearch.client.Validatable;
+import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.elasticsearch.client.core.TermVectorsRequest.createFromTemplate;
+
+public class MultiTermVectorsRequest implements ToXContentObject, Validatable {
+
+    private List<TermVectorsRequest> requests = new ArrayList<>();
+
+    /**
+     * Constructs an empty MultiTermVectorsRequest
+     * After that use {@code add} method to add individual {@code TermVectorsRequest} to it.
+     */
+    public MultiTermVectorsRequest() {};
+
+    /**
+     * Constructs a MultiTermVectorsRequest from the given document ids
+     * and a template {@code TermVectorsRequest}.
+     * Used when individual requests share the same index, type and other settings.
+     * @param ids - ids of documents for which term vectors are requested
+     * @param template - a template {@code TermVectorsRequest} that allows to set all
+     * settings only once for all requests.
+     */
+    public MultiTermVectorsRequest(String[] ids, TermVectorsRequest template) {
+        for (String id : ids) {
+            TermVectorsRequest request = createFromTemplate(template, id);
+            requests.add(request);
+        }
+    }
+
+    /**
+     * Adds another {@code TermVectorsRequest} to this {@code MultiTermVectorsRequest}
+     * @param request - {@code TermVectorsRequest} to add
+     */
+    public void add(TermVectorsRequest request) {
+        requests.add(request);
+    }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.startArray("docs");
+        for (TermVectorsRequest request : requests) {
+            request.toXContent(builder, params);
+        }
+        builder.endArray();
+        builder.endObject();
+        return builder;
+    }
+
+}

+ 77 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/core/MultiTermVectorsResponse.java

@@ -0,0 +1,77 @@
+/*
+ * 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.client.core;
+
+
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.util.List;
+import java.util.Objects;
+
+import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
+
+public class MultiTermVectorsResponse {
+    private final List<TermVectorsResponse> responses;
+
+    public MultiTermVectorsResponse(List<TermVectorsResponse> responses) {
+        this.responses = responses;
+    }
+
+    private static ConstructingObjectParser<MultiTermVectorsResponse, Void> PARSER =
+        new ConstructingObjectParser<>("multi_term_vectors", true,
+        args -> {
+            // as the response comes from server, we are sure that args[0] will be a list of TermVectorsResponse
+            @SuppressWarnings("unchecked") List<TermVectorsResponse> termVectorsResponsesList = (List<TermVectorsResponse>) args[0];
+            return new MultiTermVectorsResponse(termVectorsResponsesList);
+        }
+    );
+
+    static {
+        PARSER.declareObjectArray(constructorArg(), (p,c) -> TermVectorsResponse.fromXContent(p), new ParseField("docs"));
+    }
+
+    public static MultiTermVectorsResponse fromXContent(XContentParser parser) {
+        return PARSER.apply(parser, null);
+    }
+
+    /**
+     * Returns the list of {@code TermVectorsResponse} for this {@code MultiTermVectorsResponse}
+     */
+    public List<TermVectorsResponse> getTermVectorsResponses() {
+        return responses;
+    }
+
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof MultiTermVectorsResponse)) return false;
+        MultiTermVectorsResponse other = (MultiTermVectorsResponse) obj;
+        return Objects.equals(responses, other.responses);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(responses);
+    }
+
+}

+ 26 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/core/TermVectorsRequest.java

@@ -72,6 +72,29 @@ public class TermVectorsRequest implements ToXContentObject, Validatable {
         this.docBuilder = docBuilder;
     }
 
+
+    /**
+     * Constructs a new TermVectorRequest from a template
+     * using the provided document id
+     * @param template - a term vector request served as a template
+     * @param id - id of the requested document
+     */
+    static TermVectorsRequest createFromTemplate(TermVectorsRequest template, String id) {
+        TermVectorsRequest request = new TermVectorsRequest(template.getIndex(), template.getType(), id);
+        request.realtime = template.getRealtime();
+        request.requestPositions = template.requestPositions;
+        request.requestPayloads = template.requestPayloads;
+        request.requestOffsets = template.requestOffsets;
+        request.requestFieldStatistics = template.requestFieldStatistics;
+        request.requestTermStatistics = template.requestTermStatistics;
+        if (template.routing != null) request.setRouting(template.getRouting());
+        if (template.preference != null) request.setPreference(template.getPreference());
+        if (template.fields != null) request.setFields(template.getFields());
+        if (template.perFieldAnalyzer != null) request.setPerFieldAnalyzer(template.perFieldAnalyzer);
+        if (template.filterSettings != null) request.setFilterSettings(template.filterSettings);
+        return request;
+    }
+
     /**
      * Returns the index of the request
      */
@@ -201,6 +224,9 @@ public class TermVectorsRequest implements ToXContentObject, Validatable {
     @Override
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
         builder.startObject();
+        builder.field("_index", index);
+        builder.field("_type", type);
+        if (id != null) builder.field("_id", id);
         // set values only when different from defaults
         if (requestPositions == false) builder.field("positions", false);
         if (requestPayloads == false) builder.field("payloads", false);

+ 68 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java

@@ -44,6 +44,8 @@ import org.elasticsearch.action.search.SearchRequest;
 import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
 import org.elasticsearch.action.update.UpdateRequest;
 import org.elasticsearch.action.update.UpdateResponse;
+import org.elasticsearch.client.core.MultiTermVectorsRequest;
+import org.elasticsearch.client.core.MultiTermVectorsResponse;
 import org.elasticsearch.client.core.TermVectorsRequest;
 import org.elasticsearch.client.core.TermVectorsResponse;
 import org.elasticsearch.common.Strings;
@@ -73,6 +75,7 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.format.DateTimeFormat;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -1183,4 +1186,69 @@ public class CrudIT extends ESRestHighLevelClientTestCase {
             () -> execute(request, highLevelClient()::termvectors, highLevelClient()::termvectorsAsync));
         assertEquals(RestStatus.NOT_FOUND, exception.status());
     }
+
+    // Not entirely sure if _mtermvectors belongs to CRUD, and in the absence of a better place, will have it here
+    public void testMultiTermvectors() throws IOException {
+        final String sourceIndex = "index1";
+        {
+            // prepare : index docs
+            Settings settings = Settings.builder()
+                .put("number_of_shards", 1)
+                .put("number_of_replicas", 0)
+                .build();
+            String mappings = "\"_doc\":{\"properties\":{\"field\":{\"type\":\"text\"}}}";
+            createIndex(sourceIndex, settings, mappings);
+            assertEquals(
+                RestStatus.OK,
+                highLevelClient().bulk(
+                    new BulkRequest()
+                        .add(new IndexRequest(sourceIndex, "_doc", "1")
+                            .source(Collections.singletonMap("field", "value1"), XContentType.JSON))
+                        .add(new IndexRequest(sourceIndex, "_doc", "2")
+                            .source(Collections.singletonMap("field", "value2"), XContentType.JSON))
+                        .setRefreshPolicy(RefreshPolicy.IMMEDIATE),
+                    RequestOptions.DEFAULT
+                ).status()
+            );
+        }
+        {
+            // test _mtermvectors where MultiTermVectorsRequest is constructed with ids and a template
+            String[] expectedIds = {"1", "2"};
+            TermVectorsRequest tvRequestTemplate = new TermVectorsRequest(sourceIndex, "_doc");
+            tvRequestTemplate.setFields("field");
+            MultiTermVectorsRequest mtvRequest = new MultiTermVectorsRequest(expectedIds, tvRequestTemplate);
+
+            MultiTermVectorsResponse mtvResponse =
+                execute(mtvRequest, highLevelClient()::mtermvectors, highLevelClient()::mtermvectorsAsync);
+
+            List<String> ids = new ArrayList<>();
+            for (TermVectorsResponse tvResponse: mtvResponse.getTermVectorsResponses()) {
+                assertThat(tvResponse.getIndex(), equalTo(sourceIndex));
+                assertTrue(tvResponse.getFound());
+                ids.add(tvResponse.getId());
+            }
+            assertArrayEquals(expectedIds, ids.toArray());
+        }
+
+        {
+            // test _mtermvectors where MultiTermVectorsRequest constructed with adding each separate request
+            MultiTermVectorsRequest mtvRequest = new MultiTermVectorsRequest();
+            TermVectorsRequest tvRequest1 = new TermVectorsRequest(sourceIndex, "_doc", "1");
+            tvRequest1.setFields("field");
+            mtvRequest.add(tvRequest1);
+            TermVectorsRequest tvRequest2 = new TermVectorsRequest(sourceIndex, "_doc");
+            XContentBuilder docBuilder = XContentFactory.jsonBuilder();
+            docBuilder.startObject().field("field", "valuex").endObject();
+            tvRequest2.setDoc(docBuilder);
+            mtvRequest.add(tvRequest2);
+
+            MultiTermVectorsResponse mtvResponse =
+                execute(mtvRequest, highLevelClient()::mtermvectors, highLevelClient()::mtermvectorsAsync);
+            for (TermVectorsResponse tvResponse: mtvResponse.getTermVectorsResponses()) {
+                assertThat(tvResponse.getIndex(), equalTo(sourceIndex));
+                assertTrue(tvResponse.getFound());
+            }
+        }
+
+    }
 }

+ 21 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java

@@ -53,6 +53,7 @@ import org.elasticsearch.action.support.master.AcknowledgedRequest;
 import org.elasticsearch.action.support.master.MasterNodeReadRequest;
 import org.elasticsearch.action.support.master.MasterNodeRequest;
 import org.elasticsearch.action.support.replication.ReplicationRequest;
+import org.elasticsearch.client.core.MultiTermVectorsRequest;
 import org.elasticsearch.client.core.TermVectorsRequest;
 import org.elasticsearch.action.update.UpdateRequest;
 import org.elasticsearch.client.RequestConverters.EndpointBuilder;
@@ -1303,6 +1304,26 @@ public class RequestConvertersTests extends ESTestCase {
         assertToXContentBody(tvRequest, request.getEntity());
     }
 
+    public void testMultiTermVectors() throws IOException {
+        MultiTermVectorsRequest mtvRequest = new MultiTermVectorsRequest();
+
+        int numberOfRequests = randomIntBetween(0, 5);
+        for (int i = 0; i < numberOfRequests; i++) {
+            String index = randomAlphaOfLengthBetween(3, 10);
+            String type = randomAlphaOfLengthBetween(3, 10);
+            String id = randomAlphaOfLengthBetween(3, 10);
+            TermVectorsRequest tvRequest = new TermVectorsRequest(index, type, id);
+            String[] fields = generateRandomStringArray(10, 5, false, false);
+            tvRequest.setFields(fields);
+            mtvRequest.add(tvRequest);
+        }
+
+        Request request = RequestConverters.mtermVectors(mtvRequest);
+        assertEquals(HttpGet.METHOD_NAME, request.getMethod());
+        assertEquals("_mtermvectors", request.getEndpoint());
+        assertToXContentBody(mtvRequest, request.getEntity());
+    }
+
     public void testFieldCaps() {
         // Create a random request.
         String[] indices = randomIndicesNames(0, 5);

+ 0 - 1
client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java

@@ -674,7 +674,6 @@ public class RestHighLevelClientTests extends ESTestCase {
             "indices.exists_type",
             "indices.get_upgrade",
             "indices.put_alias",
-            "mtermvectors",
             "render_search_template",
             "scripts_painless_execute"
         };

+ 67 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/core/MultiTermVectorsResponseTests.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.client.core;
+
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester;
+
+public class MultiTermVectorsResponseTests extends ESTestCase {
+
+    public void testFromXContent() throws IOException {
+        xContentTester(
+            this::createParser,
+            this::createTestInstance,
+            this::toXContent,
+            MultiTermVectorsResponse::fromXContent)
+            .supportsUnknownFields(true)
+            .randomFieldsExcludeFilter(field ->
+                field.endsWith("term_vectors") || field.endsWith("terms") || field.endsWith("tokens"))
+            .test();
+    }
+
+    private void toXContent(MultiTermVectorsResponse response, XContentBuilder builder) throws IOException {
+        builder.startObject();
+        List<TermVectorsResponse> termVectorsResponseList = response.getTermVectorsResponses();
+        if (termVectorsResponseList != null) {
+            builder.startArray("docs");
+            for (TermVectorsResponse tvr : termVectorsResponseList) {
+                TermVectorsResponseTests.toXContent(tvr, builder);
+            }
+            builder.endArray();
+        }
+        builder.endObject();
+    }
+
+    protected MultiTermVectorsResponse createTestInstance() {
+        int numberOfResponses = randomIntBetween(0, 5);
+        List<TermVectorsResponse> responses = new ArrayList<>(numberOfResponses);
+        for (int i = 0; i < numberOfResponses; i++) {
+            TermVectorsResponse tvResponse = TermVectorsResponseTests.createTestInstance();
+            responses.add(tvResponse);
+        }
+        return new MultiTermVectorsResponse(responses);
+    }
+}

+ 7 - 7
client/rest-high-level/src/test/java/org/elasticsearch/client/core/TermVectorsResponseTests.java

@@ -35,8 +35,8 @@ public class TermVectorsResponseTests extends ESTestCase {
     public void testFromXContent() throws IOException {
         xContentTester(
             this::createParser,
-            this::createTestInstance,
-            this::toXContent,
+            TermVectorsResponseTests::createTestInstance,
+            TermVectorsResponseTests::toXContent,
             TermVectorsResponse::fromXContent)
             .supportsUnknownFields(true)
             .randomFieldsExcludeFilter(field ->
@@ -44,7 +44,7 @@ public class TermVectorsResponseTests extends ESTestCase {
             .test();
     }
 
-    private void toXContent(TermVectorsResponse response, XContentBuilder builder) throws IOException {
+    static void toXContent(TermVectorsResponse response, XContentBuilder builder) throws IOException {
         builder.startObject();
         builder.field("_index", response.getIndex());
         builder.field("_type", response.getType());
@@ -66,7 +66,7 @@ public class TermVectorsResponseTests extends ESTestCase {
         builder.endObject();
     }
 
-    private void toXContent(TermVectorsResponse.TermVector tv, XContentBuilder builder) throws IOException {
+    private static void toXContent(TermVectorsResponse.TermVector tv, XContentBuilder builder) throws IOException {
         builder.startObject(tv.getFieldName());
         // build fields_statistics
         if (tv.getFieldStatistics() != null) {
@@ -117,7 +117,7 @@ public class TermVectorsResponseTests extends ESTestCase {
     }
 
 
-    protected TermVectorsResponse createTestInstance() {
+    static TermVectorsResponse createTestInstance() {
         String index = randomAlphaOfLength(5);
         String type = randomAlphaOfLength(5);
         String id = String.valueOf(randomIntBetween(1,100));
@@ -148,7 +148,7 @@ public class TermVectorsResponseTests extends ESTestCase {
 
 
 
-    private TermVectorsResponse.TermVector randomTermVector(String fieldName, boolean hasFieldStatistics, boolean hasTermStatistics,
+    private static TermVectorsResponse.TermVector randomTermVector(String fieldName, boolean hasFieldStatistics, boolean hasTermStatistics,
             boolean hasScores, boolean hasOffsets, boolean hasPositions, boolean hasPayloads) {
         TermVectorsResponse.TermVector.FieldStatistics fs = null;
         if (hasFieldStatistics) {
@@ -171,7 +171,7 @@ public class TermVectorsResponseTests extends ESTestCase {
         return tv;
     }
 
-    private TermVectorsResponse.TermVector.Term randomTerm(String termTxt, boolean hasTermStatistics, boolean hasScores,
+    private static TermVectorsResponse.TermVector.Term randomTerm(String termTxt, boolean hasTermStatistics, boolean hasScores,
             boolean hasOffsets, boolean hasPositions, boolean hasPayloads) {
 
         int termFreq =  randomInt(10000);

+ 76 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java

@@ -54,6 +54,8 @@ import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.RestHighLevelClient;
 import org.elasticsearch.client.RethrottleRequest;
+import org.elasticsearch.client.core.MultiTermVectorsRequest;
+import org.elasticsearch.client.core.MultiTermVectorsResponse;
 import org.elasticsearch.client.core.TermVectorsRequest;
 import org.elasticsearch.client.core.TermVectorsResponse;
 import org.elasticsearch.common.Strings;
@@ -1671,6 +1673,80 @@ public class CRUDDocumentationIT extends ESRestHighLevelClientTestCase {
 
     }
 
+
+    // Not entirely sure if _mtermvectors belongs to CRUD, and in the absence of a better place, will have it here
+    public void testMultiTermVectors() throws Exception {
+        RestHighLevelClient client = highLevelClient();
+        CreateIndexRequest authorsRequest = new CreateIndexRequest("authors").mapping("_doc", "user", "type=text");
+        CreateIndexResponse authorsResponse = client.indices().create(authorsRequest, RequestOptions.DEFAULT);
+        assertTrue(authorsResponse.isAcknowledged());
+        client.index(new IndexRequest("index", "_doc", "1").source("user", "kimchy"), RequestOptions.DEFAULT);
+        client.index(new IndexRequest("index", "_doc", "2").source("user", "s1monw"), RequestOptions.DEFAULT);
+        Response refreshResponse = client().performRequest(new Request("POST", "/authors/_refresh"));
+        assertEquals(200, refreshResponse.getStatusLine().getStatusCode());
+
+        {
+            // tag::multi-term-vectors-request
+            MultiTermVectorsRequest request = new MultiTermVectorsRequest(); // <1>
+            TermVectorsRequest tvrequest1 =
+                new TermVectorsRequest("authors", "_doc", "1");
+            tvrequest1.setFields("user");
+            request.add(tvrequest1); // <2>
+            TermVectorsRequest tvrequest2 =
+                new TermVectorsRequest("authors", "_doc");
+            XContentBuilder docBuilder = XContentFactory.jsonBuilder();
+            docBuilder.startObject().field("user", "guest-user").endObject();
+            tvrequest2.setDoc(docBuilder);
+            request.add(tvrequest2); // <3>
+            // end::multi-term-vectors-request
+        }
+
+        // tag::multi-term-vectors-request-template
+        TermVectorsRequest tvrequestTemplate =
+            new TermVectorsRequest("authors", "_doc"); // <1>
+        tvrequestTemplate.setFields("user");
+        String[] ids = {"1", "2"};
+        MultiTermVectorsRequest request =
+            new MultiTermVectorsRequest(ids, tvrequestTemplate); // <2>
+        // end::multi-term-vectors-request-template
+
+        // tag::multi-term-vectors-execute
+        MultiTermVectorsResponse response =
+            client.mtermvectors(request, RequestOptions.DEFAULT);
+        // end::multi-term-vectors-execute
+
+        // tag::multi-term-vectors-response
+        List<TermVectorsResponse> tvresponseList =
+            response.getTermVectorsResponses(); // <1>
+        if (tvresponseList != null) {
+            for (TermVectorsResponse tvresponse : tvresponseList) {
+            }
+        }
+        // end::multi-term-vectors-response
+
+        ActionListener<MultiTermVectorsResponse> listener;
+        // tag::multi-term-vectors-execute-listener
+        listener = new ActionListener<MultiTermVectorsResponse>() {
+            @Override
+            public void onResponse(MultiTermVectorsResponse mtvResponse) {
+                // <1>
+            }
+            @Override
+            public void onFailure(Exception e) {
+                // <2>
+            }
+        };
+        // end::multi-term-vectors-execute-listener
+        CountDownLatch latch = new CountDownLatch(1);
+        listener = new LatchedActionListener<>(listener, latch);
+        // tag::multi-term-vectors-execute-async
+        client.mtermvectorsAsync(
+            request, RequestOptions.DEFAULT, listener); // <1>
+        // end::multi-term-vectors-execute-async
+        assertTrue(latch.await(30L, TimeUnit.SECONDS));
+
+    }
+
     @SuppressWarnings("unused")
     public void testMultiGet() throws Exception {
         RestHighLevelClient client = highLevelClient();

+ 59 - 0
docs/java-rest/high-level/document/multi-term-vectors.asciidoc

@@ -0,0 +1,59 @@
+--
+:api: multi-term-vectors
+:request: MultiTermVectorsRequest
+:response: MultiTermVectorsResponse
+:tvrequest: TermVectorsRequest
+--
+
+[id="{upid}-{api}"]
+=== Multi Term Vectors API
+
+Multi Term Vectors API allows to get multiple term vectors at once.
+
+[id="{upid}-{api}-request"]
+==== Multi Term Vectors Request
+There are two ways to create a +{request}+.
+
+The first way is to create an empty +{request}+, and then add individual
+<<java-rest-high-document-term-vectors, term vectors requests>> to it.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request]
+--------------------------------------------------
+<1> Create an empty +{request}+.
+<2> Add the first +{tvrequest}+ to the +{request}+.
+<3> Add the second +{tvrequest}+ for an artificial doc to the +{request}+.
+
+
+The second way can be used when all term vectors requests share the same
+arguments, such as index, type, and other settings. In this case, a template
++{tvrequest}+ can be created with all necessary settings set, and
+this template request can be passed to +{request}+ along with all
+documents' ids for which to execute these requests.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request-template]
+--------------------------------------------------
+<1> Create a template +{tvrequest}+.
+<2> Pass documents' ids and the template to the +{request}+.
+
+
+include::../execution.asciidoc[]
+
+
+[id="{upid}-{api}-response"]
+==== Multi Term Vectors Response
+
++{response}+ allows to get the list of term vectors responses,
+each of which can be inspected as described in
+<<java-rest-high-document-term-vectors>>.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-response]
+--------------------------------------------------
+<1> Get a list of `TermVectorsResponse`
+
+

+ 3 - 0
docs/java-rest/high-level/supported-apis.asciidoc

@@ -24,6 +24,7 @@ Multi-document APIs::
 * <<{upid}-update-by-query>>
 * <<{upid}-delete-by-query>>
 * <<{upid}-rethrottle>>
+* <<{upid}-multi-term-vectors>>
 
 include::document/index.asciidoc[]
 include::document/get.asciidoc[]
@@ -37,6 +38,8 @@ include::document/reindex.asciidoc[]
 include::document/update-by-query.asciidoc[]
 include::document/delete-by-query.asciidoc[]
 include::document/rethrottle.asciidoc[]
+include::document/multi-term-vectors.asciidoc[]
+
 
 == Search APIs