SpringBoot2.x整合Elasticsearch
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<!-- 注意看下自己的elasticseatch的版本哦 -->
<!-- SpringBoot2.1.5,默认的elasticsearch版本6.4.3 -->
<!-- <version>${elasticsearch.version}</version> -->
</dependency>
yml
spring:
data:
elasticsearch:
cluster-name: itoyoung_es
cluster-nodes: 127.0.0.1:9300,127.0.0.1:9301,127.0.0.1:9302
启动类配置
@SpringBootApplication
public class NalaErpApplication {
public static void main(String[] args) {
/**
* Springboot整合Elasticsearch 在项目启动前设置一下的属性,防止报错
* 解决netty冲突后初始化client时抛出异常
* java.lang.IllegalStateException: availableProcessors is already set to [4], rejecting [4]
*/
System.setProperty("es.set.netty.runtime.available.processors", "false");
SpringApplication.run(NalaErpApplication.class, args);
}
}
实际应用
- 创建实体类,设置es相关的注解参数,项目启动后会自动创建索引
@Data
@Accessors(chain = true)
// 文档
@Document(
// 索引
indexName = "user_index",
// 类型
type = "user_type_first",
// 分片,默认5,可按照节点数量1.5~3倍的原则来创建分片
shards = 5,
// 副本,默认1
replicas = 1,
// 是否项目启动就创建索引,默认true
// 当已经创建好索引但是又需要修改索引参数手动创建的时候,可以设置为false
createIndex = true
)
public class UserOfEs implements Serializable {
/*
指定es的id,字段类型需要和ElasticsearchRepository<UserOfEs, String>泛型保持一致
*/
@Id
private String id;
@Field(
// 字段类型,int类型,还有为long,float,double提供使用的类型
type = FieldType.Integer,
// 是否索引。默认true,即要进行索引,只有进行索引才可以从索引库搜索到。
// 但是也有一些内容不需要索引,比如:商品图片地址只被用来展示图片,
// 不进行搜索图片,此时可以将index设为false
index = true
)
private Integer uid;
@Field(
type = FieldType.Text,
// 索引和搜索时的分词器,细粒度拆分:ik_max_word,粗粒度: ik_smart
analyzer = "ik_max_word",
// 定义搜索时使用的分词器
// 对于ik分词器建议是索引时使用ik_max_word将搜索内容进行细粒度分词,
// 搜索时使用ik_smart提高搜索精确性
searchAnalyzer = "ik_smart",
// 是否在source之外存储,每个文档索引后会在 ES中保存一份原始文档,存放在"_source"中,
// 一般情况下不需要设置store为true,因为在_source中已经有一份原始文档了
store = false
)
private String name;
@Field(
// 关键字字段,通常搜索keyword是按照整体搜索
// 所以创建keyword字段的索引时是不进行分词的,比如:邮政编码、手机号码、身份证等
type = FieldType.Keyword
)
private String email;
@Field(
type = FieldType.Text
)
private String phone;
@Field(
type = FieldType.Text
)
private String salt;
@Field(
type = FieldType.Text,
index = false
)
private String password;
@Field(
type = FieldType.Keyword
)
private String type = "NORMAL";
@Field(
type = FieldType.Keyword
)
private String status = "Y";
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = this.email == null ? null : StringUtil.md5(this.phone);
}
}
-
Spring Data
- 创建Repository,注意ElasticsearchRepository的ID类型要和实体类中的id的类型保持一致
@Repository public interface UserEsRepository extends ElasticsearchRepository<UserOfEs, String> { }
- 简单应用
@RestController @RequestMapping("/userEs") public class UserEsController { @Resource private UserEsRepository userEsRepository; /** * 保存es数据,若是索引未创建还会自动创建索引 * * @param userOfEs * @return */ @PostMapping("/save") public Result save(UserOfEs userOfEs) { /* 提供了简单的CRUD方法,可分页查询 还可根据规则自定义方法 */ userEsRepository.save(userOfEs); return new Result().defaultSuccess(); } }
-
使用ElasticTemplate进行复杂操作
- 创建和删除索引
@RestController @RequestMapping("/userEs") public class UserEsController { @Resource private ElasticsearchTemplate elasticsearchTemplate; /** * 手动创建索引 * * @return */ @PostMapping("/createUserEsIndex") public Result createUserExIndex() { elasticsearchTemplate.createIndex(UserOfEs.class); elasticsearchTemplate.putMapping(UserOfEs.class); return new Result().defaultSuccess(); } /** * 根据索引名称删除索引 * * @param indexName * @return */ @PostMapping("/deleteIndex") public Result deleteIndex(String indexName) { elasticsearchTemplate.deleteIndex(indexName); // 根据索引类删除索引 // elasticsearchTemplate.deleteIndex(UserOfEs.class); return new Result().defaultSuccess(); } }
- 构造查询条件
// 多条件复杂查询 BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery() // 范围查询 .must(QueryBuilders.rangeQuery("num").gte(startNum).lte(endNum)) // 部分匹配的模糊查询,对value1分词后查询 .must(QueryBuilders.matchQuery("field1", "value1")) // 和match类似,但是match需要指定字段名,query_string是在所有字段中搜索,范围更广泛 .must(QueryBuilders.queryStringQuery("StringValue1")) // 单个词查询和match一样,多个词不同,不会对value2分词查询 // 例如文档内容"hello world"被分词为hello和world,如果value2="hello world" // 则查询不出来 .must(QueryBuilders.termQuery("field2", "value2")) .must(QueryBuilders.termsQuery("field3", list)) // 是否存在field4字段,因为后续如果索引中新增了字段field4 // 那么之前的数据集中是不会存在field4字段的 .must(QueryBuilders.existsQuery("field4")) // 会对输入做分词,但是需要结果中也包含所有的分词,而且顺序要求一样 // 如下,field5中需要包含 "value4 match" 才会被查询出 .must(QueryBuilders.matchPhraseQuery("field5", "value5 match")) // 前缀查询,根据value5前缀查询数据 .must(QueryBuilders.prefixQuery("field6", "value6"));
- 查询数据集
// 根据条件查询数据集 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() // 需要查询的索引type .withTypes("user_type_first") // 如果需要查询条件下的全量数据,则需要自动设置分页大小(0~count) // 如果不设置,es则会给你返回默认的分页数据 .withPageable(PageRequest.of(0, (int) elasticsearchTemplate.count(new NativeSearchQuery(queryBuilder), UserOfEs.class))) .withQuery(queryBuilder) .build(); List<UserOfEs> userOfEsList = elasticsearchTemplate.queryForList(searchQuery, UserOfEs.class);
- 聚合查询
NativeSearchQuery aggSearchQuery = new NativeSearchQueryBuilder() // 需要查询的索引type .withTypes("user_type_first") .withQuery(queryBuilder) // 将field1字段的值聚合后存储到密钥Field1中 .addAggregation(AggregationBuilders.sum("field1").field("myField1")) .addAggregation(AggregationBuilders.sum("field2").field("myField2")) .build(); // 聚合对象 Aggregations aggregations = elasticsearchTemplate.query(aggSearchQuery, SearchResponse::getAggregations); //从聚合对象中获取字段field1的数据 Sum fieldValue1 = aggregations.get("myField1"); double countValue1 = fieldValue1.getValue();
- 根据复杂条件删除数据
DeleteQuery deleteQuery = new DeleteQuery(); deleteQuery.setQuery(queryBuilder); elasticsearchTemplate.delete(deleteQuery, UserOfEs.class);