java spring项目整合ES-简单易懂篇
一:提供 v- 7.10.1 的ElasticSearch、Kabala、ik分词器的下载/安装/使用教程;二:提供在spring项目中引入ElasticSearch的教程
前言
看了好久的官方文档和各类博文,大多数都是几年前的老版本,很多都是弃用了的方法,再不就是复杂且不明觉厉,无力吐槽,故记录下自己的实现全过程,仅供新手参考借鉴,可通过目录选择性查看。
一、 本机装 elasticsearch 和 kibana 、ik分词器
下载安装方式
1. 官网下载
-
全版本和操作系统的 elasticsearch和 kibana 任君挑选 传送门
-
全版本 analysis-ik 中文分词器,Github传送门
展开如下图,选择自己操作系统下需要的版本,解压到自定义目录就能用,免安装。
注意:
- elasticsearch 和 kibana 的下载版本必须保持一致
- 如果公司有配置,最好和公司使用的版本一致
- 从Github下载的 ik 分词器需要先用maven指令编译成jar包,步骤可参考博文【Analysis-ik 中文分词安装】 传送门
2. 网盘下载 v 7.10.1
链接:https://pan.baidu.com/s/14KxHzno7mNOD-vyM3-bDeQ
提取码:tksb
提供 elasticsearch
、kibana
、Analysis-ik
的v-7.10.1免安装版,下载解压后将 Analysis-ik
文件包放到 **\elasticsearch-7.10.1\plugins 目录下然后重启 elasticsearch
运行
- 依次进到 ES 和 Kibana 的 bin 文件夹双击
elasticsearch.bat
和kibana.bat
- 访问默认的本地地址:
elasticsearch -> localhost:9200
Kibana -> localhost:5601
访问
在ES进行操作
想ES的浏览器页面想要进行操作是需要插件 elasticsearch-head
方法有两种:
1:是在本地装 elasticsearch-head ,那么就需要先装 nodejs
然后还得在本地运行,且只能访问本地的ES,所以推荐下面这种方法 。
2:谷歌浏览器装 ES-head 插件,谷歌上的head插件可以访问本地和远端的ES,更方便我们进行项目的开发和检测。
操作如下:
- 从这个地址直接下载压缩包 : 传送门
- 解压
- 在谷歌浏览器中点击“加载已解压的压缩程序”,找到elasticsearch-head文件夹,点击打开即完成安装。
- 该方法借鉴自 @小昭码代码 的博文:谷歌浏览器安装es-head插件
图解
装好插件后,点开第一步的扩展程序,点击 ElasticSearch Head 就能直接进入ES操作页面,输入地址链接目标ES即可。
在Kibana进行操作
- 先汉化操作界面,到安装目录下 config 配置文件夹 打开 kibana.yml 在最后一行添加下面的代码
i18n.locale: "zh-CN"
部分参数说明
### 这里配置的是将要访问的ES地址,默认本地9200
#elasticsearch.hosts: ["http://localhost:9200"]
### 这里配置的是界面语言, "zh-CN" 是中文
#i18n.locale: "en"
- 直接访问Kibana的默认地址
localhost:5601
第一次使用需要创建你的第一条索引
图解
- 分词的使用
GET _analyze //最少切分
{
"analyzer": "ik_smart",
"text": "中国共产党"
}
GET _analyze //最细力度划分,穷尽词库的可能
{
"analyzer": "ik_max_word",
"text":"中国共产党"
}
配置自定义词典
- 在 ik 的 config 配置文件中新增
my.dic
用文本编辑输入自定义词语- 在同目录的 IKAnalyzer.cfg.xml 中添加
my.dic
<entry key="ext_dict">my.dic</entry>
二、 JAVA 项目中添加 ES 的相关操作
简述
首先需要知道 Spring 是有封装 ElasticSearch 的,所以我们可以在 Spring 的官网查看官方的使用说明,要学习查官方文档。
pom.xml
添加spring boot data 对 ES 封装的 jar 就行了 别整那么多有的没的用不上的玩意儿
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
application.yml
很简单的配置,主要是需要链接的ES地址,index 中的信息是在后面业务代码中存数据时创建索引用到的,不是必要配置。
spring:
elasticsearch:
rest:
## ES 的地址
uris: localhost:9200
index:
## 索引前缀
prefix: endpoint-authmanager
length: 7
存数据
这里是通过 function 消费 MQ 中的日志,存储到 ES,
主要调用了 ElasticsearchRestTemplate
的 save( 数据,索引 )
方法
@Service
public class EndpointUserLogService {
private Logger logger = LoggerFactory.getLogger(EndpointUserLogService.class);
//这个类是用来生成索引的,原理是用yml中的索引前缀+当前时间前7位生成
@Autowired
private IndexNameProperties indexNameProperties;
//这个是spring boot data 提供的工具类
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 此处用ElasticsearchOperations操作
* 用Repository<T>.save时触发的refresh无法动态indexName
* 且多线程时存在安全问题
* 且Repository<T>.findBy需要QueryParser.escape转义特殊符号
*/
@Bean
private Consumer<EndpointUserLog> fetch() {
return userLog -> {
String indexName = indexNameProperties.getIndexName(userLog);
elasticsearchRestTemplate.save(userLog, IndexCoordinates.of(indexName));
logger.info(indexName);
};
}
}
查数据
ElasticsearchRestTemplate 中封装了很多查询方法,大致理解:
//指定索引的单个 ID 查询,通过传参指定接收泛型的封装类类型
<T> T get(String id, Class<T> clazz, IndexCoordinates index)
//指定索引的多个 ID 查询
<T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index)
//复杂查询,通过 Query 及其子类方法完成查询条件的组合, 返回已经封装好的可分页、排序的 SearchHits<T> 对象
<T> org.springframework.data.elasticsearch.core.SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index)
查询方法的调用很好理解,这里关键是如何通过Query
来满足自己的查询条件,如下:
@RestController
public class LogController {
@Autowired
ElasticsearchRestTemplate restTemplate;
@Value("${spring.elasticsearch.index.prefix}")
private String index;
/**
* id查询
* @param id
* @author luce
* @date 2021/6/21 16:24
*/
@ApiOperation(value = "id查询")
@ApiImplicitParam(name = "date", value = "日期", example = "2021-07")
@GetMapping("/log/{id}")
public RestInfo<?> findOne(@PathVariable("id") String id, @RequestParam("date") String date) throws IOException {
//指定从索引为2021-06的文件中查询
SysLog log = restTemplate.get(id, SysLog.class, IndexCoordinates.of(2021-06));
return RestUtil.setSuccessMsg("查询成功", log);
}
/**
* 全局关键字查询
* @author luce
* @date 2021/7/6 15:38
*/
@ApiOperation(value = "全局关键字查询")
@GetMapping("/log/singleWord")
public RestInfo<?> singleTitle(LogQueryDto logQueryDto) {
//queryStringQuery(字符窜):查询出匹配的记录,根据匹配度排序
Query query = new NativeSearchQueryBuilder().withQuery(queryStringQuery(logQueryDto.getValue()))
//分页条件
.withPageable(PageRequest.of(logQueryDto.getPageNum(), logQueryDto.getPageSize()))
.build();
SearchHits<SysLog> log = restTemplate.search(query, SysLog.class, IndexCoordinates.of(2021-06));
return RestUtil.setSuccessMsg("查询成功", log);
}
/**
* 指定字段查询
* @param dto
* @author luce
* @date 2021/7/6 16:23
*/
@ApiOperation(value = "指定字段查询")
@GetMapping("/log/find/field")
public RestInfo<?> findByField(LogQueryDto dto) {
//termsQuery(字段名,多个字段值),按匹配度排序
Query searchQuery = new NativeSearchQueryBuilder().withQuery(termsQuery(dto.getFieldName(), dto.getFieldValues()))
.withPageable(PageRequest.of(dto.getPageNum() - 1, dto.getPageSize()))
.build();
SearchHits<SysLog> log = restTemplate.search(searchQuery, SysLog.class, IndexCoordinates.of(2021-06));
return RestUtil.setSuccessMsg("查询成功", log);
}
/**
* 指定时间查询
* @param dto
* @author luce
* @date 2021/7/8 13:20
*/
@ApiOperation(value = "指定时间查询")
@GetMapping("/log/find/time")
public RestInfo<?> findByTime(LogQueryDto dto) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//rangeQuery(查询字段):范围查询,在方法后面接查询范围
//时间范围 .from(从什么时间开始).to(到什么时间为止)
//数值范围 .from(开始不包含, false).to(结束不包含, false)
//数值范围 .gt(大于).gte(大于等于).lt(小于).lte(小于等于)
Query searchQuery = new NativeSearchQueryBuilder().withQuery(rangeQuery("beginTime")
.from(sdf.format(dto.getBeginTime()))
.to(sdf.format(dto.getEndTime())))
.withPageable(PageRequest.of(dto.getPageNum() - 1, dto.getPageSize()))
.build();
//通过2021*指定查询范围为所有2021开头的索引
SearchHits<SysLog> log = restTemplate.search(searchQuery, SysLog.class, IndexCoordinates.of(indexNameProperties.getIndexName("2021*")));
return RestUtil.setSuccessMsg("查询成功", log);
}
/**
* 多条件组合查询
* @param dto
* @author luce
* @date 2021/7/8 15:15
*/
@ApiOperation(value = "组合查询", notes = "时间段 + 关键字")
@GetMapping("/log/find")
public RestInfo<?> find(LogQueryDto dto) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
//boolQuery():组合查询 后面接子查询
// .must(QueryBuilder) 相当于 与 & =
// .must not(QueryBuilder) 相当于 非 ~ !=
// .should(QueryBuilder) 相当于 或 | or
// .filter(QueryBuilder) 过滤
Query searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery()
.must(rangeQuery("beginTime")
.from(format.format(dto.getBeginTime()))
.to(format.format(dto.getEndTime())))
// .should(matchQuery(dto.getFieldName(), dto.getFieldValue()))
.must(queryStringQuery(dto.getWord()))
)
.withPageable(PageRequest.of(dto.getPageNum() - 1, dto.getPageSize()))
.build();
SearchHits<SysLog> log = restTemplate.search(searchQuery, SysLog.class, IndexCoordinates.of(indexNameProperties.getIndexName("2021*")));
return RestUtil.setSuccessMsg("查询成功", log);
}
}
其中
SearchHits<T>
是官方提供的已分页封装类,只需要的查询时传一个 Pageable
子类实例对象就行。
SysLog.class
是自己定义的数据接收封装类,只需要注意字段名称和ES中的保持一致就行。ES 中字段的数据类型为 json 的可以用 Object 接收 如下:
java封装类中的字段
@ApiModelProperty(value = "结束时间")
private String endTime;
@ApiModelProperty(value = "开始时间")
private String beginTime;
@ApiModelProperty(value = "请求成功否")
private Boolean success;
@ApiModelProperty(value = "结果")
private Object result;
@ApiModelProperty(value = "IP")
private String ip;
ES中的字段
"beginTime": "2021-07-06T15:20:28.124",
"endTime": "2021-07-06T15:20:28.143",
"success": true,
"result": {
"success": true,
"message": "添加成功",
"code": 200,
"timestamp": 1625556028143,
"result": 1
},
"ip": "0:0:0:0:0:0:0:1"
所有方法的传参大致可以理解为.xxx( Query 查询语句 + 接收数据的封装类.class + 查询的索引)
不足之处欢迎大佬指点一二 感谢!
更多推荐
所有评论(0)