@Test
  public void testVersioningWithBulk() {
    try {
      client.admin().indices().prepareDelete("test").execute().actionGet();
    } catch (IndexMissingException e) {
      // its ok
    }
    client.admin().indices().prepareCreate("test").execute().actionGet();
    client.admin().cluster().prepareHealth("test").setWaitForGreenStatus().execute().actionGet();

    BulkResponse bulkResponse =
        client
            .prepareBulk()
            .add(client.prepareIndex("test", "type", "1").setSource("field1", "value1_1"))
            .execute()
            .actionGet();
    assertThat(bulkResponse.hasFailures(), equalTo(false));
    assertThat(bulkResponse.getItems().length, equalTo(1));
    IndexResponse indexResponse = bulkResponse.getItems()[0].getResponse();
    assertThat(indexResponse.version(), equalTo(1l));
  }
  public static ObjectNode toJSON(ObjectMapper objectMapper, RecomputeResult success)
      throws InterruptedException, ExecutionException {
    final ObjectNode n = objectMapper.createObjectNode();

    // write targetID using index reponse
    final IndexResponse ir = success.indexResponse();
    final ObjectNode targetID = objectMapper.createObjectNode();
    targetID.put("index", ir.index());
    targetID.put("type", ir.type());
    targetID.put("id", ir.id());
    targetID.put("version", ir.version());
    n.put("targetID", targetID);

    // write dictionary of properties and their values
    final ObjectNode properties = objectMapper.createObjectNode();
    for (Map.Entry<String, JsonNode> entry : success.properties().entrySet())
      properties.put(entry.getKey(), entry.getValue());
    n.put("properties", properties);

    // write dictionary of properties and their values
    n.put("sourceDocumentAfterTransform", success.sourceDocument());

    return n;
  }
  @Test
  public void deleteTest() throws ExecutionException, InterruptedException {
    final String target = "test-target";
    final String idx = "test-index";
    final String tp = "test-type";
    final String id = "1234";
    final String othertp = "othertest-type";

    // create target index if it doesn't exist
    if (!ln.es.admin().indices().prepareExists(target).execute().actionGet().exists()) {
      final CreateIndexResponse cir =
          ln.es.admin().indices().prepareCreate(target).execute().actionGet();
      if (!cir.acknowledged()) throw new RuntimeException("failed to create index " + target);
    }

    final IndexResponse ir =
        ln.es.prepareIndex(idx, tp, id).setSource("{\"children\":[1,2,3]}").execute().actionGet();

    ln.log.info(
        "Indexed /{}/{}/{} as version {} into ES", new Object[] {idx, tp, id, ir.version()});

    final IndexResponse ir1 =
        ln.es
            .prepareIndex(idx, tp, "1")
            .setSource("{\"cheese\":\"gorgonzola\"}")
            .execute()
            .actionGet();

    ln.log.info("Indexed /{}/{}/1 as version {} into ES", new Object[] {idx, tp, ir1.version()});

    final IndexResponse ir2 =
        ln.es
            .prepareIndex(idx, tp, "2")
            .setSource("{\"cheese\":\"mozarella\"}")
            .execute()
            .actionGet();

    ln.log.info("Indexed /{}/{}/2 as version {} into ES", new Object[] {idx, tp, ir2.version()});

    final IndexResponse ir3 =
        ln.es
            .prepareIndex(idx, othertp, "3")
            .setSource("{\"cheese\":\"limburger\"}")
            .execute()
            .actionGet();

    ln.log.info(
        "Indexed /{}/{}/3 as version {} into ES", new Object[] {idx, othertp, ir3.version()});

    final List<Future<DegraphmalizeResult>> actions = new ArrayList<Future<DegraphmalizeResult>>();

    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, tp, id, ir.version()),
            ln.callback));
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, tp, "1", ir1.version()),
            ln.callback));
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, tp, "2", ir2.version()),
            ln.callback));
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, othertp, "3", ir3.version()),
            ln.callback));

    for (final Future<DegraphmalizeResult> a : actions) {
      DegraphmalizeResult result = a.get();
      ln.log.info("Degraphmalize complete for : " + result.root());
    }

    GraphUtilities.dumpGraph(new ObjectMapper(), ln.G);

    actions.clear();
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.DELETE,
            DegraphmalizeRequestScope.DOCUMENT_ANY_VERSION,
            new ID(idx, tp, id, 0),
            ln.callback));
    for (final Future<DegraphmalizeResult> a : actions) {
      DegraphmalizeResult result = a.get();
      ln.log.info("Degraphmalize of {}: {}", result.root(), result);
    }

    actions.clear();
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.DELETE,
            DegraphmalizeRequestScope.TYPE_IN_INDEX,
            new ID(idx, tp, null, 0),
            ln.callback));
    for (final Future<DegraphmalizeResult> a : actions) {
      DegraphmalizeResult result = a.get();
      ln.log.info("Degraphmalize of {}: {}", result.root(), result);
    }

    // Only the vertex of the othertp type should be present.
    Iterable<Vertex> iterable = GraphUtilities.findVerticesInIndex(ln.G, idx);
    assertThat(Iterables.size(iterable)).isEqualTo(1);

    actions.clear();
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.DELETE,
            DegraphmalizeRequestScope.INDEX,
            new ID(idx, null, null, 0),
            ln.callback));
    for (final Future<DegraphmalizeResult> a : actions) {
      DegraphmalizeResult result = a.get();
      ln.log.info("Degraphmalize of {}: {}", result.root(), result);
    }

    // No more vertices
    Iterable<Vertex> iterable2 = GraphUtilities.findVerticesInIndex(ln.G, idx);
    assertThat(Iterables.size(iterable2)).isEqualTo(0);

    // Cleanup index
    if (!ln.es.admin().indices().delete(new DeleteIndexRequest(idx)).actionGet().acknowledged()) {
      throw new RuntimeException("failed to delete index " + target);
    }
  }
  @Test
  public void fullTest() throws ExecutionException, InterruptedException, DegraphmalizerException {
    final String target = "test-target";
    final String idx = "test-index";
    final String tp = "test-type";
    final String id = "1234";

    // create target index if it doesn't exist
    if (!ln.es.admin().indices().prepareExists(target).execute().actionGet().exists()) {
      final CreateIndexResponse cir =
          ln.es.admin().indices().prepareCreate(target).execute().actionGet();
      if (!cir.acknowledged()) throw new RuntimeException("failed to create index " + target);
    }

    final IndexResponse ir =
        ln.es.prepareIndex(idx, tp, id).setSource("{\"children\":[1,2,3]}").execute().actionGet();

    ln.log.info(
        "Indexed /{}/{}/{} as version {} into ES", new Object[] {idx, tp, id, ir.version()});

    final IndexResponse ir1 =
        ln.es
            .prepareIndex(idx, tp, "1")
            .setSource("{\"cheese\":\"gorgonzola\"}")
            .execute()
            .actionGet();

    ln.log.info("Indexed /{}/{}/1 as version {} into ES", new Object[] {idx, tp, ir1.version()});

    final IndexResponse ir2 =
        ln.es
            .prepareIndex(idx, tp, "2")
            .setSource("{\"cheese\":\"mozarella\"}")
            .execute()
            .actionGet();

    ln.log.info("Indexed /{}/{}/2 as version {} into ES", new Object[] {idx, tp, ir2.version()});

    // degraphmalize "1" and wait for and print result
    final List<Future<DegraphmalizeResult>> actions = new ArrayList<Future<DegraphmalizeResult>>();

    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, tp, id, ir.version()),
            ln.callback));
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, tp, "1", ir1.version()),
            ln.callback));
    actions.add(
        ln.d.degraphmalize(
            DegraphmalizeRequestType.UPDATE,
            DegraphmalizeRequestScope.DOCUMENT,
            new ID(idx, tp, "2", ir2.version()),
            ln.callback));

    for (final Future<DegraphmalizeResult> a : actions) {
      DegraphmalizeResult result = a.get();
      ln.log.info("Degraphmalize complete for : " + result.root());
    }

    ObjectMapper mapper = new ObjectMapper();
    for (final Future<DegraphmalizeResult> a : actions) {
      final DegraphmalizeResult degraphmalizeResult = a.get();
      // Get first node in results
      final ObjectNode result = toJSON(mapper, degraphmalizeResult.results().get(0).get());

      assertThat(result.get("properties").has("nodes-in")).isTrue();
      assertThat(result.get("properties").has("nodes-out")).isTrue();

      if (degraphmalizeResult.root().id().equals("1234")) {
        assertThat(numberOfChildren(result, "nodes-out")).isZero();
        assertThat(numberOfChildren(result, "nodes-in")).isEqualTo(3);
      }

      if (degraphmalizeResult.root().id().equals("1")) {
        assertThat(numberOfChildren(result, "nodes-out")).isEqualTo(1);
        assertThat(numberOfChildren(result, "nodes-in")).isZero();
      }
    }

    GraphUtilities.dumpGraph(new ObjectMapper(), ln.G);
    // Cleanup index
    if (!ln.es.admin().indices().delete(new DeleteIndexRequest(idx)).actionGet().acknowledged()) {
      throw new RuntimeException("failed to delete index " + target);
    }
  }
  @Test
  public void testSimpleVersioningWithFlush() throws Exception {
    try {
      client.admin().indices().prepareDelete("test").execute().actionGet();
    } catch (IndexMissingException e) {
      // its ok
    }
    client.admin().indices().prepareCreate("test").execute().actionGet();
    client.admin().cluster().prepareHealth("test").setWaitForGreenStatus().execute().actionGet();

    IndexResponse indexResponse =
        client
            .prepareIndex("test", "type", "1")
            .setSource("field1", "value1_1")
            .execute()
            .actionGet();
    assertThat(indexResponse.version(), equalTo(1l));

    client.admin().indices().prepareFlush().execute().actionGet();

    indexResponse =
        client
            .prepareIndex("test", "type", "1")
            .setSource("field1", "value1_2")
            .setVersion(1)
            .execute()
            .actionGet();
    assertThat(indexResponse.version(), equalTo(2l));

    client.admin().indices().prepareFlush().execute().actionGet();

    try {
      client
          .prepareIndex("test", "type", "1")
          .setSource("field1", "value1_1")
          .setVersion(1)
          .execute()
          .actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    try {
      client2
          .prepareIndex("test", "type", "1")
          .setSource("field1", "value1_1")
          .setVersion(1)
          .execute()
          .actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    try {
      client
          .prepareIndex("test", "type", "1")
          .setCreate(true)
          .setSource("field1", "value1_1")
          .setVersion(1)
          .execute()
          .actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    try {
      client2
          .prepareIndex("test", "type", "1")
          .setCreate(true)
          .setSource("field1", "value1_1")
          .setVersion(1)
          .execute()
          .actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    try {
      client.prepareDelete("test", "type", "1").setVersion(1).execute().actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    try {
      client2.prepareDelete("test", "type", "1").setVersion(1).execute().actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    client.admin().indices().prepareRefresh().execute().actionGet();
    for (int i = 0; i < 10; i++) {
      assertThat(
          client.prepareGet("test", "type", "1").execute().actionGet().getVersion(), equalTo(2l));
    }

    for (int i = 0; i < 10; i++) {
      SearchResponse searchResponse =
          client.prepareSearch().setQuery(matchAllQuery()).setVersion(true).execute().actionGet();
      assertThat(searchResponse.getHits().getAt(0).version(), equalTo(2l));
    }
  }
  @Test
  public void testExternalVersioning() throws Exception {
    try {
      client.admin().indices().prepareDelete("test").execute().actionGet();
    } catch (IndexMissingException e) {
      // its ok
    }
    client.admin().indices().prepareCreate("test").execute().actionGet();
    client.admin().cluster().prepareHealth("test").setWaitForGreenStatus().execute().actionGet();

    IndexResponse indexResponse =
        client
            .prepareIndex("test", "type", "1")
            .setSource("field1", "value1_1")
            .setVersion(12)
            .setVersionType(VersionType.EXTERNAL)
            .execute()
            .actionGet();
    assertThat(indexResponse.version(), equalTo(12l));

    indexResponse =
        client
            .prepareIndex("test", "type", "1")
            .setSource("field1", "value1_1")
            .setVersion(14)
            .setVersionType(VersionType.EXTERNAL)
            .execute()
            .actionGet();
    assertThat(indexResponse.version(), equalTo(14l));

    try {
      client
          .prepareIndex("test", "type", "1")
          .setSource("field1", "value1_1")
          .setVersion(13)
          .setVersionType(VersionType.EXTERNAL)
          .execute()
          .actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    client.admin().indices().prepareRefresh().execute().actionGet();
    for (int i = 0; i < 10; i++) {
      assertThat(
          client.prepareGet("test", "type", "1").execute().actionGet().getVersion(), equalTo(14l));
    }

    DeleteResponse deleteResponse =
        client2
            .prepareDelete("test", "type", "1")
            .setVersion(17)
            .setVersionType(VersionType.EXTERNAL)
            .execute()
            .actionGet();
    assertThat(deleteResponse.isNotFound(), equalTo(false));
    assertThat(deleteResponse.getVersion(), equalTo(17l));

    try {
      client2
          .prepareDelete("test", "type", "1")
          .setVersion(2)
          .setVersionType(VersionType.EXTERNAL)
          .execute()
          .actionGet();
    } catch (ElasticSearchException e) {
      assertThat(e.unwrapCause(), instanceOf(VersionConflictEngineException.class));
    }

    deleteResponse =
        client2
            .prepareDelete("test", "type", "1")
            .setVersion(18)
            .setVersionType(VersionType.EXTERNAL)
            .execute()
            .actionGet();
    assertThat(deleteResponse.isNotFound(), equalTo(true));
    assertThat(deleteResponse.getVersion(), equalTo(18l));
  }