媒介
springboot 整合 ES 有两种方案,ES 官方提供的 Elasticsearch Java API Client 和 spring 提供的 [Spring Data Elasticsearch](Spring Data Elasticsearch)
两种方案各有优劣
Spring:高度封装,用着舒服。缺点是更新不及时,有可能无法使用 ES 的新 API
ES 官方:更新及时,灵活,缺点是太灵活了,基本是一比一复制 REST APIs,项目中使用需要二次封装。
Elasticsearch Java API Client
目前最新版本 ES8.12,要求 jdk8 以上,API 里面使用了大量的 builder 和 lambda
官方也提供了 测试用例
兼容
翻了不少博客,大部分都是使用 High Level Rest Client,这是旧版本的 api,新版本使用 Elasticsearch Java API Client,如何兼容旧版本,官方也提供了解决方案)
下文描述的均是新版 API
添加 jar 包
官方文档:[installation](安装| Elasticsearch Java API 客户端 [8.12] |松紧带 --- Installation | Elasticsearch Java API Client [8.12] | Elastic)
使用的是 maven,在 pom.xml 中添加- <dependency>
- <groupId>co.elastic.clients</groupId>
- <artifactId>elasticsearch-java</artifactId>
- <version>8.12.2</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.12.3</version>
- </dependency>
复制代码 假如报错 ClassNotFoundException: jakarta.json.spi.JsonProvider,则还需要添加- <dependency>
- <groupId>jakarta.json</groupId>
- <artifactId>jakarta.json-api</artifactId>
- <version>2.0.1</version>
- </dependency>
复制代码 打印请求
在 application. yml 中添加配置,打印 es 的 http 请求(建议在开辟调试时使用)- logging:
- level:
- tracer: TRACE
复制代码 连接 ES
配置文件如下,后续全部 ES 操作都通过 ElasticsearchClient 对象
更多配置请看 Common configuration- @Configuration
- public class ElasticSearchConfig {
-
- @Bean
- public ElasticsearchClient esClient() {
- // ES服务器URL
- String serverUrl = "http://127.0.0.1:9200";
- // ES用户名和密码
- String userName = "xxx";
- String password = "xxx";
-
- BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
- credsProv.setCredentials(
- AuthScope.ANY, new UsernamePasswordCredentials(userName, password)
- );
-
- RestClient restClient = RestClient
- .builder(HttpHost.create(serverUrl))
- .setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv))
- .build();
-
- ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
- return new ElasticsearchClient(transport);
- }
-
- }
复制代码 索引操作
代码中的 esClient 就是 ElasticsearchClient,请自行注入 bean- // 索引名字
- String indexName = "student";
-
- // 索引是否存在
- BooleanResponse books = esClient.indices().exists(e -> e.index(indexName));
- System.out.println("索引是否存在:" + books.value());
-
- // 创建索引
- esClient.indices().create(c -> c
- .index(indexName)
- .mappings(mappings -> mappings // 映射
- .properties("name", p -> p
- .text(t -> t // text类型,index=false
- .index(false)
- )
- )
- .properties("age", p -> p
- .long_(t -> t) // long类型
- )
- )
- );
-
- // 删除索引
- esClient.indices().delete(d -> d.index(indexName));
复制代码 文档操作 (CRUD)
下文以官方测试数据 account. json 为例
实体类
起首定义实体类,用于 ES 中的字段- public class Account {
- private String id;
- // 解决ES中字段与实体类字段不一致的问题
- @JsonProperty("account_number")
- private Long account_number;
- private String address;
- private Integer age;
- private Long balance;
- private String city;
- private String email;
- private String employer;
- private String firstname;
- private String lastname;
- private String gender;
- private String state;
- ... 省略get、set方法
- }
复制代码 新增
- String indexName = "account"; // 索引名字
- Account account = new Account();
- account.setId("1");
- account.setLastname("guyu");
- // 新增
- CreateResponse createResponse = esClient.create(c -> c
- .index(indexName) // 索引名字
- .id(account.getId()) // id
- .document(account) // 实体类
- );
复制代码 修改
- UpdateResponse<Account> updateResp = esClient.update(u -> u
- .index(indexName)
- .id(account.getId())
- .doc(account),
- Account.class
- );
复制代码 删除
- DeleteResponse deleteResp = esClient.delete(d -> d.index(indexName).id("1"));
复制代码 批量新增
批量操作需要使用到 bulk- List<Account> accountList = ...
- BulkRequest.Builder br = new BulkRequest.Builder();
- for (Account acc : accountList) {
- br.operations(op -> op
- .create(c -> c
- .index(indexName)
- .id(acc.getId())
- .document(acc)
- )
- );
- }
- BulkResponse bulkResp = esClient.bulk(br.build());
复制代码 有没有觉得批量新增的 .create () 里面的参数很眼熟,批量删除和更新请举一反三
根据 id 查询
- // 定义实体类
- GetResponse<Account> getResp = esClient.get(g -> g.index(indexName).id("1"), Account.class);
- if (getResp.found()) {
- Account source = getResp.source(); // 这就是得到的实体类
- source.setId(getResp.id());
- }
- // 不定义实体类
- GetResponse<ObjectNode> getResp = esClient.get(g -> g
- .index(indexName)
- .id("1"),
- ObjectNode.class
- );
- if (getResp.found()) {
- ObjectNode json = getResp.source();
- String firstname = json.get("firstname").asText();
- System.out.println(firstname);
- }
复制代码 搜索
搜索全部- SearchResponse<Account> searchResp = esClient.search(s -> s
- .index(indexName)
- .query(q -> q.matchAll(m -> m)) // 搜索全部
- , Account.class
- );
- HitsMetadata<Account> hits = searchResp.hits();
- long totalValue = hits.total().value(); // 匹配到的数量
- hits.hits().forEach(h -> {
- Account acc = h.source(); // 这就是得到的实体类
- acc.setId(h.id());
- });
复制代码 ES API 的对象定义,基本与返回的 json 一一对应的,所以 SearchResponse 就不过多赘述。
搜索 firstname = Amber- SearchResponse<Account> searchResp = esClient.search(s -> s
- .index(indexName)
- .query(q -> q // 查询
- .match(t -> t
- .field("firstname")
- .query("Amber")
- )
- )
- , Account.class
- );
- // 也可以这样写
- Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();
- SearchResponse<Account> searchResp = esClient.search(s -> s
- .index(indexName)
- .query(firstNameQuery)
- , Account.class
- );
复制代码 嵌套查询,比如搜索 firstname = Amber AND age = 32- Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();
- Query ageQuery = MatchQuery.of(m -> m.field("age").query(32))._toQuery();
-
- SearchResponse<Account> searchResp = esClient.search(s -> s
- .index(indexName)
- .query(q -> q
- .bool(b -> b.must(firstNameQuery, ageQuery))
- )
- , Account.class
- );
复制代码 浅分页
from 和 size 参数类似于 mysql 的 limit,详细阐明见 Paginate search results- SearchResponse<Account> searchResp = esClient.search(s -> s
- .index(indexName)
- .from(0) // 分页参数
- .size(20) // 分页参数
- , Account.class
- );
复制代码 排序
- SearchResponse<Account> searchResp = esClient.search(s -> s
- .index(indexName)
- .sort(so -> so // 排序字段1
- .field(f -> f
- .field("age")
- .order(SortOrder.Asc)
- )
- )
- .sort(so -> so // 排序字段2
- .field(f -> f
- .field("account_number")
- .order(SortOrder.Desc)
- )
- )
- , Account.class
- );
复制代码 Spring Data Elasticsearch
文档: Spring Data Elasticsearch
添加 jar 和配置
pom.xml添加依赖- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
- </dependency>
复制代码 yml 配置- spring:
- elasticsearch:
- uris: http://xxx:9200
- username: xxx
- password: xxx
- logging:
- level:
- # 输出es的查询参数(调试用)
- tracer: TRACE
复制代码 索引操作
实体类
- @Data
- @Document(indexName = "account")
- public class Account {
-
- @Id
- private String id;
- // 解决ES中字段与实体类字段不一致的问题
- @Field(name = "account_number", type = FieldType.Long)
- private Long accountNumber;
- @Field(type = FieldType.Text)
- private String address;
- @Field(type = FieldType.Integer)
- private Integer age;
- @Field(type = FieldType.Long)
- private Long balance;
- @Field(type = FieldType.Text)
- private String city;
- @Field(type = FieldType.Text)
- private String email;
- @Field(type = FieldType.Text)
- private String employer;
- @Field(type = FieldType.Text)
- private String firstname;
- @Field(type = FieldType.Text)
- private String lastname;
- @Field(type = FieldType.Text)
- private String gender;
- @Field(type = FieldType.Text)
- private String state;
- ... 省略get、set 方法
- }
复制代码- IndexOperations idxOpt = template.indexOps(Account.class);
- // 索引是否存在
- boolean idxExist = idxOpt.exists();
-
- // 创建索引
- boolean createSuccess = idxOpt.createWithMapping();
- System.out.println(createSuccess);
-
- // 删除索引
- boolean deleted = idxOpt.delete();
复制代码 文档操作(CRUD)
- Account account = new Account();
- account.setId("1");
- account.setLastname("guyu");
-
- // 这是插入或覆盖,如果id存在了就是覆盖
- template.save(account);
-
- // 修改,用的是es的_update
- template.update(account);
- // 删除
- template.delete(account)
- // 批量新增(用的是es的_bulk)
- List<Account> accountList = ...
- template.save(accountList);
- // 根据id查询
- Account account = template.get("1", Account.class);
复制代码 搜索 + 排序 + 分页
- // 搜索 firstname = Amber AND age = 32
- Criteria criteria = new Criteria();
- criteria.and(new Criteria("firstname").is("Amber"));
- criteria.and(new Criteria("age").is(32));
-
- // 分页
- int pageNum = 1; // 页码
- int pageSize = 20; // 每页数量
- Query query = new CriteriaQueryBuilder(criteria)
- .withSort(Sort.by(new Order(Sort.Direction.ASC, "age"))) // 排序字段1
- .withSort(Sort.by(new Order(Sort.Direction.DESC, "balance"))) // 排序字段1
- .withPageable(PageRequest.of(pageNum - 1, pageSize)) // 浅分页
- // 不需要查询的字段
- .withSourceFilter(new FetchSourceFilterBuilder().withExcludes("email", "address").build())
- .build();
-
- SearchHits<Account> searchHits = template.search(query, Account.class);
- long totalValue = searchHits.getTotalHits(); // 匹配到的数量
- for (SearchHit<Account> searchHit : searchHits.getSearchHits()) {
- Account account = searchHit.getContent(); // 这就是得到的实体类
- }
复制代码 总结
本文先容了 SpringBoot 整合 ElasticSearch 的两种方案,但均只是简单提及,更详细的用法需要自行查看官方文档。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |