经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Elasticsearch » 查看文章
SpringBoot3集成ElasticSearch
来源:cnblogs  作者:知了一笑  时间:2023/8/16 9:14:07  对本文有异议

标签:ElasticSearch8.Kibana8;

一、简介

Elasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎,适用于各种数据类型,数字、文本、地理位置、结构化数据、非结构化数据;

在实际的工作中,历经过Elasticsearch从6.07.0的版本升级,而这次SpringBoot3和ES8.0的集成,虽然脚本的语法变化很小,但是Java客户端的API语法变化很大;

二、环境搭建

1、下载安装包

需要注意的是,这些安装包的版本要选择对应的,不然容易出问题;

  1. 软件包:elasticsearch-8.8.2-darwin-x86_64.tar.gz
  2. 分词器工具:elasticsearch-analysis-ik-8.8.2.zip
  3. 可视化工具:kibana-8.8.2-darwin-x86_64.tar.gz

2、服务启动

不论是ES还是Kibana,在首次启动后,会初始化很多配置文件,可以根据自己的需要做相关的配置调整,比如常见的端口调整,资源占用,安全校验等;

  1. 1、启动ES
  2. elasticsearch-8.8.2/bin/elasticsearch
  3. 本地访问:localhost:9200
  4. 2、启动Kibana
  5. kibana-8.8.2/bin/kibana
  6. 本地访问:http://localhost:5601
  7. # 3、查看安装的插件
  8. http://localhost:9200/_cat/plugins -> analysis-ik 8.8.2

三、工程搭建

1、工程结构

2、依赖管理

starter-elasticsearch组件中,实际上依赖的是elasticsearch-java组件的8.7.1版本;

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  4. <version>${spring-boot.version}</version>
  5. </dependency>

3、配置文件

在上面环境搭建的过程中,已经禁用了用户和密码的登录验证,配置ES服务地址即可;

  1. spring:
  2. # ElasticSearch配置
  3. elasticsearch:
  4. uris: localhost:9200

四、基础用法

1、实体类

通过DocumentField注解描述ES索引结构的实体类,注意这里JsonIgnoreProperties注解,解决索引中字段和实体类非一一对应的而引起的JSON解析问题;

  1. @JsonIgnoreProperties(ignoreUnknown = true)
  2. @Document(indexName = "contents_index", createIndex = false)
  3. public class ContentsIndex implements Serializable {
  4. private static final long serialVersionUID=1L;
  5. @Field(type= FieldType.Integer)
  6. private Integer id;
  7. @Field(type= FieldType.Keyword)
  8. private String title;
  9. @Field(type= FieldType.Keyword)
  10. private String intro;
  11. @Field(type= FieldType.Text)
  12. private String content;
  13. @Field(type= FieldType.Integer)
  14. private Integer createId;
  15. @Field(type= FieldType.Keyword)
  16. private String createName;
  17. @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
  18. private Date createTime;
  19. }

2、初始化索引

基于ElasticsearchTemplate类和上述实体类,实现索引结构的初始化,并且将tb_contents表中的数据同步到索引中,最后通过ID查询一条测试数据;

  1. @Service
  2. public class ContentsIndexService {
  3. private static final Logger log = LoggerFactory.getLogger(ContentsIndexService.class);
  4. @Resource
  5. private ContentsService contentsService ;
  6. @Resource
  7. private ElasticsearchTemplate template ;
  8. /**
  9. * 初始化索引结构和数据
  10. */
  11. public void initIndex (){
  12. // 处理索引结构
  13. IndexOperations indexOps = template.indexOps(ContentsIndex.class);
  14. if (indexOps.exists()){
  15. boolean delFlag = indexOps.delete();
  16. log.info("contents_index exists,delete:{}",delFlag);
  17. indexOps.createMapping(ContentsIndex.class);
  18. } else {
  19. log.info("contents_index not exists");
  20. indexOps.createMapping(ContentsIndex.class);
  21. }
  22. // 同步数据库表记录
  23. List<Contents> contentsList = contentsService.queryAll();
  24. if (contentsList.size() > 0){
  25. List<ContentsIndex> contentsIndexList = new ArrayList<>() ;
  26. contentsList.forEach(contents -> {
  27. ContentsIndex contentsIndex = new ContentsIndex() ;
  28. BeanUtils.copyProperties(contents,contentsIndex);
  29. contentsIndexList.add(contentsIndex);
  30. });
  31. template.save(contentsIndexList);
  32. }
  33. // ID查询
  34. ContentsIndex contentsIndex = template.get("10",ContentsIndex.class);
  35. log.info("contents-index-10:{}",contentsIndex);
  36. }
  37. }

3、仓储接口

继承ElasticsearchRepository接口,可以对ES这种特定类型的存储库进行通用增删改查操作;在测试类中对该接口的方法进行测试;

  1. // 1、接口定义
  2. public interface ContentsIndexRepository extends ElasticsearchRepository<ContentsIndex,Long> {
  3. }
  4. // 2、接口测试
  5. public class ContentsIndexRepositoryTest {
  6. @Autowired
  7. private ContentsIndexRepository contentsIndexRepository;
  8. @Test
  9. public void testAdd (){
  10. // 单个新增
  11. contentsIndexRepository.save(buildOne());
  12. // 批量新增
  13. contentsIndexRepository.saveAll(buildList()) ;
  14. }
  15. @Test
  16. public void testUpdate (){
  17. // 根据ID查询后再更新
  18. Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(14L);
  19. if (contentsOpt.isPresent()){
  20. ContentsIndex contentsId = contentsOpt.get();
  21. System.out.println("id=14:"+contentsId);
  22. contentsId.setContent("update-content");
  23. contentsId.setCreateTime(new Date());
  24. contentsIndexRepository.save(contentsId);
  25. }
  26. }
  27. @Test
  28. public void testQuery (){
  29. // 单个ID查询
  30. Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(1L);
  31. if (contentsOpt.isPresent()){
  32. ContentsIndex contentsId1 = contentsOpt.get();
  33. System.out.println("id=1:"+contentsId1);
  34. }
  35. // 批量ID查询
  36. Iterator<ContentsIndex> contentsIterator = contentsIndexRepository
  37. .findAllById(Arrays.asList(10L,12L)).iterator();
  38. while (contentsIterator.hasNext()){
  39. ContentsIndex contentsIndex = contentsIterator.next();
  40. System.out.println("id="+contentsIndex.getId()+":"+contentsIndex);
  41. }
  42. }
  43. @Test
  44. public void testDelete (){
  45. contentsIndexRepository.deleteById(15L);
  46. contentsIndexRepository.deleteById(16L);
  47. }
  48. }

4、查询语法

无论是ElasticsearchTemplate类还是ElasticsearchRepository接口,都是对ES常用的简单功能进行封装,在实际使用时,复杂的查询语法还是依赖ElasticsearchClient和原生的API封装;

这里主要演示七个查询方法,主要涉及:ID查询,字段匹配,组合与范围查询,分页与排序,分组统计,最大值查询和模糊匹配;更多的查询API还是要多看文档中的案例才行;

  1. public class ElasticsearchClientTest {
  2. @Autowired
  3. private ElasticsearchClient client ;
  4. @Test
  5. public void testSearch1 () throws IOException {
  6. // ID查询
  7. GetResponse<ContentsIndex> resp = client.get(
  8. getReq ->getReq.index("contents_index").id("7"), ContentsIndex.class);
  9. if (resp.found()){
  10. ContentsIndex contentsIndex = resp.source() ;
  11. System.out.println("contentsIndex-7:"+contentsIndex);
  12. }
  13. }
  14. @Test
  15. public void testSearch2 () throws IOException {
  16. // 指定字段匹配
  17. SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  18. .query(query -> query.match(field -> field
  19. .field("createName").query("张三"))),ContentsIndex.class);
  20. printResp(resp);
  21. }
  22. @Test
  23. public void testSearch3 () throws IOException {
  24. // 组合查询:姓名和时间范围
  25. Query byName = MatchQuery.of(field -> field.field("createName").query("王五"))._toQuery();
  26. Query byTime = RangeQuery.of(field -> field.field("createTime")
  27. .gte(JsonData.of("2023-07-10T00:00:00"))
  28. .lte(JsonData.of("2023-07-12T00:00:00")))._toQuery();
  29. SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  30. .query(query -> query.bool(boolQuery -> boolQuery.must(byName).must(byTime))),ContentsIndex.class);
  31. printResp(resp);
  32. }
  33. @Test
  34. public void testSearch4 () throws IOException {
  35. // 排序和分页,在14条数据中,根据ID倒序排列,从第5条往后取4条数据
  36. SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  37. .from(5).size(4)
  38. .sort(sort -> sort.field(sortField -> sortField.field("id").order(SortOrder.Desc))),ContentsIndex.class);
  39. printResp(resp);
  40. }
  41. @Test
  42. public void testSearch5 () throws IOException {
  43. // 根据createId分组统计
  44. SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  45. .aggregations("createIdGroup",agg -> agg.terms(term -> term.field("createId"))),ContentsIndex.class);
  46. Aggregate aggregate = resp.aggregations().get("createIdGroup");
  47. LongTermsAggregate termsAggregate = aggregate.lterms();
  48. Buckets<LongTermsBucket> buckets = termsAggregate.buckets();
  49. for (LongTermsBucket termsBucket : buckets.array()) {
  50. System.out.println(termsBucket.key() + " : " + termsBucket.docCount());
  51. }
  52. }
  53. @Test
  54. public void testSearch6 () throws IOException {
  55. // 查询最大的ID
  56. SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  57. .aggregations("maxId",agg -> agg.max(field -> field.field("id"))),ContentsIndex.class);
  58. for (Map.Entry<String, Aggregate> entry : resp.aggregations().entrySet()){
  59. System.out.println(entry.getKey()+":"+entry.getValue().max().value());
  60. }
  61. }
  62. @Test
  63. public void testSearch7 () throws IOException {
  64. // 模糊查询title字段,允许1个误差
  65. Query byContent = FuzzyQuery.of(field -> field.field("title").value("设计").fuzziness("1"))._toQuery();
  66. SearchResponse<ContentsIndex> resp = client.search(
  67. searchReq -> searchReq.index("contents_index").query(byContent),ContentsIndex.class);
  68. printResp(resp);
  69. }
  70. private void printResp (SearchResponse<ContentsIndex> resp){
  71. TotalHits total = resp.hits().total();
  72. System.out.println("total:"+total);
  73. List<Hit<ContentsIndex>> hits = resp.hits().hits();
  74. for (Hit<ContentsIndex> hit: hits) {
  75. ContentsIndex contentsIndex = hit.source();
  76. System.out.println(hit.id()+":"+contentsIndex);
  77. }
  78. }
  79. }

五、参考源码

  1. 文档仓库:
  2. https://gitee.com/cicadasmile/butte-java-note
  3. 源码仓库:
  4. https://gitee.com/cicadasmile/butte-spring-parent

原文链接:https://www.cnblogs.com/cicada-smile/p/17632955.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号