搜索引擎ORM框架Easy-Es(搜索引擎 框架)

项目简介

Easy-Es(简称EE) 是一款基于ElasticSearch(简称Es)官方提供的RestHighLevelClient打造的ORM开发框架,在RestHighLevelClient的基础上,只做增强不做改变,为简化开发、提高效率而生,您如果有用过MyBatis-Plus(简称MP),那么您基本可以零学习成本直接上手EE,EEMPEs平替版,在有些方面甚至比MP更简单,同时也融入了更多Es独有的功能,助力您快速实现各种场景的开发。

项目地址

  • Easy-Es Gitee:gitee.com/dromara/eas…
  • Elasticsearch Download:www.elastic.co/cn/download…

Easy-Es自身优势

  • 全自动索引托管: 全球开源首创的索引托管模式,开发者无需关心索引的创建更新及数据迁移等繁琐步骤,索引全生命周期皆可托管给框架,由框架自动完成,过程零停机,用户无感知,彻底解放开发者;
  • 屏蔽语言差异: 开发者只需要会MySQL语法即可使用Es,真正做到一通百通,无需学习枯燥易忘的Es语法,Es使用相对MySQL较低频,学了长期不用也会忘,没必要浪费这时间.开发就应该专注于业务,省下的时间去撸铁,去陪女朋友陪家人,不做资本家的韭菜;
  • 代码量极少: 与直接使用RestHighLevelClient相比,相同的查询平均可以节省3-5倍左右的代码量
  • 零魔法值: 字段名称直接从实体中获取,无需输入字段名称字符串这种魔法值,提高代码可读性,杜绝因字段名称修改而代码漏改带来的Bug
  • 零额外学习成本: 开发者只要会国内最受欢迎的Mybatis-Plus语法,即可无缝迁移至EE,EE采用和前者相同的语法,消除使用者额外学习成本,直接上手;
  • 降低开发者门槛: Es通常需要中高级开发者才能驾驭,但通过接入EE,即便是只了解ES基础的初学者也可以轻松驾驭ES完成绝大多数需求的开发,可以提高人员利用率,降低企业成本。

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑;
  • 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作;
  • 强大的 CRUD 操作:内置通用Mapper,仅仅通过少量配置即可实现大部分CRUD操作,更有强大的条件构造器,满足各类使用需;
  • 支持 Lambda 形式调用:通过Lambda表达式,方便的编写各类查询条件,无需再担心字段写错段;
  • 支持主键自动生成:支持 2 种主键策略,可自由配置,完美解决主键问题;
  • 支持 ActiveRecord 模式:支持ActiveRecord形式调用,实体类只需继承Model类即可进行强大的 CRUD操作;
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置分页插件:基于RestHighLevelClient物理分页,开发者无需关心具体操作,且无需额外配置插件,写分页等同于普通List查询,且保持和PageHelper插件同样的分页返回字段,无需担心命名影响;
  • MySQL功能全覆盖MySQL中支持的功能通过EE都可以轻松实现;
  • 支持ES高阶语法:支持高亮搜索,分词查询,权重查询, Geo地理位置查询、IP查询、聚合查询等高阶语法;
  • 良好的拓展性:底层仍使用RestHighLevelClient,可保持其拓展性,开发者在使用EE的同时,仍可使用RestHighLevelClient的功能。

✨最新版本 Latest Version: v.1.1.1

Maven

xml复制代码<dependency> <groupId>cn.easy-es</groupId> <artifactId>easy-es-boot-starter</artifactId> <version>${Latest Version}</version></dependency>

Gradle

csharp复制代码compile group: 'cn.easy-es', name: 'easy-es-boot-starter', version: 'Latest Version'

Easy-Es适用场景

1️⃣ 检索类服务

  • 搜索文库
  • 电商商品搜索
  • 海量系统日志搜索

2️⃣ 问答类服务

  • 在线智能客服
  • 机器人

3️⃣ 地图类服务

  • 打车APP
  • 外卖APP
  • 社区团购配送
  • 社交APP

注解

EsMapperScan

  • 描述:mapper扫描注解,功能与Mybatis Plus@MapperScan一致
  • 使用位置:Spring Boot启动类

kotlin复制代码@EsMapperScan("cn.easy-es-mapper")public class Application{ // 省略其它...}

@IndexName

  • 描述:索引名注解,标识实体类对应的索引 对应Mybatis Plus@TableName注解,在v0.9.40之前此注解为@TableName
  • 使用位置:实体类

kotlin复制代码@IndexNamepublic class Document { // 省略其它字段}

属性

类型

是否必须指定

默认值

描述

value

String

""

索引名,可以简单理解为MySQL表名

shardsNum

int

1

索引分片数

replicasNum

int

1

索引副本数

aliasName

String

""

索引别名

keepGlobalPrefix

Boolean

false

是否保持使用全局的tablePrefix,与MP的用法一致

child

boolean

false

是否子文档

childClass

Class

DefaultChildClass.class

父子分档-子文档类

maxResultWindow

int

10000

分页返回的最大数据量,默认值为1万条,超出推荐使用searchAfter或滚动查询等方式

@IndexId

  • 描述:Easy-Es主键注解,在v0.9.40之前此注解为@TableId
  • 使用位置:实体类中被作为Easy-Es主键的字段, 对应MyBatis-Plus@TableId注解。

kotlin复制代码public class Document { @IndexId private String id; // 省略其它字段}

属性

类型

必须指定

默认值

描述

value

String

"_id"

主键字段名

type

Enum

IdType.NONE

指定主键类型

@IndexField

  • 描述:ES字段注解, 对应MP@TableField注解,在v0.9.40之前此注解为@TableField
  • 使用位置:实体类中被作为ES索引字段的字段;
  • 使用场景举例:
  1. 实体类中的字段并非ES中实际的字段,比如把实体类直接当DTO用了,加了一些ES中并不存在的无关字段,此时可以标记此字段,以便让EE框架跳过此字段,对此字段不处理;
  2. 字段的更新策略,比如在调用更新接口时,实体类的字段非Null或者非空字符串时才更新,此时可以加字段注解,对指定字段标记更新策略;
  3. 需要对类型为textkeyword_text字段聚合时,可指定其fieldData=true,否则es会报错;
  4. 对指定字段进行自定义命名,比如该字段在es中叫wu-la,但在实体model中叫ula,此时可以在value中指定value="wu-la"
  5. 在自动托管索引模式下,可指定索引分词器及索引字段类型;
  6. 在自动托管索引模式下,可指定索引中日期的format格式。

使用示例:

typescript复制代码public class Document { // 此处省略其它字段... // 场景一:标记es中不存在的字段 @IndexField(exist = false) private String notExistsField; // 场景二:更新时,此字段非空字符串才会被更新 @IndexField(strategy = FieldStrategy.NOT_EMPTY) private String creator; // 场景三: 指定fieldData @IndexField(fieldType = FieldType.text, fieldData = true) private String filedData; // 场景四:自定义字段名 @IndexField("wu-la") private String ula; // 场景五:支持日期字段在es索引中的format类型 @IndexField(fieldType = FieldType.DATE, dateFormat = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis") private String gmtCreate; // 场景六:支持指定字段在es索引中的分词器类型 @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_MAX_WORD) private String content;}

属性

类型

必须指定

默认值

描述

value

String

""

字段名

exist

boolean

true

字段是否存在

fieldType

Enum

FieldType.NONE

字段在es索引中的类型

fieldData

boolean

false

text类型字段是否支持聚合

analyzer

String

Analyzer.NONE

索引文档时用的分词器

searchAnalyzer

String

Analyzer.NONE

查询分词器

strategy

Enum

FieldStrategy.DEFAULT

字段验证策略

dateFormat

String

""

es索引中的日期格式,如yyyy-MM-dd

nestedClass

Class

DefaultNestedClass.class

嵌套类

parentName

String

""

父子文档-父名称

childName

String

""

父子文档-子名称

joinFieldClass

Class

JoinField.class

父子文档-父子类型关系字段类

@Score

  • 描述:得分注解
  • 使用位置:实体类中被作为ES查询得分返回的字段
  • 使用场景举例:比如需要知道本次匹配查询得分有多少时,可以在实体类中添加一个类型为Float/float的字段,并在该字段上添加@Score注解,在后续查询中,若es有返回当次查询的得分,则此得分会自动映射至此字段上

属性

类型

必须指定

默认值

描述

decimalPlaces

int

0

得分保留小数位,默认不处理,保持es返回的得分值

@Distance

  • 描述:距离注解
  • 使用位置:实体类中被作为ES地理位置排序距离值的返回字段
  • 使用场景举例:比如需要知道按距离由近及远查询后的数据,实际距离某一坐标有多远,可以在实体类中添加一个类型为Double/double的字段,并在该字段上添加@Distance注解,在后续查询中,若es有返回距离,则此距离会自动映射至此字段上

属性

类型

必须指定

默认值

描述

decimalPlaces

int

0

距离保留小数位,默认不处理,保持es返回的距离值

sortBuilderIndex

int

0

排序字段在sortBuilders中的位置, 默认为0,若有多个排序器,则指定为其所在位置

ES版本和Spring Boot版本

xml复制代码<properties> <java.version>1.8</java.version> <easy-es.version>1.1.1</easy-es.version> <elasticsearch.version>7.14.0</elasticsearch.version></properties><!-- easy-es --><dependency> <groupId>cn.easy-es</groupId> <artifactId>easy-es-boot-starter</artifactId> <version>${easy-es.version}</version> <exclusions> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> </exclusions></dependency><!-- elasticsearch --><dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>${elasticsearch.version}</version></dependency><dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>${elasticsearch.version}</version></dependency>

Spring Boot配置

application.yml 配置文件中添加EasyEs必须的相关配置:

yaml复制代码easy-es: enable: true #默认为true,若为false则认为不启用本框架 address : 127.0.0.1:9200 # es的连接地址,必须含端口 若为集群,则可以用逗号隔开 例如:127.0.0.1:9200,127.0.0.2:9200 username: elastic #若无 则可省略此行配置 password: WG7WVmuNMtM4GwNYkyWH #若无 则可省略此行配置

BaseEsMapper CRUD接口

Easy-Es封装了基本的CRUD操作API,通过继承BaseEsMapper<T>赋予相关能力,比如:

csharp复制代码public interface DocumentMapper extends BaseEsMapper<Document> {}

相关操作API

php复制代码public interface BaseEsMapper<T> { /** * 是否存在索引 * * @param indexName 索引名称 * @return 返回是否存在的布尔值 */ Boolean existsIndex(String indexName); /** * 获取当前索引信息 * * @return 当前索引信息 */ GetIndexResponse getIndex(); /** * 获取指定索引信息 * * @param indexName 指定索引名 * @return 指定索引信息 */ GetIndexResponse getIndex(String indexName); /** * 创建索引,根据当前mapper对应实体类信息及其注解配置生成索引信息 * * @return 是否创建成功 */ Boolean createIndex(); /** * 创建索引,根据当前mapper对应实体类信息及其注解配置生成索引信息 可指定索引名进行创建 适用于定时任务按日期创建索引场景 * * @param indexName 指定的索引名,会覆盖注解上指定的索引名 * @return 是否创建成功 */ Boolean createIndex(String indexName); /** * 创建索引 * * @param wrapper 条件 * @return 是否成功 */ Boolean createIndex(LambdaEsIndexWrapper<T> wrapper); /** * 更新索引 * * @param wrapper 条件 * @return 是否成功 */ Boolean updateIndex(LambdaEsIndexWrapper<T> wrapper); /** * 删除指定索引 * * @param indexNames 索引名称数组 * @return 是否成功 */ Boolean deleteIndex(String... indexNames); /** * 标准查询 * * @param wrapper 条件 * @return es标准结果 */ SearchResponse search(LambdaEsQueryWrapper<T> wrapper); /** * 获取SearchSourceBuilder,可用于本框架生成基础查询条件,不支持的高阶语法用户可通过SearchSourceBuilder 进一步封装 * * @param wrapper 条件 * @return 查询参数 */ SearchSourceBuilder getSearchSourceBuilder(LambdaEsQueryWrapper<T> wrapper); /** * es原生查询 * * @param searchrequest 查询请求参数 * @param requestOptions 类型 * @return es原生返回结果 * @throws IOException IO异常 */ SearchResponse search(SearchRequest searchRequest, RequestOptions requestOptions) throws IOException; /** * es原生滚动查询 * * @param searchScrollRequest 查询请求参数 * @param requestOptions 类型 * @return es原生返回结果 * @throws IOException IO异常 */ SearchResponse scroll(SearchScrollRequest searchScrollRequest, RequestOptions requestOptions) throws IOException; /** * 获取通过本框架生成的查询参数,可用于检验本框架生成的查询参数是否正确 * * @param wrapper 条件 * @return 查询JSON格式参数 */ String getSource(LambdaEsQueryWrapper<T> wrapper); /** * 指定返回类型及分页参数 * * @param wrapper 条件 * @param pageNum 当前页 * @param pageSize 每页条数 * @return 指定的返回类型 */ EsPageInfo<T> pageQuery(LambdaEsQueryWrapper<T> wrapper, Integer pageNum, Integer pageSize); /** * searchAfter类型分页 * * @param wrapper 条件 * @param searchAfter 当前页 第一页时为null * @param pageSize 每页条数 * @return 指定的返回类型 */ SAPageInfo<T> searchAfterPage(LambdaEsQueryWrapper<T> wrapper, List<Object> searchAfter, Integer pageSize); /** * 获取总数(智能推断:若wrapper中指定了去重字段则去重,若未指定则不去重 推荐使用) * * @param wrapper 条件 * @return 总数 */ Long selectCount(LambdaEsQueryWrapper<T> wrapper); /** * 无论wrapper中是否指定去重字段,都以用户传入的distinct布尔值作为是否去重的条件 * * @param wrapper 条件 * @param distinct 是否去重 * @return 总数 */ Long selectCount(LambdaEsQueryWrapper<T> wrapper, boolean distinct); /** * 插入一条记录 * * @param entity 插入的数据对象 * @return 成功条数 */ Integer insert(T entity); /** * 插入一条记录,可指定多索引插入 * * @param entity 插入的数据对象 * @param indexNames 指定插入的索引名数组 * @return 总成功条数 */ Integer insert(T entity, String... indexNames); /** * 批量插入 * * @param entityList 插入的数据对象列表 * @return 总成功条数 */ Integer insertBatch(Collection<T> entityList); /** * 批量插入 * * @param entityList 插入的数据对象列表 * @param indexNames 指定插入的索引名数组 * @return 总成功条数 */ Integer insertBatch(Collection<T> entityList, String... indexNames); /** * 根据 ID 删除 * * @param id 主键 * @return 成功条数 */ Integer deleteById(Serializable id); /** * 根据 ID 删除 * * @param id 主键 * @param indexNames 指定删除的索引名数组 * @return 总成功条数 */ Integer deleteById(Serializable id, String... indexNames); /** * 删除(根据ID 批量删除) * * @param idList 主键列表 * @return 总成功条数 */ Integer deleteBatchIds(Collection<? extends Serializable> idList); /** * 删除(根据ID 批量删除) * * @param idList 主键列表 * @param indexNames 指定删除的索引名数组 * @return 总成功条数 */ Integer deleteBatchIds(Collection<? extends Serializable> idList, String... indexNames); /** * 根据 entity 条件,删除记录 * * @param wrapper 条件 * @return 总成功条数 */ Integer delete(LambdaEsQueryWrapper<T> wrapper); /** * 根据 ID 更新 * * @param entity 更新对象 * @return 总成功条数 */ Integer updateById(T entity); /** * 根据 ID 更新 * * @param entity 更新对象 * @param indexNames 指定更新的索引名称数组 * @return 总成功条数 */ Integer updateById(T entity, String... indexNames); /** * 根据ID 批量更新 * * @param entityList 更新对象列表 * @return 总成功条数 */ Integer updateBatchByIds(Collection<T> entityList); /** * 根据ID 批量更新 * * @param entityList 更新对象列表 * @param indexNames 指定更新的索引名称数组 * @return 总成功条数 */ Integer updateBatchByIds(Collection<T> entityList, String... indexNames); /** * 根据 whereEntity 条件,更新记录 * * @param entity 更新对象 * @param updateWrapper 条件 * @return 成功条数 */ Integer update(T entity, LambdaEsUpdateWrapper<T> updateWrapper); /** * 根据 ID 查询 * * @param id 主键 * @return 指定的返回对象 */ T selectById(Serializable id); /** * 根据 ID 查询 * * @param id 主键 * @param indexNames 指定查询的索引名数组 * @return 指定的返回对象 */ T selectById(Serializable id, String... indexNames); /** * 查询(根据ID 批量查询) * * @param idList 主键列表 * @return 指定的返回对象列表 */ List<T> selectBatchIds(Collection<? extends Serializable> idList); /** * 查询(根据ID 批量查询) * * @param idList 主键列表 * @param indexNames 指定查询的索引名数组 * @return 指定的返回对象列表 */ List<T> selectBatchIds(Collection<? extends Serializable> idList, String... indexNames); /** * 根据 entity 条件,查询一条记录 * * @param wrapper 条件 * @return 指定的返回对象 */ T selectOne(LambdaEsQueryWrapper<T> wrapper); /** * 根据 entity 条件,查询全部记录 * * @param wrapper 条件 * @return 指定的返回对象列表 */ List<T> selectList(LambdaEsQueryWrapper<T> wrapper); /** * 设置当前Mapper默认激活的全局索引名称 务必谨慎操作,设置后全局生效,永驻jvm,除非项目重启 * * @param indexName 索引名称 * @return 是否成功 */ Boolean setCurrentActiveIndex(String indexName);}

Tips提示:

CRUD接口用法基本与MyBatis-Plus一致;

用户需要继承的MapperBaseEsMapper,而非BaseMapper

EE没有提供Service层,而是把MyBatis-Plus中一些Service层的方法直接下沉到Mapper层了,用户用起来会更方便。

Easy-ES整合实践

  • 第一步:确保你的环境已经安装ElasticSearch,博主这里的演示版本为7.14.0,运行ElasticSearch
  • 第二步:创建Maven Project,引入Easy-Es依赖;
  • 第三步:定义文档类Document,测试Easy-EsCRUD基本操作。

1️⃣ 运行ElasticSearch

搜索引擎ORM框架Easy-Es(搜索引擎 框架)

访问:http://localhost:9200/,查看节点信息。

bash复制代码{ "name": "LAPTOP-8G2EVQFP", # 默认节点名称 "cluster_name": "elasticsearch", "cluster_uuid": "nnFJExSMRj-gy09OeHRk5A", "version": { # ES版本 "number": "7.3.2", "build_flavor": "default", "build_type": "zip", "build_hash": "1c1faf1", "build_date": "2019-09-06T14:40:30.409026Z", "build_snapshot": false, "lucene_version": "8.1.0", "minimum_wire_compatibility_version": "6.8.0", "minimum_index_compatibility_version": "6.0.0-beta1" }, "tagline": "You Know, for Search"}

2️⃣ 创建Spring Boot项目,添加依赖

搜索引擎ORM框架Easy-Es(搜索引擎 框架)

添加POM依赖:

xml复制代码<properties> <java.version>1.8</java.version> <easy-es.version>1.1.1</easy-es.version> <elasticsearch.version>7.14.0</elasticsearch.version> <spring-boot-data-elasticsearch.version>3.0.4</spring-boot-data-elasticsearch.version></properties><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- easy-es --> <dependency> <groupId>cn.easy-es</groupId> <artifactId>easy-es-boot-starter</artifactId> <version>${easy-es.version}</version> <exclusions> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> </exclusions> </dependency> <!-- elasticsearch --> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>${elasticsearch.version}</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>${elasticsearch.version}</version> </dependency></dependencies>

文档类Document

arduino复制代码@Datapublic class Document { /** * es中的唯一id */ private String id; /** * 文档标题 */ private String title; /** * 文档内容 */ private String content;}

DocumentMapper

csharp复制代码public interface DocumentMapper extends BaseEsMapper<Document> {}

EsDocumentController

typescript复制代码@RestController@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class EsDocumentController { private final DocumentMapper documentMapper; @GetMapping("/insert") public Integer insert() { // 初始化-> 新增数据 Document document = new Document(); document.setId("1"); document.setTitle("austin"); document.setContent("austin-framework-elasticsearch"); return documentMapper.insert(document); } @RequestMapping("/update") public Integer update() { // 更新 Document document = new Document(); document.setId("1"); document.setContent("updated content!"); return documentMapper.updateById(document); } @GetMapping("/search") public List<Document> search() { // 查询出所有标题为austin的文档列表 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.eq(Document::getTitle, "austin"); return documentMapper.selectList(wrapper); } @DeleteMapping("/delete") public Integer delete() { // 根据ID删除 LambdaEsQueryWrapper<Document> esQueryWrapper = new LambdaEsQueryWrapper<>(); esQueryWrapper.eq(Document::getId, "1"); return documentMapper.delete(esQueryWrapper); }}

applcation.properties

ini复制代码server.port=26686spring.application.name=framework-elasticsearchspring.datasource.url=jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghaispring.datasource.username=rootspring.datasource.password=rootspring.datasource.driver-class-name=com.mysql.cj.jdbc.Drivereasy-es.address=127.0.0.1:9200easy-es.enable=trueeasy-es.username=elasticeasy-es.password=

3️⃣ 执行CRUD操作

访问:http://localhost:26686/insert 插入条数据,接着访问:http://localhost:26686/search 根据title 字段查询文档信息。

搜索引擎ORM框架Easy-Es(搜索引擎 框架)

总结

实际上,Easy-Es还有很多拓展功能,比如:混合查询、原生查询、分页查询、嵌套查询、Join父子类型、获取DSL语句,同时也包含很多高阶语法:查询字段过滤、排序、聚合查询、分词查询、权重、高亮查询、GEO地理位置查询等等功能。

链接:https://juejin.cn/post/7207410405785337916

相关新闻

联系我们
联系我们
公众号
公众号
在线咨询
分享本页
返回顶部