Elasticsearch客戶端
客戶端介紹
在elasticsearch官網中提供了各種語言的客戶端(也就是用來連接ES,用來操作ES的)
官方地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html
我們接下來要學習的是JavaRestClient的客戶端。注意點擊進入后,選擇版本到6.8
,和我們的es版本對應
Low Level Rest Client
是低級別封裝,提供一些基礎功能,但更靈活High Level Rest Client
,是在Low Level Rest Client基礎上進行的高級別封裝,功能更豐富和完善,而且API會變的簡單
創建Demo工程
初始化項目
創建springboot es項目
修改pom.xml
<!-- fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<!-- High-level-Rest-Client-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.8.3</version>
</dependency>
<!-- es 依賴 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.8.3</version>
</dependency>
索引庫及映射
創建索引庫的同時,我們也會創建type及其映射關系,但是這些操作不建議使用Java客戶端完成,原因如下
- 索引庫和映射往往是初始化時完成,不需要頻繁操作,不如提前配置好
- 官方提供的創建索引庫及映射API非常繁瑣,需要通過字符串拼接Json結構
因此,這些操作建議還是使用Rest風格API去實現,我們接下來以這樣一個商品數據為例來創建索引庫
/**
* @author BNTang
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
private Long id;
/**
* 標題
*/
private String title;
/**
* 分類
*/
private String category;
/**
* 品牌
*/
private String brand;
/**
* 價格
*/
private Double price;
/**
* 圖片地址
*/
private String images;
}
分析一下數據結構
- id:可以認為是主鍵,將來判斷數據是否重復的標示,不分詞,可以使用keyword類型
- title:商品標題,搜索字段,需要分詞,可以用text類型
- category:商品分類,這個是整體,不分詞,可以使用keyword類型
- brand:品牌,與分類類似,不分詞,可以使用keyword類型
- price:價格,這個是double類型
- images:圖片,用來展示的字段,不搜索,index為false,不分詞,可以使用keyword類型
我們可以編寫這樣的映射配置
PUT /bntang
{
"mappings": {
"product": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"category": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "double"
}
}
}
}
}
文檔操作
有了索引庫,我們接下來看看如何新增索引數據
配置客戶端
@Configuration
public class ESConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")));
}
}
新增文檔
接下來我們看看如何通過代碼往es服務器新增一個文檔。新增時,如果傳遞的id是已經存在的,則會完成修改操作,如果不存在,則是新增
- 准備需要保存到索引庫的Json文檔數據
- 創建IndexRequest請求對象,指定索引庫、類型、id(可選)
- 調用source方法將請求數據封裝到IndexRequest請求對象中
- 調用方法進行數據通信
- 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 新增文檔
*/
@Test
public void addDoc() {
// 1.准備需要保存到索引庫的Json文檔數據
Product product = new Product(1L, "小米手機", "手機", "小米", 2899.00, "http://www.baidu.com");
// 2.將對象轉為Json字符串
String jsonString = JSON.toJSONString(product);
// 3.創建請求對象,指定索引庫、類型、id(可選)
IndexRequest indexRequest = new IndexRequest("bntang", "product", "1");
// 4.調用source方法將請求數據封裝到IndexRequest請求對象中
indexRequest.source(jsonString, XContentType.JSON);
try {
// 5.調用方法進行數據通信
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
// 6.解析輸出結果
System.out.println("結果:" + JSON.toJSONString(indexResponse));
} catch (IOException e) {
e.printStackTrace();
}
}
}
查看文檔
剛剛我們保存了一條數據進去,接下來我們根據REST風格,查看應該是根據id進行get查詢,難點是對結果的解析
相關類
- GetRquest:封裝get
請求
參數 - GetResponse:封裝get數據
響應
- 構建GetRequest請求對象,指定索引庫、類型、id
- 調用方法進行數據通信
- 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 根據id查看文檔
*/
@Test
public void getDocById() {
// 1.構建GetRequest請求對象,指定索引庫、類型、id
GetRequest getRequest = new GetRequest("bntang", "product", "1");
try {
// 2.調用方法進行數據通信
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
// 3.解析輸出結果
System.out.println("結果:" + JSON.toJSONString(getResponse));
} catch (IOException e) {
e.printStackTrace();
}
}
}
批量新增
當需要新增的數據較多時,單個新增比較耗費性能,所以這時候批量新增功能就比較好用了
- 構建批量新增BulkRequest請求對象
- 准備需要保存到索引庫的Json文檔數據封裝到IndexRequest請求對象中
- 添加IndexRequest請求對象至批量新增BulkRequest請求對象
- 調用方法進行數據通信
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 批量新增文檔
*/
@Test
public void bulkAddDoc() {
// 1.構建批量新增BulkRequest請求對象
BulkRequest bulkRequest = new BulkRequest();
// 2.准備需要保存到索引庫的Json文檔數據封裝到IndexRequest請求對象中
for (long i = 2; i < 9; i++) {
// 2.1准備需要保存到索引庫的Json文檔數據
Product product = new Product(i, "小米手機" + i, "手機", "小米", 2899.00 + i, "http://www.baidu.com");
String jsonString = JSON.toJSONString(product);
// 2.2創建請求對象,指定索引庫、類型、id(可選)
IndexRequest indexRequest = new IndexRequest("bntang", "product", "" + i);
// 2.3將請求數據封裝到IndexRequest請求對象中
indexRequest.source(jsonString, XContentType.JSON);
// 3.添加IndexRequest請求對象至批量新增BulkRequest請求對象
bulkRequest.add(indexRequest);
}
try {
// 4.調用方法進行數據通信
client.bulk(bulkRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
}
}
通過Kibana查詢所有
關鍵點
- BulkRequest:批量請求,可以添加多個IndexRequest對象,完成批處理
修改文檔
restAPI只提供了按文檔id進行修改的操作
- 構建修改請求對象,指定索引庫、類型、id
- 准備需要修改的Json文檔數據
- 將需要修改的Json文檔數據封裝到UpdateRequest請求對象中
- 調用方法進行數據通信
- 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 根據id修改文檔
*/
@Test
public void bulkAddDoc() {
// 1.構建修改請求對象,指定索引庫、類型、id
UpdateRequest updateRequest = new UpdateRequest("bntang", "product", "1");
// 2.准備需要修改的Json文檔數據
Product product = new Product(1L, "大米手機", "手機", "大米", 2899.00, "http://www.baidu.com");
String jsonString = JSON.toJSONString(product);
// 3.將需要修改的Json文檔數據封裝到UpdateRequest請求對象中
updateRequest.doc(jsonString, XContentType.JSON);
try {
// 4.調用方法進行數據通信
UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
// 5.解析輸出結果
System.out.println("結果:" + JSON.toJSONString(updateResponse));
} catch (IOException e) {
e.printStackTrace();
}
}
}
刪除文檔
resetApi只提供了根據id刪除
- 構建刪除請求對象,指定索引庫、類型、id
- 調用方法進行數據通信
- 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 根據id刪除文檔
*/
@Test
public void delDocById() {
try{
// 1.構建刪除請求對象,指定索引庫、類型、id
DeleteRequest deleteRequest = new DeleteRequest("bntang", "product", "2");
// 2.調用方法進行數據通信
DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
// 3.解析輸出結果
System.out.println("結果:" + JSON.toJSONString(deleteResponse));
}catch (IOException e){
e.printStackTrace();
}
}
}
搜索數據
關鍵字搜索match
- SearchRequest:封裝搜索請求
- SearchSourceBuilder:指定查詢類型、排序、高亮等,后面幾乎所有的操作都需要該類參與
- QueryBuilders:用來構建各種查詢類型和查詢條件
步驟
- 構建SearchRequest請求對象,指定索引庫
- 構建SearchSourceBuilder查詢對象
- 構建QueryBuilder對象指定查詢方式和查詢條件
- 將QueryBuilder對象設置到SearchSourceBuilder中
- 將SearchSourceBuilder查詢對象封裝到請求對象SearchRequest中
- 調用方法進行數據通信
- 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 詞匹配搜索
*/
@Test
public void matchDoc() {
// 1.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 2.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 3.構建QueryBuilder對象指定查詢方式和查詢條件
QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "大米");
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
// 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 6.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 7.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果: " + sourceAsString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意,上面的代碼中,搜索條件是通過sourceBuilder.query(queryBuilder)
來添加的。這個query()
方法接受的參數是:QueryBuilder
接口類型
這個接口提供了很多實現類,分別對應我們在之前不同類型的查詢,例如:term查詢、match查詢、range查詢、boolean查詢等,我們如果要使用各種不同查詢,其實僅僅是傳遞給sourceBuilder.query()
方法的參數不同而已。而這些實現類不需要我們去new
,官方提供了QueryBuilders
工廠幫我們構建各種實現類
查詢所有match_all
- 構建SearchRequest請求對象,指定索引庫
- 構建SearchSourceBuilder查詢對象
- 構建QueryBuilder對象指定查詢方式
- 將QueryBuilder對象設置到SearchSourceBuilder中
- 將SearchSourceBuilder查詢對象封裝到請求對象SearchRequest中
- 調用方法進行數據通信
- 解析輸出結果
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 查詢所有
*/
@Test
public void matchDoc() {
// 1.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 2.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 3.構建QueryBuilder對象指定查詢方式和查詢條件
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
// 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 6.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 7.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果: " + sourceAsString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
其實搜索類型的變化,僅僅是利用QueryBuilders構建的查詢對象不同而已,其他代碼基本一致。因此,我們可以把這段代碼封裝,然后把查詢條件作為參數傳遞
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 公共查詢方法
*/
@Test
public void commonSearch(QueryBuilder queryBuilder) {
// 1.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 2.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
// 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 6.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 7.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果: " + sourceAsString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
source過濾
默認情況下,索引庫中所有字段都會返回,如果我們想只返回部分字段,可以通過source filter來控制
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* source過濾查詢,過濾掉不需要的字段
*/
@Test
public void testSourceSearch(){
commonSearch(QueryBuilders.matchAllQuery());
}
/**
* 公共查詢方法
*/
@Test
public void commonSearch(QueryBuilder queryBuilder) {
// 1.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 2.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
// 使用fetchSource實現過濾
sourceBuilder.fetchSource(new String[]{"id", "title"}, null);
// 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 6.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 7.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果: " + sourceAsString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
排序
依然是通過sourceBuilder來配置
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* source過濾查詢,過濾掉不需要的字段
*/
@Test
public void testSourceSearch(){
commonSearch(QueryBuilders.matchAllQuery());
}
/**
* 公共查詢方法
*/
@Test
public void commonSearch(QueryBuilder queryBuilder) {
// 1.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 2.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
/*
* 通過sort方法指定排序規則
* 第一個參數:排序字段
* 第二個參數:升序還是降序(可以不填寫,默認升序)
*/
sourceBuilder.sort("id", SortOrder.DESC);
sourceBuilder.sort("price",SortOrder.DESC);
// 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 6.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 7.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果: " + sourceAsString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
備注:默認不能使用text
類型的字段進行排序。不然會報錯
官網說明
解決方法:將需要進行排序的text類型的字段,設置fielddata=true
即可
分頁
- from:當前頁起始索引, int from= (pageNum - 1) * size
- size:每頁顯示多少條
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* source過濾查詢,過濾掉不需要的字段
*/
@Test
public void testSourceSearch(){
commonSearch(QueryBuilders.matchAllQuery());
}
/**
* 公共查詢方法
*/
@Test
public void commonSearch(QueryBuilder queryBuilder) {
// 1.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 2.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
/*
* 通過sort方法指定排序規則
* 第一個參數:排序字段
* 第二個參數:升序還是降序(可以不填寫,默認升序)
*/
sourceBuilder.sort("id", SortOrder.DESC);
sourceBuilder.sort("price",SortOrder.DESC);
/*
from:當前頁起始索引 int from = (pageNum - 1) * size;
size:每頁顯示多少條
*/
sourceBuilder.from(0);
sourceBuilder.size(4);
// 5.將SearchSourceBuilder對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 6.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 7.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果: " + sourceAsString);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
高亮
高亮就是對匹配的內容里的關鍵詞通過html+css進行加顏色處理顯示
- 構建HighlightBuilder高亮對象
- 設置要高亮的字段
- 設置高亮樣式
- 高亮對象highlightBuilder設置到sourceBuilder中
@SpringBootTest
class EsJavaClientApplicationTests {
@Autowired
@Qualifier(value = "restHighLevelClient")
private RestHighLevelClient client;
/**
* 高亮查詢
*/
@Test
public void highLightMatch(){
// 1.構建QueryBuilder對象指定查詢方式
QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "小米");
// 2.構建SearchRequest請求對象,指定索引庫
SearchRequest searchRequest = new SearchRequest("bntang");
// 3.構建SearchSourceBuilder查詢對象
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 4.將QueryBuilder對象設置到SearchSourceBuilder中
sourceBuilder.query(queryBuilder);
// 5構建HighlightBuilder高亮對象
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 5.1設置要高亮的字段
highlightBuilder.field("title");
// 5.2設置高亮樣式
highlightBuilder.preTags("<font color='pink' >");
highlightBuilder.postTags("</font>");
// 6.將高亮對象highlightBuilder設置到sourceBuilder中
sourceBuilder.highlighter(highlightBuilder);
// 7.將SearchSourceBuilder查詢對象封裝到請求對象SearchRequest中
searchRequest.source(sourceBuilder);
try {
// 8.調用方法進行數據通信
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 9.解析輸出結果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
System.out.println("結果:" + sourceAsString);
// 10.獲取高亮結果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField title = highlightFields.get("title");
Text[] fragments = title.getFragments();
for (Text fragment : fragments) {
System.err.println("高亮結果: " + fragment.toString());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
關鍵代碼
- 查詢條件中添加高亮字段
new HighlightBuilder()
:創建高亮構建器.field("title")
:指定高亮字段.preTags("")
和.postTags("")
:指定高亮的前置和后置標簽
解析高亮結果