本文主要介绍 Elasticsearch Java API Client 的使用,相关的环境及软件信息如下:CentOS 7.6.1810、Java 1.8.0_321(客户端用)、Elasticsearch 8.2.2。
1、Java API Client 的特点
- Strongly typed requests and responses for all Elasticsearch APIs.
- Blocking and asynchronous versions of all APIs.
- Use of fluent builders and functional patterns to allow writing concise yet readable code when creating complex nested structures.
- Seamless integration of application classes by using an object mapper such as Jackson or any JSON-B implementation.
- Delegates protocol handling to an http client such as the Java Low Level REST Client that takes care of all transport-level concerns: HTTP connection pooling, retries, node discovery, and so on.
2、引入依赖
- <dependency>
- <groupId>co.elastic.clients</groupId>
- <artifactId>elasticsearch-java</artifactId>
- <version>8.2.2</version>
- </dependency>
复制代码 3、使用
Elasticsearch Java API Client 通过 API 的方式来组装请求数据,避免直接编写 JSON 字符串;请求数据的详细说明可参考:Elasticsearch 入门实战(3)--REST API 使用。
3.1、连接及关闭
Java API Client 底层依赖 Java Low Level REST Client,需先创建 Low Level REST Client。- private ElasticsearchTransport transport;
- private ElasticsearchClient client;
- @Before
- public void before() {
- RestClient restClient = RestClient.builder(
- new HttpHost("10.49.196.10", 9200),
- new HttpHost("10.49.196.11", 9200),
- new HttpHost("10.49.196.12", 9200)).build();
- ObjectMapper objectMapper = new ObjectMapper();
- transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
- client = new ElasticsearchClient(transport);
- }
- @After
- public void after() throws IOException {
- client.shutdown();
- }
复制代码 3.2、索引
3.2.1、创建索引
- @Test
- public void createIndex() throws IOException {
- CreateIndexResponse response = client.indices().create(builder -> builder
- .settings(indexSettingsBuilder -> indexSettingsBuilder.numberOfReplicas("1").numberOfShards("2"))
- .mappings(typeMappingBuilder -> typeMappingBuilder
- .properties("age", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
- .properties("name", propertyBuilder -> propertyBuilder.keyword(keywordPropertyBuilder -> keywordPropertyBuilder))
- .properties("poems", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
- .properties("about", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
- .properties("success", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
- )
- .index(INDEX_NAME));
- logger.info("acknowledged={}", response.acknowledged());
- }
复制代码 3.2.2、修改 _mapping 信息
字段可以新增,已有的字段只能修改字段的 search_analyzer 属性。- @Test
- public void modifyIndex() throws IOException {
- PutMappingResponse response = client.indices().putMapping(typeMappingBuilder -> typeMappingBuilder
- .index(INDEX_NAME)
- .properties("age", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
- .properties("name", propertyBuilder -> propertyBuilder.keyword(keywordPropertyBuilder -> keywordPropertyBuilder))
- .properties("poems", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_smart")))
- );
- logger.info("acknowledged={}", response.acknowledged());
- }
复制代码 3.2.3、删除索引
- @Test
- public void deleteIndex() throws IOException {
- DeleteIndexResponse response = client.indices().delete(builder -> builder.index(INDEX_NAME));
- logger.info("acknowledged={}", response.acknowledged());
- }
复制代码 3.2.4、查询索引列表
- @Test
- public void getIndex() throws IOException {
- //使用 * 也可以
- GetIndexResponse response = client.indices().get(builder -> builder.index("_all"));
- logger.info(response.result().toString());
- }
复制代码 3.2.5、查询索引详情
- @Test
- public void getIndexDetail() throws IOException {
- GetIndexResponse response = client.indices().get(builder -> builder.index(INDEX_NAME));
- logger.info(response.result().toString());
- }
复制代码 3.3、文档
3.3.1、创建文档
- @Test
- public void createDoc() throws IOException {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 30);
- doc.put("name", "李白");
- doc.put("poems", "静夜思");
- doc.put("about", "字太白");
- doc.put("success", "创造了古代浪漫主义文学高峰、歌行体和七绝达到后人难及的高度");
- CreateResponse response = client.create(builder -> builder.index(INDEX_NAME).id("1").document(doc));
- logger.info(response.toString());
- Poet poet = new Poet(31, "杜甫", "登高", "字子美", "唐代伟大的现实主义文学作家,唐诗思想艺术的集大成者");
- response = client.create(builder -> builder.index(INDEX_NAME).id("2").document(poet));
- logger.info(response.toString());
- }
复制代码 3.3.2、删除文档
- @Test
- public void deleteDoc() throws IOException {
- DeleteResponse response = client.delete(builder -> builder.index(INDEX_NAME).id("1"));
- logger.info(response.toString());
- }
复制代码 3.3.3、修改文档
修改文档,只修改设置的字段。- @Test
- public void updateDoc() throws IOException {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 33);
- doc.put("name", "李白2");
- UpdateResponse response = client.update(builder -> builder.index(INDEX_NAME).id("1").doc(doc), Map.class);
- logger.info(response.toString());
- Poet poet = new Poet();
- poet.setAge(40);
- poet.setName("杜甫2");
- response = client.update(builder -> builder.index(INDEX_NAME).id("2").doc(poet).docAsUpsert(true), Poet.class);
- logger.info(response.toString());
- }
复制代码 3.3.4、新增或修改文档
新增或修改文档,修改时所有的字段都会覆盖(相当于先删除在新增)。- @Test
- public void createOrUpdateDoc() throws IOException {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 33);
- doc.put("name", "李白2");
- //只更新设置的字段
- IndexResponse response = client.index(builder -> builder.index(INDEX_NAME).id("1").document(doc));
- logger.info(response.toString());
- Poet poet = new Poet();
- poet.setAge(40);
- poet.setName("杜甫2");
- response = client.index(builder -> builder.index(INDEX_NAME).id("2").document(poet));
- logger.info(response.toString());
- }
复制代码 3.3.5、批量操作
- @Test
- public void bulk() throws IOException {
- List<BulkOperation> list = new ArrayList<>();
- //批量新增
- for (int i = 0; i < 5; i++) {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 30);
- doc.put("name", "李白" + i);
- doc.put("poems", "静夜思");
- doc.put("about", "字太白");
- doc.put("success", "创造了古代浪漫主义文学高峰、歌行体和七绝达到后人难及的高度");
- String id = 10 + i + "";
- list.add(new BulkOperation.Builder().create(builder -> builder.index(INDEX_NAME).id(id).document(doc)).build());
- }
- for (int i = 0; i < 5; i++) {
- Poet poet = new Poet(31, "杜甫" + i, "登高", "字子美", "唐代伟大的现实主义文学作家,唐诗思想艺术的集大成者");
- String id = 20 + i + "";
- list.add(new BulkOperation.Builder().create(builder -> builder.index(INDEX_NAME).id(id).document(poet)).build());
- }
- //批量删除
- list.add(new BulkOperation.Builder().delete(builder -> builder.index(INDEX_NAME).id("1")).build());
- list.add(new BulkOperation.Builder().delete(builder -> builder.index(INDEX_NAME).id("2")).build());
- BulkResponse response = client.bulk(builder -> builder.index(INDEX_NAME).operations(list));
- logger.info(response.toString());
- }
复制代码 3.4、查询
3.4.1、查询所有文档
- @Test
- public void getDocAll() throws IOException {
- SearchResponse<Map> response = client.search(builder -> builder.index(INDEX_NAME), Map.class);
- logger.info(response.toString());
- }
复制代码 3.4.2、查询单个文档
- @Test
- public void getDoc() throws IOException {
- GetResponse<Map> response = client.get(builder -> builder.index(INDEX_NAME).id("1"), Map.class);
- if (response.found()) {
- logger.info(response.source().toString());
- }
- GetResponse<Poet> response2 = client.get(builder -> builder.index(INDEX_NAME).id("2"), Poet.class);
- if (response2.found()) {
- logger.info(response2.source().toString());
- }
- }
复制代码 3.4.3、term/terms 查询
term/terms查询,对输入内容不做分词处理。- @Test
- public void searchTerm() throws IOException {
- SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .term(termQueryBuilder -> termQueryBuilder
- .field("name").value("李白")))
- .sort(sortOptionsBuilder -> sortOptionsBuilder
- .field(fieldSortBuilder -> fieldSortBuilder
- .field("name").order(SortOrder.Asc)))
- .source(sourceConfigBuilder -> sourceConfigBuilder
- .filter(sourceFilterBuilder -> sourceFilterBuilder
- .includes("age", "name")))
- .from(0)
- .size(10)
- , Map.class);
- logger.info(response.toString());
- List<FieldValue> words = new ArrayList<>();
- words.add(new FieldValue.Builder().stringValue("李白").build());
- words.add(new FieldValue.Builder().stringValue("杜甫").build());
- SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .terms(termsQueryBuilder -> termsQueryBuilder
- .field("name").terms(termsQueryFieldBuilder -> termsQueryFieldBuilder.value(words))))
- .source(sourceConfigBuilder -> sourceConfigBuilder
- .filter(sourceFilterBuilder -> sourceFilterBuilder
- .excludes("about")))
- .from(0)
- .size(10)
- , Poet.class);
- logger.info(response2.toString());
- }
复制代码 3.4.4、range(范围) 查询
- @Test
- public void searchRange() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .range(rangeQueryBuilder -> rangeQueryBuilder
- .field("age").gte(JsonData.of("20")).lt(JsonData.of("40"))))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.5、全文查询
3.4.5.1、match 查询
match 查询,对输入内容先分词再查询。- @Test
- public void searchMatch() throws IOException {
- SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想")))
- , Map.class);
- logger.info(response.toString());
- }
复制代码 3.4.5.2、multi_match 查询
多个字段进行匹配。- @Test
- public void searchMultiMatch() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .multiMatch(multiMatchQueryBuilder -> multiMatchQueryBuilder
- .fields("about", "success").query("思想")))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.5.3、match_phrase 查询
匹配整个查询字符串。- @Test
- public void searchMatchPhrase() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .matchPhrase(matchPhraseQueryBuilder -> matchPhraseQueryBuilder.field("success").query("文学作家")))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.5.4、match_all 查询
查询所有文档。- @Test
- public void searchMatchAll() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .matchAll(matchAllQueryBuilder -> matchAllQueryBuilder))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.5.5、query_string 查询
query_string 可以同时实现前面几种查询方法。- @Test
- public void searchQueryString() throws IOException {
- //类似 match
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .defaultField("success").query("古典文学")))
- , Poet.class);
- logger.info(response.toString());
- //类似 mulit_match
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("about", "success").query("古典文学")))
- , Poet.class);
- logger.info(response.toString());
- //类似 match_phrase
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .defaultField("success").query(""文学作家"")))
- , Poet.class);
- logger.info(response.toString());
- //带运算符查询,运算符两边的词不再分词
- //查询同时包含 ”文学“ 和 ”伟大“ 的文档
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("success").query("文学 AND 伟大")))
- , Poet.class);
- logger.info(response.toString());
- //等同上一个查询
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("success").query("文学 伟大").defaultOperator(Operator.And)))
- , Poet.class);
- logger.info(response.toString());
- //查询 name 或 success 字段包含"文学"和"伟大"这两个单词,或者包含"李白"这个单词的文档。
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("name","success").query("(文学 AND 伟大) OR 高度")))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.5.6、simple_query_string 查询
类似 query_string,主要区别如下:
1、不支持AND OR NOT ,会当做字符处理;使用 + 代替 AND,| 代替OR,- 代替 NOT
2、会忽略错误的语法- @Test
- public void searchSimpleQueryString() throws IOException {
- //查询同时包含 ”文学“ 和 ”伟大“ 的文档
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .simpleQueryString(simpleQueryStringQueryBuilder -> simpleQueryStringQueryBuilder
- .fields("success").query("文学 + 伟大")))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.6、模糊查询
- @Test
- public void searchFuzzy() throws IOException {
- //全文查询时使用模糊参数,先分词再计算模糊选项。
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思考").fuzziness("1")))
- , Poet.class);
- logger.info(response.toString());
- //使用 fuzzy query,对输入不分词,直接计算模糊选项。
- SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .fuzzy(fuzzyQueryBuilder -> fuzzyQueryBuilder
- .field("success").fuzziness("1").value("理想")))
- , Poet.class);
- logger.info(response2.toString());
- }
复制代码 3.4.7、组合查询
- @Test
- public void searchBool() throws IOException {
- //查询 success 包含 “思想” 且 age 在 [20-40] 之间的文档
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .bool(boolQueryBuilder -> boolQueryBuilder
- .must(queryBuilder2 -> queryBuilder2
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想"))
- )
- .must(queryBuilder2 -> queryBuilder2
- .range(rangeQueryBuilder -> rangeQueryBuilder
- .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
- )
- )
- )
- , Poet.class);
- logger.info(response.toString());
- //过滤出 success 包含 “思想” 且 age 在 [20-40] 之间的文档,不计算得分
- SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .bool(boolQueryBuilder -> boolQueryBuilder
- .filter(queryBuilder2 -> queryBuilder2
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想"))
- )
- .filter(queryBuilder2 -> queryBuilder2
- .range(rangeQueryBuilder -> rangeQueryBuilder
- .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
- )
- )
- )
- , Poet.class);
- logger.info(response2.toString());
- }
复制代码 3.4.8、聚合查询
- @Test
- public void searchAggs() throws IOException {
- //求和
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("age_sum", aggregationBuilder -> aggregationBuilder
- .sum(sumAggregationBuilder -> sumAggregationBuilder
- .field("age")))
- , Poet.class);
- logger.info(response.toString());
- //类似 select count distinct(age) from poet-index
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("age_count", aggregationBuilder -> aggregationBuilder
- .cardinality(cardinalityAggregationBuilder -> cardinalityAggregationBuilder.field("age")))
- , Poet.class);
- logger.info(response.toString());
- //数量、最大、最小、平均、求和
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("age_stats", aggregationBuilder -> aggregationBuilder
- .stats(statsAggregationBuilder -> statsAggregationBuilder
- .field("age")))
- , Poet.class);
- logger.info(response.toString());
- //select name,count(*) from poet-index group by name
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
- .terms(termsAggregationBuilder -> termsAggregationBuilder
- .field("name")))
- , Poet.class);
- logger.info(response.toString());
- //select name,age,count(*) from poet-index group by name,age
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
- .terms(termsAggregationBuilder -> termsAggregationBuilder
- .field("name")
- )
- .aggregations("age_terms", aggregationBuilder2 -> aggregationBuilder2
- .terms(termsAggregationBuilder -> termsAggregationBuilder
- .field("age")
- ))
- )
- , Poet.class);
- logger.info(response.toString());
- //类似 select avg(age) from poet-index where name='李白'
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .bool(boolQueryBuilder -> boolQueryBuilder
- .filter(queryBuilder2 -> queryBuilder2
- .term(termQueryBuilder -> termQueryBuilder
- .field("name").value("李白")))))
- .aggregations("ave_age", aggregationBuilder -> aggregationBuilder
- .avg(averageAggregationBuilder -> averageAggregationBuilder.field("age")))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.9、推荐搜索
- @Test
- public void searchSuggest() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .suggest(suggesterBuilder -> suggesterBuilder
- .suggesters("success_suggest", fieldSuggesterBuilder -> fieldSuggesterBuilder
- .text("思考")
- .term(termSuggesterBuilder -> termSuggesterBuilder
- .field("success")
- .suggestMode(SuggestMode.Always)
- .minWordLength(2)
- )
- )
- )
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 该测试会报如下错误:- co.elastic.clients.json.JsonpMappingException: Error deserializing co.elastic.clients.elasticsearch.core.search.TermSuggest: co.elastic.clients.json.UnexpectedJsonEventException: Unexpected JSON event 'START_ARRAY' instead of '[START_OBJECT, KEY_NAME]' (JSON path: suggest['term#success_suggest'][0].options) (line no=1, column no=247, offset=-1)
复制代码 还有高手知道原因?
3.4.10、高亮显示
- @Test
- public void searchHighlight() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想")))
- .highlight(highlightBuilder -> highlightBuilder
- .preTags("<span color='red'>")
- .postTags("</span>")
- .fields("success", highlightFieldBuilder -> highlightFieldBuilder))
- , Poet.class);
- logger.info(response.toString());
- }
复制代码 3.4.11、SQL 查询
- @Test
- public void searchSql() throws IOException {
- QueryResponse response = client.sql().query(builder -> builder
- .format("json").query("SELECT * FROM "" + INDEX_NAME + "" limit 3"));
- logger.info(response.toString());
- }
复制代码 该测试会报如下错误:- co.elastic.clients.json.JsonpMappingException: Error deserializing co.elastic.clients.elasticsearch.sql.QueryResponse: java.lang.UnsupportedOperationException (JSON path: rows[0][0]) (line no=1, column no=184, offset=-1)
复制代码 还有高手知道原因?
3.5、完整代码
- package com.abc.demo.es;
- import co.elastic.clients.elasticsearch.ElasticsearchClient;
- import co.elastic.clients.elasticsearch._types.*;
- import co.elastic.clients.elasticsearch._types.aggregations.AverageAggregation;
- import co.elastic.clients.elasticsearch._types.aggregations.CardinalityAggregation;
- import co.elastic.clients.elasticsearch._types.aggregations.SumAggregation;
- import co.elastic.clients.elasticsearch._types.query_dsl.*;
- import co.elastic.clients.elasticsearch.cat.IndicesResponse;
- import co.elastic.clients.elasticsearch.core.*;
- import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
- import co.elastic.clients.elasticsearch.indices.*;
- import co.elastic.clients.elasticsearch.sql.QueryResponse;
- import co.elastic.clients.json.JsonData;
- import co.elastic.clients.json.jackson.JacksonJsonpMapper;
- import co.elastic.clients.transport.ElasticsearchTransport;
- import co.elastic.clients.transport.rest_client.RestClientTransport;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- import org.apache.http.HttpHost;
- import org.elasticsearch.client.RestClient;
- import org.junit.After;
- import org.junit.Before;
- import org.junit.Test;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- public class ElasticsearchJavaCase {
- private static final Logger logger = LoggerFactory.getLogger(ElasticsearchJavaCase.class.getName());
- private static final String INDEX_NAME = "poet-index";
- private ElasticsearchTransport transport;
- private ElasticsearchClient client;
- @Before
- public void before() {
- RestClient restClient = RestClient.builder(
- new HttpHost("10.49.196.10", 9200),
- new HttpHost("10.49.196.11", 9200),
- new HttpHost("10.49.196.12", 9200)).build();
- ObjectMapper objectMapper = new ObjectMapper();
- transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
- client = new ElasticsearchClient(transport);
- }
- @After
- public void after() throws IOException {
- transport.close();
- }
- /**
- * 创建索引
- */
- @Test
- public void createIndex() throws IOException {
- CreateIndexResponse response = client.indices().create(builder -> builder
- .settings(indexSettingsBuilder -> indexSettingsBuilder.numberOfReplicas("1").numberOfShards("2"))
- .mappings(typeMappingBuilder -> typeMappingBuilder
- .properties("age", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
- .properties("name", propertyBuilder -> propertyBuilder.keyword(keywordPropertyBuilder -> keywordPropertyBuilder))
- .properties("poems", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
- .properties("about", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
- .properties("success", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
- )
- .index(INDEX_NAME));
- logger.info("acknowledged={}", response.acknowledged());
- }
- /**
- * 修改索引的_mapping信息
- * 字段可以新增,已有的字段只能修改字段的search_analyzer属性
- */
- @Test
- public void modifyIndex() throws IOException {
- PutMappingResponse response = client.indices().putMapping(typeMappingBuilder -> typeMappingBuilder
- .index(INDEX_NAME)
- .properties("age", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
- .properties("name", propertyBuilder -> propertyBuilder.keyword(keywordPropertyBuilder -> keywordPropertyBuilder))
- .properties("poems", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_smart")))
- );
- logger.info("acknowledged={}", response.acknowledged());
- }
- /**
- * 删除索引
- */
- @Test
- public void deleteIndex() throws IOException {
- DeleteIndexResponse response = client.indices().delete(builder -> builder.index(INDEX_NAME));
- logger.info("acknowledged={}", response.acknowledged());
- }
- /**
- * 查询索引列表
- */
- @Test
- public void getIndex() throws IOException {
- //使用 * 也可以
- GetIndexResponse response = client.indices().get(builder -> builder.index("_all"));
- logger.info(response.result().toString());
- }
- /**
- * 查询索引详情
- */
- @Test
- public void getIndexDetail() throws IOException {
- GetIndexResponse response = client.indices().get(builder -> builder.index(INDEX_NAME));
- logger.info(response.result().toString());
- }
- /**
- * 创建文档
- */
- @Test
- public void createDoc() throws IOException {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 30);
- doc.put("name", "李白");
- doc.put("poems", "静夜思");
- doc.put("about", "字太白");
- doc.put("success", "创造了古代浪漫主义文学高峰、歌行体和七绝达到后人难及的高度");
- CreateResponse response = client.create(builder -> builder.index(INDEX_NAME).id("1").document(doc));
- logger.info(response.toString());
- Poet poet = new Poet(31, "杜甫", "登高", "字子美", "唐代伟大的现实主义文学作家,唐诗思想艺术的集大成者");
- response = client.create(builder -> builder.index(INDEX_NAME).id("2").document(poet));
- logger.info(response.toString());
- }
- /**
- * 删除文档
- */
- @Test
- public void deleteDoc() throws IOException {
- DeleteResponse response = client.delete(builder -> builder.index(INDEX_NAME).id("1"));
- logger.info(response.toString());
- }
- /**
- * 修改文档,只修改设置的字段
- */
- @Test
- public void updateDoc() throws IOException {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 33);
- doc.put("name", "李白2");
- UpdateResponse response = client.update(builder -> builder.index(INDEX_NAME).id("1").doc(doc), Map.class);
- logger.info(response.toString());
- Poet poet = new Poet();
- poet.setAge(40);
- poet.setName("杜甫2");
- response = client.update(builder -> builder.index(INDEX_NAME).id("2").doc(poet).docAsUpsert(true), Poet.class);
- logger.info(response.toString());
- }
- /**
- * 新增或修改文档,修改时所有的字段都会覆盖(相当于先删除在新增)
- */
- @Test
- public void createOrUpdateDoc() throws IOException {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 33);
- doc.put("name", "李白2");
- //只更新设置的字段
- IndexResponse response = client.index(builder -> builder.index(INDEX_NAME).id("1").document(doc));
- logger.info(response.toString());
- Poet poet = new Poet();
- poet.setAge(40);
- poet.setName("杜甫2");
- response = client.index(builder -> builder.index(INDEX_NAME).id("2").document(poet));
- logger.info(response.toString());
- }
- /**
- * 批量操作
- */
- @Test
- public void bulk() throws IOException {
- List<BulkOperation> list = new ArrayList<>();
- //批量新增
- for (int i = 0; i < 5; i++) {
- Map<String, Object> doc = new HashMap<>();
- doc.put("age", 30);
- doc.put("name", "李白" + i);
- doc.put("poems", "静夜思");
- doc.put("about", "字太白");
- doc.put("success", "创造了古代浪漫主义文学高峰、歌行体和七绝达到后人难及的高度");
- String id = 10 + i + "";
- list.add(new BulkOperation.Builder().create(builder -> builder.index(INDEX_NAME).id(id).document(doc)).build());
- }
- for (int i = 0; i < 5; i++) {
- Poet poet = new Poet(31, "杜甫" + i, "登高", "字子美", "唐代伟大的现实主义文学作家,唐诗思想艺术的集大成者");
- String id = 20 + i + "";
- list.add(new BulkOperation.Builder().create(builder -> builder.index(INDEX_NAME).id(id).document(poet)).build());
- }
- //批量删除
- list.add(new BulkOperation.Builder().delete(builder -> builder.index(INDEX_NAME).id("1")).build());
- list.add(new BulkOperation.Builder().delete(builder -> builder.index(INDEX_NAME).id("2")).build());
- BulkResponse response = client.bulk(builder -> builder.index(INDEX_NAME).operations(list));
- logger.info(response.toString());
- }
- /**
- * 查询索有文档
- */
- @Test
- public void getDocAll() throws IOException {
- SearchResponse<Map> response = client.search(builder -> builder.index(INDEX_NAME), Map.class);
- logger.info(response.toString());
- }
- /**
- * 查询单个文档
- */
- @Test
- public void getDoc() throws IOException {
- GetResponse<Map> response = client.get(builder -> builder.index(INDEX_NAME).id("1"), Map.class);
- if (response.found()) {
- logger.info(response.source().toString());
- }
- GetResponse<Poet> response2 = client.get(builder -> builder.index(INDEX_NAME).id("2"), Poet.class);
- if (response2.found()) {
- logger.info(response2.source().toString());
- }
- }
- /**
- * term/terms查询,对输入内容不做分词处理
- */
- @Test
- public void searchTerm() throws IOException {
- SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .term(termQueryBuilder -> termQueryBuilder
- .field("name").value("李白")))
- .sort(sortOptionsBuilder -> sortOptionsBuilder
- .field(fieldSortBuilder -> fieldSortBuilder
- .field("name").order(SortOrder.Asc)))
- .source(sourceConfigBuilder -> sourceConfigBuilder
- .filter(sourceFilterBuilder -> sourceFilterBuilder
- .includes("age", "name")))
- .from(0)
- .size(10)
- , Map.class);
- logger.info(response.toString());
- List<FieldValue> words = new ArrayList<>();
- words.add(new FieldValue.Builder().stringValue("李白").build());
- words.add(new FieldValue.Builder().stringValue("杜甫").build());
- SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .terms(termsQueryBuilder -> termsQueryBuilder
- .field("name").terms(termsQueryFieldBuilder -> termsQueryFieldBuilder.value(words))))
- .source(sourceConfigBuilder -> sourceConfigBuilder
- .filter(sourceFilterBuilder -> sourceFilterBuilder
- .excludes("about")))
- .from(0)
- .size(10)
- , Poet.class);
- logger.info(response2.toString());
- }
- /**
- * range查询,范围查询
- */
- @Test
- public void searchRange() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .range(rangeQueryBuilder -> rangeQueryBuilder
- .field("age").gte(JsonData.of("20")).lt(JsonData.of("40"))))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * match查询,对输入内容先分词再查询
- */
- @Test
- public void searchMatch() throws IOException {
- SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想")))
- , Map.class);
- logger.info(response.toString());
- }
- /**
- * multi_match查询,-
- */
- @Test
- public void searchMultiMatch() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .multiMatch(multiMatchQueryBuilder -> multiMatchQueryBuilder
- .fields("about", "success").query("思想")))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * match_phrase 查询,匹配整个查询字符串
- */
- @Test
- public void searchMatchPhrase() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .matchPhrase(matchPhraseQueryBuilder -> matchPhraseQueryBuilder.field("success").query("文学作家")))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * match_all 查询,查询所有
- */
- @Test
- public void searchMatchAll() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .matchAll(matchAllQueryBuilder -> matchAllQueryBuilder))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * query_string 查询
- */
- @Test
- public void searchQueryString() throws IOException {
- //类似 match
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .defaultField("success").query("古典文学")))
- , Poet.class);
- logger.info(response.toString());
- //类似 mulit_match
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("about", "success").query("古典文学")))
- , Poet.class);
- logger.info(response.toString());
- //类似 match_phrase
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .defaultField("success").query(""文学作家"")))
- , Poet.class);
- logger.info(response.toString());
- //带运算符查询,运算符两边的词不再分词
- //查询同时包含 ”文学“ 和 ”伟大“ 的文档
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("success").query("文学 AND 伟大")))
- , Poet.class);
- logger.info(response.toString());
- //等同上一个查询
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("success").query("文学 伟大").defaultOperator(Operator.And)))
- , Poet.class);
- logger.info(response.toString());
- //查询 name 或 success 字段包含"文学"和"伟大"这两个单词,或者包含"李白"这个单词的文档。
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
- .fields("name","success").query("(文学 AND 伟大) OR 高度")))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * simple_query_string 查询,和query_string类似
- * 不支持AND OR NOT,会当做字符串处理
- * 使用 +替代AND,|替代OR,-替代NOT
- */
- @Test
- public void searchSimpleQueryString() throws IOException {
- //查询同时包含 ”文学“ 和 ”伟大“ 的文档
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .simpleQueryString(simpleQueryStringQueryBuilder -> simpleQueryStringQueryBuilder
- .fields("success").query("文学 + 伟大")))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * 模糊查询
- */
- @Test
- public void searchFuzzy() throws IOException {
- //全文查询时使用模糊参数,先分词再计算模糊选项。
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思考").fuzziness("1")))
- , Poet.class);
- logger.info(response.toString());
- //使用 fuzzy query,对输入不分词,直接计算模糊选项。
- SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .fuzzy(fuzzyQueryBuilder -> fuzzyQueryBuilder
- .field("success").fuzziness("1").value("理想")))
- , Poet.class);
- logger.info(response2.toString());
- }
- /**
- * bool查询,组合查询
- */
- @Test
- public void searchBool() throws IOException {
- //查询 success 包含 “思想” 且 age 在 [20-40] 之间的文档
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .bool(boolQueryBuilder -> boolQueryBuilder
- .must(queryBuilder2 -> queryBuilder2
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想"))
- )
- .must(queryBuilder2 -> queryBuilder2
- .range(rangeQueryBuilder -> rangeQueryBuilder
- .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
- )
- )
- )
- , Poet.class);
- logger.info(response.toString());
- //过滤出 success 包含 “思想” 且 age 在 [20-40] 之间的文档,不计算得分
- SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .bool(boolQueryBuilder -> boolQueryBuilder
- .filter(queryBuilder2 -> queryBuilder2
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想"))
- )
- .filter(queryBuilder2 -> queryBuilder2
- .range(rangeQueryBuilder -> rangeQueryBuilder
- .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
- )
- )
- )
- , Poet.class);
- logger.info(response2.toString());
- }
- /**
- * aggs查询,聚合查询
- */
- @Test
- public void searchAggs() throws IOException {
- //求和
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("age_sum", aggregationBuilder -> aggregationBuilder
- .sum(sumAggregationBuilder -> sumAggregationBuilder
- .field("age")))
- , Poet.class);
- logger.info(response.toString());
- //类似 select count distinct(age) from poet-index
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("age_count", aggregationBuilder -> aggregationBuilder
- .cardinality(cardinalityAggregationBuilder -> cardinalityAggregationBuilder.field("age")))
- , Poet.class);
- logger.info(response.toString());
- //数量、最大、最小、平均、求和
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("age_stats", aggregationBuilder -> aggregationBuilder
- .stats(statsAggregationBuilder -> statsAggregationBuilder
- .field("age")))
- , Poet.class);
- logger.info(response.toString());
- //select name,count(*) from poet-index group by name
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
- .terms(termsAggregationBuilder -> termsAggregationBuilder
- .field("name")))
- , Poet.class);
- logger.info(response.toString());
- //select name,age,count(*) from poet-index group by name,age
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
- .terms(termsAggregationBuilder -> termsAggregationBuilder
- .field("name")
- )
- .aggregations("age_terms", aggregationBuilder2 -> aggregationBuilder2
- .terms(termsAggregationBuilder -> termsAggregationBuilder
- .field("age")
- ))
- )
- , Poet.class);
- logger.info(response.toString());
- //类似 select avg(age) from poet-index where name='李白'
- response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .bool(boolQueryBuilder -> boolQueryBuilder
- .filter(queryBuilder2 -> queryBuilder2
- .term(termQueryBuilder -> termQueryBuilder
- .field("name").value("李白")))))
- .aggregations("ave_age", aggregationBuilder -> aggregationBuilder
- .avg(averageAggregationBuilder -> averageAggregationBuilder.field("age")))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * suggest查询,推荐搜索, 报错
- */
- @Test
- public void searchSuggest() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .suggest(suggesterBuilder -> suggesterBuilder
- .suggesters("success_suggest", fieldSuggesterBuilder -> fieldSuggesterBuilder
- .text("思考")
- .term(termSuggesterBuilder -> termSuggesterBuilder
- .field("success")
- .suggestMode(SuggestMode.Always)
- .minWordLength(2)
- )
- )
- )
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * 高亮显示
- */
- @Test
- public void searchHighlight() throws IOException {
- SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
- .index(INDEX_NAME)
- .query(queryBuilder -> queryBuilder
- .match(matchQueryBuilder -> matchQueryBuilder
- .field("success").query("思想")))
- .highlight(highlightBuilder -> highlightBuilder
- .preTags("<span color='red'>")
- .postTags("</span>")
- .fields("success", highlightFieldBuilder -> highlightFieldBuilder))
- , Poet.class);
- logger.info(response.toString());
- }
- /**
- * sql查询,报错
- */
- @Test
- public void searchSql() throws IOException {
- QueryResponse response = client.sql().query(builder -> builder
- .format("json").query("SELECT * FROM "" + INDEX_NAME + "" limit 1"));
- logger.info(response.toString());
- }
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- static class Poet {
- private Integer age;
- private String name;
- private String poems;
- private String about;
- /**成就*/
- private String success;
- }
- }
复制代码 ElasticsearchJavaCase.java
详细的 Elasticsearch Java API Client 使用说明,请参考官网文档:https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |