@Override
  public ActionRequestValidationException validate() {
    ActionRequestValidationException validationException = super.validate();
    if (type == null) {
      validationException = addValidationError("type is missing", validationException);
    }
    if (source == null) {
      validationException = addValidationError("source is missing", validationException);
    }

    if (opType() == OpType.CREATE) {
      if (versionType != VersionType.INTERNAL || version != Versions.MATCH_DELETED) {
        validationException =
            addValidationError(
                "create operations do not support versioning. use index instead",
                validationException);
        return validationException;
      }
    }

    if (opType() != OpType.INDEX && id == null) {
      addValidationError("an id is required for a " + opType() + " operation", validationException);
    }

    if (!versionType.validateVersionForWrites(version)) {
      validationException =
          addValidationError(
              "illegal version value ["
                  + version
                  + "] for version type ["
                  + versionType.name()
                  + "]",
              validationException);
    }

    if (versionType == VersionType.FORCE) {
      validationException =
          addValidationError("version type [force] may no longer be used", validationException);
    }

    if (id != null && id.getBytes(StandardCharsets.UTF_8).length > 512) {
      validationException =
          addValidationError(
              "id is too long, must be no longer than 512 bytes but was: "
                  + id.getBytes(StandardCharsets.UTF_8).length,
              validationException);
    }

    if (id == null
        && (versionType == VersionType.INTERNAL && version == Versions.MATCH_ANY) == false) {
      validationException =
          addValidationError(
              "an id must be provided if version type or value are set", validationException);
    }

    return validationException;
  }
 @Override
 public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
   DeleteIndexedScriptRequest deleteIndexedScriptRequest =
       new DeleteIndexedScriptRequest(getScriptLang(request), request.param("id"));
   deleteIndexedScriptRequest.version(
       request.paramAsLong("version", deleteIndexedScriptRequest.version()));
   deleteIndexedScriptRequest.versionType(
       VersionType.fromString(
           request.param("version_type"), deleteIndexedScriptRequest.versionType()));
   client.deleteIndexedScript(
       deleteIndexedScriptRequest,
       new RestBuilderListener<DeleteIndexedScriptResponse>(channel) {
         @Override
         public RestResponse buildResponse(
             DeleteIndexedScriptResponse result, XContentBuilder builder) throws Exception {
           builder
               .startObject()
               .field(Fields.FOUND, result.isFound())
               .field(Fields._INDEX, result.getIndex())
               .field(Fields._TYPE, result.getType())
               .field(Fields._ID, result.getId())
               .field(Fields._VERSION, result.getVersion())
               .endObject();
           RestStatus status = OK;
           if (!result.isFound()) {
             status = NOT_FOUND;
           }
           return new BytesRestResponse(status, builder);
         }
       });
 }
  private RestChannelConsumer parseExistingDocPercolate(
      PercolateRequest percolateRequest, RestRequest restRequest, NodeClient client) {
    String index = restRequest.param("index");
    String type = restRequest.param("type");
    percolateRequest.indices(
        Strings.splitStringByCommaToArray(restRequest.param("percolate_index", index)));
    percolateRequest.documentType(restRequest.param("percolate_type", type));

    GetRequest getRequest = new GetRequest(index, type, restRequest.param("id"));
    getRequest.routing(restRequest.param("routing"));
    getRequest.preference(restRequest.param("preference"));
    getRequest.refresh(restRequest.paramAsBoolean("refresh", getRequest.refresh()));
    getRequest.realtime(restRequest.paramAsBoolean("realtime", getRequest.realtime()));
    getRequest.version(RestActions.parseVersion(restRequest));
    getRequest.versionType(
        VersionType.fromString(restRequest.param("version_type"), getRequest.versionType()));

    percolateRequest.getRequest(getRequest);
    percolateRequest.routing(restRequest.param("percolate_routing"));
    percolateRequest.preference(restRequest.param("percolate_preference"));
    percolateRequest.source(restRequest.contentOrSourceParam());

    percolateRequest.indicesOptions(
        IndicesOptions.fromRequest(restRequest, percolateRequest.indicesOptions()));
    return channel -> executePercolate(client, percolateRequest, channel);
  }
 @Override
 public int hashCode() {
   int result = index.hashCode();
   result = 31 * result + (type != null ? type.hashCode() : 0);
   result = 31 * result + id.hashCode();
   result = 31 * result + (routing != null ? routing.hashCode() : 0);
   result = 31 * result + (fields != null ? Arrays.hashCode(fields) : 0);
   result = 31 * result + (int) (version ^ (version >>> 32));
   result = 31 * result + versionType.hashCode();
   result = 31 * result + (fetchSourceContext != null ? fetchSourceContext.hashCode() : 0);
   return result;
 }
 @Override
 public void readFrom(StreamInput in) throws IOException {
   super.readFrom(in);
   type = in.readUTF();
   id = in.readUTF();
   if (in.readBoolean()) {
     routing = in.readUTF();
   }
   refresh = in.readBoolean();
   version = in.readLong();
   versionType = VersionType.fromValue(in.readByte());
 }
    @Override
    public void writeTo(StreamOutput out) throws IOException {
      out.writeString(index);
      out.writeOptionalString(type);
      out.writeString(id);
      out.writeOptionalString(routing);
      out.writeOptionalString(parent);
      out.writeOptionalStringArray(storedFields);
      out.writeLong(version);
      out.writeByte(versionType.getValue());

      out.writeOptionalWriteable(fetchSourceContext);
    }
 @Override
 public int hashCode() {
   int result = index.hashCode();
   result = 31 * result + (type != null ? type.hashCode() : 0);
   result = 31 * result + id.hashCode();
   result = 31 * result + (routing != null ? routing.hashCode() : 0);
   result = 31 * result + (parent != null ? parent.hashCode() : 0);
   result = 31 * result + (storedFields != null ? Arrays.hashCode(storedFields) : 0);
   result = 31 * result + Long.hashCode(version);
   result = 31 * result + versionType.hashCode();
   result = 31 * result + (fetchSourceContext != null ? fetchSourceContext.hashCode() : 0);
   return result;
 }
    @Override
    public void readFrom(StreamInput in) throws IOException {
      index = in.readString();
      type = in.readOptionalString();
      id = in.readString();
      routing = in.readOptionalString();
      parent = in.readOptionalString();
      storedFields = in.readOptionalStringArray();
      version = in.readLong();
      versionType = VersionType.fromValue(in.readByte());

      fetchSourceContext = in.readOptionalWriteable(FetchSourceContext::new);
    }
 @Override
 public void writeTo(StreamOutput out) throws IOException {
   super.writeTo(out);
   out.writeSharedString(type);
   out.writeOptionalString(id);
   out.writeOptionalString(routing);
   out.writeOptionalString(parent);
   out.writeOptionalString(timestamp);
   out.writeLong(ttl);
   out.writeBytesReference(source);
   out.writeByte(opType.id());
   out.writeBoolean(refresh);
   out.writeLong(version);
   out.writeByte(versionType.getValue());
 }
 @Override
 public void writeTo(StreamOutput out) throws IOException {
   super.writeTo(out);
   out.writeUTF(type);
   out.writeUTF(id);
   if (routing == null) {
     out.writeBoolean(false);
   } else {
     out.writeBoolean(true);
     out.writeUTF(routing);
   }
   out.writeBoolean(refresh);
   out.writeLong(version);
   out.writeByte(versionType.getValue());
 }
  @Override
  public void readFrom(StreamInput in) throws IOException {
    super.readFrom(in);
    type = in.readSharedString();
    id = in.readOptionalString();
    routing = in.readOptionalString();
    parent = in.readOptionalString();
    timestamp = in.readOptionalString();
    ttl = in.readLong();
    source = in.readBytesReference();
    sourceUnsafe = false;

    opType = OpType.fromId(in.readByte());
    refresh = in.readBoolean();
    version = in.readLong();
    versionType = VersionType.fromValue(in.readByte());
  }
    @Override
    public void readFrom(StreamInput in) throws IOException {
      index = in.readString();
      type = in.readOptionalString();
      id = in.readString();
      routing = in.readOptionalString();
      int size = in.readVInt();
      if (size > 0) {
        fields = new String[size];
        for (int i = 0; i < size; i++) {
          fields[i] = in.readString();
        }
      }
      version = in.readLong();
      versionType = VersionType.fromValue(in.readByte());

      fetchSourceContext = FetchSourceContext.optionalReadFromStream(in);
    }
 @Override
 public void writeTo(StreamOutput out) throws IOException {
   super.writeTo(out);
   out.writeOptionalString(type);
   out.writeOptionalString(id);
   out.writeOptionalString(routing);
   out.writeOptionalString(parent);
   if (out.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) {
     out.writeOptionalString(null);
     out.writeOptionalWriteable(null);
   }
   out.writeBytesReference(source);
   out.writeByte(opType.getId());
   out.writeLong(version);
   out.writeByte(versionType.getValue());
   out.writeOptionalString(pipeline);
   out.writeBoolean(isRetry);
   out.writeLong(autoGeneratedTimestamp);
 }
 @Override
 public void readFrom(StreamInput in) throws IOException {
   super.readFrom(in);
   type = in.readOptionalString();
   id = in.readOptionalString();
   routing = in.readOptionalString();
   parent = in.readOptionalString();
   if (in.getVersion().before(Version.V_6_0_0_alpha1_UNRELEASED)) {
     in.readOptionalString(); // timestamp
     in.readOptionalWriteable(TimeValue::new); // ttl
   }
   source = in.readBytesReference();
   opType = OpType.fromId(in.readByte());
   version = in.readLong();
   versionType = VersionType.fromValue(in.readByte());
   pipeline = in.readOptionalString();
   isRetry = in.readBoolean();
   autoGeneratedTimestamp = in.readLong();
 }
    @Override
    public void writeTo(StreamOutput out) throws IOException {
      out.writeString(index);
      out.writeOptionalString(type);
      out.writeString(id);
      out.writeOptionalString(routing);
      if (fields == null) {
        out.writeVInt(0);
      } else {
        out.writeVInt(fields.length);
        for (String field : fields) {
          out.writeString(field);
        }
      }

      out.writeLong(version);
      out.writeByte(versionType.getValue());

      FetchSourceContext.optionalWriteToStream(fetchSourceContext, out);
    }
  @Override
  public void handleRequest(
      final RestRequest request, final RestChannel channel, final NodeClient client) {
    DeleteRequest deleteRequest =
        new DeleteRequest(request.param("index"), request.param("type"), request.param("id"));
    deleteRequest.routing(request.param("routing"));
    deleteRequest.parent(
        request.param(
            "parent")); // order is important, set it after routing, so it will set the routing
    deleteRequest.timeout(request.paramAsTime("timeout", DeleteRequest.DEFAULT_TIMEOUT));
    deleteRequest.setRefreshPolicy(request.param("refresh"));
    deleteRequest.version(RestActions.parseVersion(request));
    deleteRequest.versionType(
        VersionType.fromString(request.param("version_type"), deleteRequest.versionType()));

    String consistencyLevel = request.param("consistency");
    if (consistencyLevel != null) {
      deleteRequest.consistencyLevel(WriteConsistencyLevel.fromString(consistencyLevel));
    }

    client.delete(deleteRequest, new RestStatusToXContentListener<>(channel));
  }
  public static void parseDocuments(
      XContentParser parser,
      List<Item> items,
      @Nullable String defaultIndex,
      @Nullable String defaultType,
      @Nullable String[] defaultFields,
      @Nullable FetchSourceContext defaultFetchSource,
      @Nullable String defaultRouting,
      boolean allowExplicitIndex)
      throws IOException {
    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
      if (token != XContentParser.Token.START_OBJECT) {
        throw new IllegalArgumentException("docs array element should include an object");
      }
      String index = defaultIndex;
      String type = defaultType;
      String id = null;
      String routing = defaultRouting;
      String parent = null;
      List<String> fields = null;
      long version = Versions.MATCH_ANY;
      VersionType versionType = VersionType.INTERNAL;

      FetchSourceContext fetchSourceContext = null;

      while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
        if (token == XContentParser.Token.FIELD_NAME) {
          currentFieldName = parser.currentName();
        } else if (token.isValue()) {
          if ("_index".equals(currentFieldName)) {
            if (!allowExplicitIndex) {
              throw new IllegalArgumentException("explicit index in multi get is not allowed");
            }
            index = parser.text();
          } else if ("_type".equals(currentFieldName)) {
            type = parser.text();
          } else if ("_id".equals(currentFieldName)) {
            id = parser.text();
          } else if ("_routing".equals(currentFieldName) || "routing".equals(currentFieldName)) {
            routing = parser.text();
          } else if ("_parent".equals(currentFieldName) || "parent".equals(currentFieldName)) {
            parent = parser.text();
          } else if ("fields".equals(currentFieldName)) {
            fields = new ArrayList<>();
            fields.add(parser.text());
          } else if ("_version".equals(currentFieldName) || "version".equals(currentFieldName)) {
            version = parser.longValue();
          } else if ("_version_type".equals(currentFieldName)
              || "_versionType".equals(currentFieldName)
              || "version_type".equals(currentFieldName)
              || "versionType".equals(currentFieldName)) {
            versionType = VersionType.fromString(parser.text());
          } else if ("_source".equals(currentFieldName)) {
            if (parser.isBooleanValue()) {
              fetchSourceContext = new FetchSourceContext(parser.booleanValue());
            } else if (token == XContentParser.Token.VALUE_STRING) {
              fetchSourceContext = new FetchSourceContext(new String[] {parser.text()});
            } else {
              throw new ElasticsearchParseException("illegal type for _source: [{}]", token);
            }
          }
        } else if (token == XContentParser.Token.START_ARRAY) {
          if ("fields".equals(currentFieldName)) {
            fields = new ArrayList<>();
            while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
              fields.add(parser.text());
            }
          } else if ("_source".equals(currentFieldName)) {
            ArrayList<String> includes = new ArrayList<>();
            while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
              includes.add(parser.text());
            }
            fetchSourceContext = new FetchSourceContext(includes.toArray(Strings.EMPTY_ARRAY));
          }

        } else if (token == XContentParser.Token.START_OBJECT) {
          if ("_source".equals(currentFieldName)) {
            List<String> currentList = null, includes = null, excludes = null;

            while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
              if (token == XContentParser.Token.FIELD_NAME) {
                currentFieldName = parser.currentName();
                if ("includes".equals(currentFieldName) || "include".equals(currentFieldName)) {
                  currentList = includes != null ? includes : (includes = new ArrayList<>(2));
                } else if ("excludes".equals(currentFieldName)
                    || "exclude".equals(currentFieldName)) {
                  currentList = excludes != null ? excludes : (excludes = new ArrayList<>(2));
                } else {
                  throw new ElasticsearchParseException(
                      "source definition may not contain [{}]", parser.text());
                }
              } else if (token == XContentParser.Token.START_ARRAY) {
                while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
                  currentList.add(parser.text());
                }
              } else if (token.isValue()) {
                currentList.add(parser.text());
              } else {
                throw new ElasticsearchParseException(
                    "unexpected token while parsing source settings");
              }
            }

            fetchSourceContext =
                new FetchSourceContext(
                    includes == null
                        ? Strings.EMPTY_ARRAY
                        : includes.toArray(new String[includes.size()]),
                    excludes == null
                        ? Strings.EMPTY_ARRAY
                        : excludes.toArray(new String[excludes.size()]));
          }
        }
      }
      String[] aFields;
      if (fields != null) {
        aFields = fields.toArray(new String[fields.size()]);
      } else {
        aFields = defaultFields;
      }
      items.add(
          new Item(index, type, id)
              .routing(routing)
              .fields(aFields)
              .parent(parent)
              .version(version)
              .versionType(versionType)
              .fetchSourceContext(
                  fetchSourceContext == null ? defaultFetchSource : fetchSourceContext));
    }
  }
  @Override
  public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client)
      throws IOException {
    UpdateRequest updateRequest =
        new UpdateRequest(request.param("index"), request.param("type"), request.param("id"));
    updateRequest.routing(request.param("routing"));
    updateRequest.parent(request.param("parent"));
    updateRequest.timeout(request.paramAsTime("timeout", updateRequest.timeout()));
    updateRequest.setRefreshPolicy(request.param("refresh"));
    String waitForActiveShards = request.param("wait_for_active_shards");
    if (waitForActiveShards != null) {
      updateRequest.waitForActiveShards(ActiveShardCount.parseString(waitForActiveShards));
    }
    updateRequest.docAsUpsert(request.paramAsBoolean("doc_as_upsert", updateRequest.docAsUpsert()));
    FetchSourceContext fetchSourceContext = FetchSourceContext.parseFromRestRequest(request);
    String sField = request.param("fields");
    if (sField != null && fetchSourceContext != null) {
      throw new IllegalArgumentException(
          "[fields] and [_source] cannot be used in the same request");
    }
    if (sField != null) {
      DEPRECATION_LOGGER.deprecated("Deprecated field [fields] used, expected [_source] instead");
      String[] sFields = Strings.splitStringByCommaToArray(sField);
      updateRequest.fields(sFields);
    } else if (fetchSourceContext != null) {
      updateRequest.fetchSource(fetchSourceContext);
    }

    updateRequest.retryOnConflict(
        request.paramAsInt("retry_on_conflict", updateRequest.retryOnConflict()));
    updateRequest.version(RestActions.parseVersion(request));
    updateRequest.versionType(
        VersionType.fromString(request.param("version_type"), updateRequest.versionType()));

    // see if we have it in the body
    if (request.hasContent()) {
      updateRequest.fromXContent(request.content());
      IndexRequest upsertRequest = updateRequest.upsertRequest();
      if (upsertRequest != null) {
        upsertRequest.routing(request.param("routing"));
        upsertRequest.parent(
            request.param(
                "parent")); // order is important, set it after routing, so it will set the routing
        upsertRequest.timestamp(request.param("timestamp"));
        if (request.hasParam("ttl")) {
          upsertRequest.ttl(request.param("ttl"));
        }
        upsertRequest.version(RestActions.parseVersion(request));
        upsertRequest.versionType(
            VersionType.fromString(request.param("version_type"), upsertRequest.versionType()));
      }
      IndexRequest doc = updateRequest.doc();
      if (doc != null) {
        doc.routing(request.param("routing"));
        doc.parent(
            request.param(
                "parent")); // order is important, set it after routing, so it will set the routing
        doc.timestamp(request.param("timestamp"));
        if (request.hasParam("ttl")) {
          doc.ttl(request.param("ttl"));
        }
        doc.version(RestActions.parseVersion(request));
        doc.versionType(VersionType.fromString(request.param("version_type"), doc.versionType()));
      }
    }

    return channel ->
        client.update(
            updateRequest,
            new RestStatusToXContentListener<>(
                channel, r -> r.getLocation(updateRequest.routing())));
  }
      private void parse(BytesReference data) throws Exception {
        XContent xContent = XContentFactory.xContent(data);
        String source = XContentBuilder.builder(xContent).string();
        int from = 0;
        int length = data.length();
        byte marker = xContent.streamSeparator();
        int nextMarker = findNextMarker(marker, from, data, length);
        if (nextMarker == -1) {
          nextMarker = length;
        }
        // now parse the action
        XContentParser parser = xContent.createParser(data.slice(from, nextMarker - from));

        try {
          // move pointers
          from = nextMarker + 1;

          // Move to START_OBJECT
          XContentParser.Token token = parser.nextToken();
          if (token == null) {
            throw new Exception("Wrong object structure");
          }
          assert token == XContentParser.Token.START_OBJECT;
          // Move to FIELD_NAME, that's the action
          // token = parser.nextToken();
          // assert token == XContentParser.Token.FIELD_NAME;
          // String action = parser.currentName();

          String id = null;
          String routing = null;
          String parent = null;
          String timestamp = null;
          Long ttl = null;
          String opType = null;
          long version = 0;
          VersionType versionType = VersionType.INTERNAL;
          String percolate = null;

          // at this stage, next token can either be END_OBJECT
          // (and use default index and type, with auto generated
          // id)
          // or START_OBJECT which will have another set of
          // parameters

          String currentFieldName = null;
          while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
            if (token == XContentParser.Token.FIELD_NAME) {
              currentFieldName = parser.currentName();
            } else if (token.isValue()) {
              if ("_index".equals(currentFieldName)) {
                index = parser.text();
              } else if ("_type".equals(currentFieldName)) {
                type = parser.text();
              } else if ("_queryString".equals(currentFieldName)) {
                queryString = parser.text();
              } else if ("_id".equals(currentFieldName)) {
                id = parser.text();
              } else if ("_routing".equals(currentFieldName)
                  || "routing".equals(currentFieldName)) {
                routing = parser.text();
              } else if ("_parent".equals(currentFieldName) || "parent".equals(currentFieldName)) {
                parent = parser.text();
              } else if ("_timestamp".equals(currentFieldName)
                  || "timestamp".equals(currentFieldName)) {
                timestamp = parser.text();
              } else if ("_ttl".equals(currentFieldName) || "ttl".equals(currentFieldName)) {
                if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
                  ttl = TimeValue.parseTimeValue(parser.text(), null).millis();
                } else {
                  ttl = parser.longValue();
                }
              } else if ("op_type".equals(currentFieldName) || "opType".equals(currentFieldName)) {
                opType = parser.text();
              } else if ("_version".equals(currentFieldName)
                  || "version".equals(currentFieldName)) {
                version = parser.longValue();
              } else if ("_version_type".equals(currentFieldName)
                  || "_versionType".equals(currentFieldName)
                  || "version_type".equals(currentFieldName)
                  || "versionType".equals(currentFieldName)) {
                versionType = VersionType.fromString(parser.text());
              } else if ("percolate".equals(currentFieldName)
                  || "_percolate".equals(currentFieldName)) {
                percolate = parser.textOrNull();
              }
            }
          }
          if (nextMarker < length) {
            nextMarker = findNextMarker(marker, from, data, length);
            if (nextMarker == -1) {
              nextMarker = length;
            }
            content = getString(data.slice(from, nextMarker - from));
          }

        } finally {
          parser.close();
        }
      }
  public void testSerialization() throws IOException {
    MultiGetRequest multiGetRequest = new MultiGetRequest();
    if (randomBoolean()) {
      multiGetRequest.preference(randomAsciiOfLength(randomIntBetween(1, 10)));
    }
    if (randomBoolean()) {
      multiGetRequest.realtime(false);
    }
    if (randomBoolean()) {
      multiGetRequest.refresh(true);
    }
    multiGetRequest.ignoreErrorsOnGeneratedFields(randomBoolean());

    MultiGetShardRequest multiGetShardRequest =
        new MultiGetShardRequest(multiGetRequest, "index", 0);
    int numItems = iterations(10, 30);
    for (int i = 0; i < numItems; i++) {
      MultiGetRequest.Item item =
          new MultiGetRequest.Item(
              "alias-" + randomAsciiOfLength(randomIntBetween(1, 10)), "type", "id-" + i);
      if (randomBoolean()) {
        int numFields = randomIntBetween(1, 5);
        String[] fields = new String[numFields];
        for (int j = 0; j < fields.length; j++) {
          fields[j] = randomAsciiOfLength(randomIntBetween(1, 10));
        }
        item.fields(fields);
      }
      if (randomBoolean()) {
        item.version(randomIntBetween(1, Integer.MAX_VALUE));
        item.versionType(randomFrom(VersionType.values()));
      }
      if (randomBoolean()) {
        item.fetchSourceContext(new FetchSourceContext(randomBoolean()));
      }
      multiGetShardRequest.add(0, item);
    }

    BytesStreamOutput out = new BytesStreamOutput();
    out.setVersion(randomVersion(random()));
    multiGetShardRequest.writeTo(out);

    StreamInput in = StreamInput.wrap(out.bytes());
    in.setVersion(out.getVersion());
    MultiGetShardRequest multiGetShardRequest2 = new MultiGetShardRequest();
    multiGetShardRequest2.readFrom(in);

    assertThat(multiGetShardRequest2.index(), equalTo(multiGetShardRequest.index()));
    assertThat(multiGetShardRequest2.preference(), equalTo(multiGetShardRequest.preference()));
    assertThat(multiGetShardRequest2.realtime(), equalTo(multiGetShardRequest.realtime()));
    assertThat(multiGetShardRequest2.refresh(), equalTo(multiGetShardRequest.refresh()));
    assertThat(
        multiGetShardRequest2.ignoreErrorsOnGeneratedFields(),
        equalTo(multiGetShardRequest.ignoreErrorsOnGeneratedFields()));
    assertThat(multiGetShardRequest2.items.size(), equalTo(multiGetShardRequest.items.size()));
    for (int i = 0; i < multiGetShardRequest2.items.size(); i++) {
      MultiGetRequest.Item item = multiGetShardRequest.items.get(i);
      MultiGetRequest.Item item2 = multiGetShardRequest2.items.get(i);
      assertThat(item2.index(), equalTo(item.index()));
      assertThat(item2.type(), equalTo(item.type()));
      assertThat(item2.id(), equalTo(item.id()));
      assertThat(item2.fields(), equalTo(item.fields()));
      assertThat(item2.version(), equalTo(item.version()));
      assertThat(item2.versionType(), equalTo(item.versionType()));
      assertThat(item2.fetchSourceContext(), equalTo(item.fetchSourceContext()));
    }
    assertThat(multiGetShardRequest2.indices(), equalTo(multiGetShardRequest.indices()));
    assertThat(
        multiGetShardRequest2.indicesOptions(), equalTo(multiGetShardRequest.indicesOptions()));
  }
  @Override
  public void handleRequest(final RestRequest request, final RestChannel channel) {
    UpdateRequest updateRequest =
        new UpdateRequest(request.param("index"), request.param("type"), request.param("id"));
    updateRequest.listenerThreaded(false);
    updateRequest.routing(request.param("routing"));
    updateRequest.parent(
        request.param(
            "parent")); // order is important, set it after routing, so it will set the routing
    updateRequest.timeout(request.paramAsTime("timeout", updateRequest.timeout()));
    updateRequest.refresh(request.paramAsBoolean("refresh", updateRequest.refresh()));
    String replicationType = request.param("replication");
    if (replicationType != null) {
      updateRequest.replicationType(ReplicationType.fromString(replicationType));
    }
    String consistencyLevel = request.param("consistency");
    if (consistencyLevel != null) {
      updateRequest.consistencyLevel(WriteConsistencyLevel.fromString(consistencyLevel));
    }
    updateRequest.percolate(request.param("percolate", null));
    updateRequest.script(request.param("script"));
    updateRequest.scriptLang(request.param("lang"));
    for (Map.Entry<String, String> entry : request.params().entrySet()) {
      if (entry.getKey().startsWith("sp_")) {
        updateRequest.addScriptParam(entry.getKey().substring(3), entry.getValue());
      }
    }
    String sField = request.param("fields");
    if (sField != null) {
      String[] sFields = Strings.splitStringByCommaToArray(sField);
      if (sFields != null) {
        updateRequest.fields(sFields);
      }
    }
    updateRequest.retryOnConflict(
        request.paramAsInt("retry_on_conflict", updateRequest.retryOnConflict()));

    // see if we have it in the body
    if (request.hasContent()) {
      try {
        updateRequest.source(
            request.contentByteArray(), request.contentByteArrayOffset(), request.contentLength());
        IndexRequest upsertRequest = updateRequest.upsertRequest();
        if (upsertRequest != null) {
          upsertRequest.routing(request.param("routing"));
          upsertRequest.parent(
              request.param(
                  "parent")); // order is important, set it after routing, so it will set the
          // routing
          upsertRequest.timestamp(request.param("timestamp"));
          if (request.hasParam("ttl")) {
            upsertRequest.ttl(request.paramAsTime("ttl", null).millis());
          }
          upsertRequest.version(RestActions.parseVersion(request));
          upsertRequest.versionType(
              VersionType.fromString(request.param("version_type"), upsertRequest.versionType()));
        }
        IndexRequest doc = updateRequest.doc();
        if (doc != null) {
          doc.routing(request.param("routing"));
          doc.parent(
              request.param(
                  "parent")); // order is important, set it after routing, so it will set the
          // routing
          doc.timestamp(request.param("timestamp"));
          if (request.hasParam("ttl")) {
            doc.ttl(request.paramAsTime("ttl", null).millis());
          }
          doc.version(RestActions.parseVersion(request));
          doc.versionType(VersionType.fromString(request.param("version_type"), doc.versionType()));
        }
      } catch (Exception e) {
        try {
          channel.sendResponse(new XContentThrowableRestResponse(request, e));
        } catch (IOException e1) {
          logger.warn("Failed to send response", e1);
        }
        return;
      }
    }

    client.update(
        updateRequest,
        new ActionListener<UpdateResponse>() {
          @Override
          public void onResponse(UpdateResponse response) {
            try {
              XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
              builder
                  .startObject()
                  .field(Fields.OK, true)
                  .field(Fields._INDEX, response.index())
                  .field(Fields._TYPE, response.type())
                  .field(Fields._ID, response.id())
                  .field(Fields._VERSION, response.version());

              if (response.getResult() != null) {
                builder.startObject(Fields.GET);
                response.getResult().toXContentEmbedded(builder, request);
                builder.endObject();
              }

              if (response.matches() != null) {
                builder.startArray(Fields.MATCHES);
                for (String match : response.matches()) {
                  builder.value(match);
                }
                builder.endArray();
              }
              builder.endObject();
              RestStatus status = OK;
              if (response.version() == 1) {
                status = CREATED;
              }
              channel.sendResponse(new XContentRestResponse(request, status, builder));
            } catch (Exception e) {
              onFailure(e);
            }
          }

          @Override
          public void onFailure(Throwable e) {
            try {
              channel.sendResponse(new XContentThrowableRestResponse(request, e));
            } catch (IOException e1) {
              logger.error("Failed to send failure response", e1);
            }
          }
        });
  }