官方文档

简介 | Easy-Es

配置

application.yml

easy-es:
  compatible: true
  enable: true
  address: 192.168.1.129:9200  #填你的es连接地址
  username: elastic
  password: elastic

logging:
  level:
    tracer: trace # 设置日志级别为trace,开发时可开启以打印ES全部请求信息及DSL语句

Spring Boot 启动类

@EsMapperScan("org.kele.mapper")

实体类

示例:

@Data
@IndexName("product_info")
public class ProductInfo implements Serializable {
    // 序列化版本号(避免反序列化异常)
    @Serial
    private static final long serialVersionUID = 1L;

    // 1. 内容唯一标识字段
    /** 内容唯一ID(主键) */
    @IndexId(type = IdType.CUSTOMIZE)
    private String id;

    /** 内容网页访问地址(相对路径) */
    @IndexField(fieldType = FieldType.KEYWORD)
    private String url;

    /** 发布时间戳(Unix时间,单位:秒) */
    @IndexField(fieldType = FieldType.LONG)
    private String shijianchuo;

    // 2. 内容主体字段
    /** 内容标题(商品名/活动主题/标识编码) */
    @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD, searchAnalyzer = Analyzer.IK_SMART)
    private String title;

    /** 内容纯文本正文(活动规则/商品信息) */
    @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD, searchAnalyzer = Analyzer.IK_SMART)
    private String content;

    /** 带HTML标签的正文(含图片、链接、样式,用于网页展示) */
    @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD, searchAnalyzer = Analyzer.IK_SMART)
    private String contentHtml;

    // 3. 发布与分类字段
    /** 发布日期(格式:YYYY-MM-DD) */
    @IndexField(fieldType = FieldType.KEYWORD)
    private String datetime;

    /** 发布具体时间(格式:HH:mm) */
    @IndexField(fieldType = FieldType.KEYWORD)
    private String shorttime;

    /** 分类ID(数字字符串,对应分类体系) */
    @IndexField(fieldType = FieldType.KEYWORD)
    private String cateid;

    /** 分类名称(如"微博情报-食品-淘宝|猫超") */
    @IndexField(fieldType = FieldType.KEYWORD)
    private String catename;

    /** 楼主(内容发布者昵称) */
    @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD)
    private String louzhu;

    /** 楼主注册时间(可为null,未记录时为null) */
    @IndexField(fieldType = FieldType.KEYWORD)
    private String louzhuregtime;

    // 4. 互动数据字段
    /** 内容评论数(0-20) */
    @IndexField(fieldType = FieldType.INTEGER)
    private Integer comments;
}

@Settings

  • 描述:索引Settings信息注解,可设置ES索引中的Settings信息

  • 使用位置:实体类

@Settings(shardsNum = 3, replicasNum = 2, settingsProvider = MySettingsProvider.class)
public class Document {
    // 省略其它字段
}

属性

类型

必须指定

默认值

描述

shardsNum

Int

否

1

索引分片数,默认值为1

replicasNum

Int

否

1

索引副本数,默认值为1

maxResultWindow

Int

否

10000

默认最大返回数

refreshInterval

String

否

""

索引的刷新间隔 es默认值为1s ms:表示毫秒 s:表示秒 m:表示分钟

settingsProvider

Class

否

DefaultSettingsProvider.class

自定义settings提供类 默认为DefaultSettingsProvider空实现 如需自定义,可继承此类并覆写getSettings方法 将settings信息以Map返回

@IndexName

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

  • 使用位置:实体类

@IndexName
public class Document {
    // 省略其它字段
}

属性

类型

必须指定

默认值

描述

value

String

否

""

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

aliasName

String

否

""

索引别名

keepGlobalPrefix

boolean

否

false

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

refreshPolicy

Enum

否

NONE

索引数据刷新策略,默认为不刷新,其取值参考RefreshPolicy枚举类,一共有3种刷新策略

@IndexId(opens new window)

  • 描述:ES主键注解

  • 使用位置:实体类中被作为ES主键的字段, 对应MP的@TableId注解

public class Document {
    @IndexId
    private String id;
    // 省略其它字段
}

属性

类型

必须指定

默认值

描述

value

String

否

""

自定义主键名称,不建议使用,推荐用默认名

type

Enum

否

IdType.NONE

指定主键类型

writeToSource

boolean

否

true

是否将主键写入到source中

@IndexField

  • 描述:ES字段注解, 对应MP的@TableField注解

  • 使用位置:实体类中被作为ES索引字段的字段

  • 使用场景举例:

  1. 实体类中的字段并非ES中实际的字段,比如把实体类直接当DTO用了,加了一些ES中并不存在的无关字段,此时可以标记此字段,以便让EE框架跳过此字段,对此字段不处理.

  2. 字段的更新策略,比如在调用更新接口时,实体类的字段非Null或者非空字符串时才更新,此时可以加字段注解,对指定字段标记更新策略.

  3. 需要对类型为text或keyword_tex字段聚合时,可指定其fieldData=true,否则es会报错.

  4. 对指定字段进行自定义命名,比如该字段在es中叫wu-la,但在实体model中叫ula,此时可以在value中指定value="wu-la".

  5. 在自动托管索引模式下,可指定索引分词器及索引字段类型.

  6. 在自动托管索引模式下,可指定索引中日期的format格式.

  7. 可指定创建索引时,自动对指定字段添加忽略大小写的配置.

  8. 可指定稠密向量类型,支持向量检索.

  9. 可指定复制字段,复制某个字段至指定字段,支持将多个字段值复制到同一字段,然后就可以只用一个字段即可实现多字段检索,在某些场景下可提升查询性能.

  10. ...

使用示例:

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;
    
    // 场景七:支持指定字段在es的索引中忽略大小写,以便在term查询时不区分大小写,仅对keyword类型字段生效,es的规则,并非框架限制.
    @IndexField(fieldType = FieldType.KEYWORD, ignoreCase = true)
    private String caseTest;
    
    // 场景八:支持稠密向量类型 稠密向量类型,dims必须同时指定,非负 最大为2048
    @IndexField(fieldType = FieldType.DENSE_VECTOR, dims = 3)
    private double[] vector;
    
    // 场景九:支持复制字段,复制当前字段至指定字段,支持将多个字段值复制到同一字段,与原生用法一致
    @IndexField(copyTo = "creator") 
    private String copiedField;
}

属性

类型

必须指定

默认值

描述

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

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

ignoreCase

boolean

否

false

keyword类型字段是否忽略大小写

ignoreAbove

int

否

256

字符串将被索引或存储的最大长度

scalingFactor

int

否

100

用于指定浮点数字段的缩放因子,scaled_float类型字段必须指定此参数,否则es创建索引报错

denseVector

String

否

""

稠密向量类型,可用于向量检索

dims

int

否

-1

向量的维度大小,不能超过2048 且非负

copyTo

String[]

否

{}

复制字段,可将当前字段复制到多个指定字段

@HighLight(opens new window)

  • 描述:高亮注解

  • 使用位置:实体类中需要高亮的被查询字段

  • 使用场景举例:比如输入关键词"老汉"进行查询,期望内容中包含"老汉"的部分被展示为红色或加粗

属性

类型

必须指定

默认值

描述

mappingField

String

否

""

高亮内容映射字段的名称,比如我想把高亮内容"老汉"赋值到字段pushCar上,就可以指定此属性值为pushCar

fragmentSize

int

否

100

高亮字段截取长度,默认为100

numberOfFragments

int

否

-1

搜索返回的高亮片段数量,默认全部返回

preTag

String

否

< em >

高亮标签,高亮内容将处于preTag之后

postTag

String

否

< /em >

高亮标签,高亮内容将处于postTag之前

highLightType

HighLightTypeEnum

否

UNIFIED

高亮类型

requireFieldMatch

boolean

否

true

高亮内容是否需要与查询字段匹配,默认值为true,当为否时,命中内容中非查询字段包含高亮内容时也会被高亮

编写Mapper类

@Component
public interface ProductInfoMapper extends BaseEsMapper<ProductInfo>{
}

CRUD操作

CRUD索引

API介绍

// 1.根据当前mapper对应实体类信息及其注解配置生成索引信息 适用于大多数场景
Boolean createIndex();

// 2.根据当前mapper对应实体类信息及其注解配置生成索引信息 可指定索引名进行创建 适用于定时任务按日期创建索引场景
Boolean createIndex(String indexName);

// 3.根据自定义条件创建索引
Boolean createIndex(Wrapper<T> wrapper);

创建索引一共提供了上述三种方式,使用难度: 方式1 <= 方式2 < 方式3 , 灵活度 方式3 > 方式2 >= 方式1

使用案例:

    /**
     * 方式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);
    }
    
    /**
     * 方式3 变体,使用难度最高,但灵活性也最高
     */
    @Test
    public void testCreateIndexByMap() {
        // 演示通过自定义map创建索引,最为灵活 可支持es本身能支持的所有索引场景
        LambdaEsIndexWrapper<Document> wrapper = new LambdaEsIndexWrapper<>();
        wrapper.indexName(Document.class.getSimpleName().toLowerCase());
        wrapper.settings(3, 2);
        Map<String, Object> map = new HashMap<>();
        Map<String, Object> prop = new HashMap<>();
        Map<String, String> field = new HashMap<>();
        field.put("type", FieldType.KEYWORD.getType());
        prop.put("this_is_field", field);
        map.put("properties", prop);
        wrapper.mapping(map);
        boolean isOk = documentMapper.createIndex(wrapper);
        Assertions.assertTrue(isOk);
    }

查询索引

API介绍

// 是否存在索引
Boolean existsIndex(String indexName);

// 获取当前mapper对应索引信息
GetIndexResponse getIndex();

// 获取指定索引信息
GetIndexResponse getIndex(String indexName);

使用案例:

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

更新索引

API介绍

// 根据条件更新索引
Boolean updateIndex(Wrapper<T> wrapper);

使用案例:

    /**
     * 更新索引
     */
    @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);
    }

删除索引

API介绍

// 删除指定索引 支持同时删多个索引,谨慎操作,后果自负,删除索引后数据也会被一起删除,类似Mysql中的删库跑路...
Boolean deleteIndex(String... indexNames);

使用案例:

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

更新索引

提示

ES中数据的写入是近实时的,并不像传统关系型数据库,ES的数据写入后存在一定的延迟,写完立即查询很可能查不到,具体延迟多少是需要结合机器和ES配置共同决定的,ES从内存刷数据至磁盘是一个批处理,所以存在一定延迟,如果您对数据的实时性要求比较高, 我们推荐您参考配置章节配置数据刷新策略refresh-policy,当然您也可以通过调用下面提供的两个API,来实现刷新指定索引的数据,执行后数据将从内存load至磁盘.

API介绍

// 刷新当前mapper对应索引, 返回刷新成功分片数
Integer refresh();

// 刷新指定索引列表, 返回总刷新成功分片数
Integer refresh(String... indexNames);

使用案例:

    @Test
    public void testRefresh() {
        // 刷新当前mapper对应索引
        int successShardsCount = documentMapper.refresh();
    }

CRUD文档数据

Insert

// 插入一条记录,默认插入至当前mapper对应的索引
Integer insert(T entity);
// 插入一条记录 可指定具体插入的路由
Integer insert(String routing, T entity);
// 父子类型 插入一条记录 可指定路由, 父id
Integer insert(String routing, String parentId, T entity);
// 插入数据 可指定具体插入的索引,多个用逗号隔开
Integer insert(T entity, String... indexNames);
// 插入数据,可指定路由及多索引插入
Integer insert(String routing, T entity, String... indexNames);
// 父子类型 插入数据,可指定路由,父id及多索引插入
Integer insert(String routing, String parentId, T entity, String... indexNames);

// 批量插入多条记录
Integer insertBatch(Collection<T> entityList)
// 批量插入 可指定路由
Integer insertBatch(String routing, Collection<T> entityList);
// 父子类型 批量插入 可指定路由, 父id
Integer insertBatch(String routing, String parentId, Collection<T> entityList);

// 批量插入多条记录 可指定具体插入的索引,多个用逗号隔开 
Integer insertBatch(Collection<T> entityList, String... indexNames);
// 批量插入 可指定路由及多索引
Integer insertBatch(String routing, Collection<T> entityList, String... indexNames);
// 父子类型 批量插入 可指定路由,父id及多索引
Integer insertBatch(String routing, String parentId, Collection<T> entityList, String... indexNames);
#参数说明

类型

参数名

描述

String

routing

路由

String

indexNames

索引列表

T

entity

实体对象

Collection<T>

entityList

实体对象集合

#Delete

// 根据 ID 删除
Integer deleteById(Serializable id);
// 根据 ID 删除 可指定路由
Integer deleteById(String routing, Serializable id);
// 根据 ID 删除 可指定具体的索引,多个用逗号隔开 
Integer deleteById(Serializable id, String... indexNames);
// 根据 ID 删除 可指定路由及多索引
Integer deleteById(String routing, Serializable id, String... indexNames);

// 根据 entity 条件,删除记录
Integer delete(LambdaEsQueryWrapper<T> wrapper);

// 删除(根据ID 批量删除)
Integer deleteBatchIds(Collection<? extends Serializable> idList);
// 删除(根据ID 批量删除)可指定路由
Integer deleteBatchIds(String routing, Collection<? extends Serializable> idList);
// 删除(根据ID 批量删除)可指定具体的索引,多个用逗号隔开 
Integer deleteBatchIds(Collection<? extends Serializable> idList, String... indexNames);
// 删除(根据ID 批量删除) 可指定路由及多索引
Integer deleteBatchIds(String routing, Collection<? extends Serializable> idList, String... indexNames);

#参数说明

类型

参数名

描述

String

routing

路由

String

indexNames

索引列表

Wrapper<T>

queryWrapper

实体包装类 QueryWrapper

Serializable

id

主键ID

Collection<? extends Serializable>

idList

主键ID列表

#Update

//根据 ID 更新
Integer updateById(T entity);
// 根据 ID 更新 可指定路由
Integer updateById(String routing, T entity);
// 根据 ID 更新 可指定具体的索引,多个用逗号隔开 
Integer updateById(T entity, String... indexNames);
// 根据 ID 更新 可指定路由和多索引
Integer updateById(String routing, T entity, String... indexNames);


// 根据ID 批量更新
Integer updateBatchByIds(Collection<T> entityList);
// 根据ID 批量更新 可指定路由
Integer updateBatchByIds(String routing, Collection<T> entityList);

//根据 ID 批量更新 可指定具体的索引,多个用逗号隔开 
Integer updateBatchByIds(Collection<T> entityList, String... indexNames);
// 根据ID 批量更新 可指定路由及多索引
Integer updateBatchByIds(String routing, Collection<T> entityList, String... indexNames);

// 根据动态条件 更新记录
Integer update(T entity, LambdaEsUpdateWrapper<T> updateWrapper);
#参数说明

类型

参数名

描述

String

routing

路由

String

indexNames

索引列表

T

entity

实体对象

Wrapper<T>

updateWrapper

实体对象封装操作类 UpdateWrapper

Collection<T>

entityList

实体对象集合

#Select

	// 获取总数
    Long selectCount(LambdaEsQueryWrapper<T> wrapper);
    // 获取总数 distinct为是否去重 若为ture则必须在wrapper中指定去重字段
    Long selectCount(Wrapper<T> wrapper, boolean distinct);
    
 	// 根据 ID 查询 
    T selectById(Serializable id);
    // 根据 ID 查询 可指定路由
    T selectById(String routing, Serializable id);

    // 根据 ID 查询 可指定具体的索引,多个用逗号隔开 
    T selectById(Serializable id, String... indexNames);
    // 根据 ID 查询 可指定路由及多索引
    T selectById(String routing, Serializable id, String... indexNames);

    // 查询(根据ID 批量查询)
    List<T> selectBatchIds(Collection<? extends Serializable> idList);
    // 查询(根据ID 批量查询) 可指定路由
    List<T> selectBatchIds(String routing, Collection<? extends Serializable> idList);

    // 查询(根据ID 批量查询)可指定具体的索引,多个用逗号隔开 
    List<T> selectBatchIds(Collection<? extends Serializable> idList, String... indexNames);
    // 查询(根据ID 批量查询) 可指定路由及多索引
    List<T> selectBatchIds(String routing, Collection<? extends Serializable> idList, String... indexNames);

    // 根据动态查询条件,查询一条记录 若存在多条记录 会报错
    T selectOne(LambdaEsQueryWrapper<T> wrapper);
    // 根据动态查询条件,查询全部记录
    List<T> selectList(LambdaEsQueryWrapper<T> wrapper);
#参数说明

类型

参数名

描述

String

routing

路由

String

indexNames

索引列表

Wrapper<T>

queryWrapper

实体包装类 QueryWrapper

Serializable

id

主键ID

Collection<? extends Serializable>

idList

主键ID列表