Elasticsearch集成SpringBoot(一)

手机游戏开发者 2024-9-25 11:50:18 107 0 来自 中国
前言:

之前的项目都有用到ES,但不是自己搭建和利用,包罗ES语法和数据存储结构都不知道,趁着偶然间来学习下ES的根本利用,很早就知道ES版本兼容标题有坑,唯有自己踩坑才印象深刻;
公司服务器太多人用,动不动就搞出标题,以是我就用当地环境搭建Elasticsearch+Kibana+Spring-boot-starter-data-elasticsearch来集成,如许学习本钱是比力低的,SpringBootData已经帮我们集成好了只需开箱即用,反面在优化代码通过自定义注解提供通用ES查询,现在先把代码跑起来。
ES版本选择:



先查抄自己SpringBoot版本,我是用的SpringBoot版本是2.3.12.RELEASE,以是ES安装版本用的elasticsearch-7.6.2/kibana-7.6.2-windows-x86_64(ES和kibana版本必须划一)
ES对JDK的支持可参考:https://www.elastic.co/cn/support/matrix#matrix_jvm
windows环境的搭建:

参考:https://blog.csdn.net/pan_junbiao/article/details/114309373
ES组件根本先容:

集成到SpringBoot:


  • 引入依赖
        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>        </dependency>

  • 设置文件
spring:  data:    elasticsearch: #ElasticsearchProperties      cluster-name: elasticsearch #默认即为elasticsearch      cluster-nodes: 127.0.0.1:9300 #设置es节点信息,逗号分隔,如果没有指定,则启动ClientNode

  • 定义实体类
/** * indexName 索引库名,个人建议以项目名称定名 * type 范例,个人建议以实体类名称定名 留意:ES7.0之后将放弃type * shards 默认分区数 * replicas 每个分区默认的备份数 * indexStoreType 索引文件存储范例 * refreshInterval 革新隔断 */@Document(indexName="oms",shards=5,replicas=1,indexStoreType="fs",refreshInterval="-1")@Datapublic class ElasticsearchOrderVo implements Serializable {    private static final long serialVersionUID = 551589397625941750L;    @Id    private String id;    /**     * orderId     */    private Long orderId;    /**     * 订单编号     */    private String orderSn;    /**     * 收货人     */    private String receiverName;    /**     * 省市区     */    private String receiverAreaName;    /**     * 具体地址     */    private String receiverAddress;    /**     * 手机     */    private String receiverPhone;    /**     * 总金额     */    private Long totalAmount;    /**     * 现实付出金额     */    private Long payAmount;    /**     * 运费金额     */    private Long freightAmount;    /**     * 积分抵扣金额     */    private Long integrationAmount;    /**     * 优惠券抵扣金额     */    private Long couponAmount;    /**     * 创建时间     */    private Date createTime;    /**     * 逾期时间     */    private Date expireTime;    /**     * 可获取的积分     */    private Long integration;    /**     * 备注     */    private String memo;    /**     * 订单状态,1:待付款,2:待发货,3:待收货,4:已完成     */    private Integer status;    /**     * 会员ID     */    private Long memberId;    /**     * 发票范例:0->不开辟票;1->电子发票;2->纸质发票     */    private Integer billType;    /**     * 发票仰面     */    private String billHeader;    /**     * 发票内容     */    private String billContent;    /**     * 收票人电话     */    private String billReceiverPhone;    /**     * 收票人邮箱     */    private String billReceiverEmail;    /**     * 订单范例:0->正常订单;1->秒杀订单     */    private Integer orderType;}

  • 定义Repository接口
@Componentpublic interface ElasticsearchOrderRepository extends ElasticsearchRepository<ElasticsearchOrderVo, String> {    //泛型<T,ID> T是文档内容,ID是文档ID范例}

  • 定义Service
public interface ElasticsearchOrderService {    /**     * 保存     * @param elasticsearchOrderVoList     * @return     */    Iterable<ElasticsearchOrderVo> saveAll(List<ElasticsearchOrderVo> elasticsearchOrderVoList);}@Slf4j@Servicepublic class ElasticsearchOrderServiceImpl implements ElasticsearchOrderService {    @Autowired    private ElasticsearchOrderRepository elasticsearchOrderRepository;    @Override    public Iterable<ElasticsearchOrderVo>  saveAll(List<ElasticsearchOrderVo> elasticsearchOrderVoList) {        return elasticsearchOrderRepository.saveAll(elasticsearchOrderVoList);    }}

  • 定义Controller
    public CommonResp es() {        List<ElasticsearchOrderVo> elasticsearchOrderVoList = new ArrayList<>();        for (int i = 1; i < 6; i++) {            ElasticsearchOrderVo elasticsearchOrderVo = new ElasticsearchOrderVo();            elasticsearchOrderVo.setId(UUID.randomUUID().toString());            elasticsearchOrderVo.setOrderId(Long.valueOf(i));            elasticsearchOrderVo.setOrderSn("Order_"+i);            elasticsearchOrderVo.setReceiverName("小明"+i);            elasticsearchOrderVo.setReceiverPhone("1201212122"+i);            elasticsearchOrderVo.setReceiverAreaName("广东省深圳市南山区");            elasticsearchOrderVo.setReceiverAddress("四方精创资讯大厦"+i+"楼");            elasticsearchOrderVo.setBillReceiverEmail("12456789@qq.com");            elasticsearchOrderVo.setOrderType(1);            elasticsearchOrderVo.setCouponAmount(100L);            elasticsearchOrderVo.setFreightAmount(8L);            elasticsearchOrderVo.setIntegrationAmount(1L);            elasticsearchOrderVo.setPayAmount(200L);            elasticsearchOrderVo.setStatus(4);            elasticsearchOrderVo.setBillHeader("深圳市 南山区 软基"+i + "层");            elasticsearchOrderVoList.add(elasticsearchOrderVo);        }        return CommonResp.ok(elasticsearchOrderService.saveAll(elasticsearchOrderVoList));    }

  • 欣赏器哀求:

    3.png
  • 通过Kibana进去查询下数据,看下数据是否有被插入进去

    4.png
这里可以看到数据是有写入ES,但好像不是按次序写进去的;
took:执行搜刮耗时,单位毫秒
time_out:搜刮是否超时
_shards:多少分片被搜刮,乐成多少,失败多少
hits:搜刮效果展示
hits.total:匹配条件的总当总数
hits.max_score:最大匹配得分
hits._score:返回文档的匹配得分(得分越高,匹配程度越高,越靠前)
_index:索引名称
_type:这里我默认没有指定type,因为ES7之后就放弃type了,在没有指定type的环境体系默认设置type=_doc
_id:这个ID代码用UID天生的,也可以不指定,ES会默认天生一个ID
_source:这个就是我们用到的字段了

  • 这里阐明下:_idex_type_id可以直接定位到指定文档,比如:

    5.png
  • 返回指定字段:

    6.png
  • 按照条件模糊匹配:名字便是小明5


  • 按照条件精准匹配:名字便是小明5


官方文档参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html

SpringBoot实现ES分页

ES分页先容:


  • from+size
    优点:支持随机翻页
    缺点:受制于 max_result_window 设置,不能无穷定翻页。存在深度翻页标题,越今后翻页越慢。
    实用场景:小型数据集大概大数据集返回 Top N(N <= 10000)效果集的业务场景,搜刮引擎(谷歌、bing、百度、360、sogou等)支持随机跳转分页的业务场景
  • Scroll 遍历查询
    优点:支持全量遍历。ps:单次遍历的 size 值也不能凌驾 max_result_window 巨细。
    缺点:相应时间非实时。保存上下文必要充足的堆内存空间。
    实用场景:全量或数据量很大时遍历效果数据,而非分页查询。
    官方文档夸大:不再建议利用scroll API举行深度分页。如果要分页检索凌驾 Top 10,000+ 效果时,保举利用:PIT+search_after
  • search_after
    优点:不严格受制于 max_result_window,可以无穷定今后翻页。ps:不严格寄义:单次哀求值不能凌驾 max_result_window;但总翻页效果集可以凌驾。
    缺点:只支持向后翻页,不支持随机翻页。
    实用场景:不支持随机翻页,更恰当手机端应用的场景。
from+size+自定义注解代码实现:


  • 自定义注解
@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface EsEquals {    /**     * filed name     */    String name() default "";}@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface EsIn {    /**     * filed name     */    String name() default "";}@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface EsLike {    /**     * filed name     */    String name() default "";    boolean leftLike() default false;    boolean rightLike() default false;}@Target({ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface EsRange {    /**     * filed name     */    String name() default "";    /**     * <     */    boolean lt() default false;    /**     * >     */    boolean gt() default false;    /**     * 包含上界     */    boolean includeUpper() default false;    /**     * 包含下界     */    boolean includeLower() default false;}

  • 定义抽象类:AbstractSearchQueryEngine
public abstract class AbstractSearchQueryEngine<T,R> {    @Autowired    protected ElasticsearchRestTemplate elasticsearchRestTemplate;    /**     * from+size 分页查询     * @param requestPara     * @param clazz     * @param pageable     * @return     */    public abstract SearchHits<R> findPage(T requestPara, Class<R> clazz, Pageable pageable);    //todo search_after 深度分页    //todo Scroll分页...}

  • 定义实现类
@Componentpublic class SimpleSearchQueryEngine extends AbstractSearchQueryEngine{    @Override    public SearchHits findPage(Object requestPara, Class clazz, Pageable pageable) {        Query query = EsQueryParse.convertQuery(requestPara);        //组装分页        query.setPageable(pageable);        return elasticsearchRestTemplate.search(query, clazz);    }}

  • 自定义注解转换工具类
/** * 条件构造器转换类 * QueryBuilder.matchAllQuery(); 匹配全部 * QueryBuilder.ermQuery精准匹配(); 巨细写敏感且不支持 * QueryBuilder.matchPhraseQuery(); 对中文正确匹配 * QueryBuilder.matchQuery();单个匹配, field不支持通配符, 前缀具高级特性 * QueryBuilder.multiMatchQuery("text", "field1", "field2"..); 匹配多个字段 * *参考ES官方文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-query-builders.html */public class EsQueryParse {    public static <T> Query convertQuery(T t) {        try {            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();            Class<?> clazz = t.getClass();            Field[] fields = clazz.getDeclaredFields();            for (Field field : fields) {                Object value = ClassUtils.getPublicMethod(clazz, "get" + captureName(field.getName())).invoke(t);                if (value == null) {                    continue;                }                if (field.isAnnotationPresent(EsLike.class)) {                    WildcardQueryBuilder query = getLikeQuery(field, (String) value);                    boolQueryBuilder.must(query);                }                if (field.isAnnotationPresent(EsEquals.class)) {                    MatchQueryBuilder query = getEqualsQuery(field, value);                    boolQueryBuilder.must(query);                }                if (field.isAnnotationPresent(EsRange.class)) {                    RangeQueryBuilder rangeQueryBuilder = getRangeQuery(field, value);                    boolQueryBuilder.must(rangeQueryBuilder);                }                if (field.isAnnotationPresent(EsIn.class)) {                    TermsQueryBuilder query = getInQuery(field, (List<?>) value);                    boolQueryBuilder.must(query);                }            }            return queryBuilder.withQuery(boolQueryBuilder).build();        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {            e.printStackTrace();        }        return Query.findAll();    }    /**     * 一次匹配多个值     * @param field     * @param value     * @return     */    private static TermsQueryBuilder getInQuery(Field field, List<?> value) {        EsIn esIn = field.getAnnotation(EsIn.class);        String filedName = StringUtils.isBlank(esIn.name()) ? field.getName() : esIn.name();        return QueryBuilders.termsQuery(filedName, value);    }    /**     * 巨细范围查询     * @param field     * @param value     * @return     */    private static RangeQueryBuilder getRangeQuery(Field field, Object value) {        EsRange esRange = field.getAnnotation(EsRange.class);        String filedName = StringUtils.isBlank(esRange.name()) ? field.getName() : esRange.name();        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(filedName)                .includeLower(esRange.includeLower())                .includeUpper(esRange.includeUpper());        if (esRange.lt()) {            rangeQueryBuilder.lt(value);        }        if (esRange.gt()) {            rangeQueryBuilder.gt(value);        }        return rangeQueryBuilder;    }    /**     * 分词短语匹配:QueryBuilders。matchQuery 会将搜刮词分词,再与目标查询字段举行匹配,若分词中的恣意一个词与目标字段匹配上,则可查询到。     * 精准匹配短语: QueryBuilders.matchPhraseQuery()     * 完全匹配: QueryBuilders.termQuery()     * @param field     * @param value     * @return     */    private static MatchQueryBuilder getEqualsQuery(Field field, Object value) {        EsEquals esEquals = field.getAnnotation(EsEquals.class);        String filedName = StringUtils.isBlank(esEquals.name()) ? field.getName() : esEquals.name();        return QueryBuilders.matchQuery(filedName, value);    }    /**     * 模糊查询,?匹配单个字符,*匹配多个字符     * @param field     * @param likeValue     * @return     */    private static WildcardQueryBuilder getLikeQuery(Field field, String likeValue) {        EsLike esLike = field.getAnnotation(EsLike.class);        String filedName = StringUtils.isBlank(esLike.name()) ? field.getName() : esLike.name();        if (esLike.leftLike()) {            likeValue = "%" + likeValue;        }        if (esLike.rightLike()) {            likeValue = likeValue + "%";        }        return QueryBuilders.wildcardQuery(filedName, likeValue);    }    /**     * 首字母大写     * @param name     * @return     */    public static String captureName(String name) {        char[] cs = name.toCharArray();        cs[0] -= 32;        return String.valueOf(cs);    }}

  • Service方法
    /**     * 单个保存     * @param elasticsearchOrderVo     * @return     */    ElasticsearchOrderVo save(ElasticsearchOrderVo elasticsearchOrderVo);    /**     * 批量保存     * @param elasticsearchOrderVoList     * @return     */    Iterable<ElasticsearchOrderVo> saveAll(List<ElasticsearchOrderVo> elasticsearchOrderVoList);    /**     * 查询     * @param elasticsearchOrderQueryVo     * @return     */    Page findElasticsearchOrderVoList(ElasticsearchOrderQueryVo elasticsearchOrderQueryVo);

  • Service实现
@Slf4j@Servicepublic class ElasticsearchOrderServiceImpl implements ElasticsearchOrderService {    @Autowired    private SimpleSearchQueryEngine simpleSearchQueryEngine;    @Autowired    private ElasticsearchOrderRepository elasticsearchOrderRepository;    @Override    public ElasticsearchOrderVo save(ElasticsearchOrderVo elasticsearchOrderVo) {        return elasticsearchOrderRepository.save(elasticsearchOrderVo);    }    @Override    public Iterable<ElasticsearchOrderVo>  saveAll(List<ElasticsearchOrderVo> elasticsearchOrderVoList) {        return elasticsearchOrderRepository.saveAll(elasticsearchOrderVoList);    }    @Override    public Page findElasticsearchOrderVoList(ElasticsearchOrderQueryVo elasticsearchOrderQueryVo) {        SearchHits<ElasticsearchOrderVo> searchHits =  simpleSearchQueryEngine.findPage(elasticsearchOrderQueryVo,                ElasticsearchOrderVo.class,                PageRequest.of(elasticsearchOrderQueryVo.getPage(),elasticsearchOrderQueryVo.getSize(),Sort.by(Sort.Order.desc("orderId"),Sort.Order.desc("createTime"))));        Page page = new Page(elasticsearchOrderQueryVo.getPage(),elasticsearchOrderQueryVo.getSize(),searchHits.getTotalHits());        page.setRecords(searchHits.get().collect(Collectors.toList()));        return page;    }}

  • Controller
    @PostMapping("/page")    public CommonResp findElasticsearchOrderVoList(@RequestBody ElasticsearchOrderQueryVo elasticsearchOrderQueryVo){        return iomsOrderItemService.findElasticsearchOrderVoList(elasticsearchOrderQueryVo);    }

  • RequestVO(ElasticsearchOrderQueryVo)
/**     * 分页     */    private Integer page;    /**     * 数量     */    private Integer size;    /**     * orderId     */    private Long orderId;    /**     * 订单编号     */    private String orderSn;    /**     * 收货人     */    @EsEquals    private String receiverName;    /**     * 省市区     */    private String receiverAreaName;    /**     * 具体地址     */    private String receiverAddress;    /**     * 手机     */    private String receiverPhone;    /**     * 现实付出金额     */    @EsRange(lt = true)    private Long payAmount;    /**     * 创建时间     */    private Date createTime;

  • POSTMAN相应效果:


您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2025-4-28 12:41, Processed in 0.187126 second(s), 36 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表