在 Elasticsearch 的设计中,一般来说更新或者删除文档并不常见。这其中的原因最主要是 Elasticsearch 以搜索为主,针对大多数的文档来说,比如日志,指标,根本就不需要更新或者删除。更新文档通常会给 Elasticsearch 的性能带来一些影响,所以一般来说不建议频繁地更新文档。

在我的上一篇文章 “Elasticsearch:运用 Java 创建索引并写入数据” 中, 我详细地介绍了几种常见的通过 Java 创建 Elasticsearch 文档的方法。在今天的文章中,我来介绍几种如何更新 Elasticsearch 文档的方法。你也可以参阅我之前的另外一篇文章 “Elasticsearch:Java 运用示例”。

为了方便大家学习,我把最终的代码放在 github 上:https://github.com/liu-xiao-guo/ElasticsearchJava-update

首先,我们可以运用自己喜欢的 IDE 来创建一个简单的 Java 应用。有关 Maven pom.xml 的设计,请参阅我之前的文章   “Elasticsearch:运用 Java 创建索引并写入数据”。这里就不再赘述了。

在做练习之前,我们需要在 Kibana 中创建如下的两个文档:

PUT employees/_doc/1
{
  "id": "1",
  "sex": "male",
  "age": 28,
  "name": "Mark"
}


PUT employees/_doc/2
{
  "id": "2",
  "sex": "female",
  "age": 22,
  "name": "Grace"
}

通过上面的命令,我们创建了两个文档。它们的文档 _id 分别是 "1" 和 "2"。请注意这个 _id 和文档里的 id 字段是不一样的。

我们可以通过如下的命令来进行查询:

GET employees/_search
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "employees",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "id" : "1",
          "sex" : "male",
          "age" : 28,
          "name" : "Mark"
        }
      },
      {
        "_index" : "employees",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "id" : "2",
          "sex" : "female",
          "age" : 22,
          "name" : "Grace"
        }
      }
    ]
  }
}

更新文档

方法一:更新字段

我们可以使用如下的代码来进行更新:

    // Method 1: update a sample string value
        UpdateRequest updateRequest = new UpdateRequest("employees", "1");
        updateRequest.doc("age", 25);

        UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println("updated response id: "+updateResponse.getId());

在上面我们使用 update 接口,我把 _id 为 "1" 的文档的 age 字段值修改为 25。运行上面的代码:

updated response id: 1

上面显示被更新的文档 id。我们可以通过如下的命令来查看被修改的文档的值:

GET employees/_doc/1
{
  "_index" : "employees",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 2,
  "_seq_no" : 2,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "id" : "1",
    "sex" : "male",
    "age" : 25,
    "name" : "Mark"
  }
}

上面显示 age 被更新的值为 25 而不是之前的 28。

上面的命令实际上是在我之前的教程 “开始使用 Elasticsearch (1)” 中的如下命令:

POST employees/_update/1
{
  "doc": {
    "age": "25"
  }
}

方法二:使用 Map 值更新一个特定的 id

        // Method 2: Update id with particular Map values
        Map<String, Object> updateMap = new HashMap<String, Object>();
        updateMap.put("id","3");
        updateMap.put("sex","male");
        updateMap.put("age", 40);
        updateMap.put("name", "liuxg");
        updateMap.put("addtionalfield", "anything");

        UpdateRequest request = new UpdateRequest("employees", "2").doc(updateMap);
        UpdateResponse updateResponse2= client.update(request, RequestOptions.DEFAULT);
        System.out.println("updated response id: "+updateResponse2.getId());

在上,我们使用 update 接口,但是使用 Map 值来进行更新。除了它更新已有的字段,我们还同时添加了一个叫做 additionalfield 的字段,并给它赋值为 anything。运行上面的代码,并通过 Kibana 来查看 _id 为 2 的文档:

GET employees/_doc/2
{
  "_index" : "employees",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 2,
  "_seq_no" : 3,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "id" : "3",
    "sex" : "male",
    "age" : 40,
    "name" : "liuxg",
    "addtionalfield" : "anything"
  }
}

我们可以看到各个字段都被更新了,并且有了新的字段。

方法三:使用 index API 来进行更新

        // Method 3: Use index API to update
        IndexRequest request3 = new IndexRequest("employees");
        request3.id("1");
        request3.source("age", 20);
        IndexResponse indexResponse3 = client.index(request3, RequestOptions.DEFAULT);
        System.out.println("response id: " + indexResponse3.getId());
        System.out.println(indexResponse3.getResult().name());

在上面,我们使用 index API 接口来进行更新。特别值得指出的是,这个 index API 的使用将会删除之前所有的已有的文档字段,就好像在原有的 _id 里创建一个崭新的文档一样。

运行上面的代码,并通过如想的代码来进行查看:

GET employees/_doc/1
{
  "_index" : "employees",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 3,
  "_seq_no" : 4,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "age" : 20
  }
}

我们可以看到所有的其它字段都被删除了,只有有个字段。这个类似如在 Kibana 中使用如的命令:

POST employees/_doc/1
{
  "age": 20
}

方法四:使用 Index API 来更新 - 通过 map

        // Method 4: use index API to update via Map
        Map<String, Object> updateMap4 = new HashMap<String, Object>();
        updateMap4.put("field1","field1");
        updateMap4.put("field2","field2");
        updateMap4.put("field3", 30);
        IndexRequest request4 = new IndexRequest("employees");
        request4.id("2");
        request4.source(updateMap4);
        IndexResponse indexResponseUpdate4 = client.index(request4, RequestOptions.DEFAULT);
        System.out.println("response id: " + indexResponseUpdate4.getId());
        System.out.println(indexResponseUpdate4.getResult().name());

和上面的代码一样,所有之前关于这个 _id 的字段将被删除,并以 Map 中定义的字段来创建新的文档。运行上面的代码:

GET employees/_doc/2
{
  "_index" : "employees",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 3,
  "_seq_no" : 11,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "field1" : "field1",
    "field3" : 30,
    "field2" : "field2"
  }
}

我们可以看到所有的字段都被新的字段所代替。

方法五:通过 Java Polo object 来更新

我们需要设计一个 Employee 的 Java class,并通过如下代码来更新:

        // Method 5: Use a PoJo Java object
        Employee employee = new Employee("myid", "Martin");
        IndexRequest indexRequest5 = new IndexRequest("employees");
        indexRequest5.id("1");
        indexRequest5.source(new ObjectMapper().writeValueAsString(employee), XContentType.JSON);
        IndexResponse indexResponse5 = client.index(indexRequest5, RequestOptions.DEFAULT);
        System.out.println("response id: "+indexResponse5.getId());
        System.out.println("response name: "+indexResponse5.getResult().name());

运行上面的代码:

GET employees/_doc/1
{
  "_index" : "employees",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 12,
  "_seq_no" : 16,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "id" : "myid",
    "name" : "Martin"
  }
}

方法六:通过 updateByQuery 来更新

这个需要使用 Painless 脚本来进行更新:

       // Method 6: Update it with UpdateByQuery
        Map<String, Object> updateMap6 = new HashMap<String, Object>();
        updateMap6.put("id","66");
        updateMap6.put("name","Bill");
        UpdateByQueryRequest updateByQueryRequest6 = new UpdateByQueryRequest("employees");
        updateByQueryRequest6.setConflicts("proceed");
        updateByQueryRequest6.setQuery(new TermQueryBuilder("_id", "1"));
        Script script = new Script(ScriptType.INLINE, "painless","ctx._source = params", updateMap6);
        updateByQueryRequest6.setScript(script);

        try {
            BulkByScrollResponse bulkResponse = client.updateByQuery(updateByQueryRequest6, RequestOptions.DEFAULT);
            long totalDocs = bulkResponse.getTotal();
            System.out.println("updated response id: "+totalDocs);
        } catch (IOException e) {
            e.printStackTrace();
        }

运行上面的代码:

GET employees/_doc/1
{
  "_index" : "employees",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 16,
  "_seq_no" : 22,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "Bill",
    "id" : "66"
  }
}

这个类似于如下的 Kibana 命令。如果你对这个不是很熟的话,请参阅 “开始使用 Elasticsearch (1)”:

POST employees/_update_by_query
{
  "query": {
    "term": {
      "_id": {
        "value": "1"
      }
    }
  },
  "script": {
    "source": "ctx._source.id = params.id;ctx._source.name = params.name",
    "lang": "painless",
    "params": {
      "id": "66",
      "name": "Bill"
    }
  }
}

删除文档

删除一个文档非常简单直接:

        // Delete a document
        DeleteRequest deleteRequest = new DeleteRequest("employees","1");
        DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println("response id: "+deleteResponse.getId());

在上面,我们删除 _id 为 “1” 的文档。上面的命令类似于 Kibana 中的如下命令:

DELETE employees/_doc/1

删除整个索引

有时我们需要删除不必要的索引。我们可以使用如下的代码来完成:

        //Delete an index
        DeleteIndexRequest requestDeleteIndex = new DeleteIndexRequest("employees");
        client.indices().delete(requestDeleteIndex, RequestOptions.DEFAULT);
        System.out.println("Index is deleted ");

我们可以通过如下的命令来检查 employees 这个索引是否已经被删除了:

GET _cat/indices

上面的命令相当于 Kibana 中的如下命令:

DELETE employees

好了,今天的讲解就到这里。在今天的文章中,我介绍了几种最为常见的方法来更新一个已经存在的文章。希望对大家的开发有所启发。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐