/**
  * Indicates whether the response should contain the stored _source.
  *
  * @return this for chaining
  */
 public GetRequestBuilder setFetchSource(boolean fetch) {
   FetchSourceContext context =
       request.fetchSourceContext() == null
           ? FetchSourceContext.FETCH_SOURCE
           : request.fetchSourceContext();
   request.fetchSourceContext(
       new FetchSourceContext(fetch, context.includes(), context.excludes()));
   return this;
 }
 /**
  * Indicate that _source should be returned, with an "include" and/or "exclude" set which can
  * include simple wildcard elements.
  *
  * @param includes An optional list of include (optionally wildcarded) pattern to filter the
  *     returned _source
  * @param excludes An optional list of exclude (optionally wildcarded) pattern to filter the
  *     returned _source
  */
 public GetRequestBuilder setFetchSource(
     @Nullable String[] includes, @Nullable String[] excludes) {
   FetchSourceContext context =
       request.fetchSourceContext() == null
           ? FetchSourceContext.FETCH_SOURCE
           : request.fetchSourceContext();
   request.fetchSourceContext(new FetchSourceContext(context.fetchSource(), includes, excludes));
   return this;
 }
 @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 boolean equals(Object o) {
      if (this == o) return true;
      if (!(o instanceof Item)) return false;

      Item item = (Item) o;

      if (version != item.version) return false;
      if (fetchSourceContext != null
          ? !fetchSourceContext.equals(item.fetchSourceContext)
          : item.fetchSourceContext != null) return false;
      if (!Arrays.equals(storedFields, item.storedFields)) return false;
      if (!id.equals(item.id)) return false;
      if (!index.equals(item.index)) return false;
      if (routing != null ? !routing.equals(item.routing) : item.routing != null) return false;
      if (parent != null ? !parent.equals(item.parent) : item.parent != null) return false;
      if (type != null ? !type.equals(item.type) : item.type != null) return false;
      if (versionType != item.versionType) return false;

      return true;
    }
  @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())));
  }
  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> storedFields = null;
      long version = Versions.MATCH_ANY;
      VersionType versionType = VersionType.INTERNAL;

      FetchSourceContext fetchSourceContext = FetchSourceContext.FETCH_SOURCE;

      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)) {
            throw new ParsingException(
                parser.getTokenLocation(),
                "Unsupported field [fields] used, expected [stored_fields] instead");
          } else if ("stored_fields".equals(currentFieldName)) {
            storedFields = new ArrayList<>();
            storedFields.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(),
                      fetchSourceContext.includes(),
                      fetchSourceContext.excludes());
            } else if (token == XContentParser.Token.VALUE_STRING) {
              fetchSourceContext =
                  new FetchSourceContext(
                      fetchSourceContext.fetchSource(),
                      new String[] {parser.text()},
                      fetchSourceContext.excludes());
            } else {
              throw new ElasticsearchParseException("illegal type for _source: [{}]", token);
            }
          }
        } else if (token == XContentParser.Token.START_ARRAY) {
          if ("fields".equals(currentFieldName)) {
            throw new ParsingException(
                parser.getTokenLocation(),
                "Unsupported field [fields] used, expected [stored_fields] instead");
          } else if ("stored_fields".equals(currentFieldName)) {
            storedFields = new ArrayList<>();
            while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
              storedFields.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(
                    fetchSourceContext.fetchSource(),
                    includes.toArray(Strings.EMPTY_ARRAY),
                    fetchSourceContext.excludes());
          }

        } 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(
                    fetchSourceContext.fetchSource(),
                    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 (storedFields != null) {
        aFields = storedFields.toArray(new String[storedFields.size()]);
      } else {
        aFields = defaultFields;
      }
      items.add(
          new Item(index, type, id)
              .routing(routing)
              .storedFields(aFields)
              .parent(parent)
              .version(version)
              .versionType(versionType)
              .fetchSourceContext(
                  fetchSourceContext == FetchSourceContext.FETCH_SOURCE
                      ? defaultFetchSource
                      : fetchSourceContext));
    }
  }
 @Override
 public boolean sourceRequested() {
   return fetchSourceContext != null && fetchSourceContext.fetchSource();
 }