标签:ElasticSearch8.Kibana8;

一、简介

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

在理论的工作中,历经过Elasticsearch从6.07.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、启动ESelasticsearch-8.8.2/bin/elasticsearch本地拜访:localhost:92002、启动Kibanakibana-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、实体类

通过DocumentField注解形容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查问一条测试数据;

@Servicepublic 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