使用过 Spring Data 操作 ES 的小伙伴应该有所了解,它只能实现一些非常基本的数据管理工作,一旦遇到稍微复杂点的查询,基本都要依赖 ES 官方提供的 RestHighLevelClient,Spring Data 只是在其基础上进行了简单的封装。最近发现一款更优雅的 ES ORM 框架 Easy-Es
,使用它能像 MyBatis-Plus 一样操作 ES。
Easy-Es 简介
Easy-Es(简称 EE)是一款基于 Elasticsearch(简称 ES)官方提供的 RestHighLevelClient 打造的 ORM 开发框架,在 RestHighLevelClient 的基础上,只做增强不做改变,为简化开发、提高效率而生。EE 和 Mybatis-Plus(简称 MP)的用法非常相似,如果你之前使用过 MP 的话,应该能很快上手 EE。EE 的理念是:把简单、易用、方便留给用户,把复杂留给框架。
EE 的主要特性如下:
- 全自动索引托管:开发者无需关心索引的创建、更新及数据迁移等繁琐步骤,框架能自动完成。
- 屏蔽语言差异:开发者只需要会 MySQL 的语法即可使用 ES。
- 代码量极少:与直接使用官方提供的 RestHighLevelClient 相比,相同的查询平均可以节省 3-5 倍的代码量。
- 零魔法值:字段名称直接从实体中获取,无需手写。
- 零额外学习成本:开发者只要会国内最受欢迎的 Mybatis-Plus 用法,即可无缝迁移至 EE。v
MySQL 与 Easy-Es 语法对比
首先我们来对 MySQL、Easy-Es 和 RestHighLevelClient 的语法做过对比,来快速学习下 Easy-Es 的语法。
MySQL | Easy-Es | es-DSL/es java api |
---|---|---|
and | and | must |
or | or | should |
= | eq | term |
!= | ne | boolQueryBuilder.mustNot(queryBuilder) |
> | gt | QueryBuilders.rangeQuery(‘es field’).gt() |
>= | ge | .rangeQuery(‘es field’).gte() |
< | lt | .rangeQuery(‘es field’).lt() |
<= | le | .rangeQuery(‘es field’).lte() |
like ‘%field%’ | like | QueryBuilders.wildcardQuery(field,value) |
not like ‘%field%’ | notLike | must not wildcardQuery(field,value) |
like ‘%field’ | likeLeft | QueryBuilders.wildcardQuery(field,*value) |
like ‘field%’ | likeRight | QueryBuilders.wildcardQuery(field,value*) |
between | between | QueryBuilders.rangeQuery(‘es field’).from(xx).to(xx) |
notBetween | notBetween | must not QueryBuilders.rangeQuery(‘es field’).from(xx).to(xx) |
is null | isNull | must not QueryBuilders.existsQuery(field) |
is notNull | isNotNull | QueryBuilders.existsQuery(field) |
in | in | QueryBuilders.termsQuery(“ xx es field”, xx) |
not in | notIn | must not QueryBuilders.termsQuery(“ xx es field”, xx) |
group by | groupBy | AggregationBuilders.terms() |
order by | orderBy | fieldSortBuilder.order(ASC/DESC) |
min | min | AggregationBuilders.min |
max | max | AggregationBuilders.max |
avg | avg | AggregationBuilders.avg |
sum | sum | AggregationBuilders.sum |
order by xxx asc | orderByAsc | fieldSortBuilder.order(SortOrder.ASC) |
order by xxx desc | orderByDesc | fieldSortBuilder.order(SortOrder.DESC) |
- | match | matchQuery |
- | matchPhrase | QueryBuilders.matchPhraseQuery |
- | matchPrefix | QueryBuilders.matchPhrasePrefixQuery |
- | queryStringQuery | QueryBuilders.queryStringQuery |
select * | matchAllQuery | QueryBuilders.matchAllQuery() |
- | highLight | HighlightBuilder.Field |
… | … | … |
集成及配置
- 首先需要在
pom.xml
中添加 Easy-Es 的相关依赖;
1 | <dependency> |
- 由于底层使用了 ES 官方提供的 RestHighLevelClient,这里 ES 的相关依赖版本需要统一下,这里使用的 ES 客户端版本为
7.14.0
,ES 版本为7.17.3
;
1 | <dependencyManagement> |
- 再修改配置文件
application.yml
对 Easy-Es 进行配置。
1 | easy-es: |
- 添加 Easy-Es 的 Java 配置,使用
@EsMapperScan
配置好 Easy-Es 的 Mapper 接口和文档对象路径,如果你使用了 MyBatis-Plus 的话,需要和它的扫描路径区分开来。
1 | /** |
使用
注解的使用
- 首先我们需要创建文档对象
EsProduct
,然后给类和字段添加上 Easy-Es 的注解;
1 | /** |
- EsProduct 中的注解具体说明如下:
注解名称 | 用途 | 参数 |
---|---|---|
@IndexName | 索引名注解 | value:指定索引名;shardsNum:分片数;replicasNum:副本数 |
@IndexId | ES 主键注解 | type:指定注解类型,CUSTOMIZE 表示自定义 |
@IndexField | ES 字段注解 | fieldType:字段在索引中的类型;analyzer:索引文档时用的分词器;nestedClass:嵌套类 |
@Score | 得分注解 | decimalPlaces:得分保留小数位,实体类中被作为 ES 查询得分返回的字段使用 |
- EsProduct 中嵌套类型 EsProductAttributeValue 的代码如下。
1 | /** |
商品信息维护
下面我们来实现几个简单的商品信息维护接口,包括商品信息的导入、创建和删除。
- 首先我们需要定义一个 Mapper,继承 BaseEsMapper;
1 | /** |
- 然后在 Service 实现类中直接使用 EsProductMapper 内置方法实现即可,是不是和 MyBatis-Plus 的用法一致?
1 | /** |
简单商品搜索
下面我们来实现一个最简单的商品搜索,分页搜索商品名称、副标题、关键词中包含指定关键字的商品。
- 通过 QueryWrapper 来构造查询条件,然后使用 Mapper 中的方法来进行查询,使用过 MyBatis-Plus 的小伙伴应该很熟悉了;
1 | /** |
- 使用 Swagger 访问接口后,可以在控制台输出查看生成的 DSL 语句,访问地址:http://localhost:8080/swagger-ui/
- 把 DSL 语句直接复制 Kibana 中即可执行查看结果了,这和我们手写 DSL 语句没什么两样的。
综合商品搜索
下面我们来实现一个复杂的商品搜索,涉及到过滤、不同字段匹配权重不同以及可以进行排序。
- 首先来说需求,按输入的关键字搜索商品名称(权重 10)、副标题(权重 5)和关键词(权重 2),可以按品牌和分类进行筛选,可以有 5 种排序方式,默认按相关度进行排序,看下接口文档有助于理解;
- 这个功能之前使用 Spring Data 来实现非常复杂,使用 Easy-Es 来实现确实简洁不少,下面是使用 Easy-Es 的实现方式;
1 | /** |
- 再对比下之前使用 Spring Data 的实现方式,没有 QueryWrapper 来构造条件,还要硬编码字段名称,确实优雅了不少!
相关商品推荐
当我们查看相关商品的时候,一般底部会有一些商品推荐,这里简单来实现下。
- 首先来说下需求,可以根据指定商品的 ID 来查找相关商品,看下接口文档有助于理解;
- 这里我们的实现原理是这样的:首先根据 ID 获取指定商品信息,然后以指定商品的名称、品牌和分类来搜索商品,并且要过滤掉当前商品,调整搜索条件中的权重以获取最好的匹配度;
- 使用 Easy-Es 来实现依旧是那么简洁!
1 | /** |
聚合搜索商品相关信息
在搜索商品时,经常会有一个筛选界面来帮助我们找到想要的商品,这里我们来简单实现下。
- 首先来说下需求,可以根据搜索关键字获取到与关键字匹配商品相关的分类、品牌以及属性,下面这张图有助于理解;
- 这里我们可以使用 ES 的聚合来实现,搜索出相关商品,聚合出商品的品牌、商品的分类以及商品的属性,只要出现次数最多的前十个即可;
- 由于 Easy-Es 目前只用
groupBy
实现了简单的聚合,对于我们这种有嵌套对象的聚合无法支持,所以需要使用 RestHighLevelClient 来实现,如果你对照之前的 Spring Data 实现方式的话,可以发现用法差不多,看样子 Spring Data 只是做了简单的封装而已。
1 | /** |
总结
今天将之前的使用 Spring Data 的商品搜索案例使用 Easy-Es 改写了一下,确实使用 Easy-Es 更简单,但是对于复杂的聚合搜索功能,两者都需要使用原生的 RestHighLevelClient 用法来实现。使用 Easy-Es 来操作 ES 确实足够优雅,它类似 MyBatis-Plus 的用法能大大降低我们的学习成本,快速完成开发工作!