ElasticSearch 高階 REST 客戶端
起步
閱讀文件須知,文件基於Elasticsearch 6.x
,閱讀要求,熟悉 ElasticSearch
的語法
相容性
高階客戶端要求最低的 java
版本是1.8 ,它依賴 Elasticsearch
的核心工程,客戶端的版本應該和 Elasticsearch
的版本保持一致,高階客戶端和 TransportClient
【TCP 連線客戶端】 接受一樣的請求引數,並且返回一樣的響應結果,如果你想從 TransportClient
客戶端遷移到 REST
客戶端,請參考遷移手冊
高階客戶端保證能夠與執行在相同主要版本和更高版本上的Elasticsearch
節點進行通訊。它不需要與通訊的Elasticsearch
節點處於相同的版本,因為它是向前相容的,這意味著它支援與更高版本的Elasticsearch
進行通訊,而不是與其開發的版本進行通訊。
6.0 客戶端能夠與任何6.x版本的 Elasticsearch
節點通訊,而6.1客戶端肯定能夠與6.1,6.2和任何更高版本的6.x版本通訊,但在與老版的Elasticsearch
節點通訊時可能存在不相容問題版本,例如6.1和6.0,6.1客戶端為一些 api
新增了新的請求體欄位支援,然而6.0節點卻不支援。
建議在將Elasticsearch
叢集升級到新的主版本時升級高階客戶端,因為REST API
中斷更改可能會導致意外結果,具體取決於請求所針對的節點,並且新新增的API僅支援新版本的客戶端。一旦叢集中的所有節點都升級到新的主版本,客戶端應保持同步更新。
Java api 文件
文件地址:<https://artifacts.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-high-level-client/6.3.1/index.html>
maven 倉庫
高階Java REST客戶端託管在 Maven Central上。所需的最低Java版本是1.8
。
高階REST客戶端與Elasticsearch具有相同的釋出週期。將版本替換為所需的客戶端版本。
如果您正在尋找SNAPSHOT版本,可以通過snapshots.elastic.co/maven/獲取Elastic Maven Snapshot儲存庫。
Maven 配置
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.3.1</version>
</dependency>
複製程式碼
Gradel 配置
dependencies {
compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client:6.3.1'
}
複製程式碼
依賴
高階客戶端依賴下面的元件及其傳遞依賴性:
- org.elasticsearch.client:elasticsearch-rest-client
- org.elasticsearch:elasticsearch
初始化
一個RestHighLevelClient
例項需要一個低階客戶端的Builder 來構建如下:
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
複製程式碼
高階客戶端將在內部建立用於執行請求的低階客戶端,低階客戶端基於框架提供的builder
,並管理其生命週期。
高階客戶端例項應該在不再需要時關閉,以便正確釋放它使用的所有資源,以及底層的http客戶端例項及其執行緒。這可以通過close
方法完成,該方法將關閉內部RestClient
例項。
client.close();
複製程式碼
Document API
Java高階REST客戶端支援以下文件API:
單文件API:
- index api - 索引API
- get api - 獲取API
- delete api - 刪除API
- update api - 更新API
多文件API
- bulk api - 批量操作 api
- Multi-Get API - 批量獲取 api
Index API
Index 請求體
一個引索請求需要下面的引數:
IndexRequest request = new IndexRequest(
"posts", //index 名
"doc", //type 名
"1"); //文件 ID
String jsonString = "{" +
"\"user\":\"kimchy\"," +
"\"postDate\":\"2013-01-30\"," +
"\"message\":\"trying out Elasticsearch\"" +
"}";
request.source(jsonString, XContentType.JSON);//設定 string 型別的文件source
複製程式碼
構建文件 source 的方式
除了String
上面顯示的示例之外,還可以以不同方式提供文件源 :
方式一:以 Map
的方式提供的文件源,Map
自動轉換為JSON
格式
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", "doc", "1")
.source(jsonMap);
複製程式碼
方式二:以XContentBuilder
物件方式提供,Elasticsearch
內建了helper
生成JSON
內容
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", "doc", "1")
.source(builder);
複製程式碼
方式三:以鍵值對方式提供,轉換為JSON
格式
IndexRequest indexRequest = new IndexRequest("posts", "doc", "1")
.source("user", "kimchy",
"postDate", new Date(),
"message", "trying out Elasticsearch");
複製程式碼
可選引數
可以選擇以下引數:
- 設定路由
request.routing("routing");
複製程式碼
- 設定父文件
request.parent("parent");
複製程式碼
- 設定超時時間
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
複製程式碼
- 設定重新整理策略
request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
request.setRefreshPolicy("wait_for");
複製程式碼
- 設定版本
request.version(2);
複製程式碼
- 設定版本型別
request.versionType(VersionType.EXTERNAL);
複製程式碼
- 設定文件操作型別
request.opType(DocWriteRequest.OpType.CREATE);
request.opType("create");
複製程式碼
- 文件執行之前,設定
pipeline
名
request.setPipeline("pipeline");
複製程式碼
同步執行方式
IndexResponse indexResponse = client.index(request);
複製程式碼
非同步執行方式
索引請求的非同步執行需要將IndexRequest
例項和ActionListener
例項都傳遞給非同步方法:
ActionListener<IndexResponse> listener = new ActionListener<IndexResponse>() {
@Override
public void onResponse(IndexResponse indexResponse) {
}
@Override
public void onFailure(Exception e) {
}
};
IndexResponse indexResponse = client.index(request);
複製程式碼
非同步方法不會阻塞並立即返回。一旦完成,如果執行成功,則使用該方法ActionListener
回撥onResponse
,如果失敗則回撥onFailure
方法。
引索響應結果
返回的IndexResponse
包含了有關已執行操作的資訊,如下所示:
String index = indexResponse.getIndex();
String type = indexResponse.getType();
String id = indexResponse.getId();
long version = indexResponse.getVersion();
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", "doc", "1")
.source("field", "value")
.version(1);
try {
IndexResponse response = client.index(request);
} catch(ElasticsearchException e) {
if (e.status() == RestStatus.CONFLICT) {
//引發的異常表示返回了版本衝突錯誤
}
}
複製程式碼
如果已存在具有相同索引,型別和ID的文件,opType
設定為create
也會發生衝突:
IndexRequest request = new IndexRequest("posts", "doc", "1")
.source("field", "value")
.opType(DocWriteRequest.OpType.CREATE);
try {
IndexResponse response = client.index(request);
} catch(ElasticsearchException e) {
if (e.status() == RestStatus.CONFLICT) {
}
}
複製程式碼
Get API
Get 請求體
構建 GetRequest 的引數如下:
GetRequest getRequest = new GetRequest("posts","doc","1");
複製程式碼
可選引數
- 設定返回響應不包含任何欄位,預設情況下返回響應包含該所有欄位
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);
String message = getResponse.getField("message").getValue();//獲取message儲存的值 (要求將欄位單獨儲存在對映中)
複製程式碼
- 設定路由
request.routing("routing");
複製程式碼
- 設定父文件
request.parent("parent");
複製程式碼
- 設定偏好
request.preference("preference");
複製程式碼
- 設定實時標識,預設
true
request.realtime(false);
複製程式碼
- 設定每次獲取文件之前是否執行重新整理操作,預設
false
request.refresh(true);
複製程式碼
- 設定版本號
request.version(2);
複製程式碼
- 設定版本型別
request.versionType(VersionType.EXTERNAL);
複製程式碼
同步執行
GetResponse getResponse = client.get(getRequest);
複製程式碼
非同步執行
get
請求的非同步執行需要將GetRequest 例項和ActionListener例項都傳遞給非同步方法
ActionListener<GetResponse> listener = new ActionListener<GetResponse>() {
@Override
public void onResponse(GetResponse getResponse) {
}
@Override
public void onFailure(Exception e) {
}
};
GetResponse getResponse = client.get(getRequest);
複製程式碼
非同步方法不會阻塞並立即返回。一旦完成,如果執行成功,則使用該方法ActionListener
回撥onResponse
,如果失敗則回撥onFailure
方法。
響應結果
返回的IndexResponse
包含了有關已執行操作的資訊,如下所示:
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 {
//處理未找到文件的方案。注意,雖然返回的響應具有404狀態程式碼,但他會返回一個有效GetResponse而不是丟擲異常。此類響應不包含任何文件欄位,而且isExists方法返回false。
}
複製程式碼
當對不存在的索引(index
)執行get請求時,響應會有404
狀態程式碼,但是會丟擲ElasticsearchException
,需要按如下方式處理:
GetRequest request = new GetRequest("does_not_exist", "doc", "1");
try {
GetResponse getResponse = client.get(request);
} catch (ElasticsearchException e) {
if (e.status() == RestStatus.NOT_FOUND) {
}
}
複製程式碼
如果請求特定版本的文件,並且現有文件具有不同的版本號,則會引發版本衝突:
try {
GetRequest request = new GetRequest("posts", "doc", "1").version(2);
GetResponse getResponse = client.get(request);
} catch (ElasticsearchException exception) {
if (exception.status() == RestStatus.CONFLICT) {
//處理版本衝突
}
}
複製程式碼
Exists API
如果文件存在就返回 true
,否則返回 false
Exists Request
它的GetRequest
就像Get API一樣。支援所有可選引數 。由於exists()
只返回true
或false
,所有建議關閉返回_source
和任何儲存的欄位,以便請求更加輕量:
GetRequest getRequest = new GetRequest(
"posts",
"doc",
"1");
getRequest.fetchSourceContext(new FetchSourceContext(false)); //不返回_source
getRequest.storedFields("_none_"); //不返回儲存欄位
複製程式碼
同步執行
boolean exists = client.exists(getRequest);
複製程式碼
非同步執行
ActionListener<Boolean> listener = new ActionListener<Boolean>() {
@Override
public void onResponse(Boolean exists) {
}
@Override
public void onFailure(Exception e) {
}
};
client.existsAsync(getRequest, listener);
複製程式碼
Delete API
Delete Request
DeleteRequest
引數如下:
DeleteRequest request = new DeleteRequest("posts","doc","1");
複製程式碼
可選引數
- 設定路由
request.routing("routing");
複製程式碼
- 設定父文件
request.parent("parent");
複製程式碼
- 設定超時
request.timeout(TimeValue.timeValueMinutes(2));
request.timeout("2m");
複製程式碼
- 設定重新整理策略
request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
request.setRefreshPolicy("wait_for");
複製程式碼
- 設定版本號
request.version(2);
複製程式碼
- 設定版本型別
request.versionType(VersionType.EXTERNAL);
複製程式碼
同步執行
DeleteResponse deleteResponse = client.delete(request);
複製程式碼
非同步執行
ActionListener<DeleteResponse> listener = new ActionListener<DeleteResponse>() {
@Override
public void onResponse(DeleteResponse deleteResponse) {
}
@Override
public void onFailure(Exception e) {
}
};
client.deleteAsync(request, listener);
複製程式碼
Delete Response
返回的DeleteResponse
包含了有關已執行操作的資訊,如下所示:
String index = deleteResponse.getIndex();
String type = deleteResponse.getType();
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", "doc", "does_not_exist");
DeleteResponse deleteResponse = client.delete(request);
if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
//如果找不到要刪除的文件
}
複製程式碼
如果請求的文件版本衝突,會拋ElasticsearchException
異常
try {
DeleteRequest request = new DeleteRequest("posts", "doc", "1").version(2);
DeleteResponse deleteResponse = client.delete(request);
} catch (ElasticsearchException exception) {
if (exception.status() == RestStatus.CONFLICT) {
//引發的異常表示返回了版本衝突錯誤
}
}
複製程式碼
Update API
UpdateRequest
引數如下:
UpdateRequest request = new UpdateRequest("posts", "doc", "1");
複製程式碼
Update API
允許使用指令碼或傳遞部分文件來更新現有文件。
使用指令碼更新文件
使用內聯指令碼
Map<String, Object> parameters = singletonMap("count", 4); //使用Map物件作為指令碼引數
Script inline = new Script(ScriptType.INLINE, "painless",
"ctx._source.field += params.count", parameters); //使用painless語言和提供的引數建立內聯指令碼
UpdateRequest request = new UpdateRequest("posts", "doc", "1");
request.script(inline); //將指令碼設定為更新請求
複製程式碼
或者使用儲存在es 中的指令碼
Script stored =new Script(ScriptType.STORED, null, "increment-field", parameters);//使用儲存在 es 中的painless指令碼,指令碼名為increment-field
request.script(stored);
複製程式碼
傳遞部分文件作為引數來更新文件
當使用部分文件來更新現有的文件時,部分文件將與現有文件合併。
部分文件可以以不同方式提供:
UpdateRequest request = new UpdateRequest("posts", "doc", "1");
String jsonString = "{" +
"\"updated\":\"2017-01-01\"," +
"\"reason\":\"daily update\"" +
"}";
request.doc(jsonString, XContentType.JSON);//用json格式的字串作為部分文件源
複製程式碼
以 Map
提供部分文件源,會被自動轉化成 json
格式,如下
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("updated", new Date());
jsonMap.put("reason", "daily update");
UpdateRequest request = new UpdateRequest("posts", "doc", "1")
.doc(jsonMap);
複製程式碼
用XContentBuilder
物件作為部分文件源,Elasticsearch
內建的 helpers 會自動將它轉化為 json 文件
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.timeField("updated", new Date());
builder.field("reason", "daily update");
}
builder.endObject();
UpdateRequest request = new UpdateRequest("posts", "doc", "1")
.doc(builder);
複製程式碼
使用鍵值對作為部分文件源,他會被轉換成 json 文字
UpdateRequest request = new UpdateRequest("posts", "doc", "1")
.doc("updated", new Date(),
"reason", "daily update");
複製程式碼
Upserts
如果文件尚不存在,則可以使用以下upsert
方法來將它作為新文件插入:
String jsonString = "{\"created\":\"2017-01-01\"}";
request.upsert(jsonString, XContentType.JSON);
複製程式碼
和部分文件更新一樣,upsert
方法接受String
, Map
, XContentBuilder
or 鍵值對作為入參
可選引數
- 設定路由
request.routing("routing");
複製程式碼
- 設定父文件
request.parent("parent");
複製程式碼
- 設定超時時間
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
複製程式碼
- 設定重新整理策略
request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
request.setRefreshPolicy("wait_for");
複製程式碼
- 設定重試更新操作的次數
如果要更新的文件已在更新操作的get和indexing階段之間的另一個操作更改,則重試
request.retryOnConflict(3);
複製程式碼
- 設定是否獲取新文件內容,預設
false
request.fetchSource(true);
複製程式碼
- 指定返回哪些欄位
String[] includes = new String[]{"updated", "r*"};//正則匹配
String[] excludes = Strings.EMPTY_ARRAY;
request.fetchSource(new FetchSourceContext(true, includes, excludes));
複製程式碼
- 指定不返回哪些欄位
String[] includes = new String[]{"updated", "r*"};
String[] excludes = Strings.EMPTY_ARRAY;
request.fetchSource(new FetchSourceContext(true, includes, excludes));
複製程式碼
- 設定文件版本號
request.version(2);
複製程式碼
- 設定是否啟用noop 檢測
request.detectNoop(false);
複製程式碼
- 指示指令碼必須執行,無論文件是否存在,即如果文件尚不存在,指令碼將負責建立文件
request.scriptedUpsert(true);
複製程式碼
- 如果文件不存在,則表明必須將部分文件用作upsert文件
request.docAsUpsert(true);
複製程式碼
- 設定在執行更新操作之前必須處於活動狀態的分片副本數
request.waitForActiveShards(2);
request.waitForActiveShards(ActiveShardCount.ALL);
複製程式碼
同步執行
UpdateResponse updateResponse = client.update(request);
複製程式碼
非同步執行
client.updateAsync(request, new ActionListener<UpdateResponse>() {
@Override
public void onResponse(UpdateResponse updateResponse) {
}
@Override
public void onFailure(Exception e) {
}
});
複製程式碼
UpdateResponse
返回的UpdateResponse
包含了有關已執行操作的資訊,如下所示:
String index = updateResponse.getIndex();
String type = updateResponse.getType();
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)
}
複製程式碼
UpdateRequest
通過fetchSource
方法啟用獲取文件功能時,響應包含更新文件的來源:
GetResult result = updateResponse.getGetResult();
if (result.isExists()) {
String sourceAsString = result.sourceAsString();
Map<String, Object> sourceAsMap = result.sourceAsMap();
byte[] sourceAsBytes = result.source();
} 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", "type", "does_not_exist")
.doc("field", "value");
try {
UpdateResponse updateResponse = client.update(request);
} catch (ElasticsearchException e) {
if (e.status() == RestStatus.NOT_FOUND) {
}
}
複製程式碼
如果發生文件版本衝突,會丟擲異常:
UpdateRequest request = new UpdateRequest("posts", "doc", "1")
.doc("field", "value")
.version(1);
try {
UpdateResponse updateResponse = client.update(request);
} catch(ElasticsearchException e) {
if (e.status() == RestStatus.CONFLICT) {
}
}
複製程式碼
Bulk API
BulkRequest
BulkRequest
可用於使用單個請求執行多個索引,更新和/或刪除操作
它要求至少將一個操作新增到批量請求:
BulkRequest request = new BulkRequest();
request.add(new IndexRequest("posts", "doc", "1")
.source(XContentType.JSON,"field", "foo"));
request.add(new IndexRequest("posts", "doc", "2")
.source(XContentType.JSON,"field", "bar"));
request.add(new IndexRequest("posts", "doc", "3")
.source(XContentType.JSON,"field", "baz"));
複製程式碼
並且可以新增不同的操作型別BulkRequest
:
BulkRequest request = new BulkRequest();
request.add(new DeleteRequest("posts", "doc", "3"));
request.add(new UpdateRequest("posts", "doc", "2")
.doc(XContentType.JSON,"other", "test"));
request.add(new IndexRequest("posts", "doc", "4")
.source(XContentType.JSON,"field", "baz"));
複製程式碼
可選引數
- 設定超時時間
request.timeout(TimeValue.timeValueMinutes(2));
request.timeout("2m");
複製程式碼
- 設定重新整理策略
request.timeout(TimeValue.timeValueMinutes(2));
request.timeout("2m");
複製程式碼
- 設定在索引/更新/刪除操作之前必須處於活動狀態的分片副本數
request.waitForActiveShards(2);
request.waitForActiveShards(ActiveShardCount.ALL); //可選ActiveShardCount.ALL、 ActiveShardCount.ONE 、 ActiveShardCount.DEFAULT
複製程式碼
同步執行
BulkResponse bulkResponse = client.bulk(request);
複製程式碼
非同步執行
ActionListener<BulkResponse> listener = new ActionListener<BulkResponse>() {
@Override
public void onResponse(BulkResponse bulkResponse) {
}
@Override
public void onFailure(Exception e) {
}
};
client.bulkAsync(request, listener);
複製程式碼
BulkResponse
響應結果,允許迭代每個結果,如下所示:
for (BulkItemResponse bulkItemResponse : bulkResponse) {
//可以是IndexResponse、UpdateResponse、DeleteResponse,他們可以全部被視為DocWriteResponse例項
DocWriteResponse itemResponse = bulkItemResponse.getResponse();
if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.INDEX
|| bulkItemResponse.getOpType() == DocWriteRequest.OpType.CREATE) {
IndexResponse indexResponse = (IndexResponse) itemResponse;
} else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.UPDATE) {
UpdateResponse updateResponse = (UpdateResponse) itemResponse;
} else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.DELETE) {
DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
}
}
複製程式碼
批量響應提供了一種快速檢查一個或多個操作是否失敗的方法:
if(bulkResponse.hasFailures()){
//如果至少有一個操作失敗,則返回true
}
複製程式碼
在這種情況下,有必要迭代所有操作結果,以檢查操作是否失敗,如果是,則獲取相應的失敗資訊:
for(BulkItemResponse bulkItemResponse:bulkResponse){
if(bulkItemResponse.isFailed()){
BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
}
}
複製程式碼
批量處理器
BulkProcessor
提供了一個工具類簡化操作,它可以透明地執行新增到 processor
中的 index
/update
/delete
操作。
為了執行請求,BulkProcessor
需要以下元件:
-
RestHighLevelClient
此客戶端用於執行
BulkRequest
和獲取BulkResponse
-
BulkProcessor.Listener
在每次
BulkRequest
執行之前、之後或BulkRequest
失敗時呼叫器監聽
然後該BulkProcessor.builder
方法可用於構建新的BulkProcessor
:
BulkProcessor.Listener listener = new BulkProcessor.Listener() {
@Override
public void beforeBulk(long executionId, BulkRequest request) {
}
@Override
public void afterBulk(long executionId, BulkRequest request,
BulkResponse response) {
}
@Override
public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
}
};
BulkProcessor bulkProcessor =
BulkProcessor.builder(client::bulkAsync, listener).build();
複製程式碼
BulkProcessor.Builder
提供了方法來配置BulkProcessor
處理請求的行為:
BulkProcessor.Builder builder = BulkProcessor.builder(client::bulkAsync, 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)); //BulkRequest如果間隔超過,則 設定重新整理間隔重新整理任何掛起(預設為未設定)
builder.setBackoffPolicy(BackoffPolicy
.constantBackoff(TimeValue.timeValueSeconds(1L), 3));//設定一個最初等待1秒的常量重試策略,最多重試3次。見BackoffPolicy.noBackoff()、BackoffPolicy.constantBackoff()、BackoffPolicy.exponentialBackoff() 提供更多的選擇
複製程式碼
一旦BulkProcessor
被建立,請求可以被新增到processor
:
IndexRequest one = new IndexRequest("posts", "doc", "1").
source(XContentType.JSON, "title",
"In which order are my Elasticsearch queries executed?");
IndexRequest two = new IndexRequest("posts", "doc", "2")
.source(XContentType.JSON, "title",
"Current status and upcoming changes in Elasticsearch");
IndexRequest three = new IndexRequest("posts", "doc", "3")
.source(XContentType.JSON, "title",
"The Future of Federated Search in Elasticsearch");
bulkProcessor.add(one);
bulkProcessor.add(two);
bulkProcessor.add(three);
複製程式碼
BulkProcessor
執行所有的請求,並且為每次的 BulkRequest
回撥BulkProcessor.Listener
,監聽器提供了訪問 BulkRequest
和 BulkResponse
的方法
BulkProcessor.Listener listener = new BulkProcessor.Listener() {
@Override
public void beforeBulk(long executionId, BulkRequest request) {
int numberOfActions = request.numberOfActions();
logger.debug("Executing bulk [{}] with {} requests",
executionId, numberOfActions);
}
@Override
public void afterBulk(long executionId, BulkRequest request,
BulkResponse response) {
if (response.hasFailures()) {
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);
}
};
複製程式碼
將所有請求新增到BulkProcessor
後,需要關閉其例項,有兩種關閉方式。
該awaitClose()
方法可用於等待所有請求都已處理或指定的等待時間:
boolean terminated = bulkProcessor.awaitClose(30L,TimeUnit.SECONDS);//true:如果所有批量請求都已完成,false:在所有批量請求完成之前等待時間已過
複製程式碼
close()
方法可用於立即關閉BulkProcessor
:
bulkProcessor.close();
複製程式碼
兩種方法在關閉處理器之前重新整理已經新增到處理器的請求,並且禁止新增新請求
Multi-Get API
multiGet API
可以在單個請求中執行多個 get
請求
Multi-Get Request
獲取一個 MultiGetRequest
例項,然後新增多個 MultiGetRequest.Item
:
MultiGetRequest request = new MultiGetRequest();
request.add(new MultiGetRequest.Item(
"index",
"type",
"example_id"));
request.add(new MultiGetRequest.Item("index", "type", "another_id"));
複製程式碼
可選引數
multiGet
和 get Api
支援相同的可選引數. 你可以在 Item
上設定可選引數:
- 設定不返回任何文件,預設返回文件
request.add(new MultiGetRequest.Item("index", "type", "example_id")
.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE)
);
複製程式碼
- 設定返回文件的哪些欄位
String[] includes = new String[] {"foo", "*r"};
String[] excludes = Strings.EMPTY_ARRAY;
FetchSourceContext fetchSourceContext =
new FetchSourceContext(true, includes, excludes);
request.add(new MultiGetRequest.Item("index", "type", "example_id")
.fetchSourceContext(fetchSourceContext));
複製程式碼
- 設定不返回文件的哪些欄位
String[] includes = Strings.EMPTY_ARRAY;
String[] excludes = new String[] {"foo", "*r"};
FetchSourceContext fetchSourceContext =
new FetchSourceContext(true, includes, excludes);
request.add(new MultiGetRequest.Item("index", "type", "example_id")
.fetchSourceContext(fetchSourceContext));
複製程式碼
- 配置返回指定的儲存欄位
request.add(new MultiGetRequest.Item("index", "type", "example_id")
.storedFields("foo")); //設定返回儲存欄位 foo
MultiGetResponse response = client.multiGet(request);
MultiGetItemResponse item = response.getResponses()[0];
String value = item.getResponse().getField("foo").getValue(); //獲取儲存欄位foo的值
複製程式碼
- 其他可選引數
request.add(new MultiGetRequest.Item("index", "type", "with_routing")
.routing("some_routing"));//設定路由
request.add(new MultiGetRequest.Item("index", "type", "with_parent")
.parent("some_parent"));//設定父文件
request.add(new MultiGetRequest.Item("index", "type", "with_version")
.versionType(VersionType.EXTERNAL)//設定文件版本型別
.version(10123L)); //設定版本號
request.preference("some_preference"); //設定偏好
request.realtime(false); //設定實時標識,預設為 true
request.refresh(true); //獲取文件之前執行重新整理操作,預設 false
複製程式碼
同步執行
MultiGetResponse response = client.multiGet(request);
複製程式碼
非同步執行
ActionListener<MultiGetResponse> listener = new ActionListener<MultiGetResponse>() {
@Override
public void onResponse(MultiGetResponse response) {
}
@Override
public void onFailure(Exception e) {
}
};
MultiGetResponse response = client.multiGet(request);
複製程式碼
MultiGetResponse
返回的MultiGetResponse
通過getResponses
方法可以獲取一個 MultiGetItemResponse
列表,列表中的響應與請求的順序相同,如果 get
成功 MultiGetItemResponse
包含一個 GetResponse
,如果它失敗了會包含一個MultiGetResponse.Failure
MultiGetItemResponse firstItem = response.getResponses()[0];
assertNull(firstItem.getFailure());//如果成功,返回 null
GetResponse firstGet = firstItem.getResponse(); //獲取 GetResponse
String index = firstItem.getIndex();
String type = firstItem.getType();
String id = firstItem.getId();
if(firstGet.isExists())//判斷文件是否存在
long version = firstGet.getVersion();
String sourceAsString = firstGet.getSourceAsString();
Map <String,Object> sourceAsMap = firstGet.getSourceAsMap();
byte [] sourceAsBytes = firstGet.getSourceAsBytes();
} else {
}
複製程式碼
如果請求的 index 不存在,則返回響應會包含一個異常資訊
assertNull(missingIndexItem.getResponse());
Exception e = missingIndexItem.getFailure().getFailure();
ElasticsearchException ee = (ElasticsearchException) e;
// TODO status is broken! fix in a followup
// assertEquals(RestStatus.NOT_FOUND, ee.status());
assertThat(e.getMessage(),
containsString("reason=no such index"));
複製程式碼
請求文件版本衝突,則返回響應會包含一個異常資訊
MultiGetRequest request = new MultiGetRequest();
request.add(new MultiGetRequest.Item("index", "type", "example_id")
.version(1000L));
MultiGetResponse response = client.multiGet(request);
MultiGetItemResponse item = response.getResponses()[0];
assertNull(item.getResponse());
Exception e = item.getFailure().getFailure();
ElasticsearchException ee = (ElasticsearchException) e;
// TODO status is broken! fix in a followup
// assertEquals(RestStatus.CONFLICT, ee.status());
assertThat(e.getMessage(),
containsString("version conflict, current version [1] is "
+ "different than the one provided [1000]"));
複製程式碼
Search API
高階客戶端支援下面的 Search API
:
- Search API
- Search Scroll API
- Clear Scroll API
- Multi-Search API
- Ranking Evaluation API
Search API
SearchRequest
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder);
複製程式碼
可選引數
SearchRequest searchRequest = new SearchRequest("posts");
searchRequest.types("doc");
searchRequest.routing("routing");
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
searchRequest.preference("_local");
複製程式碼
使用 SearchBuilder
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy"));
sourceBuilder.from(0);
sourceBuilder.size(5);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
複製程式碼
構建查詢語句
MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("user", "kimchy");
matchQueryBuilder.fuzziness(Fuzziness.AUTO);
matchQueryBuilder.prefixLength(3);
matchQueryBuilder.maxExpansions(10);
matchQueryBuilder.fuzziness(Fuzziness.AUTO);
matchQueryBuilder.prefixLength(3);
matchQueryBuilder.maxExpansions(10);
searchSourceBuilder.query(matchQueryBuilder);
複製程式碼
設定排序
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
sourceBuilder.sort(new FieldSortBuilder("_uid").order(SortOrder.ASC));
複製程式碼
文件過濾
sourceBuilder.fetchSource(false);
String[] includeFields = new String[] {"title", "user", "innerObject.*"};
String[] excludeFields = new String[] {"_type"};
sourceBuilder.fetchSource(includeFields, excludeFields);
複製程式碼
欄位高亮
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
HighlightBuilder highlightBuilder = new HighlightBuilder();
HighlightBuilder.Field highlightTitle =
new HighlightBuilder.Field("title");
highlightTitle.highlighterType("unified");
highlightBuilder.field(highlightTitle);
HighlightBuilder.Field highlightUser = new HighlightBuilder.Field("user");
highlightBuilder.field(highlightUser);
searchSourceBuilder.highlighter(highlightBuilder);
複製程式碼
新增聚合查詢
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
TermsAggregationBuilder aggregation = AggregationBuilders.terms("by_company")
.field("company.keyword");
aggregation.subAggregation(AggregationBuilders.avg("average_age")
.field("age"));
searchSourceBuilder.aggregation(aggregation);
複製程式碼
請求建議詞
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
SuggestionBuilder termSuggestionBuilder =
SuggestBuilders.termSuggestion("user").text("kmichy");
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("suggest_user", termSuggestionBuilder);
searchSourceBuilder.suggest(suggestBuilder);
複製程式碼
分析查詢和聚合
profile API
可用於為特定搜尋分析查詢和聚合的執行情況。為了使用它, 必須在 SearchSourceBuilder 上設定profile
標誌為 true
:
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.profile(true);
複製程式碼
執行 SearchRequest
後, 相應的 SearchResponse
將包含分析結果。
同步執行
SearchResponse searchResponse = client.search(searchRequest);
複製程式碼
非同步執行
執行 SearchRequest
也可以以非同步方式進行, 以便客戶端可以直接返回。使用者需要通過將請求和監聽器傳遞給非同步搜尋方法來指定如何處理響應或潛在故障:
ActionListener<SearchResponse> listener = new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
}
@Override
public void onFailure(Exception e) {
}
};
client.searchAsync(searchRequest, listener);
複製程式碼
非同步方法不會阻止執行緒,也不會立即返回。完成該操作後, 如果執行成功則回撥ActionListener
的 onResponse
方法,失敗則回撥 onFailure` 方法
SearchResponse
執行搜尋返回的 SearchResponse
提供了有關搜尋執行本身以及對返回訪問的文件的詳細資訊。看下有關於請求執行操作的資訊, 如 HTTP 狀態程式碼、執行時間或請求是否提前終止或超時:
RestStatus status = searchResponse.status();
TimeValue took = searchResponse.getTook();
Boolean terminatedEarly = searchResponse.isTerminatedEarly();
boolean timedOut = searchResponse.isTimedOut();
複製程式碼
其次, 響應還提供有關在分片級別上執行的資訊, 提供有關受影響搜尋的分片總數以及成功與失敗的分片的統計資料。潛在的故障也可以通過迭代 ShardSearchFailure
陣列來處理, 如下面的示例所示:
int totalShards = searchResponse.getTotalShards();
int successfulShards = searchResponse.getSuccessfulShards();
int failedShards = searchResponse.getFailedShards();
for (ShardSearchFailure failure : searchResponse.getShardFailures()) {
// failures should be handled here
}
複製程式碼
獲取搜尋命中的文件
要獲得對返回的文件的訪問許可權, 我們首先需要得到響應中包含的 SearchHits:
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits();
float maxScore = hits.getMaxScore();
複製程式碼
SearchHits 提供有關所有命中的全域性資訊, 如命中總數或最大得分:
long totalHits = hits.getTotalHits();
float maxScore = hits.getMaxScore();
複製程式碼
巢狀在 SearchHits 中的是可以迭代的單個搜尋結果:
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
// do something with the SearchHit
}
複製程式碼
SearchHit 提供對基本資訊的訪問, 如索引、型別、docId 和每個搜尋命中的分數:
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
複製程式碼
此外, 它還允許您返回文件源, 既可以是簡單的 JSON 字串, 也可以是鍵/值對的對映。在此對映中,通常鍵值對的鍵為欄位名, 值為欄位值。多值欄位作為物件的列表返回, 巢狀物件作為另一個鍵/值對映。這些案件需要相應地強制執行:
String sourceAsString = hit.getSourceAsString();
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String documentTitle = (String) sourceAsMap.get("title");
List<Object> users = (List<Object>) sourceAsMap.get("user");
Map<String, Object> innerObject =
(Map<String, Object>) sourceAsMap.get("innerObject");
複製程式碼
獲取高亮結果
可以從結果中獲取每個 SearchHit
中高亮顯示的文字片段。SearchHit
提供對 HighlightField
例項的訪問, 其中每一個都包含一個或多個突出顯示的文字片段:
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits.getHits()) {
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField highlight = highlightFields.get("title");
Text[] fragments = highlight.fragments();
String fragmentString = fragments[0].string();
}
複製程式碼
獲取聚合結果
可以從 SearchResponse 獲取聚合結果, 首先獲取聚合樹的根、聚合物件, 然後按名稱獲取聚合
Aggregations aggregations = searchResponse.getAggregations();
Terms byCompanyAggregation = aggregations.get("by_company");
Bucket elasticBucket = byCompanyAggregation.getBucketByKey("Elastic");
Avg averageAge = elasticBucket.getAggregations().get("average_age");
double avg = averageAge.getValue();
複製程式碼
請注意, 如果按名稱訪問聚合, 則需要根據所請求的聚合型別指定聚合介面, 否則將引發丟擲:
Range range = aggregations.get("by_company"); //這將引發異常, 因為 "by_company" 是一個term聚合, 但這裡嘗試將它一範圍聚合取出
複製程式碼
還可以將所有的聚合 轉化成 map
,以聚合名作為 key
值。在這種情況下,需要顯示強制轉換到正確的型別:
Map<String, Aggregation> aggregationMap = aggregations.getAsMap();
Terms companyAggregation = (Terms) aggregationMap.get("by_company");
複製程式碼
還有一些 getter 將所有頂層聚合作為列表返回:
List<Aggregation> aggregationList = aggregations.asList();
複製程式碼
最後, 可以遍歷所有聚合, 然後根據它們的型別決定如何進一步處理它們:
for (Aggregation agg : aggregations) {
String type = agg.getType();
if (type.equals(TermsAggregationBuilder.NAME)) {
Bucket elasticBucket = ((Terms) agg).getBucketByKey("Elastic");
long numberOfDocs = elasticBucket.getDocCount();
}
}
複製程式碼
獲取建議
要從 SearchResponse 中返回suggestions
, 請使用suggestion
物件作為入口點, 然後檢索巢狀的建議物件:
Suggest suggest = searchResponse.getSuggest();
TermSuggestion termSuggestion = suggest.getSuggestion("suggest_user");
for (TermSuggestion.Entry entry : termSuggestion.getEntries()) {
for (TermSuggestion.Entry.Option option : entry) {
String suggestText = option.getText().string();
}
}
複製程式碼
獲取效能分析結果
使用 getProfileResults ()
方法從 SearchResponse
檢索效能分析結果。此方法返回一個Map
,包含 SearchRequest
執行中所涉及的每個分片的 ProfileShardResult
物件。ProfileShardResult
儲存在對映中, 使用唯一標識配置檔案結果對應的碎片的鍵.
下面是一個示例程式碼, 它演示如何迴圈訪問每個分片的所有效能分析結果:
Map<String, ProfileShardResult> profilingResults =
searchResponse.getProfileResults();
for (Map.Entry<String, ProfileShardResult> profilingResult : profilingResults.entrySet()) {
String key = profilingResult.getKey();
ProfileShardResult profileShardResult = profilingResult.getValue();
}
複製程式碼
ProfileShardResult
物件本身包含一個或多個QueryProfileShardResult
:
List<QueryProfileShardResult> queryProfileShardResults =
profileShardResult.getQueryProfileResults();
for (QueryProfileShardResult queryProfileResult : queryProfileShardResults) {
}
複製程式碼
for (ProfileResult profileResult : queryProfileResult.getQueryResults()) {
String queryName = profileResult.getQueryName();
long queryTimeInMillis = profileResult.getTime();
List<ProfileResult> profiledChildren = profileResult.getProfiledChildren();
}
複製程式碼