@Test
  public void testUpsertFields() throws Exception {
    createIndex();
    ensureGreen();

    UpdateResponse updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setUpsert(XContentFactory.jsonBuilder().startObject().field("bar", "baz").endObject())
            .setScript("ctx._source.extra = \"foo\"", ScriptService.ScriptType.INLINE)
            .setFields("_source")
            .execute()
            .actionGet();

    assertThat(updateResponse.getGetResult(), notNullValue());
    assertThat(updateResponse.getGetResult().sourceAsMap().get("bar").toString(), equalTo("baz"));
    assertThat(updateResponse.getGetResult().sourceAsMap().get("extra"), nullValue());

    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setUpsert(XContentFactory.jsonBuilder().startObject().field("bar", "baz").endObject())
            .setScript("ctx._source.extra = \"foo\"", ScriptService.ScriptType.INLINE)
            .setFields("_source")
            .execute()
            .actionGet();

    assertThat(updateResponse.getGetResult(), notNullValue());
    assertThat(updateResponse.getGetResult().sourceAsMap().get("bar").toString(), equalTo("baz"));
    assertThat(updateResponse.getGetResult().sourceAsMap().get("extra").toString(), equalTo("foo"));
  }
  @Test
  public void testUpsert() throws Exception {
    createIndex();
    ensureGreen();

    UpdateResponse updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setUpsert(XContentFactory.jsonBuilder().startObject().field("field", 1).endObject())
            .setScript("ctx._source.field += 1", ScriptService.ScriptType.INLINE)
            .execute()
            .actionGet();
    assertTrue(updateResponse.isCreated());

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("1"));
    }

    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setUpsert(XContentFactory.jsonBuilder().startObject().field("field", 1).endObject())
            .setScript("ctx._source.field += 1", ScriptService.ScriptType.INLINE)
            .execute()
            .actionGet();
    assertFalse(updateResponse.isCreated());

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("2"));
    }
  }
  public void testUpdate() {
    createIndexWithAlias();
    ensureYellow("test");

    UpdateRequestBuilder updateRequestBuilder =
        client()
            .prepareUpdate(indexOrAlias(), "type1", "1")
            .setUpsert("field1", "value1")
            .setDoc("field2", "value2");

    UpdateResponse updateResponse = updateRequestBuilder.get();
    assertThat(updateResponse.getIndex(), equalTo("test"));
    assertThat(updateResponse.getType(), equalTo("type1"));
    assertThat(updateResponse.getId(), equalTo("1"));
    assertThat(updateResponse.isCreated(), equalTo(true));

    GetResponse getResponse = client().prepareGet("test", "type1", "1").get();
    assertThat(getResponse.isExists(), equalTo(true));
    assertThat(getResponse.getSourceAsMap().containsKey("field1"), equalTo(true));
    assertThat(getResponse.getSourceAsMap().containsKey("field2"), equalTo(false));

    updateResponse = updateRequestBuilder.get();
    assertThat(updateResponse.getIndex(), equalTo("test"));
    assertThat(updateResponse.getType(), equalTo("type1"));
    assertThat(updateResponse.getId(), equalTo("1"));
    assertThat(updateResponse.isCreated(), equalTo(false));

    getResponse = client().prepareGet("test", "type1", "1").get();
    assertThat(getResponse.isExists(), equalTo(true));
    assertThat(getResponse.getSourceAsMap().containsKey("field1"), equalTo(true));
    assertThat(getResponse.getSourceAsMap().containsKey("field2"), equalTo(true));
  }
  @Test
  public void testWithDeletes() throws Exception {
    String indexName = "xyz";
    assertAcked(
        prepareCreate(indexName)
            .addMapping("parent")
            .addMapping("child", "_parent", "type=parent", "count", "type=long"));

    List<IndexRequestBuilder> requests = new ArrayList<>();
    requests.add(client().prepareIndex(indexName, "parent", "1").setSource("{}"));
    requests.add(
        client().prepareIndex(indexName, "child", "0").setParent("1").setSource("count", 1));
    requests.add(
        client().prepareIndex(indexName, "child", "1").setParent("1").setSource("count", 1));
    requests.add(
        client().prepareIndex(indexName, "child", "2").setParent("1").setSource("count", 1));
    requests.add(
        client().prepareIndex(indexName, "child", "3").setParent("1").setSource("count", 1));
    indexRandom(true, requests);

    for (int i = 0; i < 10; i++) {
      SearchResponse searchResponse =
          client()
              .prepareSearch(indexName)
              .addAggregation(
                  children("children")
                      .childType("child")
                      .subAggregation(sum("counts").field("count")))
              .get();

      assertNoFailures(searchResponse);
      Children children = searchResponse.getAggregations().get("children");
      assertThat(children.getDocCount(), equalTo(4l));

      Sum count = children.getAggregations().get("counts");
      assertThat(count.getValue(), equalTo(4.));

      String idToUpdate = Integer.toString(randomInt(3));
      UpdateResponse updateResponse =
          client()
              .prepareUpdate(indexName, "child", idToUpdate)
              .setParent("1")
              .setDoc("count", 1)
              .get();
      assertThat(updateResponse.getVersion(), greaterThan(1l));
      refresh();
    }
  }
  @Test
  public void testUpsertDoc() throws Exception {
    createIndex();
    ensureGreen();

    UpdateResponse updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("bar", "baz").endObject())
            .setDocAsUpsert(true)
            .setFields("_source")
            .execute()
            .actionGet();
    assertThat(updateResponse.getGetResult(), notNullValue());
    assertThat(updateResponse.getGetResult().sourceAsMap().get("bar").toString(), equalTo("baz"));
  }
  public void testUpdateDelete() {
    // update action goes to the primary, delete op gets executed locally, then replicated
    String[] updateShardActions =
        new String[] {UpdateAction.NAME + "[s]", DeleteAction.NAME + "[r]"};
    interceptTransportActions(updateShardActions);

    String indexOrAlias = randomIndexOrAlias();
    client().prepareIndex(indexOrAlias, "type", "id").setSource("field", "value").get();
    UpdateRequest updateRequest =
        new UpdateRequest(indexOrAlias, "type", "id").script(new Script("ctx.op='delete'"));
    UpdateResponse updateResponse =
        internalCluster().clientNodeClient().update(updateRequest).actionGet();
    assertThat(updateResponse.isCreated(), equalTo(false));

    clearInterceptedActions();
    assertSameIndices(updateRequest, updateShardActions);
  }
  public static String save(
      ElasticSearchContextListener es, String userId, String memeId, String value) {
    try {
      String pendingItemId = getPendingItemId(es, userId, memeId);
      Map<String, Object> data = getRatingsObjectMap(userId, memeId, value);

      if (pendingItemId != null) {
        UpdateResponse response = es.updateRequest(INDEX_NAME, pendingItemId, data).actionGet();
        return response.getId();
      } else {
        IndexResponse response = es.indexRequest(INDEX_NAME, data).actionGet();
        return response.getId();
      }
    } catch (Exception e) {
      return null;
    }
  }
  public void testUpdateUpsert() {
    // update action goes to the primary, index op gets executed locally, then replicated
    String[] updateShardActions =
        new String[] {UpdateAction.NAME + "[s]", IndexAction.NAME + "[r]"};
    interceptTransportActions(updateShardActions);

    String indexOrAlias = randomIndexOrAlias();
    UpdateRequest updateRequest =
        new UpdateRequest(indexOrAlias, "type", "id")
            .upsert("field", "value")
            .doc("field1", "value1");
    UpdateResponse updateResponse =
        internalCluster().clientNodeClient().update(updateRequest).actionGet();
    assertThat(updateResponse.isCreated(), equalTo(true));

    clearInterceptedActions();
    assertSameIndices(updateRequest, updateShardActions);
  }
  @Test
  public void testUpdate() throws Exception {
    createIndex();
    ClusterHealthResponse clusterHealth =
        client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
    assertThat(clusterHealth.timedOut(), equalTo(false));
    assertThat(clusterHealth.status(), equalTo(ClusterHealthStatus.GREEN));

    try {
      client
          .prepareUpdate("test", "type1", "1")
          .setScript("ctx._source.field++")
          .execute()
          .actionGet();
      assert false;
    } catch (DocumentMissingException e) {
      // all is well
    }

    client.prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();

    UpdateResponse updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += 1")
            .execute()
            .actionGet();
    assertThat(updateResponse.version(), equalTo(2L));

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.sourceAsMap().get("field").toString(), equalTo("2"));
    }

    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += count")
            .addScriptParam("count", 3)
            .execute()
            .actionGet();
    assertThat(updateResponse.version(), equalTo(3L));

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.sourceAsMap().get("field").toString(), equalTo("5"));
    }

    // check noop
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx.op = 'none'")
            .execute()
            .actionGet();
    assertThat(updateResponse.version(), equalTo(3L));

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.sourceAsMap().get("field").toString(), equalTo("5"));
    }

    // check delete
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx.op = 'delete'")
            .execute()
            .actionGet();
    assertThat(updateResponse.version(), equalTo(4L));

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.exists(), equalTo(false));
    }

    // check percolation
    client.prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();
    logger.info("--> register a query");
    client
        .prepareIndex("_percolator", "test", "1")
        .setSource(jsonBuilder().startObject().field("query", termQuery("field", 2)).endObject())
        .setRefresh(true)
        .execute()
        .actionGet();
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += 1")
            .setPercolate("*")
            .execute()
            .actionGet();
    assertThat(updateResponse.matches().size(), equalTo(1));

    // check TTL is kept after an update without TTL
    client
        .prepareIndex("test", "type1", "2")
        .setSource("field", 1)
        .setTTL(86400000L)
        .setRefresh(true)
        .execute()
        .actionGet();
    GetResponse getResponse =
        client.prepareGet("test", "type1", "2").setFields("_ttl").execute().actionGet();
    long ttl = ((Number) getResponse.field("_ttl").value()).longValue();
    assertThat(ttl, greaterThan(0L));
    client
        .prepareUpdate("test", "type1", "2")
        .setScript("ctx._source.field += 1")
        .execute()
        .actionGet();
    getResponse = client.prepareGet("test", "type1", "2").setFields("_ttl").execute().actionGet();
    ttl = ((Number) getResponse.field("_ttl").value()).longValue();
    assertThat(ttl, greaterThan(0L));

    // check TTL update
    client
        .prepareUpdate("test", "type1", "2")
        .setScript("ctx._ttl = 3600000")
        .execute()
        .actionGet();
    getResponse = client.prepareGet("test", "type1", "2").setFields("_ttl").execute().actionGet();
    ttl = ((Number) getResponse.field("_ttl").value()).longValue();
    assertThat(ttl, greaterThan(0L));
    assertThat(ttl, lessThanOrEqualTo(3600000L));

    // check timestamp update
    client
        .prepareIndex("test", "type1", "3")
        .setSource("field", 1)
        .setRefresh(true)
        .execute()
        .actionGet();
    client
        .prepareUpdate("test", "type1", "3")
        .setScript("ctx._timestamp = \"2009-11-15T14:12:12\"")
        .execute()
        .actionGet();
    getResponse =
        client.prepareGet("test", "type1", "3").setFields("_timestamp").execute().actionGet();
    long timestamp = ((Number) getResponse.field("_timestamp").value()).longValue();
    assertThat(timestamp, equalTo(1258294332000L));

    // check fields parameter
    client.prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += 1")
            .setFields("_source", "field")
            .execute()
            .actionGet();
    assertThat(updateResponse.getResult(), notNullValue());
    assertThat(updateResponse.getResult().sourceRef(), notNullValue());
    assertThat(updateResponse.getResult().field("field").value(), notNullValue());

    // check updates without script
    // add new field
    client.prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("field2", 2).endObject())
            .execute()
            .actionGet();
    for (int i = 0; i < 5; i++) {
      getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.sourceAsMap().get("field").toString(), equalTo("1"));
      assertThat(getResponse.sourceAsMap().get("field2").toString(), equalTo("2"));
    }

    // change existing field
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("field", 3).endObject())
            .execute()
            .actionGet();
    for (int i = 0; i < 5; i++) {
      getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.sourceAsMap().get("field").toString(), equalTo("3"));
      assertThat(getResponse.sourceAsMap().get("field2").toString(), equalTo("2"));
    }

    // recursive map
    Map<String, Object> testMap = new HashMap<String, Object>();
    Map<String, Object> testMap2 = new HashMap<String, Object>();
    Map<String, Object> testMap3 = new HashMap<String, Object>();
    testMap3.put("commonkey", testMap);
    testMap3.put("map3", 5);
    testMap2.put("map2", 6);
    testMap.put("commonkey", testMap2);
    testMap.put("map1", 8);

    client.prepareIndex("test", "type1", "1").setSource("map", testMap).execute().actionGet();
    updateResponse =
        client
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("map", testMap3).endObject())
            .execute()
            .actionGet();
    for (int i = 0; i < 5; i++) {
      getResponse = client.prepareGet("test", "type1", "1").execute().actionGet();
      Map map1 = (Map) getResponse.sourceAsMap().get("map");
      assertThat(map1.size(), equalTo(3));
      assertThat(map1.containsKey("map1"), equalTo(true));
      assertThat(map1.containsKey("map3"), equalTo(true));
      assertThat(map1.containsKey("commonkey"), equalTo(true));
      Map map2 = (Map) map1.get("commonkey");
      assertThat(map2.size(), equalTo(3));
      assertThat(map2.containsKey("map1"), equalTo(true));
      assertThat(map2.containsKey("map2"), equalTo(true));
      assertThat(map2.containsKey("commonkey"), equalTo(true));
    }
  }
  @Override
  protected Tuple<BulkShardResponse, BulkShardRequest> shardOperationOnPrimary(
      ClusterState clusterState, PrimaryOperationRequest shardRequest) {
    final BulkShardRequest request = shardRequest.request;
    final IndexService indexService = indicesService.indexServiceSafe(request.index());
    final IndexShard indexShard = indexService.shardSafe(shardRequest.shardId.id());

    long[] preVersions = new long[request.items().length];
    VersionType[] preVersionTypes = new VersionType[request.items().length];
    Translog.Location location = null;
    for (int requestIndex = 0; requestIndex < request.items().length; requestIndex++) {
      BulkItemRequest item = request.items()[requestIndex];
      if (item.request() instanceof IndexRequest) {
        IndexRequest indexRequest = (IndexRequest) item.request();
        preVersions[requestIndex] = indexRequest.version();
        preVersionTypes[requestIndex] = indexRequest.versionType();
        try {
          WriteResult<IndexResponse> result =
              shardIndexOperation(request, indexRequest, clusterState, indexShard, true);
          location = locationToSync(location, result.location);
          // add the response
          IndexResponse indexResponse = result.response();
          setResponse(
              item,
              new BulkItemResponse(item.id(), indexRequest.opType().lowercase(), indexResponse));
        } catch (Throwable e) {
          // rethrow the failure if we are going to retry on primary and let parent failure to
          // handle it
          if (retryPrimaryException(e)) {
            // restore updated versions...
            for (int j = 0; j < requestIndex; j++) {
              applyVersion(request.items()[j], preVersions[j], preVersionTypes[j]);
            }
            throw (ElasticsearchException) e;
          }
          if (ExceptionsHelper.status(e) == RestStatus.CONFLICT) {
            logger.trace(
                "{} failed to execute bulk item (index) {}", e, shardRequest.shardId, indexRequest);
          } else {
            logger.debug(
                "{} failed to execute bulk item (index) {}", e, shardRequest.shardId, indexRequest);
          }
          // if its a conflict failure, and we already executed the request on a primary (and we
          // execute it
          // again, due to primary relocation and only processing up to N bulk items when the shard
          // gets closed)
          // then just use the response we got from the successful execution
          if (item.getPrimaryResponse() != null && isConflictException(e)) {
            setResponse(item, item.getPrimaryResponse());
          } else {
            setResponse(
                item,
                new BulkItemResponse(
                    item.id(),
                    indexRequest.opType().lowercase(),
                    new BulkItemResponse.Failure(
                        request.index(), indexRequest.type(), indexRequest.id(), e)));
          }
        }
      } else if (item.request() instanceof DeleteRequest) {
        DeleteRequest deleteRequest = (DeleteRequest) item.request();
        preVersions[requestIndex] = deleteRequest.version();
        preVersionTypes[requestIndex] = deleteRequest.versionType();

        try {
          // add the response
          final WriteResult<DeleteResponse> writeResult =
              shardDeleteOperation(request, deleteRequest, indexShard);
          DeleteResponse deleteResponse = writeResult.response();
          location = locationToSync(location, writeResult.location);
          setResponse(item, new BulkItemResponse(item.id(), OP_TYPE_DELETE, deleteResponse));
        } catch (Throwable e) {
          // rethrow the failure if we are going to retry on primary and let parent failure to
          // handle it
          if (retryPrimaryException(e)) {
            // restore updated versions...
            for (int j = 0; j < requestIndex; j++) {
              applyVersion(request.items()[j], preVersions[j], preVersionTypes[j]);
            }
            throw (ElasticsearchException) e;
          }
          if (ExceptionsHelper.status(e) == RestStatus.CONFLICT) {
            logger.trace(
                "{} failed to execute bulk item (delete) {}",
                e,
                shardRequest.shardId,
                deleteRequest);
          } else {
            logger.debug(
                "{} failed to execute bulk item (delete) {}",
                e,
                shardRequest.shardId,
                deleteRequest);
          }
          // if its a conflict failure, and we already executed the request on a primary (and we
          // execute it
          // again, due to primary relocation and only processing up to N bulk items when the shard
          // gets closed)
          // then just use the response we got from the successful execution
          if (item.getPrimaryResponse() != null && isConflictException(e)) {
            setResponse(item, item.getPrimaryResponse());
          } else {
            setResponse(
                item,
                new BulkItemResponse(
                    item.id(),
                    OP_TYPE_DELETE,
                    new BulkItemResponse.Failure(
                        request.index(), deleteRequest.type(), deleteRequest.id(), e)));
          }
        }
      } else if (item.request() instanceof UpdateRequest) {
        UpdateRequest updateRequest = (UpdateRequest) item.request();
        preVersions[requestIndex] = updateRequest.version();
        preVersionTypes[requestIndex] = updateRequest.versionType();
        //  We need to do the requested retries plus the initial attempt. We don't do <
        // 1+retry_on_conflict because retry_on_conflict may be Integer.MAX_VALUE
        for (int updateAttemptsCount = 0;
            updateAttemptsCount <= updateRequest.retryOnConflict();
            updateAttemptsCount++) {
          UpdateResult updateResult;
          try {
            updateResult = shardUpdateOperation(clusterState, request, updateRequest, indexShard);
          } catch (Throwable t) {
            updateResult = new UpdateResult(null, null, false, t, null);
          }
          if (updateResult.success()) {
            if (updateResult.writeResult != null) {
              location = locationToSync(location, updateResult.writeResult.location);
            }
            switch (updateResult.result.operation()) {
              case UPSERT:
              case INDEX:
                WriteResult<IndexResponse> result = updateResult.writeResult;
                IndexRequest indexRequest = updateResult.request();
                BytesReference indexSourceAsBytes = indexRequest.source();
                // add the response
                IndexResponse indexResponse = result.response();
                UpdateResponse updateResponse =
                    new UpdateResponse(
                        indexResponse.getShardInfo(),
                        indexResponse.getIndex(),
                        indexResponse.getType(),
                        indexResponse.getId(),
                        indexResponse.getVersion(),
                        indexResponse.isCreated());
                if (updateRequest.fields() != null && updateRequest.fields().length > 0) {
                  Tuple<XContentType, Map<String, Object>> sourceAndContent =
                      XContentHelper.convertToMap(indexSourceAsBytes, true);
                  updateResponse.setGetResult(
                      updateHelper.extractGetResult(
                          updateRequest,
                          shardRequest.request.index(),
                          indexResponse.getVersion(),
                          sourceAndContent.v2(),
                          sourceAndContent.v1(),
                          indexSourceAsBytes));
                }
                item =
                    request.items()[requestIndex] =
                        new BulkItemRequest(request.items()[requestIndex].id(), indexRequest);
                setResponse(item, new BulkItemResponse(item.id(), OP_TYPE_UPDATE, updateResponse));
                break;
              case DELETE:
                WriteResult<DeleteResponse> writeResult = updateResult.writeResult;
                DeleteResponse response = writeResult.response();
                DeleteRequest deleteRequest = updateResult.request();
                updateResponse =
                    new UpdateResponse(
                        response.getShardInfo(),
                        response.getIndex(),
                        response.getType(),
                        response.getId(),
                        response.getVersion(),
                        false);
                updateResponse.setGetResult(
                    updateHelper.extractGetResult(
                        updateRequest,
                        shardRequest.request.index(),
                        response.getVersion(),
                        updateResult.result.updatedSourceAsMap(),
                        updateResult.result.updateSourceContentType(),
                        null));
                // Replace the update request to the translated delete request to execute on the
                // replica.
                item =
                    request.items()[requestIndex] =
                        new BulkItemRequest(request.items()[requestIndex].id(), deleteRequest);
                setResponse(item, new BulkItemResponse(item.id(), OP_TYPE_UPDATE, updateResponse));
                break;
              case NONE:
                setResponse(
                    item, new BulkItemResponse(item.id(), OP_TYPE_UPDATE, updateResult.noopResult));
                item.setIgnoreOnReplica(); // no need to go to the replica
                break;
            }
            // NOTE: Breaking out of the retry_on_conflict loop!
            break;
          } else if (updateResult.failure()) {
            Throwable t = updateResult.error;
            if (updateResult.retry) {
              // updateAttemptCount is 0 based and marks current attempt, if it's equal to
              // retryOnConflict we are going out of the iteration
              if (updateAttemptsCount >= updateRequest.retryOnConflict()) {
                setResponse(
                    item,
                    new BulkItemResponse(
                        item.id(),
                        OP_TYPE_UPDATE,
                        new BulkItemResponse.Failure(
                            request.index(), updateRequest.type(), updateRequest.id(), t)));
              }
            } else {
              // rethrow the failure if we are going to retry on primary and let parent failure to
              // handle it
              if (retryPrimaryException(t)) {
                // restore updated versions...
                for (int j = 0; j < requestIndex; j++) {
                  applyVersion(request.items()[j], preVersions[j], preVersionTypes[j]);
                }
                throw (ElasticsearchException) t;
              }
              // if its a conflict failure, and we already executed the request on a primary (and we
              // execute it
              // again, due to primary relocation and only processing up to N bulk items when the
              // shard gets closed)
              // then just use the response we got from the successful execution
              if (item.getPrimaryResponse() != null && isConflictException(t)) {
                setResponse(item, item.getPrimaryResponse());
              } else if (updateResult.result == null) {
                setResponse(
                    item,
                    new BulkItemResponse(
                        item.id(),
                        OP_TYPE_UPDATE,
                        new BulkItemResponse.Failure(
                            shardRequest.request.index(),
                            updateRequest.type(),
                            updateRequest.id(),
                            t)));
              } else {
                switch (updateResult.result.operation()) {
                  case UPSERT:
                  case INDEX:
                    IndexRequest indexRequest = updateResult.request();
                    if (ExceptionsHelper.status(t) == RestStatus.CONFLICT) {
                      logger.trace(
                          "{} failed to execute bulk item (index) {}",
                          t,
                          shardRequest.shardId,
                          indexRequest);
                    } else {
                      logger.debug(
                          "{} failed to execute bulk item (index) {}",
                          t,
                          shardRequest.shardId,
                          indexRequest);
                    }
                    setResponse(
                        item,
                        new BulkItemResponse(
                            item.id(),
                            OP_TYPE_UPDATE,
                            new BulkItemResponse.Failure(
                                request.index(), indexRequest.type(), indexRequest.id(), t)));
                    break;
                  case DELETE:
                    DeleteRequest deleteRequest = updateResult.request();
                    if (ExceptionsHelper.status(t) == RestStatus.CONFLICT) {
                      logger.trace(
                          "{} failed to execute bulk item (delete) {}",
                          t,
                          shardRequest.shardId,
                          deleteRequest);
                    } else {
                      logger.debug(
                          "{} failed to execute bulk item (delete) {}",
                          t,
                          shardRequest.shardId,
                          deleteRequest);
                    }
                    setResponse(
                        item,
                        new BulkItemResponse(
                            item.id(),
                            OP_TYPE_DELETE,
                            new BulkItemResponse.Failure(
                                request.index(), deleteRequest.type(), deleteRequest.id(), t)));
                    break;
                }
              }
              // NOTE: Breaking out of the retry_on_conflict loop!
              break;
            }
          }
        }
      } else {
        throw new IllegalStateException("Unexpected index operation: " + item.request());
      }

      assert item.getPrimaryResponse() != null;
      assert preVersionTypes[requestIndex] != null;
    }

    processAfter(request.refresh(), indexShard, location);
    BulkItemResponse[] responses = new BulkItemResponse[request.items().length];
    BulkItemRequest[] items = request.items();
    for (int i = 0; i < items.length; i++) {
      responses[i] = items[i].getPrimaryResponse();
    }
    return new Tuple<>(
        new BulkShardResponse(shardRequest.shardId, responses), shardRequest.request);
  }
  @Test
  public void testRequiredRoutingMapping_variousAPIs() throws Exception {
    client()
        .admin()
        .indices()
        .prepareCreate("test")
        .addAlias(new Alias("alias"))
        .addMapping(
            "type1",
            XContentFactory.jsonBuilder()
                .startObject()
                .startObject("type1")
                .startObject("_routing")
                .field("required", true)
                .endObject()
                .endObject()
                .endObject())
        .execute()
        .actionGet();
    ensureGreen();

    logger.info("--> indexing with id [1], and routing [0]");
    client()
        .prepareIndex(indexOrAlias(), "type1", "1")
        .setRouting("0")
        .setSource("field", "value1")
        .get();
    logger.info("--> indexing with id [2], and routing [0]");
    client()
        .prepareIndex(indexOrAlias(), "type1", "2")
        .setRouting("0")
        .setSource("field", "value2")
        .setRefresh(true)
        .get();

    logger.info("--> verifying get with id [1] with routing [0], should succeed");
    assertThat(
        client()
            .prepareGet(indexOrAlias(), "type1", "1")
            .setRouting("0")
            .execute()
            .actionGet()
            .isExists(),
        equalTo(true));

    logger.info("--> verifying get with id [1], with no routing, should fail");
    try {
      client().prepareGet(indexOrAlias(), "type1", "1").get();
      fail();
    } catch (RoutingMissingException e) {
      assertThat(e.getMessage(), equalTo("routing is required for [test]/[type1]/[1]"));
    }

    logger.info("--> verifying explain with id [2], with routing [0], should succeed");
    ExplainResponse explainResponse =
        client()
            .prepareExplain(indexOrAlias(), "type1", "2")
            .setQuery(QueryBuilders.matchAllQuery())
            .setRouting("0")
            .get();
    assertThat(explainResponse.isExists(), equalTo(true));
    assertThat(explainResponse.isMatch(), equalTo(true));

    logger.info("--> verifying explain with id [2], with no routing, should fail");
    try {
      client()
          .prepareExplain(indexOrAlias(), "type1", "2")
          .setQuery(QueryBuilders.matchAllQuery())
          .get();
      fail();
    } catch (RoutingMissingException e) {
      assertThat(e.getMessage(), equalTo("routing is required for [test]/[type1]/[2]"));
    }

    logger.info("--> verifying term vector with id [1], with routing [0], should succeed");
    TermVectorResponse termVectorResponse =
        client().prepareTermVector(indexOrAlias(), "type1", "1").setRouting("0").get();
    assertThat(termVectorResponse.isExists(), equalTo(true));
    assertThat(termVectorResponse.getId(), equalTo("1"));

    try {
      client().prepareTermVector(indexOrAlias(), "type1", "1").get();
      fail();
    } catch (RoutingMissingException e) {
      assertThat(e.getMessage(), equalTo("routing is required for [test]/[type1]/[1]"));
    }

    UpdateResponse updateResponse =
        client()
            .prepareUpdate(indexOrAlias(), "type1", "1")
            .setRouting("0")
            .setDoc("field1", "value1")
            .get();
    assertThat(updateResponse.getId(), equalTo("1"));
    assertThat(updateResponse.getVersion(), equalTo(2l));

    try {
      client().prepareUpdate(indexOrAlias(), "type1", "1").setDoc("field1", "value1").get();
      fail();
    } catch (RoutingMissingException e) {
      assertThat(e.getMessage(), equalTo("routing is required for [test]/[type1]/[1]"));
    }

    logger.info("--> verifying mget with ids [1,2], with routing [0], should succeed");
    MultiGetResponse multiGetResponse =
        client()
            .prepareMultiGet()
            .add(new MultiGetRequest.Item(indexOrAlias(), "type1", "1").routing("0"))
            .add(new MultiGetRequest.Item(indexOrAlias(), "type1", "2").routing("0"))
            .get();
    assertThat(multiGetResponse.getResponses().length, equalTo(2));
    assertThat(multiGetResponse.getResponses()[0].isFailed(), equalTo(false));
    assertThat(multiGetResponse.getResponses()[0].getResponse().getId(), equalTo("1"));
    assertThat(multiGetResponse.getResponses()[1].isFailed(), equalTo(false));
    assertThat(multiGetResponse.getResponses()[1].getResponse().getId(), equalTo("2"));

    logger.info("--> verifying mget with ids [1,2], with no routing, should fail");
    multiGetResponse =
        client()
            .prepareMultiGet()
            .add(new MultiGetRequest.Item(indexOrAlias(), "type1", "1"))
            .add(new MultiGetRequest.Item(indexOrAlias(), "type1", "2"))
            .get();
    assertThat(multiGetResponse.getResponses().length, equalTo(2));
    assertThat(multiGetResponse.getResponses()[0].isFailed(), equalTo(true));
    assertThat(multiGetResponse.getResponses()[0].getFailure().getId(), equalTo("1"));
    assertThat(
        multiGetResponse.getResponses()[0].getFailure().getMessage(),
        equalTo("routing is required for [test]/[type1]/[1]"));
    assertThat(multiGetResponse.getResponses()[1].isFailed(), equalTo(true));
    assertThat(multiGetResponse.getResponses()[1].getFailure().getId(), equalTo("2"));
    assertThat(
        multiGetResponse.getResponses()[1].getFailure().getMessage(),
        equalTo("routing is required for [test]/[type1]/[2]"));

    MultiTermVectorsResponse multiTermVectorsResponse =
        client()
            .prepareMultiTermVectors()
            .add(new TermVectorRequest(indexOrAlias(), "type1", "1").routing("0"))
            .add(new TermVectorRequest(indexOrAlias(), "type1", "2").routing("0"))
            .get();
    assertThat(multiTermVectorsResponse.getResponses().length, equalTo(2));
    assertThat(multiTermVectorsResponse.getResponses()[0].getId(), equalTo("1"));
    assertThat(multiTermVectorsResponse.getResponses()[0].isFailed(), equalTo(false));
    assertThat(multiTermVectorsResponse.getResponses()[0].getResponse().getId(), equalTo("1"));
    assertThat(multiTermVectorsResponse.getResponses()[0].getResponse().isExists(), equalTo(true));
    assertThat(multiTermVectorsResponse.getResponses()[1].getId(), equalTo("2"));
    assertThat(multiTermVectorsResponse.getResponses()[1].isFailed(), equalTo(false));
    assertThat(multiTermVectorsResponse.getResponses()[1].getResponse().getId(), equalTo("2"));
    assertThat(multiTermVectorsResponse.getResponses()[1].getResponse().isExists(), equalTo(true));

    multiTermVectorsResponse =
        client()
            .prepareMultiTermVectors()
            .add(new TermVectorRequest(indexOrAlias(), "type1", "1"))
            .add(new TermVectorRequest(indexOrAlias(), "type1", "2"))
            .get();
    assertThat(multiTermVectorsResponse.getResponses().length, equalTo(2));
    assertThat(multiTermVectorsResponse.getResponses()[0].getId(), equalTo("1"));
    assertThat(multiTermVectorsResponse.getResponses()[0].isFailed(), equalTo(true));
    assertThat(
        multiTermVectorsResponse.getResponses()[0].getFailure().getMessage(),
        equalTo("routing is required for [test]/[type1]/[1]"));
    assertThat(multiTermVectorsResponse.getResponses()[0].getResponse(), nullValue());
    assertThat(multiTermVectorsResponse.getResponses()[1].getId(), equalTo("2"));
    assertThat(multiTermVectorsResponse.getResponses()[1].isFailed(), equalTo(true));
    assertThat(multiTermVectorsResponse.getResponses()[1].getResponse(), nullValue());
    assertThat(
        multiTermVectorsResponse.getResponses()[1].getFailure().getMessage(),
        equalTo("routing is required for [test]/[type1]/[2]"));
  }
  @Test
  public void testUpdate() throws Exception {
    createIndex();
    ensureGreen();

    try {
      client()
          .prepareUpdate("test", "type1", "1")
          .setScript("ctx._source.field++", ScriptService.ScriptType.INLINE)
          .execute()
          .actionGet();
      fail();
    } catch (DocumentMissingException e) {
      // all is well
    }

    client().prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();

    UpdateResponse updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += 1", ScriptService.ScriptType.INLINE)
            .execute()
            .actionGet();
    assertThat(updateResponse.getVersion(), equalTo(2L));
    assertFalse(updateResponse.isCreated());

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("2"));
    }

    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += count", ScriptService.ScriptType.INLINE)
            .addScriptParam("count", 3)
            .execute()
            .actionGet();
    assertThat(updateResponse.getVersion(), equalTo(3L));
    assertFalse(updateResponse.isCreated());

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("5"));
    }

    // check noop
    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx.op = 'none'", ScriptService.ScriptType.INLINE)
            .execute()
            .actionGet();
    assertThat(updateResponse.getVersion(), equalTo(3L));
    assertFalse(updateResponse.isCreated());

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("5"));
    }

    // check delete
    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx.op = 'delete'", ScriptService.ScriptType.INLINE)
            .execute()
            .actionGet();
    assertThat(updateResponse.getVersion(), equalTo(4L));
    assertFalse(updateResponse.isCreated());

    for (int i = 0; i < 5; i++) {
      GetResponse getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.isExists(), equalTo(false));
    }

    // check TTL is kept after an update without TTL
    client()
        .prepareIndex("test", "type1", "2")
        .setSource("field", 1)
        .setTTL(86400000L)
        .setRefresh(true)
        .execute()
        .actionGet();
    GetResponse getResponse =
        client().prepareGet("test", "type1", "2").setFields("_ttl").execute().actionGet();
    long ttl = ((Number) getResponse.getField("_ttl").getValue()).longValue();
    assertThat(ttl, greaterThan(0L));
    client()
        .prepareUpdate("test", "type1", "2")
        .setScript("ctx._source.field += 1", ScriptService.ScriptType.INLINE)
        .execute()
        .actionGet();
    getResponse = client().prepareGet("test", "type1", "2").setFields("_ttl").execute().actionGet();
    ttl = ((Number) getResponse.getField("_ttl").getValue()).longValue();
    assertThat(ttl, greaterThan(0L));

    // check TTL update
    client()
        .prepareUpdate("test", "type1", "2")
        .setScript("ctx._ttl = 3600000", ScriptService.ScriptType.INLINE)
        .execute()
        .actionGet();
    getResponse = client().prepareGet("test", "type1", "2").setFields("_ttl").execute().actionGet();
    ttl = ((Number) getResponse.getField("_ttl").getValue()).longValue();
    assertThat(ttl, greaterThan(0L));
    assertThat(ttl, lessThanOrEqualTo(3600000L));

    // check timestamp update
    client()
        .prepareIndex("test", "type1", "3")
        .setSource("field", 1)
        .setRefresh(true)
        .execute()
        .actionGet();
    client()
        .prepareUpdate("test", "type1", "3")
        .setScript("ctx._timestamp = \"2009-11-15T14:12:12\"", ScriptService.ScriptType.INLINE)
        .execute()
        .actionGet();
    getResponse =
        client().prepareGet("test", "type1", "3").setFields("_timestamp").execute().actionGet();
    long timestamp = ((Number) getResponse.getField("_timestamp").getValue()).longValue();
    assertThat(timestamp, equalTo(1258294332000L));

    // check fields parameter
    client().prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();
    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setScript("ctx._source.field += 1", ScriptService.ScriptType.INLINE)
            .setFields("_source", "field")
            .execute()
            .actionGet();
    assertThat(updateResponse.getGetResult(), notNullValue());
    assertThat(updateResponse.getGetResult().sourceRef(), notNullValue());
    assertThat(updateResponse.getGetResult().field("field").getValue(), notNullValue());

    // check updates without script
    // add new field
    client().prepareIndex("test", "type1", "1").setSource("field", 1).execute().actionGet();
    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("field2", 2).endObject())
            .execute()
            .actionGet();
    for (int i = 0; i < 5; i++) {
      getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("1"));
      assertThat(getResponse.getSourceAsMap().get("field2").toString(), equalTo("2"));
    }

    // change existing field
    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("field", 3).endObject())
            .execute()
            .actionGet();
    for (int i = 0; i < 5; i++) {
      getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      assertThat(getResponse.getSourceAsMap().get("field").toString(), equalTo("3"));
      assertThat(getResponse.getSourceAsMap().get("field2").toString(), equalTo("2"));
    }

    // recursive map
    Map<String, Object> testMap = new HashMap<>();
    Map<String, Object> testMap2 = new HashMap<>();
    Map<String, Object> testMap3 = new HashMap<>();
    testMap3.put("commonkey", testMap);
    testMap3.put("map3", 5);
    testMap2.put("map2", 6);
    testMap.put("commonkey", testMap2);
    testMap.put("map1", 8);

    client().prepareIndex("test", "type1", "1").setSource("map", testMap).execute().actionGet();
    updateResponse =
        client()
            .prepareUpdate("test", "type1", "1")
            .setDoc(XContentFactory.jsonBuilder().startObject().field("map", testMap3).endObject())
            .execute()
            .actionGet();
    for (int i = 0; i < 5; i++) {
      getResponse = client().prepareGet("test", "type1", "1").execute().actionGet();
      Map map1 = (Map) getResponse.getSourceAsMap().get("map");
      assertThat(map1.size(), equalTo(3));
      assertThat(map1.containsKey("map1"), equalTo(true));
      assertThat(map1.containsKey("map3"), equalTo(true));
      assertThat(map1.containsKey("commonkey"), equalTo(true));
      Map map2 = (Map) map1.get("commonkey");
      assertThat(map2.size(), equalTo(3));
      assertThat(map2.containsKey("map1"), equalTo(true));
      assertThat(map2.containsKey("map2"), equalTo(true));
      assertThat(map2.containsKey("commonkey"), equalTo(true));
    }
  }