目录
标签:ElasticSearch8.Kibana8;
一、简介
Elasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎,适用于各种数据类型,数字、文本、地理位置、结构化数据、非结构化数据;
在实际的工作中,历经过Elasticsearch从6.0到7.0的版本升级,而这次SpringBoot3和ES8.0的集成,虽然脚本的语法变化很小,但是Java客户端的API语法变化很大;
二、环境搭建
1、下载安装包
需要注意的是,这些安装包的版本要选择对应的,不然容易出问题;- 软件包:elasticsearch-8.8.2-darwin-x86_64.tar.gz
- 分词器工具:elasticsearch-analysis-ik-8.8.2.zip
- 可视化工具:kibana-8.8.2-darwin-x86_64.tar.gz
复制代码 2、服务启动
不论是ES还是Kibana,在首次启动后,会初始化很多配置文件,可以根据自己的需要做相关的配置调整,比如常见的端口调整,资源占用,安全校验等;
 - 1、启动ES
- elasticsearch-8.8.2/bin/elasticsearch
- 本地访问:localhost:9200
- 2、启动Kibana
- kibana-8.8.2/bin/kibana
- 本地访问:http://localhost:5601
- # 3、查看安装的插件
- http://localhost:9200/_cat/plugins -> analysis-ik 8.8.2
复制代码 三、工程搭建
1、工程结构

2、依赖管理
在starter-elasticsearch组件中,实际上依赖的是elasticsearch-java组件的8.7.1版本;- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
- <version>${spring-boot.version}</version>
- </dependency>
复制代码 3、配置文件
在上面环境搭建的过程中,已经禁用了用户和密码的登录验证,配置ES服务地址即可;- spring:
- # ElasticSearch配置
- elasticsearch:
- uris: localhost:9200
复制代码 四、基础用法
1、实体类
通过Document和Field注解描述ES索引结构的实体类,注意这里JsonIgnoreProperties注解,解决索引中字段和实体类非一一对应的而引起的JSON解析问题;- @JsonIgnoreProperties(ignoreUnknown = true)
- @Document(indexName = "contents_index", createIndex = false)
- public class ContentsIndex implements Serializable {
- private static final long serialVersionUID=1L;
- @Field(type= FieldType.Integer)
- private Integer id;
- @Field(type= FieldType.Keyword)
- private String title;
- @Field(type= FieldType.Keyword)
- private String intro;
- @Field(type= FieldType.Text)
- private String content;
- @Field(type= FieldType.Integer)
- private Integer createId;
- @Field(type= FieldType.Keyword)
- private String createName;
- @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
- private Date createTime;
- }
复制代码 2、初始化索引
基于ElasticsearchTemplate类和上述实体类,实现索引结构的初始化,并且将tb_contents表中的数据同步到索引中,最后通过ID查询一条测试数据;- @Service
- public class ContentsIndexService {
- private static final Logger log = LoggerFactory.getLogger(ContentsIndexService.class);
- @Resource
- private ContentsService contentsService ;
- @Resource
- private ElasticsearchTemplate template ;
- /**
- * 初始化索引结构和数据
- */
- public void initIndex (){
- // 处理索引结构
- IndexOperations indexOps = template.indexOps(ContentsIndex.class);
- if (indexOps.exists()){
- boolean delFlag = indexOps.delete();
- log.info("contents_index exists,delete:{}",delFlag);
- indexOps.createMapping(ContentsIndex.class);
- } else {
- log.info("contents_index not exists");
- indexOps.createMapping(ContentsIndex.class);
- }
- // 同步数据库表记录
- List<Contents> contentsList = contentsService.queryAll();
- if (contentsList.size() > 0){
- List<ContentsIndex> contentsIndexList = new ArrayList<>() ;
- contentsList.forEach(contents -> {
- ContentsIndex contentsIndex = new ContentsIndex() ;
- BeanUtils.copyProperties(contents,contentsIndex);
- contentsIndexList.add(contentsIndex);
- });
- template.save(contentsIndexList);
- }
- // ID查询
- ContentsIndex contentsIndex = template.get("10",ContentsIndex.class);
- log.info("contents-index-10:{}",contentsIndex);
- }
- }
复制代码 3、仓储接口
继承ElasticsearchRepository接口,可以对ES这种特定类型的存储库进行通用增删改查操作;在测试类中对该接口的方法进行测试;- // 1、接口定义
- public interface ContentsIndexRepository extends ElasticsearchRepository<ContentsIndex,Long> {
- }
- // 2、接口测试
- public class ContentsIndexRepositoryTest {
- @Autowired
- private ContentsIndexRepository contentsIndexRepository;
- @Test
- public void testAdd (){
- // 单个新增
- contentsIndexRepository.save(buildOne());
- // 批量新增
- contentsIndexRepository.saveAll(buildList()) ;
- }
- @Test
- public void testUpdate (){
- // 根据ID查询后再更新
- Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(14L);
- if (contentsOpt.isPresent()){
- ContentsIndex contentsId = contentsOpt.get();
- System.out.println("id=14:"+contentsId);
- contentsId.setContent("update-content");
- contentsId.setCreateTime(new Date());
- contentsIndexRepository.save(contentsId);
- }
- }
- @Test
- public void testQuery (){
- // 单个ID查询
- Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(1L);
- if (contentsOpt.isPresent()){
- ContentsIndex contentsId1 = contentsOpt.get();
- System.out.println("id=1:"+contentsId1);
- }
- // 批量ID查询
- Iterator<ContentsIndex> contentsIterator = contentsIndexRepository
- .findAllById(Arrays.asList(10L,12L)).iterator();
- while (contentsIterator.hasNext()){
- ContentsIndex contentsIndex = contentsIterator.next();
- System.out.println("id="+contentsIndex.getId()+":"+contentsIndex);
- }
- }
- @Test
- public void testDelete (){
- contentsIndexRepository.deleteById(15L);
- contentsIndexRepository.deleteById(16L);
- }
- }
复制代码 4、查询语法
无论是ElasticsearchTemplate类还是ElasticsearchRepository接口,都是对ES常用的简单功能进行封装,在实际使用时,复杂的查询语法还是依赖ElasticsearchClient和原生的API封装;
这里主要演示七个查询方法,主要涉及:ID查询,字段匹配,组合与范围查询,分页与排序,分组统计,最大值查询和模糊匹配;更多的查询API还是要多看文档中的案例才行;- public class ElasticsearchClientTest {
- @Autowired
- private ElasticsearchClient client ;
- @Test
- public void testSearch1 () throws IOException {
- // ID查询
- GetResponse<ContentsIndex> resp = client.get(
- getReq ->getReq.index("contents_index").id("7"), ContentsIndex.class);
- if (resp.found()){
- ContentsIndex contentsIndex = resp.source() ;
- System.out.println("contentsIndex-7:"+contentsIndex);
- }
- }
- @Test
- public void testSearch2 () throws IOException {
- // 指定字段匹配
- SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
- .query(query -> query.match(field -> field
- .field("createName").query("张三"))),ContentsIndex.class);
- printResp(resp);
- }
- @Test
- public void testSearch3 () throws IOException {
- // 组合查询:姓名和时间范围
- Query byName = MatchQuery.of(field -> field.field("createName").query("王五"))._toQuery();
- Query byTime = RangeQuery.of(field -> field.field("createTime")
- .gte(JsonData.of("2023-07-10T00:00:00"))
- .lte(JsonData.of("2023-07-12T00:00:00")))._toQuery();
- SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
- .query(query -> query.bool(boolQuery -> boolQuery.must(byName).must(byTime))),ContentsIndex.class);
- printResp(resp);
- }
- @Test
- public void testSearch4 () throws IOException {
- // 排序和分页,在14条数据中,根据ID倒序排列,从第5条往后取4条数据
- SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
- .from(5).size(4)
- .sort(sort -> sort.field(sortField -> sortField.field("id").order(SortOrder.Desc))),ContentsIndex.class);
- printResp(resp);
- }
- @Test
- public void testSearch5 () throws IOException {
- // 根据createId分组统计
- SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
- .aggregations("createIdGroup",agg -> agg.terms(term -> term.field("createId"))),ContentsIndex.class);
- Aggregate aggregate = resp.aggregations().get("createIdGroup");
- LongTermsAggregate termsAggregate = aggregate.lterms();
- Buckets<LongTermsBucket> buckets = termsAggregate.buckets();
- for (LongTermsBucket termsBucket : buckets.array()) {
- System.out.println(termsBucket.key() + " : " + termsBucket.docCount());
- }
- }
- @Test
- public void testSearch6 () throws IOException {
- // 查询最大的ID
- SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
- .aggregations("maxId",agg -> agg.max(field -> field.field("id"))),ContentsIndex.class);
- for (Map.Entry<String, Aggregate> entry : resp.aggregations().entrySet()){
- System.out.println(entry.getKey()+":"+entry.getValue().max().value());
- }
- }
- @Test
- public void testSearch7 () throws IOException {
- // 模糊查询title字段,允许1个误差
- Query byContent = FuzzyQuery.of(field -> field.field("title").value("设计").fuzziness("1"))._toQuery();
- SearchResponse<ContentsIndex> resp = client.search(
- searchReq -> searchReq.index("contents_index").query(byContent),ContentsIndex.class);
- printResp(resp);
- }
- private void printResp (SearchResponse<ContentsIndex> resp){
- TotalHits total = resp.hits().total();
- System.out.println("total:"+total);
- List<Hit<ContentsIndex>> hits = resp.hits().hits();
- for (Hit<ContentsIndex> hit: hits) {
- ContentsIndex contentsIndex = hit.source();
- System.out.println(hit.id()+":"+contentsIndex);
- }
- }
- }
复制代码 五、参考源码
- 文档仓库:
- https://gitee.com/cicadasmile/butte-java-note
- 源码仓库:
- https://gitee.com/cicadasmile/butte-spring-parent
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |