赞
踩
Easy-Es官网: Easy-Es
参考部署ES及配置, 还有配置
IK分词器
跳转链接: 部署ES及配置IK分词器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> </exclusions> </dependency> <!--ES相关--> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.14.0</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>7.14.0</version> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.14.0</version> </dependency> <dependency> <groupId>org.dromara.easy-es</groupId> <artifactId>easy-es-boot-starter</artifactId> <version>2.0.0-beta4</version> </dependency>
如果出现版本冲突就排除Spring管理的ES依赖
# Easy-ES配置 easy-es: # 是否启动(预先关闭) enable: true # es连接地址+端口 格式必须为ip:port,如果是集群则可用逗号隔开 address: 192.168.164.128:9200 # 如果无账号密码则可不配置此行 #username: # 如果无账号密码则可不配置此行 #password: # 默认为http 可缺省 schema: http # 默认为true 打印banner 若您不期望打印banner,可配置为false banner: false # 心跳策略时间 单位:ms keep-alive-millis: 30000 # 连接超时时间 单位:ms connect-timeout: 5000 # 通信超时时间 单位:ms socket-timeout: 600000 # 连接请求超时时间 单位:ms connection-request-timeout: 5000 # 最大连接数 单位:个 max-conn-total: 100 # 最大连接路由数 单位:个 max-conn-per-route: 100 global-config: # 索引处理模式,smoothly:平滑模式, not_smoothly:非平滑模式, manual:手动模式,,默认开启此模式 process-index-mode: manual # 开启控制台打印通过本框架生成的DSL语句,默认为开启,测试稳定后的生产环境建议关闭,以提升少量性能 print-dsl: true # 当前项目是否分布式项目,默认为true,在非手动托管索引模式下,若为分布式项目则会获取分布式锁,非分布式项目只需synchronized锁. distributed: false # 重建索引超时时间 单位小时,默认72H 可根据ES中存储的数据量调整 reindexTimeOutHours: 72 # 异步处理索引是否阻塞主线程 默认阻塞 数据量过大时调整为非阻塞异步进行 项目启动更快 async-process-index-blocking: true db-config: # 是否开启下划线转驼峰 默认为false map-underscore-to-camel-case: true # 索引前缀,可用于区分环境 默认为空 用法和MP的tablePrefix一样的作用和用法 # index-prefix: template_ # id生成策略 customize为自定义,id值由用户生成,比如取MySQL中的数据id,如缺省此项配置,则id默认策略为es自动生成 id-type: customize # 数据刷新策略,默认为不刷新,若对数据时效性要求比较高,可以调整为immediate,但性能损耗高,也可以调整为折中的wait_until # refresh-policy: immediate
@Data @IndexName(shardsNum = 3,replicasNum = 2) // 可指定分片数,副本数,若缺省则默认均为1 public class Document { /** * es中的唯一id,如果你想自定义es中的id为你提供的id,比如MySQL中的id,请将注解中的type指定为customize,如此id便支持任意数据类型) */ @IndexId(type = IdType.CUSTOMIZE) private Long id; /** * 文档标题,不指定类型默认被创建为keyword_text类型,可进行精确查询 */ private String title; /** * 文档内容,指定了类型及存储/查询分词器 */ @HighLight(mappingField="highlightContent") @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_MAX_WORD) private String content; /** * 作者 加@TableField注解,并指明strategy = FieldStrategy.NOT_EMPTY 表示更新的时候的策略为 创建者不为空字符串时才更新 */ @IndexField(strategy = FieldStrategy.NOT_EMPTY) private String creator; /** * 创建时间 */ @IndexField(fieldType = FieldType.DATE, dateFormat = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis") private String gmtCreate; /** * es中实际不存在的字段,但模型中加了,为了不和es映射,可以在此类型字段上加上 注解@TableField,并指明exist=false */ @IndexField(exist = false) private String notExistsField; /** * 地理位置经纬度坐标 例如: "40.13933715136454,116.63441990026217" */ @IndexField(fieldType = FieldType.GEO_POINT) private String location; /** * 图形(例如圆心,矩形) */ @IndexField(fieldType = FieldType.GEO_SHAPE) private String geoLocation; /** * 自定义字段名称 */ @IndexField(value = "wu-la") private String customField; /** * 高亮返回值被映射的字段 */ private String highlightContent; }
easy-es:
socketTimeout: 600000 # 请求通信超时时间 单位:ms 默认值600000ms 在平滑模式下,由于要迁移数据,用户可根据数据量大小调整此参数值大小,否则请求容易超时导致索引托管失败,建议您尽量给大不给小,跟那玩意一样,大点没事,太小你懂的!
global-config:
process_index_mode: smoothly #smoothly:平滑模式, not_smoothly:非平滑模式, manual:手动模式
async-process-index-blocking: true # 异步处理索引是否阻塞主线程 默认阻塞
distributed: false # 项目是否分布式环境部署,默认为true, 如果是单机运行可填false,将不加分布式锁,效率更高.
reindexTimeOutHours: 72 # 重建索引超时时间 单位小时,默认72H 根据迁移索引数据量大小灵活指定
索引托管自动挡无需考虑索引的问题, 会根据模板类自动更新索引库
若依框架不适配Easy-Es
, 无法使用索引自动托管, 只能使用手动挡创建索引
easy-es:
global-config:
process_index_mode: manual # 手动挡模式
/** * 实体类信息 **/ @Data @IndexName(shardsNum = 3, replicasNum = 2, keepGlobalPrefix = true) public class Document { /** * es中的唯一id,如果你想自定义es中的id为你提供的id,比如MySQL中的id,请将注解中的type指定为customize或直接在全局配置文件中指定,如此id便支持任意数据类型) */ @IndexId(type = IdType.CUSTOMIZE) private String id; /** * 文档标题,不指定类型默认被创建为keyword类型,可进行精确查询 */ private String title; /** * 文档内容,指定了类型及存储/查询分词器 */ @HighLight(mappingField = "highlightContent") @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_MAX_WORD) private String content; // 省略其它字段... }
/** * 方式1 */ @Test public void testCreateIndexByEntity() { // 绝大多数场景推荐使用 简单至上 documentMapper.createIndex(); } /** * 方式2 */ @Test public void testCreateIndexByEntity() { // 适用于定时任务按日期创建索引场景 String indexName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); documentMapper.createIndex(indexName); } /** * 方式3 */ @Test public void testCreateIndex() { // 复杂场景使用 LambdaEsIndexWrapper<Document> wrapper = new LambdaEsIndexWrapper<>(); // 此处简单起见 索引名称须保持和实体类名称一致,字母小写 后面章节会教大家更如何灵活配置和使用索引 wrapper.indexName(Document.class.getSimpleName().toLowerCase()); // 此处将文章标题映射为keyword类型(不支持分词),文档内容映射为text类型(支持分词查询) wrapper.mapping(Document::getTitle, FieldType.KEYWORD, 2.0f) .mapping(Document::getLocation, FieldType.GEO_POINT) .mapping(Document::getGeoLocation, FieldType.GEO_SHAPE) .mapping(Document::getContent, FieldType.TEXT, Analyzer.IK_SMART, Analyzer.IK_MAX_WORD); // 0.9.8+版本,增加对符串字段名称的支持,Document实体中须在对应字段上加上@Tablefield(value="wu-la")用于映射此字段值 wrapper.mapping("wu-la", FieldType.TEXT, Analyzer.IK_MAX_WORD, Analyzer.IK_MAX_WORD); // 设置分片及副本信息,可缺省 wrapper.settings(3, 2); // 设置别名信息,可缺省 String aliasName = "daily"; wrapper.createAlias(aliasName); // 设置父子信息,若无父子文档关系则无需设置, 可缺省 wrapper.join("joinField", "document", "comment"); // 创建索引 boolean isOk = documentMapper.createIndex(wrapper); Assertions.assertTrue(isOk); }
@Test
public void testExistsIndex() {
// 测试是否存在指定名称的索引
String indexName = Document.class.getSimpleName().toLowerCase();
boolean existsIndex = documentMapper.existsIndex(indexName);
Assertions.assertTrue(existsIndex);
}
@Test
public void testGetIndex() {
GetIndexResponse indexResponse = documentMapper.getIndex();
// 这里打印下索引结构信息 其它分片等信息皆可从indexResponse中取
indexResponse.getMappings().forEach((k, v) -> System.out.println(v.getSourceAsMap()));
}
@Test
public void testUpdateIndex() {
// 测试更新索引
LambdaEsIndexWrapper<Document> wrapper = new LambdaEsIndexWrapper<>();
// 指定要更新哪个索引
String indexName = Document.class.getSimpleName().toLowerCase();
wrapper.indexName(indexName);
wrapper.mapping(Document::getCreator, FieldType.KEYWORD);
wrapper.mapping(Document::getGmtCreate, FieldType.DATE);
boolean isOk = documentMapper.updateIndex(wrapper);
Assertions.assertTrue(isOk);
}
@Test
public void testDeleteIndex() {
// 指定要删除哪个索引
String indexName = Document.class.getSimpleName().toLowerCase();
boolean isOk = documentMapper.deleteIndex(indexName);
Assertions.assertTrue(isOk);
}
参考
RestClient操作
, 使用MQ做异步通知来同步MYSQL
和ES
的数据
跳转链接: RestClient操作
// 插入一条记录,默认插入至当前mapper对应的索引
Integer insert(T entity);
// 插入一条记录 可指定具体插入的索引,多个用逗号隔开
Integer insert(T entity, String... indexNames);
// 批量插入多条记录
Integer insertBatch(Collection<T> entityList)
// 批量插入多条记录 可指定具体插入的索引,多个用逗号隔开
Integer insertBatch(Collection<T> entityList, String... indexNames);
// 根据 ID 删除
Integer deleteById(Serializable id);
// 根据 ID 删除 可指定具体的索引,多个用逗号隔开
Integer deleteById(Serializable id, String... indexNames);
// 根据 entity 条件,删除记录
Integer delete(LambdaEsQueryWrapper<T> wrapper);
// 删除(根据ID 批量删除)
Integer deleteBatchIds(Collection<? extends Serializable> idList);
// 删除(根据ID 批量删除)可指定具体的索引,多个用逗号隔开
Integer deleteBatchIds(Collection<? extends Serializable> idList, String... indexNames);
在传入的
entity
中带上id, 可以和新增使用同一份接口, 省事~
//根据 ID 更新
Integer updateById(T entity);
//根据 ID 更新 可指定具体的索引,多个用逗号隔开
Integer updateById(T entity, String... indexNames);
// 根据ID 批量更新
Integer updateBatchByIds(Collection<T> entityList);
//根据 ID 批量更新 可指定具体的索引,多个用逗号隔开
Integer updateBatchByIds(Collection<T> entityList, String... indexNames);
// 根据动态条件 更新记录
Integer update(T entity, LambdaEsUpdateWrapper<T> updateWrapper);
// 获取总数 Long selectCount(LambdaEsQueryWrapper<T> wrapper); // 获取总数 distinct为是否去重 若为ture则必须在wrapper中指定去重字段 Long selectCount(Wrapper<T> wrapper, boolean distinct); // 根据 ID 查询 T selectById(Serializable id); // 根据 ID 查询 可指定具体的索引,多个用逗号隔开 T selectById(Serializable id, String... indexNames); // 查询(根据ID 批量查询) List<T> selectBatchIds(Collection<? extends Serializable> idList); // 查询(根据ID 批量查询)可指定具体的索引,多个用逗号隔开 List<T> selectBatchIds(Collection<? extends Serializable> idList, String... indexNames); // 根据动态查询条件,查询一条记录 若存在多条记录 会报错 T selectOne(LambdaEsQueryWrapper<T> wrapper); // 根据动态查询条件,查询全部记录 List<T> selectList(LambdaEsQueryWrapper<T> wrapper);
/** * 场景一: 嵌套and的使用 */ @Test public void testNestedAnd() { // 下面查询条件等价于MySQL中的 select * from document where star_num in (1, 2) and (title = '老汉' or title = '推*') LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.in(Document::getStarNum, 1, 2) .and(w -> w.eq(Document::getTitle, "老汉").or().eq(Document::getTitle, "推*")); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景二: 拼接and的使用 */ @Test public void testAnd(){ // 下面查询条件等价于MySQL中的 select * from document where title = '老汉' and content like '推*' // 拼接and比较特殊,因为使用场景最多,所以条件与条件之间默认就是拼接and,所以可以直接省略,这点和MP是一样的 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.eq(Document::getTitle, "老汉") .match(Document::getContent, "推*"); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景二: 嵌套or的使用 */ @Test public void testNestedOr() { // 下面查询条件等价于MySQL中的 select * from document where star_num = 1 or (title = '老汉' and creator = '糟老头子') LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.eq(Document::getStarNum, 1) .or(i -> i.eq(Document::getTitle, "老汉").eq(Document::getCreator, "糟老头子")); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景三: 拼接or的使用 */ @Test public void testOr() { // 下面查询条件等价于MySQL中的 select * from document where title = '老汉' or title = '痴汉' LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.eq(Document::getTitle, "老汉") .or() .eq(Document::getTitle, "痴汉"); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景四: 嵌套filter的使用 其实和场景一一样,只不过filter中的条件不计算得分,无法按得分排序,查询性能稍高 */ @Test public void testNestedFilter() { // 下面查询条件等价于MySQL中的 select * from document where star_num in (1, 2) and (title = '老汉' or title = '推*') LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.in(Document::getStarNum, 1, 2) .filter(w -> w.eq(Document::getTitle, "老汉").or().eq(Document::getTitle, "推*")); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景五: 拼接filter的使用 filter中的条件不计算得分,无法按得分排序,查询性能稍高 */ @Test public void testFilter() { // 下面查询条件等价于MySQL中的 select * from document where title = '老汉' LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.filter().eq(Document::getTitle, "老汉"); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景六: 嵌套mustNot的使用 */ @Test public void testNestedNot() { // 下面查询条件等价于MySQL中的 select * from document where title = '老汉' and (size != 18 and age != 18) LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.eq(Document::getTitle, "老汉") .not(i->i.eq(size,18).eq(age,18)); List<Document> documents = documentMapper.selectList(wrapper); } /** * 场景六: 拼接not()的使用 */ @Test public void testNot() { // 下面查询条件等价于MySQL中的 select * from document where title = '老汉' and size != 18 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.eq(Document::getTitle, "老汉") .not() .eq(size,18); List<Document> documents = documentMapper.selectList(wrapper); }
// 索引链式构造器
LambdaEsIndexChainWrapper<T> lambdaChainIndex(BaseEsMapper<T> baseEsMapper);
// 查询链式构造器
LambdaEsQueryChainWrapper<T> lambdaChainQuery(BaseEsMapper<T> baseEsMapper);
// 更新(含删除)链式构造器
LambdaEsUpdateChainWrapper<T> lambdaChainUpdate(BaseEsMapper<T> baseEsMapper);
@Test
public void testOne() {
// 隔壁老汉写的链式调用
Document document = EsWrappers.lambdaChainQuery(documentMapper).eq(Document::getTitle, "隔壁老汉").one();
}
@Test
public void testSelectOne() {
// 隔壁老王写的半吊子链式调用
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(Document::getTitle, "隔壁老王")
.limit(1);
Document document = documentMapper.selectOne(wrapper);
}
/** * 正确使用姿势0(最实用,最简单,最推荐的使用姿势):EE满足的语法,直接用,不满足的可以构造原生QueryBuilder,然后通过wrapper.mix传入QueryBuilder * @since 2.0.0-beta2 2.0.0-beta2才正式引入此方案,此方案为混合查询的最优解决方案,由于QueryBuilder涵盖了ES中全部的查询,所以通过此方案 * 理论上可以处理任何复杂查询,并且可以和EE提供的四大嵌套类型无缝衔接,彻底简化查询,解放生产力! */ @Test public void testMix0(){ // 查询标题为老汉,内容匹配 推*,且最小匹配度不低于80%的数据 // 当前我们提供的开箱即用match并不支持设置最小匹配度,此时就可以自己去构造一个matchQueryBuilder来实现 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); QueryBuilder queryBuilder = QueryBuilders.matchQuery("content", "推*").minimumShouldMatch("80%"); wrapper.eq(Document::getTitle,"老汉").mix(queryBuilder); List<Document> documents = documentMapper.selectList(wrapper); System.out.println(documents); } /** * 混合查询正确使用姿势1: EE提供的功能不支持某些过细粒度的功能,所有查询条件通过原生语法构造,仅利用EE提供的数据解析功能 */ @Test public void testMix1() { // RestHighLevelClient原生语法 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchQuery("content", "推*").minimumShouldMatch("80%")); // 仅利用EE查询并解析数据功能 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.setSearchSourceBuilder(searchSourceBuilder); List<Document> documents = documentMapper.selectList(wrapper); System.out.println(documents); }
// 物理分页
EsPageInfo<T> pageQuery(LambdaEsQueryWrapper<T> wrapper, Integer pageNum, Integer pageSize);
@Test
public void testPageQuery() {
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.match(Document::getTitle, "老汉");
EsPageInfo<Document> documentPageInfo = documentMapper.pageQuery(wrapper,1,10);
System.out.println(documentPageInfo);
}
// 降序排列
wrapper.orderByDesc(排序字段,支持多字段)
// 升序排列
wrapper.orderByAsc(排序字段,支持多字段)
// 根据得分排序(此功能0.9.7+版本支持;不指定SortOrder时默认降序,得分高的在前,支持升序/降序)
wrapper.sortByScore(boolean condition,SortOrder sortOrder)
@Test public void testMatch(){ // 会对输入做分词,只要所有分词中有一个词在内容中有匹配就会查询出该数据,无视分词顺序 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.match(Document::getContent,"技术"); List<Document> documents = documentMapper.selectList(wrapper); System.out.println(documents.size()); } @Test public void testMatchPhrase() { // 会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样,否则就无法查询出结果 // 例如es中数据是 技术过硬,如果搜索关键词为过硬技术就无法查询出结果 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.matchPhrase(Document::getContent, "技术"); List<Document> documents = documentMapper.selectList(wrapper); System.out.println(documents); } @Test public void testMatchAllQuery() { // 查询所有数据,类似mysql select all. LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.matchAllQuery(); List<Document> documents = documentMapper.selectList(wrapper); System.out.println(documents); } @Test public void testMultiMatchQuery() { // 从多个指定字段中查询包含老王的数据 LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>(); wrapper.multiMatchQuery("老王", Document::getTitle, Document::getContent, Document::getCreator, Document::getCustomField); // 其中,默认的Operator为OR,默认的minShouldMatch为60% 这两个参数都可以按需调整,我们api是支持的 例如: // 其中AND意味着所有搜索的Token都必须被匹配,OR表示只要有一个Token匹配即可. minShouldMatch 80 表示只查询匹配度大于80%的数据 // wrapper.multiMatchQuery("老王",Operator.AND,80,Document::getCustomField,Document::getContent); List<Document> documents = documentMapper.selectList(wrapper); System.out.println(documents.size()); System.out.println(documents); }
/**
* 场景五: 拼接filter的使用 filter中的条件不计算得分,无法按得分排序,查询性能稍高
*/
@Test
public void testFilter() {
// 下面查询条件等价于MySQL中的 select * from document where title = '老汉'
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.filter().eq(Document::getTitle, "老汉");
List<Document> documents = documentMapper.selectList(wrapper);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。