java spring项目整合ES-简单易懂篇


看了好久的官方文档和各类博文,大多数都是几年前的老版本,很多都是弃用了的方法,再不就是复杂且不明觉厉,无力吐槽,故记录下自己的实现全过程,仅供新手参考借鉴,可通过目录选择性查看。

一、 本机装 elasticsearch 和 kibana 、ik分词器

下载安装方式

1. 官网下载
  • 全版本和操作系统的 elasticsearch和 kibana 任君挑选 传送门

  • 全版本 analysis-ik 中文分词器,Github传送门

    展开如下图,选择自己操作系统下需要的版本,解压到自定义目录就能用,免安装。

ES
Kibana

注意:

  1. elasticsearch 和 kibana 的下载版本必须保持一致
  2. 如果公司有配置,最好和公司使用的版本一致
  3. 从Github下载的 ik 分词器需要先用maven指令编译成jar包,步骤可参考博文【Analysis-ik 中文分词安装】 传送门
2. 网盘下载 v 7.10.1

链接:https://pan.baidu.com/s/14KxHzno7mNOD-vyM3-bDeQ
提取码:tksb

提供 elasticsearchkibanaAnalysis-ik 的v-7.10.1免安装版,下载解压后将 Analysis-ik文件包放到 **\elasticsearch-7.10.1\plugins 目录下然后重启 elasticsearch

运行

  1. 依次进到 ES 和 Kibana 的 bin 文件夹双击 elasticsearch.batkibana.bat
  2. 访问默认的本地地址:
    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进行操作
  1. 先汉化操作界面,到安装目录下 config 配置文件夹 打开 kibana.yml 在最后一行添加下面的代码
 i18n.locale: "zh-CN"

部分参数说明

### 这里配置的是将要访问的ES地址,默认本地9200
#elasticsearch.hosts: ["http://localhost:9200"] 

### 这里配置的是界面语言, "zh-CN" 是中文
#i18n.locale: "en" 
  1. 直接访问Kibana的默认地址 localhost:5601 第一次使用需要创建你的第一条索引

图解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 分词的使用
GET _analyze	//最少切分
{
  "analyzer": "ik_smart",
  "text": "中国共产党"
}


GET _analyze 	//最细力度划分,穷尽词库的可能
{
  "analyzer": "ik_max_word",
  "text":"中国共产党"
}

配置自定义词典

  1. 在 ik 的 config 配置文件中新增 my.dic 用文本编辑输入自定义词语
  2. 在同目录的 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,
主要调用了 ElasticsearchRestTemplatesave( 数据,索引 ) 方法

@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 + 查询的索引)

不足之处欢迎大佬指点一二 感谢!

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值