Elasticsearch Java API Client
這是用于Elasticsearch的官方Java API客戶端的文檔??蛻舳藶樗蠩lasticsearch API提供強(qiáng)類型請求和響應(yīng)。我們要注意原來的HighRestAPIClient以及停用了,這是趨勢,包括SpringData-ElasticSearch4.4.5之后配合ES8的推出也會更換
官方地址
https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/7.17/indexing.html
特點(diǎn)
- 所有Elasticsearch API的強(qiáng)類型請求和響應(yīng)。
- 所有API的阻塞和異步版本。
- 使用流暢的構(gòu)建器和函數(shù)模式,在創(chuàng)建復(fù)雜的嵌套結(jié)構(gòu)時,允許編寫簡潔但可讀的代碼
- 通過使用對象映射器(如杰克遜或任何JSON-B實現(xiàn))無縫集成應(yīng)用程序類。
- 將協(xié)議處理委托給http客戶端(如Java低級REST客戶端),該客戶端負(fù)責(zé)處理所有傳輸級問題:HTTP連接池、重試、節(jié)點(diǎn)發(fā)現(xiàn)等。
需求
- Java11版本
- 一 個 JSON 對象映射庫,允許您的應(yīng)用程序類與Elasticsearch API無縫集成 ,Java 客戶端支持 Jackson 或 JSON-B 庫 ( 如 Eclipse Yasson )
核心三大組成
Java API客戶端由三個主要組件構(gòu)成:
- API client classes(API客戶端類)。它們?yōu)镋lasticsearch API提供了強(qiáng)類型數(shù)據(jù)結(jié)構(gòu)和方法。由于Elasticsearch API很大,它以特性組(也稱為“命名空間”)的形式進(jìn)行結(jié)構(gòu)化,每個特性組都有自己的客戶端類。Elasticsearch核心功能在ElasticsearchClient類中實現(xiàn)。
- A JSON object mapper(JSON對象映射器):這將應(yīng)用程序類映射到JSON,并將它們與API客戶機(jī)無縫集成。
- A transport layer implementation(一種傳輸層實現(xiàn)):這是所有HTTP請求處理發(fā)生的地方。
包和命名空間
Elasticsearch API很大,并被組織成功能組,這可以在Elasticsearch API文檔中看到。
Java API客戶端遵循以下結(jié)構(gòu):特征組被稱為“命名空間”,并且每個命名空間位于co.elastic.clients.elasticsearch
的子包中。
每個命名空間客戶端都可以從頂級Elasticsearch客戶端訪問。唯一的例外是“search”和“document”API,它們位于核心子包中,可以在主Elasticsearch客戶端對象上訪問。
QuickStart
1.導(dǎo)入依賴
這里記住你的elasticsearch-java
必須對應(yīng)你電腦上裝的ES版本
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
2.開啟連接
//創(chuàng)建一個低級的客戶端
final RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
//創(chuàng)建JSON對象映射器
final RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
//創(chuàng)建API客戶端
final ElasticsearchClient client = new ElasticsearchClient(transport);
3.關(guān)閉連接
client.shutdown();
transport.close();
restClient.close();
完整代碼
public class Client {
public static void main(String[] args) throws IOException {
//創(chuàng)建一個低級的客戶端
final RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
//創(chuàng)建JSON對象映射器
final RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
//創(chuàng)建API客戶端
final ElasticsearchClient client = new ElasticsearchClient(transport);
//查詢所有索引-------------------------------------------------------------------------------------
final GetIndexResponse response = client.indices().get(query -> query.index("_all"));
final IndexState products = response.result().get("products");
System.out.println(products.toString());
//關(guān)閉
client.shutdown();
transport.close();
restClient.close();
}
}
@JsonIgnore注解
標(biāo)記注釋,指示訪問器(字段、getter/setter方法或[JsonCreator帶注釋的構(gòu)造函數(shù)或工廠方法]的Creator參數(shù))的邏輯屬性將被基于內(nèi)省的序列化和反序列化功能忽略。
上面是官方的解釋,在我們的ES API中這個注解用于在實體類上的某個屬性進(jìn)行添加,用于忽略這個屬性,即使用該注解標(biāo)注的屬性在查詢時會被輸出為null
(前提是你設(shè)置了映射含有,若本來就沒有映射,則不會看到這個屬性),而在創(chuàng)建時則會直接忽略
例如:
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Produce {
@JsonIgnore
private String sku;
private String name;
private String des;
private double price;
}
我們在sku上添加了這個注解,那么創(chuàng)建文檔時就不會有這個屬性(請大家運(yùn)行以下代碼進(jìn)行驗證即可),一般我們在真實業(yè)務(wù)中也會這樣做,因為文檔的_id
字段會與之重合導(dǎo)致不必要的數(shù)據(jù)冗余
public class CreateDocClass {
public static void main(String[] args) throws IOException {
final RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
final RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
final ElasticsearchClient client = new ElasticsearchClient(transport);
//創(chuàng)建文檔
final Produce produce = new Produce("aabbcc555", "旺仔牛比糖", "旺仔牛比糖吃了炒雞牛筆", 6.66D);
final IndexResponse response = client.index(builder -> builder.index("produces").id(produce.getSku()).document(produce));
System.err.println(response.version());
client.shutdown();
transport.close();
restClient.close();
}
}
JsonData類
原始JSON值??梢允褂肑sonpMapper將其轉(zhuǎn)換為JSON節(jié)點(diǎn)樹或任意對象。 此類型在API類型中用于沒有靜態(tài)定義類型或無法表示為封閉數(shù)據(jù)結(jié)構(gòu)的泛型參數(shù)的值。 API客戶端返回的此類實例保留對客戶端的JsonpMapper的引用,并且可以使用to(class)轉(zhuǎn)換為任意類型,而不需要顯式映射器
我們一般在ES的DSL范圍查詢中會使用到!
核心方法:
- to:將此對象轉(zhuǎn)換為目標(biāo)類。必須在創(chuàng)建時提供映射器
- from:從讀取器創(chuàng)建原始JSON值
- of:從現(xiàn)有對象創(chuàng)建原始JSON值,以及用于進(jìn)一步轉(zhuǎn)換的映射器
- deserialize:使用反序列化程序轉(zhuǎn)換此對象。必須在創(chuàng)建時提供映射器
這個類不多大家自己看一下應(yīng)該就知道怎么用了,位于package co.elastic.clients.json;
下
API使用
以下所有API和我之前的文章對應(yīng)請查看:ElasticSearch系列——Kibana,核心概念的使用Kibana對ES進(jìn)行操作部分
開啟連接
- 構(gòu)建RestClient低級客戶端
- 構(gòu)建對象映射器
- 構(gòu)建ES的API客戶端
final RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
final RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
final ElasticsearchClient client = new ElasticsearchClient(transport);
關(guān)閉連接
對應(yīng)開啟
- 關(guān)閉ES的API客戶端
- 關(guān)閉對象映射器
- 關(guān)閉低級客戶端
client.shutdown();
transport.close();
restClient.close();
查詢所有索引
indices是index的復(fù)數(shù)
其實看起來很好理解
使用GET方法進(jìn)行查詢index
查詢所有的就是GET http://localhost:9200/_all
//省略連接...
final GetIndexResponse all = client.indices().get(query -> query.index("_all"));
System.out.println(all.toString());
//省略關(guān)閉...
查詢某個索引
原來我們使用GET http://localhost:9200/索引名稱
這里也是一樣設(shè)置索引名稱即可
//查詢某個索引
final GetIndexResponse products = client.indices().get(query -> query.index("products"));
System.err.println(products.toString());
創(chuàng)建索引
//創(chuàng)建指定索引
boolean exists = client.indices().exists(query -> query.index("products")).value();
System.out.println(exists);
if (exists) {
System.err.println("索引已存在");
} else {
final CreateIndexResponse products = client.indices().create(builder -> builder.index("products"));
System.err.println(products.acknowledged());
}
刪除指定索引
//刪除指定索引
boolean exists = client.indices().exists(query -> query.index("products")).value();
System.out.println(exists);
if (exists) {
DeleteIndexResponse response = client.indices().delete(query -> query.index("products"));
System.err.println(response.acknowledged());
} else {
System.err.println("索引不存在");
}
查詢索引的映射
沒有直接去查映射的是根據(jù)索引向下找到映射信息,主要是一下代碼
response.result().get(索引名稱).mappings(
//查詢映射信息
final GetIndexResponse response = client.indices().get(builder -> builder.index("produces"));
System.err.println(response.result().get("produces").mappings());
創(chuàng)建索引指定映射
- numberOfReplicas(“1”):設(shè)置副本
- numberOfShards(“1”):設(shè)置分片
//創(chuàng)建索引指定映射,分片和副本信息
final CreateIndexResponse response = client.indices().create(builder ->
builder.settings(indexSetting -> indexSetting.numberOfReplicas("1").numberOfShards("1")).mappings(
map -> map
.properties("name", propertyBuilder -> propertyBuilder.keyword(keywordProperty -> keywordProperty))
.properties("price", propertyBuilder -> propertyBuilder.double_(doubleNumProperty -> doubleNumProperty))
.properties("des", propertyBuilder -> propertyBuilder.text(textProperty -> textProperty.analyzer("ik_smart").searchAnalyzer("ik_smart")))
).index("produces")
);
創(chuàng)建文檔
使用HashMap作為數(shù)據(jù)存儲容器
//創(chuàng)建文檔
//1.創(chuàng)建HashMap進(jìn)行存儲數(shù)據(jù),文檔要對應(yīng)映射
final HashMap<String, Object> doc = new HashMap<>();
doc.put("name","辣條");
doc.put("price",5.62D);
doc.put("des","衛(wèi)龍辣條,草雞好吃");
//2.將文檔存入索引中
final IndexResponse response = client.index(builder -> builder.index("produces").id("116688").document(doc));
System.err.println(response.version());
在我們使用HashMap的時候我們可以將文檔的ID直接放在Map里,如下:
但是我們要知道的是如果你這樣設(shè)置了,你的索引的映射就會變化增加一個id的映射
//創(chuàng)建文檔
//1.創(chuàng)建HashMap進(jìn)行存儲數(shù)據(jù),文檔要對應(yīng)映射
final HashMap<String, Object> doc = new HashMap<>();
doc.put("id","116677");
doc.put("name","辣條");
doc.put("price",5.62D);
doc.put("des","衛(wèi)龍辣條,草雞好吃");
//2.將文檔存入索引中
final IndexResponse response = client.index(builder -> builder.index("produces").id(doc.get("id").toString()).document(doc));
System.err.println(response.version());
使用自定義類作為數(shù)據(jù)存儲容器
實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Produce {
private String sku;
private String name;
private String des;
private double price;
}
//創(chuàng)建文檔
final Produce produce = new Produce("aabbcc123", "旺仔牛比糖", "旺仔牛比糖吃了炒雞牛筆", 6.66D);
final IndexResponse response = client.index(builder -> builder.index("produces").id(produce.getSku()).document(produce));
System.err.println(response.version());
使用外部JSON數(shù)據(jù)創(chuàng)建
這里要注意我們需要使用StringReader進(jìn)行讀取時使用replace函數(shù)將設(shè)置的'
改為"
,當(dāng)然這在真實的業(yè)務(wù)中肯定不會有,因為真實業(yè)務(wù)中一定是標(biāo)準(zhǔn)的JSON數(shù)據(jù),無需使用replace進(jìn)行替換了
//創(chuàng)建文檔
final StringReader input = new StringReader(
"{'name':'農(nóng)夫三拳','price':3.00,'des':'農(nóng)夫三拳有點(diǎn)甜'}".replace('\'', '"')
);
final IndexResponse response = client.index(builder -> builder.index("produces").id("44514").withJson(input));
System.err.println(response.version());
查詢所有文檔
//查詢所有文檔
final SearchResponse<Object> response = client.search(builder -> builder.index("produces"), Object.class);
final List<Hit<Object>> hits = response.hits().hits();
hits.forEach(
x-> System.err.println(x)
);
根據(jù)ID查詢文檔
使用HashMap對應(yīng)查詢
//查詢文檔
final GetResponse<Map> response = client.get(builder -> builder.index("produces").id("116677"), Map.class);
final Map source = response.source();
source.forEach((x,y)->{
System.err.println(x+":"+y);
});
使用自定義類對應(yīng)查詢
final GetResponse<Produce> response1 = client.get(builder -> builder.index("produces").id("aabbcc123"), Produce.class);
final Produce source1 = response1.source();
System.err.println(source1.toString());
刪除指定文檔
//根據(jù)ID刪除文檔
final DeleteResponse response = client.delete(builder -> builder.index("produces").id("44514"));
System.err.println(response.shards().successful());
修改文檔
覆蓋寫
//修改文檔(覆蓋)
final Produce produce = new Produce("ccaabb123", "旺仔搖滾洞", "旺仔搖滾洞亂搖亂滾", 10.23D);
final UpdateResponse<Produce> response = client.update(builder -> builder.index("produces").id("aabbcc123").doc(produce), Produce.class);
System.err.println(response.shards().successful());
修改部分文檔
區(qū)別在于我們需要設(shè)置.docAsUpsert(true)
表明是修改部分而不是覆蓋
//修改文檔(部分修改)
// final Produce produce = new Produce("ccaabb123", "旺仔搖滾洞", "旺仔搖滾洞亂搖亂滾", 10.23D);
final Produce produce = new Produce();
produce.setName("旺仔搖不動");
final UpdateResponse<Produce> response = client.update(builder -> builder.index("produces").id("aabbcc123").doc(produce).docAsUpsert(true), Produce.class);
System.err.println(response.shards().successful());
批量操作
對應(yīng)批量操作大家應(yīng)該知道概念:看起來是批量,實際是逐條處理
所以我一開始想要寫成for循環(huán)的單個文檔創(chuàng)建,后來想了一下,如果這樣寫,其實沒有什么必要了,嘗試了一下確實有bulk函數(shù),寫成lambda方式后發(fā)現(xiàn)直接使用lambda構(gòu)建實際是不行的,會報方法只能調(diào)用一次的錯誤,那么我們就需要在外部先構(gòu)建出批量的條件然后再直接調(diào)用build方法即可final BulkResponse response = client.bulk(br.build());
public class BulkDoc {
public static void main(String[] args) throws IOException {
final RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
final RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
final ElasticsearchClient client = new ElasticsearchClient(transport);
//批量操作
//準(zhǔn)備數(shù)據(jù)
final Produce produce1 = new Produce("1000", "風(fēng)油精", "風(fēng)油精辣眼睛", 20.23D);
final Produce produce2 = new Produce("1001", "六神花露水", "蚊蟲叮咬就用Six God", 8.28D);
final Produce produce3 = new Produce("1002", "龍虎萬精油", "龍虎牌萬精油趕走疲勞做回自己", 13.3D);
final ArrayList<Produce> produceList = new ArrayList<>();
produceList.add(produce1);
produceList.add(produce2);
produceList.add(produce3);
//構(gòu)建BulkRequest
final BulkRequest.Builder br = new BulkRequest.Builder();
for (Produce produce : produceList) {
br.operations(op->op.index(idx->idx.index("produces").id(produce.getSku()).document(produce)));
}
final BulkResponse response = client.bulk(br.build());
System.err.println(response.toString());
client.shutdown();
transport.close();
restClient.close();
}
}
DSL條件查詢
根據(jù)具體的條件對文檔進(jìn)行查詢,這里的條件就是name為龍虎萬精油
注意的是若使用條件查詢必須傳入一個你要轉(zhuǎn)化得到的實體對象在這里是Produce.class
若你的索引中存了和其不符合的文檔則會報錯!
//簡單query方式查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q ->
q.match(t ->
t.field("name")
.query("龍虎萬精油"))), Produce.class);
System.err.println(response.hits().hits());
DSL matchAll查詢所有文檔
//matchAll
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q ->
q.matchAll(
v->v
)), Produce.class);
System.err.println(response.hits().hits());
多ID查詢(ids)
在values中我們可以傳入一個List也可以像我這樣直接寫多個ID,因為其使用了不定長參數(shù)
//多ID查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q ->
q.ids(sid->sid.values("1000","1001"))), Produce.class);
System.err.println(response.hits().hits());
term不分詞查詢
//term不分詞條件查詢
final SearchResponse<Produce> response = client.search(builder -> builder.index("produces")
.query(q -> q.term(t -> t.field("name").value("風(fēng)油精"))), Produce.class);
System.err.println(response.hits().hits());
范圍查詢
//范圍查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q ->
q.range(r ->
r.field("price").gt(JsonData.of(5D)).lt(JsonData.of(15D)))),
Produce.class);
System.err.println(response.hits().hits());
前綴查詢
//前綴查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q ->q.prefix(p->p.field("name").value("六"))),
Produce.class);
System.err.println(response.hits().hits());
匹配查詢
*全字符匹配
//匹配查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q ->q.wildcard(w->w.field("name").value("風(fēng)*"))),
Produce.class);
System.err.println(response.hits().hits());
?單字符匹配
//匹配查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q ->q.wildcard(w->w.field("name").value("風(fēng)?精"))),
Produce.class);
System.err.println(response.hits().hits());
模糊查詢
//模糊查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q ->q.fuzzy(f->f.field("name").value("六仙花露水"))),
Produce.class);
System.err.println(response.hits().hits());
多條件查詢
使用bool關(guān)鍵字配合must,should,must_not
- must:所有條件必須同時成立
- must_not:所有條件必須同時不成立
- should:所有條件中成立一個即可
//多條件
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q ->
q.bool(b ->
b.must(t ->
t.term(v ->
v.field("name")
.value("旺仔搖不動")))
.must(t2 ->
t2.term(v2 ->
v2.field("price")
.value(0.0D))))),
Produce.class);
System.err.println(response.hits().hits());
多字段查詢
//多字段查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q->q.multiMatch(qs->qs.query("蚊蟲叮咬 辣眼睛").fields("name","des"))),
Produce.class);
System.err.println(response.hits().hits());
字段分詞查詢
//字段分詞查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces").query(q->q.queryString(qs->qs.defaultField("des").query("搖滾"))),
Produce.class);
System.err.println(response.hits().hits());
高亮顯示
我們注意要設(shè)置前綴和后綴
//高亮顯示
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q -> q.match(v -> v.field("name").query("風(fēng)油精")))
.highlight(h -> h.preTags("<span>").postTags("<span>").fields("name", hf -> hf)),
Produce.class);
System.err.println(response.toString());
分頁查詢
我們使用match_all進(jìn)行全部搜索的時候使用size關(guān)鍵字設(shè)置每一頁的大小,使用from關(guān)鍵字設(shè)置頁碼
from的計算公式:(頁碼-1)*size
//分頁查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q->q.matchAll(v->v)).size(2).from(0),
Produce.class);
System.err.println(response.hits().hits());
排序
使用sort關(guān)鍵字指定需要進(jìn)行排序的字段設(shè)置排序類型即可,我們這里會使用到SortOrder
枚舉類來進(jìn)行指定排序方式文章來源:http://www.zghlxwxcb.cn/news/detail-790007.html
- desc:降序
- asc:升序
//排序
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q->q.matchAll(v->v))
.sort(builder1 -> builder1.field(f->f.field("price").order(SortOrder.Asc))),
Produce.class);
System.err.println(response.hits().hits());
指定字段查詢
使用_source關(guān)鍵字在數(shù)組中設(shè)置需要展示的字段
值得注意的是在source方法中需要我們寫filter去指定是include包含
或是exclude去除
xx字段文章來源地址http://www.zghlxwxcb.cn/news/detail-790007.html
//指定字段查詢
final SearchResponse<Produce> response = client.search(builder ->
builder.index("produces")
.query(q->q.matchAll(v->v))
.source(s->s.filter(v->v.includes("price","des"))),
Produce.class);
System.err.println(response.hits().hits());
到了這里,關(guān)于ElasticSearch系列——Elasticsearch Java API Client的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!