Java如何操作ElasticSearch,看这一篇就够了

Lou.Chen2023年1月8日
大约 16 分钟

Java操作ElasticSearch的几种方案

使用HTTP请求

直接使用 HTTP 请求,去操作 Es。HTTP 请求工具,可以使用 Java 自带的 HttpUrlConnection。也可以使用一些 HTTP 请求库,例如 HttpClientOKHttp、Spring 中的 RestTemplate 都可以,但是需要导入第三方库。

这种方式有一个弊端,就是要自己组装请求参数,自己去解析响应的 JSON。

示例:

  • 查询books索引中的所有文档
  • 使用JDK中自带的HttpURLConnection作为请求工具,直接请求ES服务端
  • pretty=true 美化返回的数据
public class JavaApiElasticSearch {
    public static void main(String[] args) throws IOException {
        //构建请求地址和参数
        URL url = new URL("http://localhost:9200/books/_search?pretty=true");
        HttpURLConnection response= (HttpURLConnection) url.openConnection();
        //如果响应结果为状态码为200
        if (response.getResponseCode() == 200) {
            InputStream inputStream = response.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            String str=null;
            while ((str = br.readLine()) != null) {
                System.out.println(str);
            }
            br.close();
        }
    }
}

Low Level REST Client

用于 Es 的官方的低级客户端。这种方式允许通过 HTTP 与 Es 集群进行通信,但是请求时候的 JSON 参数和响应的 JSON 参数交给用户去处理。这种方式好处就是兼容所有的 Es 版本。但是就是数据处理比较麻烦。

导入所需依赖:

    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-client</artifactId>
        <version>7.10.0</version>
    </dependency>

同步请求

public class JavaApiElasticSearch2 {
    public static void main(String[] args) throws IOException {
        //构建RestClient对象
        RestClientBuilder builder = RestClient.builder(
                new HttpHost("localhost", 9200, "http")
                //如果还有其他es集群,直接添加即可
                // new HttpHost("localhost", 9300, "http"),
                // new HttpHost("localhost", 9400, "http")
        );
        //如果想在请求头添加参数
        // builder.setDefaultHeaders(new Header[]{new BasicHeader("key", "value")});
        RestClient restClient = builder.build();
        //构建请求对象
        Request request = new Request("GET", "/books/_search");
        //添加请求参数
        request.addParameter("pretty", "true");
        //发送同步请求
        Response response = restClient.performRequest(request);
        //解析Response对象
        BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
        String str=null;
        while ((str = br.readLine()) != null) {
            System.out.println(str);
        }
        br.close();
    }
}

异步请求

public class JavaApiElasticSearch3 {
    public static void main(String[] args) throws IOException {
        //构建RestClient对象
        RestClientBuilder builder = RestClient.builder(
                new HttpHost("localhost", 9200, "http")
                //如果还有其他es集群,直接添加即可
                // new HttpHost("localhost", 9300, "http"),
                // new HttpHost("localhost", 9400, "http")
        );
        //如果想在请求头添加参数
        // builder.setDefaultHeaders(new Header[]{new BasicHeader("key", "value")});
        RestClient restClient = builder.build();
        //构建请求对象
        Request request = new Request("GET", "/books/_search");
        //添加请求参数
        request.addParameter("pretty", "true");
        //发送异步请求
        restClient.performRequestAsync(request, new ResponseListener() {
            @Override
            public void onSuccess(Response response) {
                try {
                    //解析Response对象
                    BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                    String str=null;
                    while ((str = br.readLine()) != null) {
                        System.out.println(str);
                    }
                    br.close();
                    restClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(Exception e) {
            }
        });

    }
}

携带JSON参数

public class JavaApiElasticSearch3 {
    public static void main(String[] args) throws IOException {
        //构建RestClient对象
        RestClientBuilder builder = RestClient.builder(
                new HttpHost("localhost", 9200, "http")
                //如果还有其他es集群,直接添加即可
                // new HttpHost("localhost", 9300, "http"),
                // new HttpHost("localhost", 9400, "http")
        );
        //如果想在请求头添加参数
        // builder.setDefaultHeaders(new Header[]{new BasicHeader("key", "value")});
        RestClient restClient = builder.build();
        //构建请求对象
        Request request = new Request("GET", "/books/_search");
        //添加请求参数
        request.addParameter("pretty", "true");
        //添加json参数
        request.setEntity(new NStringEntity("{\"query\": {\"term\":{\"name\":\"java\"} }}", ContentType.APPLICATION_JSON));
        //发送异步请求
        restClient.performRequestAsync(request, new ResponseListener() {
            @Override
            public void onSuccess(Response response) {
                try {
                    //解析Response对象
                    BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                    String str=null;
                    while ((str = br.readLine()) != null) {
                        System.out.println(str);
                    }
                    br.close();
                    restClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(Exception e) {
            }
        });

    }
}

High Level REST Client

从字面上来理解,这个叫做高级客户端,也是目前使用最多的一种客户端。它其实有点像之前的 TransportClient。

这个所谓的高级客户端它的内部其实还是基于低级客户端,只不过针对 ElasticSearch 它提供了更多的 API,将请求参数和响应参数都封装成了相应的 API,开发者只需要调用相关的方法就可以拼接参数或者解析响应结果。

Java High Level REST Client 中的每个 API 都可以同步或异步调用,同步方法返回一个响应对象,而异步方法的名称则以 Async 为后缀结尾,异步请求一般需要一个监听器参数,用来处理响应结果。

相对于低级客户端,高级客户端的兼容性就要差很多(因为 JSON 的拼接和解析它已经帮我们做好了)。高级客户端需要 JDK1.8 及以上版本并且依赖版本需要与 ElasticSearch 版本相同(主版本号需要一致,次版本号不必相同)。

举个简单例子:

7.0 客户端能够与任何 7.x ElasticSearch 节点进行通信,而 7.1 客户端肯定能够与 7.1,7.2 和任何后来的 7.x 版本进行通信,但与旧版本的 ElasticSearch 节点通信时可能会存在不兼容的问题。

以下所有章节内容都是基于High Level REST Client来操作的

 <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.10.0</version>
    </dependency>

需要注意,依赖的版本和 Es 的版本要对应。

Java API Client (ES官方推荐)

ElasticSearch 7.17开始,ES作废Java REST Client 也就是High Level REST ClientJava Transport Client

参考文档:https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.htmlopen in new window

索引管理

创建/删除索引

  • 删除索引
  • 创建索引时配置settings信息:分片、副本等
  • 创建索引时配置mappings信息: properites属性。支持三种方式:
    • JSON字符串(常用)
    • Map方式
    • XContentBuilder对象
  • 创建索引时为索引别名
  • 将索引创建的时的settingsmappingsalias等配置全部通过JSON字符串创建。和kibana中的json一致
  • 请求超时时间,连接所有节点的超时时间
  • 连接 master 节点的超时时间
  • 索引的创建同样也支持同步异步
public class JavaApiElasticSearch4 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //1、删除已经存在的索引 删除blog索引
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("blog");
        highLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);

        //2、创建一个索引 创建blog索引
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("blog");

        //3、配置 settings 信息,例如分片、副本等配置信息
        createIndexRequest.settings(Settings.builder()
                // 创建分片数为3
                .put("index.number_of_shards", 3)
                // 副本数为2
                .put("index.number_of_replicas", 2)
        );

        //4、配置 mapping 信息,例如属性信息 有以下三种方式:

        //4.1第一种:JSON 字符串
        // createIndexRequest.mapping("{\n" +
        //         "  \"properties\": {\n" +
        //         "    \"title\":{\n" +
        //         "      \"type\": \"keyword\"\n" +
        //         "    }\n" +
        //         "  }\n" +
        //         "}", XContentType.JSON);

        //4.2第二种方式:通过 map 构建 mapping
        // Map<String, String> titleProperties = new HashMap<>();
        // titleProperties.put("type", "keyword");
        // Map<String,Object> title=new HashMap<>();
        // title.put("title", titleProperties);
        // Map<String,Object> properties=new HashMap<>();
        // properties.put("properties", title);
        // createIndexRequest.mapping(properties);

        //4.3第三种方式:通过 XContentBuilder
        // XContentBuilder builder= XContentFactory.jsonBuilder();
        // builder.startObject(); //{
        // builder.startObject("properties"); //"properties":{
        // builder.startObject("title"); // "title:{"
        // builder.field("type", "keyword"); // "type" : "keyword"
        // builder.endObject(); //"}"
        // builder.endObject(); //"}"
        // builder.endObject(); //"}"
        // createIndexRequest.mapping(builder);

        //5、给索引创建别名
        // createIndexRequest.alias(new Alias("my_blog_alias"));

        //6、将以上的配置 settings、mapping、alias 等配置全部使用json来配置
        createIndexRequest.source("{\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"title\":{\n" +
                "        \"type\": \"keyword\"\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"settings\": {\n" +
                "    \"number_of_shards\": 3,\n" +
                "    \"number_of_replicas\": 3\n" +
                "  },\n" +
                "  \"aliases\": {\n" +
                "    \"my_blog\": {}\n" +
                "  }\n" +
                "}",XContentType.JSON);


        //7、执行创建索引操作 同步操作
        // highLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);

        //8、请求超时时间,连接所有节点的超时时间
        createIndexRequest.setTimeout(TimeValue.timeValueMinutes(2));

        //9、连接 master 节点的超时时间
        createIndexRequest.setMasterTimeout(TimeValue.timeValueMinutes(1));

        //10、执行创建索引操作 异步操作
        highLevelClient.indices().createAsync(createIndexRequest, RequestOptions.DEFAULT, new ActionListener<CreateIndexResponse>() {
            @Override
            public void onResponse(CreateIndexResponse createIndexResponse) {
                //关闭client
                try {
                    highLevelClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(Exception e) {

            }
        });

        //同步关闭
        // highLevelClient.close();
    }
}

查询索引是否存在

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //判断索引是否存在
        GetIndexRequest getIndexRequest = new GetIndexRequest("my_blog");
        boolean exists = highLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        System.out.println("exists=" + exists);
        
        //关闭客户端
        highLevelClient.close();
    }
}

关闭/打开索引

关闭索引,可以同时指定关闭多个索引。指定的索引不能为别名

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //关闭索引
        CloseIndexRequest closeIndexRequest = new CloseIndexRequest("blog");
        CloseIndexResponse close = highLevelClient.indices().close(closeIndexRequest, RequestOptions.DEFAULT);
        List<CloseIndexResponse.IndexResult> indices = close.getIndices();
        for (CloseIndexResponse.IndexResult index : indices) {
            System.out.println(index.getIndex());
        }
        //关闭客户端
        highLevelClient.close();
    }
}

打开索引

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //打开索引
        OpenIndexRequest openIndexRequest = new OpenIndexRequest("blog");
        highLevelClient.indices().open(openIndexRequest, RequestOptions.DEFAULT);
        
        //关闭客户端
        highLevelClient.close();
    }
}

索引修改

  • 修改settings中的内容
    • "blocks.write": true 阻塞写
    • "blocks.read": true 阻塞读
    • "blocks.read_only": true 只读
public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );
        
        //修改索引中的settings的内容
        UpdateSettingsRequest request = new UpdateSettingsRequest("blog");
        //设置要更改的属性 :开启阻塞写/阻塞读/只读等
        request.settings(Settings.builder().put("index.blocks.write", true).build());
        highLevelClient.indices().putSettings(request, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}

克隆索引

被克隆的索引需要是只读索引,可以通过索引修改中的方式设置索引为阻塞写。不能修改为只读blocks.read_only

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );
        
        //克隆索引
        ResizeRequest resizeRequest = new ResizeRequest("new_blog", "blog");
        highLevelClient.indices().clone(resizeRequest, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}

查看索引

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //查看索引 可以同时查看多个索引
        GetSettingsRequest getSettingsRequest = new GetSettingsRequest().indices("blog");
        //只获取索引的指定属性 若不设置,则返回索引的所有属性值
        getSettingsRequest.names("index.number_of_shards");
        GetSettingsResponse settings = highLevelClient.indices().getSettings(getSettingsRequest, RequestOptions.DEFAULT);
        //获取索引的所有settings信息
        ImmutableOpenMap<String, Settings> indexToSettings = settings.getIndexToSettings();
        System.out.println(indexToSettings);
        //获取索引的指定属性值 这里获取 blog 索引的 number_of_shards 值
        System.out.println(settings.getSetting("blog", "index.number_of_shards"));
        
        //关闭客户端
        highLevelClient.close();
    }
}

Refresh & Flush

Es 底层依赖 Lucene,而 Lucene 中有 reopen 和 commit 两种操作,还有一个特殊的概念叫做 segment。

Es 中,基本的存储单元是 shard,对应到 Lucene 上,就是一个索引,Lucene 中的索引由 segment 组成,每个 segment 相当于 es 中的倒排索引。每个 es 文档创建时,都会写入到一个新的 segment 中,删除文档时,只是从属于它的 segment 处标记为删除,并没有从磁盘中删除。

Lucene 中:

reopen 可以让数据搜索到,但是不保证数据被持久化到磁盘中。

commit 可以让数据持久化。

Es 中:

默认是每秒 refresh 一次(Es 中文档被索引之后,首先添加到内存缓冲区,refresh 操作将内存缓冲区中的数据拷贝到新创建的 segment 中,这里是在内存中操作的)。

flush 将内存中的数据持久化到磁盘中。一般来说,flush 的时间间隔比较久,默认 30 分钟。

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        RefreshRequest request = new RefreshRequest("blog");
        highLevelClient.indices().refresh(request, RequestOptions.DEFAULT);

        FlushRequest request1 = new FlushRequest("blog");
        highLevelClient.indices().flush(request1, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}

索引别名

索引的别名类似于 MySQL 中的视图。

添加别名

  • 给索引books添加别名my_books
public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        IndicesAliasesRequest aliasesRequest=new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions aliasActions = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD);
        aliasActions.index("books").alias("my_books");
        aliasesRequest.addAliasAction(aliasActions);
        highLevelClient.indices().updateAliases(aliasesRequest, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}
  • 添加一个带 filter 的别名
  • 当通过 GET my_books_filter/_search查询的时候会携带此filter中的条件
public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        IndicesAliasesRequest aliasesRequest=new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions aliasActions = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD);
        aliasActions.index("books").alias("my_books_filter").filter("{\n" +
                "  \"term\": {\n" +
                "    \"name\": {\n" +
                "      \"value\": \"java\"\n" +
                "    }\n" +
                "  }\n" +
                "}");
        aliasesRequest.addAliasAction(aliasActions);
        highLevelClient.indices().updateAliases(aliasesRequest, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}

删除别名

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        IndicesAliasesRequest aliasesRequest=new IndicesAliasesRequest();
        IndicesAliasesRequest.AliasActions aliasActions = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.REMOVE);
        aliasActions.index("books").alias("my_books");
        aliasesRequest.addAliasAction(aliasActions);
        highLevelClient.indices().updateAliases(aliasesRequest, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}

第二种方式

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        DeleteAliasRequest deleteAliasRequest = new DeleteAliasRequest("books", "my_books_filter");
        highLevelClient.indices().deleteAlias(deleteAliasRequest, RequestOptions.DEFAULT);

        //关闭客户端
        highLevelClient.close();
    }
}

判断别名是否存在

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //可以指定多个索引别名
        GetAliasesRequest getAliasesRequest = new GetAliasesRequest("my_books");
        //指定的索引,可以指定多个索引,如果不指定,则会搜索所有索引的别名
        getAliasesRequest.indices("books");
        boolean alias = highLevelClient.indices().existsAlias(getAliasesRequest, RequestOptions.DEFAULT);
        System.out.println(alias);

        //关闭客户端
        highLevelClient.close();
    }
}

获取别名

public class JavaApiElasticSearch5 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //可以指定多个索引别名
        GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
        //指定的索引,可以指定多个索引,如果不指定,则会搜索所有索引的别名
        getAliasesRequest.indices("books");
        GetAliasesResponse getAliasesResponse = highLevelClient.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
        Map<String, Set<AliasMetadata>> aliases = getAliasesResponse.getAliases();
        System.out.println(aliases);

        //关闭客户端
        highLevelClient.close();
    }
}

文档管理

添加文档

  • 添加文档时需要指定索引,若指定的索引不存在,则会创建索引
  • 添加文档时可以指定id:
    • 如果不指定文档id,那么每次执行都会随机生成id,类型为添加
    • 如果文档id不存在,则会添加
    • 如果文档id存在,则会更新。这种会全部覆盖文档中的所有内容,如果想要更新某个字段,则这种方式不行
  • 添加文档的方式有三种:JSON、Map、XContentBuilder
  • 可以直接指定文档操作的类型:添加/更新
    • 如果添加时文档id已经存在,那么直接抛出异常
  • 创建文档时可以选择同步或者异步
  • 创建文档之后:
    • 获取创建的文档id
    • 获取创建的文档的对应的索引名称
    • 判断文档是否创建成功
    • 判断文档是否更新成功
    • 获取操作后的分片信息
      • 判断分片是否存在异常(分片总数>分片操作成功数)
      • 如果异常打印异常信息
public class JavaApiElasticSearch6 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //指定要创建文档的索引
        //如果指定的索引不存在,默认会创建1个分片,1个副本的索引
        IndexRequest indexRequest = new IndexRequest("book");

        //指定文档id
        //如果不指定文档id,那么每次执行都会随机生成id,类型为添加
        //如果文档id不存在,则会添加
        //如果文档id存在,则会更新
        indexRequest.id("1");

        //添加文档的方式有三种:JSON、Map、XContentBuilder
        //这里采用JSON
        indexRequest.source("{\n" +
                "  \"title\": \"西游记\",\n" +
                "  \"author\": \"吴承恩\"\n" +
                "}", XContentType.JSON);
      
        //直接指定操作的类型,添加/更新
        //如果指定的id存在,那么直接报错
        // indexRequest.opType(DocWriteRequest.OpType.CREATE);

        //创建索引操作 同步
        IndexResponse indexResponse = highLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        //创建索引操作 异步
        // highLevelClient.indexAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {}

        //解析操作的结果
        //获取操作后的文档id
        String id = indexResponse.getId();
        System.out.println("id = " + id);
        //获取操作后的索引名称
        String index = indexResponse.getIndex();
        System.out.println("index = " + index);
        //判断文档是否创建成功
        if (indexResponse.getResult()== DocWriteResponse.Result.CREATED) {
            System.out.println("文档添加成功");
        }
        //判断文档是否更新成功(如果id已经存在)
        if (indexResponse.getResult()== DocWriteResponse.Result.UPDATED) {
            System.out.println("文档更新成功");
        }
        //获取操作后的分片信息
        ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
        //判断是否有分片异常
        //例如:只有一个ES实例,创建的索引为1个分片和1个副本,那么总分片数就是2,副本分片需要在另一个ES上。那么这是就会出现副本分片执行不到的情况
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            System.out.println("总分片不等于操作成功的分片,有分片异常");
        }
        //如果失败的分片数大于0
        //如果分片执行异常才会进行为分片失败,分片执行不到不算异常,例如:副本数过多,ES节点数少的情况。
        if (shardInfo.getFailed() > 0) {
            //打印错误原因
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                System.out.println("failure = " + failure.reason());
            }
        }
        //关闭客户端
        highLevelClient.close();
    }
}

获取文档

  • 根据 id 获取文档
  • 执行操作后,可以判断文档是否存在,但是内容会返回_source数据内容,如果单纯想只判断文档是否存在,那么使用判断文档是否存在
public class JavaApiElasticSearch7 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //指定要查询的索引的文档id
        GetRequest getRequest = new GetRequest("book","1");

        //获取执行操作的结果
        GetResponse getResponse = highLevelClient.get(getRequest, RequestOptions.DEFAULT);

        //获取文档id
        System.out.println("getResponse.getId() = " + getResponse.getId());
        //获取索引名称
        System.out.println("getResponse.getIndex() = " + getResponse.getIndex());
        //判断文档是否存在
        if (getResponse.isExists()) {
            //如果文档存在
            //获取版本号
            System.out.println("getResponse.getVersion() = " + getResponse.getVersion());
            //获取 _source 也就是数据内容
            System.out.println("getResponse.getSourceAsString() = " + getResponse.getSourceAsString());
        }else{
            System.out.println("文档不存在");
        }

        //关闭客户端
        highLevelClient.close();
    }
}

判断文档是否存在

  • 判断文档是否存在和获取文档的 API 是一致的。只不过在判断文档是否存在时,不需要获取 source。
public class JavaApiElasticSearch7 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //指定要查询的索引的文档id
        GetRequest getRequest = new GetRequest("book","1");

        //只需要判断是否存在即可,无需返回_source数据内容
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        boolean exists = highLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        System.out.println(exists);

        //关闭客户端
        highLevelClient.close();
    }
}

删除文档

  • 通过id删除文档
  • 删除文档的响应和添加文档成功的响应类似
public class JavaApiElasticSearch7 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //通过id删除指定索引中的指定文档
        DeleteRequest deleteRequest = new DeleteRequest("book", "1");
        DeleteResponse deleteResponse = highLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println("response.getId() = " + deleteResponse.getId());
        System.out.println("response.getIndex() = " + deleteResponse.getIndex());
        System.out.println("response.getVersion() = " + deleteResponse.getVersion());
        ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            System.out.println("有分片存在问题");
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                System.out.println("failure.reason() = " + failure.reason());
            }
        }

        //关闭客户端
        highLevelClient.close();
    }
}

更新文档

  • 通过script脚本更新
  • 通过JSON更新
  • 通过Map更新
  • 通过XContentBuilder更新
  • 通过 upsert 更新
    • 如果指定的文档id存在,那么就会执行UpdateRequest中的doc的方法 实现文档更新
    • 如果指定的文档id不存在,那么会执行UpdateRequest的upsert的方法方法 实现文档新增
public class JavaApiElasticSearch7 {
    public static void main(String[] args) throws IOException {

        RestHighLevelClient highLevelClient = new RestHighLevelClient(RestClient.builder(
                //可以添加多个集群的客户端
                new HttpHost("localhost", 9200, "http"))
        );

        //通过id指定索引中的文档
        UpdateRequest updateRequest = new UpdateRequest("book", "2");

        //1、通过script脚本更新
        // Map<String,Object> params= Collections.singletonMap("title","三国演义_3");
        // //指定只需要更新的字段为_source中的name字段,并通过params参数传递进来
        // Script script = new Script(ScriptType.INLINE, "painless", "ctx._source.title=params.title", params);
        // updateRequest.script(script);

        //2、通过JSON更新
        // updateRequest.doc("{\"title\": \"三国演义\"}", XContentType.JSON);

        //3、通过Map更新
        // HashMap<String, Object> source = new HashMap<>();
        // source.put("title", "三国_演义");
        // updateRequest.doc(source);

        //4、通过XContentBuilder更新
        // XContentBuilder xContentBuilder= XContentFactory.jsonBuilder();
        // xContentBuilder.startObject();
        // xContentBuilder.field("title", "三国");
        // xContentBuilder.endObject();
        // updateRequest.doc(xContentBuilder);

        //5、通过 upsert 更新
        XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
        jsonBuilder.startObject();
        jsonBuilder.field("title", "三国演义666");
        jsonBuilder.endObject();
        updateRequest.doc(jsonBuilder);
        //如果指定的文档id存在,那么就会执行上述方法 实现文档更新
        //如果指定的文档id不存在,那么会执行下述方法 实现文档新增
        updateRequest.upsert("{\n" +
                "  \"title\": \"三国演义\"\n" +
                "}", XContentType.JSON);

        //处理响应结果
        UpdateResponse updateResponse = highLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println("response.getId() = " + updateResponse.getId());
        System.out.println("response.getIndex() = " + updateResponse.getIndex());
        System.out.println("response.getVersion() = " + updateResponse.getVersion());

        if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            System.out.println("更新成功");
        } else if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
            System.out.println("添加成功");
        }

        //关闭客户端
        highLevelClient.close();
    }
}