官方文档

简介 | 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列表