首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

优雅而强大:用easy-es简化ElasticSearch操作

使用过Spring Data操作ES的小伙伴应该有所了解,它只能实现一些非常基本的数据管理工作,一旦遇到稍微复杂点的查询,基本都要依赖ES官方提供的RestHighLevelClient,Spring Data只是在其基础上进行了简单的封装。最近发现一款更优雅的ES ORM框架Easy-Es,使用它能像MyBatis-Plus一样操作ES,今天就以一个实际的mall商城项目中的商品搜索功能为例,来聊聊它的使用!

Easy-Es简介

Easy-Es(简称EE)是基于Elasticsearch(简称ES)官方提供的RestHighLevelClient开发的ORM框架,旨在简化开发流程并提高效率。EE在保持RestHighLevelClient原有功能的基础上进行增强,而不做任何改变。与Mybatis-Plus(简称MP)相比,EE的用法非常相似,如果您之前使用过MP,应该能够很快上手EE。EE的设计理念是:将简单、易用和方便留给用户,而将复杂的任务交由框架来处理。

EE的主要特性如下:

全自动索引管理:开发者无需繁琐地处理索引的创建、更新和数据迁移等步骤,框架会自动完成这些任务。

屏蔽语言差异:开发者只需要熟悉MySQL语法,就能够轻松地使用ES进行开发。

更少的代码量:相比直接使用官方提供的RestHighLevelClient,使用EE平均可以节省3-5倍的代码量来进行相同的查询操作。

零魔法值:字段名称会直接从实体中获取,无需手动编写。

无额外学习成本:如果开发者已经熟悉国内最受欢迎的Mybatis-Plus的用法,那么可以无缝迁移至EE,无需额外学习成本。

MySQL与Easy-Es语法对比

集成及配置

首先需要在pom.xml中添加Easy-Es的相关依赖;

org.elasticsearch.client

elasticsearch-rest-high-level-client

7.14.0

org.elasticsearch

elasticsearch

7.14.0

再修改配置文件application.yml对Easy-Es进行配置。

easy-es:

# 是否开启EE自动配置

enable: true

# ES连接地址+端口

address: localhost:9200

# 关闭自带banner

banner: false

添加Easy-Es的Java配置,使用@EsMapperScan配置好Easy-Es的Mapper接口和文档对象路径,如果你使用了MyBatis-Plus的话,需要和它的扫描路径区分开来。

/**

* EasyEs配置类

* Created by macro on 2022/9/16.

*/

@Configuration

@EsMapperScan("com.macro.mall.tiny.easyes")

public class EasyEsConfig {

}

使用

注解的使用

首先我们需要创建文档对象EsProduct,然后给类和字段添加上Easy-Es的注解;

/**

* 搜索商品的信息

* Created by macro on 2018/6/19.

*/

@Data

@EqualsAndHashCode

@IndexName(value = "pms", shardsNum = 1, replicasNum = 0)

public class EsProduct implements Serializable {

private static final long serialVersionUID = -1L;

@IndexId(type = IdType.CUSTOMIZE)

private Long id;

@IndexField(fieldType = FieldType.KEYWORD)

private String productSn;

private Long brandId;

@IndexField(fieldType = FieldType.KEYWORD)

private String brandName;

private Long productCategoryId;

@IndexField(fieldType = FieldType.KEYWORD)

private String productCategoryName;

private String pic;

@IndexField(fieldType = FieldType.TEXT, analyzer = "ik_max_word")

private String name;

@IndexField(fieldType = FieldType.TEXT, analyzer = "ik_max_word")

private String subTitle;

@IndexField(fieldType = FieldType.TEXT, analyzer = "ik_max_word")

private String keywords;

private BigDecimal price;

private Integer sale;

private Integer newStatus;

private Integer recommandStatus;

private Integer stock;

private Integer promotionType;

private Integer sort;

@IndexField(fieldType = FieldType.NESTED, nestedClass = EsProductAttributeValue.class)

private List attrValueList;

@Score

private Float score;

}

EsProduct中的注解具体说明如下:

EsProduct中嵌套类型EsProductAttributeValue的代码如下。

/**

* 搜索商品的属性信息

* Created by macro on 2018/6/27.

*/

@Data

@EqualsAndHashCode

public class EsProductAttributeValue implements Serializable {

private static final long serialVersionUID = 1L;

@IndexField(fieldType = FieldType.LONG)

private Long id;

@IndexField(fieldType = FieldType.KEYWORD)

private Long productAttributeId;

//属性值

@IndexField(fieldType = FieldType.KEYWORD)

private String value;

//属性参数:0->规格;1->参数

@IndexField(fieldType = FieldType.INTEGER)

private Integer type;

//属性名称

@IndexField(fieldType=FieldType.KEYWORD)

private String name;

}

商品信息维护

下面我们来实现几个简单的商品信息维护接口,包括商品信息的导入、创建和删除。

首先我们需要定义一个Mapper,继承BaseEsMapper;

/**

* 商品ES操作类

* Created by macro on 2018/6/19.

*/

public interface EsProductMapper extends BaseEsMapper {

}

然后在Service实现类中直接使用EsProductMapper内置方法实现即可,是不是和MyBatis-Plus的用法一致?

/**

* 搜索商品管理Service实现类

* Created by macro on 2018/6/19.

*/

@Service

public class EsProductServiceImpl implements EsProductService {

@Autowired

private EsProductDao productDao;

@Autowired

private EsProductMapper esProductMapper;

@Override

public int importAll() {

List esProductList = productDao.getAllEsProductList(null);

return esProductMapper.insertBatch(esProductList);

}

@Override

public void delete(Long id) {

esProductMapper.deleteById(id);

}

@Override

public EsProduct create(Long id) {

EsProduct result = null;

List esProductList = productDao.getAllEsProductList(id);

if (esProductList.size() > 0) {

result = esProductList.get(0);

esProductMapper.insert(result);

}

return result;

}

@Override

public void delete(List ids) {

if (!CollectionUtils.isEmpty(ids)) {

esProductMapper.deleteBatchIds(ids);

}

}

}

简单商品搜索

下面我们来实现一个最简单的商品搜索,分页搜索商品名称、副标题、关键词中包含指定关键字的商品。

通过QueryWrapper来构造查询条件,然后使用Mapper中的方法来进行查询,使用过MyBatis-Plus的小伙伴应该很熟悉了;

/**

* 搜索商品管理Service实现类

* Created by macro on 2018/6/19.

*/

@Service

public class EsProductServiceImpl implements EsProductService {

@Autowired

private EsProductMapper esProductMapper;

@Override

public PageInfo search(String keyword, Integer pageNum, Integer pageSize) {

LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper();

if(StrUtil.isEmpty(keyword)){

wrapper.matchAllQuery();

}else{

wrapper.multiMatchQuery(keyword,EsProduct::getName,EsProduct::getSubTitle,EsProduct::getKeywords);

}

return esProductMapper.pageQuery(wrapper, pageNum, pageSize);

}

}

使用Swagger访问接口后,可以在控制台输出查看生成的DSL语句,访问地址:http://localhost:8080/swagger-ui/

把DSL语句直接复制Kibana中即可执行查看结果了,这和我们手写DSL语句没什么两样的。

综合商品搜索

下面我们来实现一个复杂的商品搜索,涉及到过滤、不同字段匹配权重不同以及可以进行排序。

首先来说需求,按输入的关键字搜索商品名称(权重10)、副标题(权重5)和关键词(权重2),可以按品牌和分类进行筛选,可以有5种排序方式,默认按相关度进行排序,看下接口文档有助于理解;

这个功能之前使用Spring Data来实现非常复杂,使用Easy-Es来实现确实简洁不少,下面是使用Easy-Es的实现方式;

/**

* 搜索商品管理Service实现类

* Created by macro on 2018/6/19.

*/

@Service

public class EsProductServiceImpl implements EsProductService {

@Autowired

private EsProductMapper esProductMapper;

@Override

public PageInfo search(String keyword, Long brandId, Long productCategoryId, Integer pageNum, Integer pageSize,Integer sort) {

LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper();

//过滤

if (brandId != null || productCategoryId != null) {

if (brandId != null) {

wrapper.eq(EsProduct::getBrandId,brandId);

}

if (productCategoryId != null) {

wrapper.eq(EsProduct::getProductCategoryId,productCategoryId).enableMust2Filter(true);

}

}

//搜索

if (StrUtil.isEmpty(keyword)) {

wrapper.matchAllQuery();

} else {

wrapper.and(i -> i.match(EsProduct::getName, keyword, 10f)

.or().match(EsProduct::getSubTitle, keyword, 5f)

.or().match(EsProduct::getKeywords, keyword, 2f));

}

//排序

if(sort==1){

//按新品从新到旧

wrapper.orderByDesc(EsProduct::getId);

}else if(sort==2){

//按销量从高到低

wrapper.orderByDesc(EsProduct::getSale);

}else if(sort==3){

//按价格从低到高

wrapper.orderByAsc(EsProduct::getPrice);

}else if(sort==4){

//按价格从高到低

wrapper.orderByDesc(EsProduct::getPrice);

}else{

//按相关度

wrapper.sortByScore(SortOrder.DESC);

}

return esProductMapper.pageQuery(wrapper, pageNum, pageSize);

}

}

再对比下之前使用Spring Data的实现方式,没有QueryWrapper来构造条件,还要硬编码字段名称,确实优雅了不少!

当我们查看相关商品的时候,一般底部会有一些商品推荐,这里简单来实现下。

首先来说下需求,可以根据指定商品的ID来查找相关商品,看下接口文档有助于理解;

这里我们的实现原理是这样的:首先根据ID获取指定商品信息,然后以指定商品的名称、品牌和分类来搜索商品,并且要过滤掉当前商品,调整搜索条件中的权重以获取最好的匹配度;

使用Easy-Es来实现依旧是那么简洁!

/**

* 搜索商品管理Service实现类

* Created by macro on 2018/6/19.

*/

@Service

public class EsProductServiceImpl implements EsProductService {

@Autowired

private EsProductMapper esProductMapper;

@Override

public PageInfo recommend(Long id, Integer pageNum, Integer pageSize) {

LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper();

List esProductList = productDao.getAllEsProductList(id);

if (esProductList.size() > 0) {

EsProduct esProduct = esProductList.get(0);

String keyword = esProduct.getName();

Long brandId = esProduct.getBrandId();

Long productCategoryId = esProduct.getProductCategoryId();

//用于过滤掉相同的商品

wrapper.ne(EsProduct::getId,id);

//根据商品标题、品牌、分类进行搜索

wrapper.and(i -> i.match(EsProduct::getName, keyword, 8f)

.or().match(EsProduct::getSubTitle, keyword, 2f)

.or().match(EsProduct::getKeywords, keyword, 2f)

.or().match(EsProduct::getBrandId, brandId, 5f)

.or().match(EsProduct::getProductCategoryId, productCategoryId, 3f));

return esProductMapper.pageQuery(wrapper, pageNum, pageSize);

}

return esProductMapper.pageQuery(wrapper, pageNum, pageSize);

}

}

总结

今天主要介绍了Easy-Es的一些常见的用法,确实使用Easy-Es更简单,但是对于复杂的聚合搜索功能,还是需要使用原生的RestHighLevelClient用法来实现。使用Easy-Es来操作ES确实足够优雅,它类似MyBatis-Plus的用法能大大降低我们的学习成本,快速完成开发工作!

  • 发表于:
  • 原文链接https://page.om.qq.com/page/Oh8Ir07fe-SVdCtjT12Ax12g0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券