ES客户端RestHighLevelClient使用(一)

7,961 阅读21分钟

初始化

ElasticSearch提供了很多API供开发者调用,在过去的版本中,应用的比较广泛的是通过Transport端口调用相应的功能,但是新版本的ElasticSearch已经不推荐使用Transport端口,而是推荐使用REST的方式调用功能。本实例以Java REST Client为作为参考。 添加pom引用

<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.1.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.1.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client-sniffer</artifactId>
<version>7.1.1</version>
</dependency>

Java High Level REST Client需要Java 1.8,并且依赖于Elasticsearch core项目。 初始化   RestHighLevelClient实例需要按如下方式构建:

RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(
                new HttpHost("localhost", 9200, "http"),
                new HttpHost("localhost", 9201, "http")));

关闭客户端   RestHighLevelClient将根据提供的构建器在内部创建用于执行请求的客户端。该客户端维护一个连接池并启动一些线程,因此当您处理好请求时,您应该将其关闭以释放这些资源,改操作通过调用close()方法来完成: client.close();

添加新文档(indexRequest)

IndexRequest

添加新文档需要调用IndexRequest请求,可以直接传递json数据,如下:

IndexRequest request = new IndexRequest("posts"); //索引
request.id("1"); //文档id
String jsonString = "{" +
        "\"user\":\"kimchy\"," +
        "\"postDate\":\"2013-01-30\"," +
        "\"message\":\"trying out Elasticsearch\"" +
        "}";
request.source(jsonString, XContentType.JSON); //以字符串形式提供的文档源

也可以使用Map作为参数,如下

Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("user", "kimchy");
jsonMap.put("postDate", new Date());
jsonMap.put("message", "trying out Elasticsearch");
IndexRequest indexRequest = new IndexRequest("posts")
    .id("1").source(jsonMap); //以Map形式提供的文档源,可自动转换为JSON格式

还可以使用XConttentBuilder构建内容。

XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
    builder.field("user", "kimchy");
    builder.timeField("postDate", new Date());
    builder.field("message", "trying out Elasticsearch");
}
builder.endObject();
IndexRequest indexRequest = new IndexRequest("posts")
    .id("1").source(builder);

直接用键值对对象构架数据。

IndexRequest indexRequest = new IndexRequest("posts")
    .id("1")
    .source("user", "kimchy",
        "postDate", new Date(),
        "message", "trying out Elasticsearch");

可选参数

以下是官方文档提供的可选参数。 request.routing("routing"); //路由值 request.timeout(TimeValue.timeValueSeconds(1)); //设置超时 request.timeout("1s"); ////以字符串形式设置超时时间 request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); //以WriteRequest.RefreshPolicy实例形式设置刷新策略 request.setRefreshPolicy("wait_for");//以字符串形式刷新策略                   
request.version(2); //文档版本 request.versionType(VersionType.EXTERNAL); //文档类型 request.opType(DocWriteRequest.OpType.CREATE); //操作类型 request.opType("create"); //操作类型 可选create或update request.setPipeline("pipeline"); //索引文档之前要执行的摄取管道的名称

同步执行

当以下列方式执行IndexRequest时,客户端在继续执行代码之前,会等待返回索引响应: IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);   同步调用可能会在高级REST客户端中解析REST响应失败、请求超时或类似服务器没有响应的情况下抛出IOException。   在服务器返回4xx或5xx错误代码的情况下,高级客户端会尝试解析响应主体错误详细信息,然后抛出一个通用的弹性响应异常,并将原始响应异常作为抑制异常添加到其中。

异步执行

我们也可以用异步方式执行IndexRequest,以便客户端可以直接返回。用户需要通过向异步索引方法传递请求和侦听器来指定如何处理响应或潜在故障: client.indexAsync(request, RequestOptions.DEFAULT, listener);// listener是执行完成时要使用的侦听器   异步方法不会阻塞并立即返回。一旦完成,如果执行成功完成,则使用onResponse方法回调操作侦听器,如果执行失败,则使用onFailure方法回调操作侦听器。失败场景和预期异常与同步执行情况相同   一个典型的listener像下面这样:

listener = new ActionListener<IndexResponse>() {
    @Override
    public void onResponse(IndexResponse indexResponse) {//执行成功的时候调用
        
    }
    @Override
    public void onFailure(Exception e) {//执行失败的时候调用
        
    }
};

IndexResponse对象

返回的IndexResponse对象允许检索关于已执行操作的信息,如下所示:

String index = indexResponse.getIndex();
String id = indexResponse.getId();
if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {//处理创建文档的情况
    
} else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {//处理文档更新的情况
    
}
ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
if (shardInfo.getTotal() != shardInfo.getSuccessful()) {//处理成功的分片数少于总分片数时的情况
    
}
if (shardInfo.getFailed() > 0) {//处理潜在的故障
    for (ReplicationResponse.ShardInfo.Failure failure :
            shardInfo.getFailures()) {
        String reason = failure.reason(); 
    }
}
  如果存在版本冲突,将引发ElasticsearchException:
IndexRequest request = new IndexRequest("posts")
    .id("1")
    .source("field", "value")
    .setIfSeqNo(10L)
    .setIfPrimaryTerm(20);
try {
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
} catch(ElasticsearchException e) {
    if (e.status() == RestStatus.CONFLICT) {
        //引发的异常表明返回了版本冲突错误
    }
}
  如果opType被设置为创建并且已经存在具有相同索引和id的文档,也会发生同样的情况:
IndexRequest request = new IndexRequest("posts")
    .id("1")
    .source("field", "value")
    .opType(DocWriteRequest.OpType.CREATE);
try {
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
} catch(ElasticsearchException e) {
    if (e.status() == RestStatus.CONFLICT) {
        //引发的异常表明返回了版本冲突错误
    }
}

获取文档

GetRequest

GetRequest需要以下参数:

GetRequest getRequest = new GetRequest(
        "posts", 
        "doc",  
        "1");
  • posts — 索引。
  • doc — 类型。
  • 1 — 文档id。

可选参数

可以选择提供以下参数:

request.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);
  • 禁用源检索,默认情况下启用
String[] includes = new String[]{"message", "*Date"};
String[] excludes = Strings.EMPTY_ARRAY;
FetchSourceContext fetchSourceContext =
        new FetchSourceContext(true, includes, excludes);
request.fetchSourceContext(fetchSourceContext);
  • 为特定字段配置源包含
String[] includes = Strings.EMPTY_ARRAY;
String[] excludes = new String[]{"message"};
FetchSourceContext fetchSourceContext =
        new FetchSourceContext(true, includes, excludes);
request.fetchSourceContext(fetchSourceContext);
  • 为特定字段配置源排除
request.storedFields("message"); 
GetResponse getResponse = client.get(request, RequestOptions.DEFAULT);
String message = getResponse.getField("message").getValue();
  • 配置特定存储字段的检索(要求字段分别存储在映射中)。
  • 检索message存储字段(要求字段分别存储在映射中)。
request.routing("routing");
  • 路由值。
request.parent("parent");
  • parent值。
request.preference("preference");
  • 偏好值。
request.realtime(false);
  • realtime标志设置为false(默认为true)。
request.refresh(true);
  • 在检索文档之前执行刷新(默认为false)。
request.version(2);
  • 版本。
request.versionType(VersionType.EXTERNAL);
  • 版本类型。

同步执行

以下列方式执行GetRequest时,客户端在继续执行代码之前等待返回GetResponse

GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);

异步执行

执行GetRequest也可以以异步方式完成,以便客户端可以直接返回,用户需要通过将请求和侦听器传递给异步get方法来指定响应或潜在故障的处理方式:

client.getAsync(request, RequestOptions.DEFAULT, listener);
  • 要执行的GetRequest和执行完成时要使用的ActionListener

异步方法不会阻塞并立即返回,完成后,如果执行成功完成,则使用onResponse方法回调ActionListener,如果失败则使用onFailure方法。 get的典型监听器看起来像:

ActionListener<GetResponse> listener = new ActionListener<GetResponse>() {
    @Override
    public void onResponse(GetResponse getResponse) {
        
    }
    @Override
    public void onFailure(Exception e) {
        
    }
};
  • onResponse — 执行成功完成时调用。
  • onFailure — 在整个GetRequest失败时调用。

Get响应

返回的GetResponse允许检索所请求的文档及其元数据和最终存储的字段。

String index = getResponse.getIndex();
String type = getResponse.getType();
String id = getResponse.getId();
if (getResponse.isExists()) {
    long version = getResponse.getVersion();
    String sourceAsString = getResponse.getSourceAsString();        
    Map<String, Object> sourceAsMap = getResponse.getSourceAsMap(); 
    byte[] sourceAsBytes = getResponse.getSourceAsBytes();          
} else {
    
}
  • 以字符串形式检索文档。
  • 将文档检索为Map<String, Object>
  • byte[]的形式检索文档。
  • 处理未找到文档的方案,请注意,虽然返回的响应具有404状态代码,但返回有效的GetResponse而不是抛出异常,此类响应不包含任何源文档,并且其isExists方法返回false

当针对不存在的索引执行get请求时,响应具有404状态代码,抛出ElasticsearchException,需要按如下方式处理:

GetRequest request = new GetRequest("does_not_exist", "doc", "1");
try {
    GetResponse getResponse = client.get(request, RequestOptions.DEFAULT);
} catch (ElasticsearchException e) {
    if (e.status() == RestStatus.NOT_FOUND) {
        
    }
}
  • 处理抛出的异常,因为索引不存在。

如果已请求特定文档版本,并且现有文档具有不同的版本号,则会引发版本冲突:

try {
    GetRequest request = new GetRequest("posts", "doc", "1").version(2);
    GetResponse getResponse = client.get(request, RequestOptions.DEFAULT);
} catch (ElasticsearchException exception) {
    if (exception.status() == RestStatus.CONFLICT) {
        
    }
}

引发的异常表示返回了版本冲突错误。

Exists API

如果文档存在,则existsAPI返回true,否则返回false

Exists请求

它就像Get API一样使用GetRequest,支持所有可选参数,由于exists()只返回truefalse,我们建议关闭获取_source和任何存储的字段,以便请求稍微轻一点:

GetRequest getRequest = new GetRequest(
    "posts", 
    "doc",   
    "1");    
getRequest.fetchSourceContext(new FetchSourceContext(false)); 
getRequest.storedFields("_none_");
  • posts — 索引。
  • doc — 类型。
  • 1 — 索引id。
  • FetchSourceContext(false) — 禁用提取_source
  • storedFields("_none_") — 禁用提取存储的字段。

同步执行

以下列方式执行GetRequest时,客户端在继续执行代码之前等待返回boolean

boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);

异步执行

执行GetRequest也可以以异步方式完成,以便客户端可以直接返回,用户需要通过将请求和侦听器传递给异步exists方法来指定响应或潜在故障的处理方式:

client.existsAsync(getRequest, RequestOptions.DEFAULT, listener);
  • 要执行的GetRequest和执行完成时要使用的ActionListener

异步方法不会阻塞并立即返回,完成后,如果执行成功完成,则使用onResponse方法回调ActionListener,如果失败则使用onFailure方法。 exists的典型侦听器如下所示:

ActionListener<Boolean> listener = new ActionListener<Boolean>() {
    @Override
    public void onResponse(Boolean exists) {
        
    }
    @Override
    public void onFailure(Exception e) {
        
    }
};
  • onResponse — 执行成功完成时调用。
  • onFailure — 在整个GetRequest失败时调用。

删除文档

DeleteRequest

想要删除一个文档,必须构建一个DeleteRquest,如下:

DeleteRequest request = new DeleteRequest(
        "posts",    //索引
        "1");       //文档id

可选参数

DeleteRequest同样有一些可选参数:
request.routing("routing"); //路由值
request.timeout(TimeValue.timeValueMinutes(2)); //以TimeValue形式设置超时
request.timeout("2m");  //以字符串形式设置超时
request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); //以WriteRequest.RefreshPolicy
实例的形式设置刷新策略
request.setRefreshPolicy("wait_for");  //以字符串的形式设置刷新策略            
request.version(2); //版本
request.versionType(VersionType.EXTERNAL); //版本类型

同步执行

当以下列方式执行删除请求时,客户端在继续执行代码之前,会等待返回删除响应:

DeleteResponse deleteResponse = client.delete(
        request, RequestOptions.DEFAULT);

同步调用可能会在高级REST客户端中解析REST响应失败、请求超时或类似服务器没有响应的情况下抛出IOException。   在服务器返回4xx或5xx错误代码的情况下,高级客户端会尝试解析响应主体错误详细信息,然后抛出一个通用的ElasticsearchException,并将原始ResponseException作为抑制异常添加到其中。

异步执行

也可以异步方式执行DeleteRequest,以便客户端可以直接返回而无需等待。用户需要通过向异步删除方法传递请求和侦听器来指定如何处理响应或潜在问题:

client.deleteAsync(request, RequestOptions.DEFAULT, listener); //要执行的删除请求和执行
完成时要使用的操作侦听器

异步方法不会阻塞并立即返回。如果执行成功,则使用onResponse方法回调操作侦听器,如果执行失败,则使用onFailure方法回调操作侦听器。失败场景和预期异常与同步执行情况相同。   一个典型的监听器如下:

listener = new ActionListener<DeleteResponse>() {
    @Override
    public void onResponse(DeleteResponse deleteResponse) {
        //执行成功时调用
    }
    @Override
    public void onFailure(Exception e) {
        //执行失败时调用
    }
};

DeleteResponse

返回的DeleteResponse允许检索关于已执行操作的信息,如下所示:

String index = deleteResponse.getIndex();
String id = deleteResponse.getId();
long version = deleteResponse.getVersion();
ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
    //处理成功分片数少于总分片数的情况
}
if (shardInfo.getFailed() > 0) {
    for (ReplicationResponse.ShardInfo.Failure failure :
            shardInfo.getFailures()) {//处理潜在的故障
        String reason = failure.reason(); 
    }
}
  还可以检查文档是否被找到:
DeleteRequest request = new DeleteRequest("posts", "does_not_exist");
DeleteResponse deleteResponse = client.delete(
        request, RequestOptions.DEFAULT);
if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
    //如果找不到要删除的文档,执行一些操作
}
  如果存在版本冲突,将引发弹性响应异常:
try {
    DeleteResponse deleteResponse = client.delete(
        new DeleteRequest("posts", "1").setIfSeqNo(100).setIfPrimaryTerm(2),
            RequestOptions.DEFAULT);
} catch (ElasticsearchException exception) {
    if (exception.status() == RestStatus.CONFLICT) {
        //引发的异常表明返回了版本冲突错误
    }
}

更新文档

UpdateRequest

UpdateRequest request = new UpdateRequest(
        "posts", //索引
        "1");   //文档id
  Update API允许通过使用脚本或传递部分文档来更新现有文档。

用脚本更新

该脚本可以作为内嵌脚本提供:

Map<String, Object> parameters = singletonMap("count", 4); //作为对象映射提供的脚本参数
Script inline = new Script(ScriptType.INLINE, "painless",
        "ctx._source.field += params.count", parameters);  //使用painless语言和前面的参数创建内嵌脚本
request.script(inline);  //将脚本设置为更新请求

或者作为存储的脚本:

Script stored = new Script(
        ScriptType.STORED, null, "increment-field", parameters);  //引用painless语言中存储在名称增量字段下的脚本
request.script(stored);  //在更新请求中设置脚本

用部分文档更新

对部分文档使用更新时,部分文档将与现有文档合并。   部分文档可以以不同的方式提供:

UpdateRequest request = new UpdateRequest("posts", "1");
String jsonString = "{" +
        "\"updated\":\"2017-01-01\"," +
        "\"reason\":\"daily update\"" +
        "}";
request.doc(jsonString, XContentType.JSON); //以JSON格式的字符串形式提供的部分文档源
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("updated", new Date());
jsonMap.put("reason", "daily update");
UpdateRequest request = new UpdateRequest("posts", "1")
        .doc(jsonMap); //作为Map提供的部分文档源会自动转换为JSON格式
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
    builder.timeField("updated", new Date());
    builder.field("reason", "daily update");
}
builder.endObject();
UpdateRequest request = new UpdateRequest("posts", "1")
        .doc(builder);  //作为XContentBuilder对象提供的部分文档源,弹性搜索内置帮助器,用于生成JSON内容
UpdateRequest request = new UpdateRequest("posts", "1")
        .doc("updated", new Date(),
             "reason", "daily update"); //作为键值对提供的部分文档源,转换为JSON格式

Upserts

如果文档尚不存在,可以使用upsert方法定义一些将作为新文档插入的内容:

String jsonString = "{\"created\":\"2017-01-01\"}";
request.upsert(jsonString, XContentType.JSON);  //以字符串形式提供的Upsert文档源

与部分文档更新类似,可以使用接受字符串、Map、XContentBuilder或键值对的方法来定义upsert文档的内容。

可选参数

request.routing("routing"); //路由值
request.timeout(TimeValue.timeValueSeconds(1)); //设置超时
request.timeout("1s"); ////以字符串形式设置超时时间
request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); //以WriteRequest.RefreshPo
licy实例形式设置刷新策略
request.setRefreshPolicy("wait_for");//以字符串形式刷新策略  
request.retryOnConflict(3); //如果要更新的文档在更新操作的获取和索引阶段之间被另一个操作更改,
重试更新操作的次数
request.fetchSource(true); //启用源检索,默认情况下禁用
String[] includes = new String[]{"updated", "r*"};
String[] excludes = Strings.EMPTY_ARRAY;
request.fetchSource(
        new FetchSourceContext(true, includes, excludes)); //为特定字段配置源包含
String[] includes = Strings.EMPTY_ARRAY;
String[] excludes = new String[]{"updated"};
request.fetchSource(
        new FetchSourceContext(true, includes, excludes)); //为特定字段配置源排除
request.setIfSeqNo(2L); //ifSeqNo
request.setIfPrimaryTerm(1L); //ifPrimaryTerm
request.detectNoop(false); //禁用noop检测
request.scriptedUpsert(true); //指出无论文档是否存在,脚本都必须运行,即如果文档不存在,脚本负
责创建文档。
request.docAsUpsert(true); //指示如果部分文档尚不存在,则必须将其用作upsert文档。
request.waitForActiveShards(2); //设置在继续更新操作之前必须活动的碎片副本数量。
request.waitForActiveShards(ActiveShardCount.ALL); //ActiveShardCount的碎片副本数。可选
值:ActiveShardCount.ALL, ActiveShardCount.ONE或者 ActiveShardCount.DEFAULT

同步执行

当以下列方式执行更新请求时,客户端在继续执行代码之前,会等待返回更新响应:

UpdateResponse updateResponse = client.update(
        request, RequestOptions.DEFAULT);

同步调用可能会在高级REST客户端中解析REST响应失败、请求超时或类似服务器没有响应的情况下抛出IOException。   在服务器返回4xx或5xx错误代码的情况下,高级客户端会尝试解析响应主体错误详细信息,然后抛出一个通用的ElasticsearchException,并将原始ResponseException作为抑制异常添加到其中。

异步执行

执行更新请求也可以异步方式完成,以便客户端可以直接返回。用户需要通过向异步更新方法传递请求和侦听器来指定如何处理响应或潜在故障:

client.updateAsync(request, RequestOptions.DEFAULT, listener); //要执行
的更新请求和执行完成时要使用的操作侦听器

异步方法不会阻塞并立即返回。如果执行成功完成,则使用onResponse方法回调操作侦听器,如果执行失败,则使用onFailure方法回调操作侦听器。失败场景和预期异常与同步执行情况相同。   典型的更新监听器如下所示:

listener = new ActionListener<UpdateResponse>() {
    @Override
    public void onResponse(UpdateResponse updateResponse) {
        //成功的时候调用
    }
    @Override
    public void onFailure(Exception e) {
        //失败的时候调用
    }
};

Update Response

返回的更新响应允许检索关于已执行操作的信息,如下所示:

String index = updateResponse.getIndex();
String id = updateResponse.getId();
long version = updateResponse.getVersion();
if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
    //处理第一次创建文档的情况(upsert)
} else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
    //处理文档更新的情况
} else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
    //处理文档被删除的情况
} else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
   //处理文档不受更新影响的情况,即没有对文档执行任何操作(noop)   
}
  当通过fetchSource方法在更新请求中启用源检索时,响应包含更新文档的源:
GetResult result = updateResponse.getGetResult(); //以GetResult形式检索更新的文档
if (result.isExists()) {
    String sourceAsString = result.sourceAsString(); //以字符串形式检索更新文档的来源
    Map<String, Object> sourceAsMap = result.sourceAsMap(); //以Map<String, Object>的形式检索更新文档的源
    byte[] sourceAsBytes = result.source(); //以byte[]的形式检索更新文档的源
} else {
    //处理响应中不存在文档源的情况(默认情况下就是这种情况)
}
  也可以检查碎片故障:
ReplicationResponse.ShardInfo shardInfo = updateResponse.getShardInfo();
if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
    //处理成功碎片数少于总碎片数的情况
}
if (shardInfo.getFailed() > 0) {
    for (ReplicationResponse.ShardInfo.Failure failure :
            shardInfo.getFailures()) {//处理潜在的故障
        String reason = failure.reason(); 
    }
}
  当对不存在的文档执行UpdateRequest时,响应有404个状态代码,会引发一个ElasticsearchException,需要如下处理:
UpdateRequest request = new UpdateRequest("posts", "does_not_exist")
        .doc("field", "value");
try {
    UpdateResponse updateResponse = client.update(
            request, RequestOptions.DEFAULT);
} catch (ElasticsearchException e) {
    if (e.status() == RestStatus.NOT_FOUND) {
        //处理由于文档不存在而引发的异常
    }
}
  如果存在版本冲突,将引发ElasticsearchException:
UpdateRequest request = new UpdateRequest("posts", "1")
        .doc("field", "value")
        .setIfSeqNo(101L)
        .setIfPrimaryTerm(200L);
try {
    UpdateResponse updateResponse = client.update(
            request, RequestOptions.DEFAULT);
} catch(ElasticsearchException e) {
    if (e.status() == RestStatus.CONFLICT) {
        //引发的异常表明返回了版本冲突错误。
    }
}

批量Bulk API

Java高级REST客户端提供大容量处理器来帮助处理大容量请求。

BulkRequest

BulkRequest可用于使用单个请求执行多个索引、更新和/或删除操作。   它需要将至少一个操作添加到批量请求中:

BulkRequest request = new BulkRequest(); //创建BulkRequest
request.add(new IndexRequest("posts").id("1")  
        .source(XContentType.JSON,"field", "foo"));//将第一个索引请求添加到批量请求中
request.add(new IndexRequest("posts").id("2")  
        .source(XContentType.JSON,"field", "bar"));//添加第二个索引请求
request.add(new IndexRequest("posts").id("3")  
        .source(XContentType.JSON,"field", "baz"));//添加第三个索引请求

警告   Bulk API只支持JSON或SMILE中编码的文档。以任何其他格式提供文档都将导致错误。   不同的操作类型可以添加到同一个批量请求中:

BulkRequest request = new BulkRequest();
request.add(new DeleteRequest("posts", "3")); //向批量请求添加删除请求
request.add(new UpdateRequest("posts", "2") 
        .doc(XContentType.JSON,"other", "test"));//向批量请求添加更新请求。
request.add(new IndexRequest("posts").id("4")  
        .source(XContentType.JSON,"field", "baz"));//使用SMILE格式添加索引请求

可选参数

request.timeout(TimeValue.timeValueMinutes(2)); //设置超时时间
request.timeout("2m"); //设置超时时间
request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); //WriteRequest.RefreshP
olicy实例形式设置刷新策略  
request.setRefreshPolicy("wait_for"); //字符串形式设置刷新策略                     
request.waitForActiveShards(2); //设置在继续索引/更新/删除操作之前必须活动的碎片副本的数量。
request.waitForActiveShards(ActiveShardCount.ALL); //作为动态硬装载提供的碎片副本数,可
选:ActiveShardCount.ALL, ActiveShardCount.ONE或 ActiveShardCount.DEFAULT
request.pipeline("pipelineId"); //用于所有子请求的全局管道标识
request.routing("routingId"); //用于全局路由所有子请求
BulkRequest defaulted = new BulkRequest("posts"); //具有全局索引的批量请求,用于所有子请求,
除非在子请求上被重写。此参数为@Nullable,只能在批量请求创建期间设置。

同步执行

当以下列方式执行批量请求时,客户端在继续执行代码之前,会等待返回批量响应:

BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);

同步调用可能会在高级REST客户端中解析REST响应失败、请求超时或类似服务器没有响应的情况下抛出IOException。   在服务器返回4xx或5xx错误代码的情况下,高级客户端会尝试解析响应主体错误详细信息,然后抛出一个通用的ElasticsearchException,并将原始ResponseException作为抑制异常添加到其中。

异步执行

也可以异步方式执行BulkRequest,这样客户端就可以直接返回。用户需要通过将请求和侦听器传递给异步大容量方法来指定如何处理响应或潜在故障:

client.bulkAsync(request, RequestOptions.DEFAULT, listener); //要执行的批
量请求和执行完成时要使用的操作侦听器

异步方法不会阻塞并立即返回。如果执行成功,则使用onResponse方法回调操作侦听器,如果执行失败,则使用onFailure方法回调操作侦听器。失败场景和预期异常与同步执行情况相同。   一个典型的批量监听程序如下:

ActionListener<BulkResponse> listener = new ActionListener<BulkResponse>() {
    @Override
    public void onResponse(BulkResponse bulkResponse) {
        //成功的时候调用
    }
    @Override
    public void onFailure(Exception e) {
        //出错的时候调用
    }
};

BulkResponse

返回的BulkResponse包含有关已执行操作的信息,并允许迭代每个结果,如下所示:

for (BulkItemResponse bulkItemResponse : bulkResponse) { //迭代所有操作的结果
    DocWriteResponse itemResponse = bulkItemResponse.getResponse(); //检索操作的响应(成功与否),可以是索引响应、更新响应或删除响应,它们都可以被视为DocWriteResponse实例
    switch (bulkItemResponse.getOpType()) {
    case INDEX:    //处理索引操作的响应
    case CREATE:
        IndexResponse indexResponse = (IndexResponse) itemResponse;
        break;
    case UPDATE:   //处理更新操作的响应
        UpdateResponse updateResponse = (UpdateResponse) itemResponse;
        break;
    case DELETE:   //处理删除操作的响应
        DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
    }
}

批量响应提供了一种快速检查一个或多个操作是否失败的方法:

if (bulkResponse.hasFailures()) { // 如果至少有一个操作失败,此方法返回true
}

在这种情况下,有必要迭代所有操作结果,以检查操作是否失败,如果失败,则检索相应的失败:

for (BulkItemResponse bulkItemResponse : bulkResponse) {
    if (bulkItemResponse.isFailed()) { //指示给定操作是否失败
        BulkItemResponse.Failure failure =
                bulkItemResponse.getFailure(); //检索失败操作的失败
    }
}

批量处理器

批量处理器通过提供一个实用程序类简化了Bulk API的使用,该类允许索引/更新/删除操作在添加到处理器时透明地执行。   为了执行请求,批量处理器需要以下组件: RestHighLevelClient 该客户端用于执行批量请求并检索BulkResponse BulkProcessor.Listener   每次批量请求执行前后或批量请求失败时,都会调用该侦听器   那么批量处理器. builder方法可以用来构建一个新的批量处理器:

BulkProcessor.Listener listener = new BulkProcessor.Listener() { //创建BulkProcessor.Listener
    @Override
    public void beforeBulk(long executionId, BulkRequest request) {
        //每次执行BulkRequest之前都会调用此方法
    }
    @Override
    public void afterBulk(long executionId, BulkRequest request,
            BulkResponse response) {
        //每次执行BulkRequest后都会调用此方法
    }
    @Override
    public void afterBulk(long executionId, BulkRequest request,
            Throwable failure) {
        //当批量请求失败时调用此方法
    }
};

BulkProcessor.Builder 提供了配置批量处理器如何处理请求执行的方法:

BulkProcessor bulkProcessor = BulkProcessor.builder(
        (request, bulkListener) ->
            client.bulkAsync(request, RequestOptions.DEFAULT, bulkListener),
        listener).build(); 
//通过从BulkProcessor.Builder调用build()方法来创建BulkProcessor。resthighleveloclient .
//BulkAsync()方法将用于在机罩下执行BulkRequest。
//BulkProcessor.Builder 供了配置批量处理器如何处理请求执行的方法:
BulkProcessor.Builder builder = BulkProcessor.builder(
        (request, bulkListener) ->
            client.bulkAsync(request, RequestOptions.DEFAULT, bulkListener),
        listener);
builder.setBulkActions(500); //根据当前添加的操作数设置刷新新批量请求的时间(默认值为1000,
//-1禁用)
builder.setBulkSize(new ByteSizeValue(1L, ByteSizeUnit.MB)); //根据当前添加的操作大小设置
//刷新新批量请求的时间(默认为5Mb,-1禁用)
builder.setConcurrentRequests(0); //设置允许执行的并发请求数(默认为1,0仅允许执行单个请求)
builder.setFlushInterval(TimeValue.timeValueSeconds(10L)); //设置一个刷新间隔,如果间隔过去,
//刷新任何待处理的批量请求(默认为未设置)
builder.setBackoffPolicy(BackoffPolicy
        .constantBackoff(TimeValue.timeValueSeconds(1L), 3)); //设置一个恒定的后退策略,最初
        //等待1秒钟,最多重试3次。有关更多选项,请参见BackoffPolicy.noBackoff(), BackoffPolicy.constantBackoff()和BackoffPolicy.exponentialBackoff(). 
  一旦批量处理器被创建,可以向它添加请求:
IndexRequest one = new IndexRequest("posts").id("1")
        .source(XContentType.JSON, "title",
                "In which order are my Elasticsearch queries executed?");
IndexRequest two = new IndexRequest("posts").id("2")
        .source(XContentType.JSON, "title",
                "Current status and upcoming changes in Elasticsearch");
IndexRequest three = new IndexRequest("posts").id("3")
        .source(XContentType.JSON, "title",
                "The Future of Federated Search in Elasticsearch");
bulkProcessor.add(one);
bulkProcessor.add(two);
bulkProcessor.add(three);

请求将由BulkProcessor执行,它负责为每个批量请求调用BulkProcessor.Listener。   侦听器提供访问BulkRequest和BulkResponse的方法:

BulkProcessor.Listener listener = new BulkProcessor.Listener() {
    @Override
    public void beforeBulk(long executionId, BulkRequest request) {
        int numberOfActions = request.numberOfActions(); //在每次执行BulkRequest之前调用,
        这个方法允许知道在BulkRequest中将要执行的操作的数量
        logger.debug("Executing bulk [{}] with {} requests",
                executionId, numberOfActions);
    }
    @Override
    public void afterBulk(long executionId, BulkRequest request,
            BulkResponse response) {
        if (response.hasFailures()) { //在每次执行BulkRequest后调用,该方法允许知道BulkResp
            onse是否包含错误
            logger.warn("Bulk [{}] executed with failures", executionId);
        } else {
            logger.debug("Bulk [{}] completed in {} milliseconds",
                    executionId, response.getTook().getMillis());
        }
    }
    @Override
    public void afterBulk(long executionId, BulkRequest request,
            Throwable failure) {
        logger.error("Failed to execute bulk", failure); //如果BulkRequest失败,则调用该
        方法,该方法允许知道失败
    }
};

一旦所有请求都添加到BulkProcessor中,就需要使用两种可用的关闭方法之一来关闭它的实例。   awaitClose()方法可用于等待,直到处理完所有请求或经过指定的等待时间:

boolean terminated = bulkProcessor.awaitClose(30L, TimeUnit.SECONDS); //如果所有大容量请
求都已完成,则该方法返回true如果在所有大容量请求完成之前等待时间已过,则该方法返回false

close()方法可用于立即关闭批量处理器:

bulkProcessor.close();

这两种方法都会在关闭处理器之前刷新添加到处理器中的请求,并且禁止向其中添加任何新请求。