SpringBoot整合Easy-Es最佳实践

文章目录

    • 1.1 部署ES和Kibana
    • 1.2 SpringBoot整合ES及配置
      • 1.2.1 引入相关依赖
      • 1.2.2 YML相关配置
    • 1.3 索引CRUD
      • 1.3.1 索引托管自动挡
        • 1.3.1.1 配置实体模板
        • 1.3.1.2 配置启动模式
      • 1.3.2 索引手动挡
        • 1.3.2.1 配置启动模式
        • 1.3.2.2 配置实体模板
        • 1.3.2.3 创建索引
        • 1.3.2.4 查询索引
        • 1.3.2.5 更新索引(不推荐)
        • 1.3.2.6 删除索引
    • 1.4 数据CRUD
      • 1.4.1 数据同步
      • 1.4.2 数据CRUD
        • 1.4.2.1 新增数据
        • 1.4.2.2 删除数据
        • 1.4.2.3 更新数据(不推荐)
        • 1.4.2.4 查询数据(推荐分页查询)
      • 1.4.3 嵌套查询
      • 1.4.4 链式调用
    • 1.5 拓展功能
      • 1.5.1 混合查询
      • 1.5.2 分页查询
      • 1.5.3 排序
      • 1.5.4 分词 / 模糊匹配
      • 1.5.5 条件过滤

Easy-Es官网: Easy-Es

1.1 部署ES和Kibana


参考部署ES及配置, 还有配置IK分词器 跳转链接: 部署ES及配置IK分词器


1.2 SpringBoot整合ES及配置

1.2.1 引入相关依赖

<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依赖

1.2.2 YML相关配置

# Easy-ES配置
easy-es:# 是否启动(预先关闭)enable: true# es连接地址+端口 格式必须为ip:port,如果是集群则可用逗号隔开address: 192.168.164.128:9200# 如果无账号密码则可不配置此行#username:# 如果无账号密码则可不配置此行#password:# 默认为http 可缺省schema: http# 默认为true 打印banner 若您不期望打印banner,可配置为falsebanner: false# 心跳策略时间 单位:mskeep-alive-millis: 30000# 连接超时时间 单位:msconnect-timeout: 5000# 通信超时时间 单位:mssocket-timeout: 600000# 连接请求超时时间 单位:msconnection-request-timeout: 5000# 最大连接数 单位:个max-conn-total: 100# 最大连接路由数 单位:个max-conn-per-route: 100global-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: truedb-config:# 是否开启下划线转驼峰 默认为falsemap-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

1.3 索引CRUD

1.3.1 索引托管自动挡

1.3.1.1 配置实体模板
@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;
}

1.3.1.2 配置启动模式
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, 无法使用索引自动托管, 只能使用手动挡创建索引


1.3.2 索引手动挡

1.3.2.1 配置启动模式
easy-es:global-config:process_index_mode: manual # 手动挡模式

1.3.2.2 配置实体模板
/*** 实体类信息
**/
@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.3.2.3 创建索引
/*** 方式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);
}

1.3.2.4 查询索引
@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()));
}

1.3.2.5 更新索引(不推荐)
@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);
}

1.3.2.6 删除索引
@Test
public void testDeleteIndex() {// 指定要删除哪个索引String indexName = Document.class.getSimpleName().toLowerCase();boolean isOk = documentMapper.deleteIndex(indexName);Assertions.assertTrue(isOk);
}

1.4 数据CRUD

1.4.1 数据同步

参考RestClient操作, 使用MQ做异步通知来同步MYSQLES的数据
跳转链接: RestClient操作


1.4.2 数据CRUD

1.4.2.1 新增数据
// 插入一条记录,默认插入至当前mapper对应的索引
Integer insert(T entity);
// 插入一条记录 可指定具体插入的索引,多个用逗号隔开
Integer insert(T entity, String... indexNames);// 批量插入多条记录
Integer insertBatch(Collection<T> entityList)
// 批量插入多条记录 可指定具体插入的索引,多个用逗号隔开 
Integer insertBatch(Collection<T> entityList, String... indexNames);

  • 如果您在insert时传入的entity有id并且该id对应数据已存在,则此次insert实际效果为更新该id对应的数据,并且更新不计入insert接口最后返回的成功总条数.
  • 当insert接口如上所述,触发了数据更新逻辑,本次更新字段和全局配置的策略(如NOT_NULL/NOT_EMPTY)等均不生效,若您期望策略生效,可以调用update接口而非insert接口.
  • 插入后如需id值可直接从entity中取,用法和MP中一致,批量插入亦可直接从原对象中获取插入成功后的数据id,以上接口返回Integer为成功条数.

1.4.2.2 删除数据
// 根据 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);

1.4.2.3 更新数据(不推荐)

在传入的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);

1.4.2.4 查询数据(推荐分页查询)
// 获取总数
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);

1.4.3 嵌套查询

/*** 场景一: 嵌套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 != 18LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();wrapper.eq(Document::getTitle, "老汉").not().eq(size,18);List<Document> documents = documentMapper.selectList(wrapper);
}

1.4.4 链式调用

// 索引链式构造器
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);
}

1.5 拓展功能

1.5.1 混合查询

/*** 正确使用姿势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);
}

1.5.2 分页查询

// 物理分页
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);
}

1.5.3 排序

// 降序排列
wrapper.orderByDesc(排序字段,支持多字段)// 升序排列
wrapper.orderByAsc(排序字段,支持多字段)// 根据得分排序(此功能0.9.7+版本支持;不指定SortOrder时默认降序,得分高的在前,支持升序/降序)
wrapper.sortByScore(boolean condition,SortOrder sortOrder)

1.5.4 分词 / 模糊匹配

@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);
}

1.5.5 条件过滤

/*** 场景五: 拼接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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/42540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【INTEL(ALTERA)】为什么在设计迁移后,无法在Nios II BSP 编辑器中找到 DDR3 作为内存区域

目录 说明 解决方法 说明 将设计从 Quartus II 14.1 及以下迁移到 Quartus prime 17.0 时&#xff0c;DDR3 不再被识别为Nios II BSP 编辑器区域中的内存区域。 解决方法 迁移设计后&#xff0c;从 Qsys 设计中移除 DDR3 IP&#xff0c;然后将其再次添加。生成 Qsys 文件后…

车牌号查车辆信息-车牌号查车辆信息接口-汽车API接口

接口简介&#xff1a;输入车牌号&#xff0c;返回车辆相关信息&#xff08;无车主信息&#xff09;。初始登记日期、上险日期、保险到期时间、车架号、品牌这些数据会返回&#xff0c;其他数据不一定全部返回&#xff0c;,详细参数请查看返回接口文档 一般在新车上险或过户后第…

微信小程序消息通知(一次订阅)

在微信公众平台配置通知模版 通过wx.login获取code发送给后端 let that this // 登陆codewx.login({success: function (res) {if (res.code) {// 发送code到后端换取openid和session_keythat.setData({openCode: res.code})console.log(that.data.openCode, openCode);// 调…

数学系C++(六七)

目录 * &指针与地址 void指针 指针可以等于&#xff1a; const 指向常量的指针 const int *px 常指针 int * const px 指向常量的常指针const 类型标识符 * const 指针名 指针加减&#xff1a; 指针恒等式 函数指针【待续】 指针型函数&#xff1a; 指向函数的…

大数据之路 读书笔记 Day4 数据同步

回顾&#xff1a; Day 3 总结了无限客户端的日志采集 大数据之路 读书笔记 Day 3Day 2总结了浏览器端的日志采集 大数据之路 读书笔记 Day 2 数据同步 阿里数据体系中的数据同步&#xff0c;主要指的是在不同的数据存储系统之间进行数据的传输与更新&#xff0c;以保证数据的一…

【代码随想录_Day26】56 合并区间 738 单调递增的数字

Day26 OK&#xff0c;今日份的打卡&#xff01;第二十六天 以下是今日份的总结合并区间单调递增的数字 以下是今日份的总结 56 合并区间 738 单调递增的数字 今天的题目难度不低&#xff0c;尽量还是写一些简洁代码 ^ _ ^ 合并区间 思路&#xff1a; 先排序&#xff0c;按…

reactor和proactor模型

Reactor模型是非阻塞的同步IO模型。在主线程中也就是IO处理单元中&#xff0c;只负责监听文件描述符上是否有事件发生&#xff0c;有的话就立即将事件通知工作线程&#xff0c;将socket可读可写事件放入请求队列&#xff0c;交给工作线程处理。 总而言之就是主线程监听有事件发…

apk反编译修改教程系列-----修改apk 解除软件限制功能 实例操作步骤解析_3【二十二】

在前面的几期博文中有过解析去除apk中功能权限的反编译步骤。另外在以往博文中也列举了修改apk中选项功能权限的操作方法。今天以另外一款apk作为演示修改反编译去除软件功能限制的步骤。兴趣的友友可以参考其中的修改过程。 课程的目的是了解apk中各个文件的具体作用以及简单…

debain系统使用日志

账号 vboxuser changeme ssh远程登录vbox虚拟机 https://www.cnblogs.com/BuzzWeek/p/17557981.html Terminal su - root changeme sudo apt-get update sudo apt-get -y install openssh-server #启动sshd systemctl status sshd 设置允许ssh登录vbox虚拟机 参考&#xf…

【密码学】什么是密码?什么是密码学?

一、密码的定义 根据《中华人民共和国密码法》对密码的定义如下&#xff1a; 密码是指采用特定变换的方法对信息等进行加密保护、安全认证的技术、产品和服务。 二、密码学的定义 密码学是研究编制密码和破译密码的技术科学。由定义可以知道密码学分为两个主要分支&#x…

69.WEB渗透测试-信息收集- WAF、框架组件识别(9)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;68.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;8&#xff09; 有无waf存在&am…

GESP C++一级真题

PDF图片1-7 点赞❤️关注&#x1f60d;收藏⭐️ 互粉必回&#x1f64f;&#x1f64f;&#x1f64f;

DPDK源码分析之(1)libmbuf模块

DPDK源码分析之(1)libmbuf模块 Author&#xff1a;OnceDay Date&#xff1a;2024年7月2日 漫漫长路&#xff0c;有人对你笑过嘛… 全系列文档可参考专栏&#xff1a;源码分析_Once-Day的博客-CSDN博客 参考文档&#xff1a; DPDK downloadGetting Started Guide for Linux…

JAVA中的for-each(增强型for循环)

Java 8及之后的版本中与for-each循环&#xff08;也称为增强型for循环&#xff09;和数组处理结合使用时&#xff0c;它可能类似于Lambda表达式或流&#xff08;Stream&#xff09;API中的上下文。 举例&#xff1a; public class ArrLength {public static void main(String…

CGAL计算凸包(OSG进行可视化)

目录 一、什么是凸包 二、运行步骤 1、安装依赖项 2、编译osg库 3、运行代码 4、运行截图 一、什么是凸包 凸包是计算几何中的一个基本概念,用来描述一个点集的最小凸包围形。具体来说,给定一个点集,凸包是包含该点集的最小凸多边形或凸多面体。 二维凸包:在二维平面…

迭代器模式在金融业务中的应用及其框架实现

引言 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为设计模式&#xff0c;它提供了一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不需要暴露该对象的内部表示。在金融业务中&#xff0c;迭代器模式可以用于遍历复杂的数据结构&#xff0c;如交易…

算法-常见数据结构设计

文章目录 1. 带有setAll功能的哈希表2. LRU缓存结构3. O(1)时间插入删除随机(去重)4. O(1)时间插入删除随机(不去重)5. 快速获取数据流中的中位数6. 最大频率栈7. 全O(1)结构8. LFU缓存结构 本节的内容比较难, 大多是leetcodeHard难度级别的题目 1. 带有setAll功能的哈希表 哈希…

js计算两个日期直接的间隔天,2018/12/14到2017/11/10有多少天

const startDate new Date(2017-11-10)const endDate new Date(2018-12-14)const diffTime Math.abs(endDate - startDate)const diffDays Math.ceil(diffTime / (1000 * 60 * 60 * 24))console.log(diffDays) // 输出天数差 人工智能学习网站 https://chat.xutongbao.top…

解析Java中1000个常用类:DoubleSummaryStatistics类,你学会了吗?

在线工具站 推荐一个程序员在线工具站:程序员常用工具(http://cxytools.com),有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具,效率加倍嘎嘎好用。程序员资料站 推荐一个程序员编程资料站:程序员的成长之路(http://cxyroad.com),收录了一些列的技术教程…

VSCode神仙插件——Codeium (AI编程助手)

1、安装&登录插件 安装过程中会让你登录Codeium账户&#xff0c;可以通过Google账户登录&#xff0c;或者可以注册一个Codeium账户&#xff08;如果没有弹出让你登录账户的界面&#xff0c;可以等安装结束后在右下角找到登录的地方&#xff09; 右下角显示如下图所示&#…