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,可配置为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

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 != 18
    LambdaEsQueryWrapper<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.mfbz.cn/a/781550.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线程安全的原因及解决方法

什么是线程安全问题 线程安全问题指的是在多线程编程环境中&#xff0c;由于多个线程共享数据或资源&#xff0c;并且这些线程对共享数据或资源的访问和操作没有正确地同步&#xff0c;导致数据的不一致、脏读、不可重复读、幻读等问题。线程安全问题的出现&#xff0c;通常是…

【大数据综合试验区1008】揭秘企业数字化转型:大数据试验区政策数据集大公开!

今天给大家分享的是国内顶级期刊中国工业经济2023年发布的最新期刊《政策赋能、数字生态与企业数字化转型——基于国家大数据综合试验区的准自然实验》文章中所使用到的数据集——国家大数据综合试验区政策数据集以及工具变量数据&#xff0c;该文章基于2009-2019年中国上市企业…

两个全开源的3D模型素材下载网站源码 3D图纸模型素材 三维图形素材会员下载站源码

今天推荐两个全开源的3D模型素材下载网站源码 3D图纸模型素材 三维图形素材会员下载站源码&#xff0c;这两个源码完整&#xff0c;都是基于thinkphp内核开发的&#xff0c;框架稳定&#xff0c;带数据库&#xff0c;源码文件&#xff0c;可以直接部署使用。 第一个&#xff1a…

【数据结构与算法】快速排序挖坑法

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​

Redis源码整体结构

一 前言 Redis源码研究为什么先介绍整体结构呢?其实也很简单,作为程序员的,要想对一个项目有快速的认知,对项目整体目录结构有一个清晰认识,有助于我们更好的了解这个系统。 二 目录结构 Redis源码download到本地之后,对应结构如下: 从上面的截图可以看出,Redis源码一…

【密码学】信息安全五大属性

信息安全的五大属性&#xff0c;通常被称为CIA三元组加上两个额外的属性&#xff0c;他们是确保信息在存储、处理和传输过程中保持安全、完整和可用的关键要素。这些属性共同构成了信息安全的基础框架。 一、信息安全五大属性 我先给出一个直观的列表&#xff0c;方面大家后续…

BigDecimal(double)和BigDecimal(String)有什么区别?BigDecimal如何精确计数?

BigDecimal(double)和BigDecimal(String)的区别 double是不精确的&#xff0c;所以使用一个不精确的数字来创建BigDecimal&#xff0c;得到的数字也是不精确的。如0.1这个数字&#xff0c;double只能表示他的近似值。所以&#xff0c;当我们使用new BigDecimal(0.1)创建一个Bi…

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

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

前端必修技能:高手进阶核心知识分享 - CSS 阴影属性详解

CSS 涉及设计到阴影的相关内容包括三个方面&#xff1a;box-shadow属性&#xff08;盒子阴影&#xff09;、 text-shadow属性&#xff08;文本阴影&#xff09;、drop-shadow滤镜。 本篇文章旨在详细介绍和分析三种阴影的具体参数设置和典型用例。 box-shadow属性&#xff08;…

蚂蚁全媒体总编刘鑫炜谈新媒体时代艺术家如何创建及提升个人品牌

新媒体时代艺术家如何创建及提升个人品牌形象——专访蚂蚁全媒体总编刘鑫炜 图为蚂蚁全媒体总编刘鑫炜 在新媒体风潮席卷全球的今天&#xff0c;传统艺术与新媒体技术的融合越来越紧密。这种变革不仅改变了艺术作品的呈现方式&#xff0c;也给艺术家们提供了更多的可能性。那么…

从FasterTransformer源码解读开始了解大模型(2.1)代码通读03

从FasterTransformer源码解读开始了解大模型&#xff08;2.2&#xff09;代码解读03-forward函数 写在前面的话 本篇的内容继续解读forward函数&#xff0c;从650行开始进行解读 零、输出Context_embeddings和context_cum_log_probs的参数和逻辑 从653行开始&#xff0c;会…

怎样让家长单独查到自己孩子的期末成绩?

期末考试的钟声已经敲响&#xff0c;随着最后一份试卷的收卷&#xff0c;学生们的紧张情绪渐渐平息。然而&#xff0c;对于老师们来说&#xff0c;这仅仅是另一个忙碌周期的开始。成绩的统计、分析、反馈&#xff0c;每一项工作都不容小觑。尤其是将成绩单一一私信给家长&#…

【Python】组合数据类型:序列,列表,元组,字典,集合

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言组合数据类型序列类型序列常见的操作符列表列表操作len()append()insert()remove()index()sort()reverse()count() 元组三种序列类型的区别 集合类型四种操作符集合setfrozens…

分子AI预测赛Task4笔记(结束)

话不多说&#xff0c;直接上官方链接&#xff1a;‌​​​‍&#xfeff;​⁠​‌​‍​​&#xfeff;​‌​⁠‬​&#xfeff;‬​​‌​​​​‬‬​​​​‍⁠‍‌​&#xfeff;⁠Task3&#xff1a;进阶baseline详解 - 飞书云文档 (feishu.cn)Task4&#xff1a;持续尝试&…

RAID的实现

软RAID&#xff0c;在实际工作中使用较少&#xff0c;性能太次。 mdadm工具&#xff0c;主要在虚拟机上使用&#xff0c; 硬RAID 用一个单独的芯片&#xff0c;这个芯片的名字叫做RAID卡&#xff0c;数据在RAID中进行分散的时候&#xff0c;用的就是RAID卡。 模拟RAID-5工作…

【Transformer】transformer模型结构学习笔记

文章目录 1. transformer架构2. transformer子层解析3. transformer注意力机制4. transformer部分释疑 图1 transformer模型架构 图2 transformer主要模块简介 图3 encoder-decoder示意图N6 图4 encoder-decoder子层示意图 1. transformer架构 encoder-decoder框架是一种处理NL…

AI编程探索- iOS 实现类似苹果地图 App 中的半屏拉起效果

想要的效果 功能分析 想要实现这种效果&#xff0c;感觉有点复杂&#xff0c;于是就想搜一下相关资料看看&#xff0c;可问题是&#xff0c;我不知道如何描述这种效果&#x1f602;。 当我们遇到这种效果看着很熟悉&#xff0c;但是不知道如何描述它具体是什么的时候&#…

有一个日期(Date)类的对象和一个时间(Time)类的对象,均已指定了内容,要求一次输出其中的日期和时间

可以使用友元成员函数。在本例中除了介绍有关友元成员函数的简单应用外&#xff0c;还将用到类的提前引用声明&#xff0c;请读者注意。编写程序&#xff1a; 运行结果&#xff1a; 程序分析&#xff1a; 在一般情况下&#xff0c;两个不同的类是互不相干的。display函…

华为云OBS 通过S3客户端访问

华为云好像没有对S3协议的支持说明其实底层是支持S3协议的。 使用S3的时候我们会需要endpoint&#xff0c;桶名字&#xff0c;region&#xff0c;AWS_ACCESS_KEY,AWS_SECRET_KEY 其中endpoint 就是图片中的&#xff0c;桶名字也很容易找到&#xff0c;region 就是你的endpoint…