Elasticsearch 学习笔记
1 概念
1.1 关于?Elasticsearch
Elasticsearch是一个开源的分布式搜索和分析引擎,它构建在Apache Lucene库之上,并提供了RESTful API以进行快速、实时的数据检索、分析和存储。它被广泛用于构建全文搜索、日志分析、实时数据分析等应用。
之所以可以高效处理搜索,会将数据进行分词处理,将一个文本拆分为若干个文本,在进行搜索时,会检查是否匹配“拆出来的”某一个文本
1.2?相关概念
索引(index/indeices):相当于 MySql 中的Database
类型(type/types):相当于 MySql 的 Table ,如果不关心类型就用 _doc表示类型,在高版本中已经废弃这个概念
文档(document/documents):相当于MySql 中各数据表的一行行数据,由JSON格式组织
字段(field/fileds):相当于MySql的Column(列)
1.3 数据类型
在Elasticsearch 中字段也是有数据类型的,大致为:
- 字符串:text、keyword
- text 类型在处理时,默认被分词
- keyword 类型在被处理,默认不会分词
- 数值:byte、sort、integer、float、double等
- 布尔:boolean
- 日期:data
2 环境配置
2.1?安装
注:父级文件夹不能有中文和空格
2.2 运行
执行 bin 目录下的服务?elasticsearch.bat
在终端中执行命令
注:在使用的过程中不能关闭窗口,关闭窗口就停止服务,不能开机自启。
3 测试是否运行成功
默认运行端口 9200
3.1 浏览器中测试
3.2?idea中测试 http 请求
4?测试分词效果
### 测试运行 GET http://localhost:9200 ### 测试分词 -- 英文 POST http://localhost:9200/_analyze Content-Type: application/json { "text" : "nice to meet you!" } ### 测试分词 -- 中文 POST http://localhost:9200/_analyze Content-Type: application/json { "text" : "今天吃什么!" }
4.1 英文效果
4.2 中文效果
4.3?解决中文分词效果
4.3.1?添加分词插件
可以在Elasticsearch中添加新的分词插件来解决以上问题,经典的中文分词插件就是ik分词器。
下载地址:
https://robinliu.3322.org:8888/download/elasticsearch-analysis-ik-7.6.2.ziphttps://robinliu.3322.org:8888/download/elasticsearch-analysis-ik-7.6.2.ziphttps://robinliu.3322.org:8888/download/elasticsearch-analysis-ik-7.6.2.zip将下载的压缩包解压到 plugins 文件夹中,重启生效!
添加? ?"analyzer": "ik_smart",
### 测试分词 -- 中文 GET http://localhost:9200/_analyze Content-Type: application/json { "analyzer": "ik_smart", "text" : "很高兴认识你!!" }
效果图:
4.3.2 自定义配置字典
自定义自己的字典 custom.dic
将自己的字典添加到 IKAnalyzer.cfg.xml 文件中
4.3.3 IK 分词器类型
一共有两款:对于分词的严格限制不同
- "analyzer": "ik_smart",
- "analyzer": "ik_max_word"
5 操作汇总
5.1 汇总
5.2?添加文档
5.2.1?使用ES随机生成的ID值
请求格式
使用 ES 随机生成的 ID 值
注:如果没有索引、类型会自动创建
POST http://localhost:9200/{索引名}/{类型}
### 添加文档,使用 ES 随机生成的 ID值 POST http://localhost:9200/index_crud/_doc Content-Type: application/json { "id": 1, "title": "亏本大甩卖!" }
5.2.2?自行指定ID值
PUT http://localhost:9200/{索引名}/{类型名}/{文档ID}
### 添加文档(自行指定ID值)
PUT http://localhost:9200/index_crud/_doc/Tea9527
Content-Type: application/json{
? "id": 999,
? "title": "2023老茶上市,卖得也相当不错",
? "sort": 199
}
5.3 查看文档
5.3.1 查某个索引的所有文档
请求格式
GET http://localhost:9200/{索引名}/_search
### 查看某个索引的所有文档 GET http://localhost:9200/index_crud/_search
5.3.2?查某个索引的基本信息
请求格式
GET http://localhost:9200/{索引名}
### 查看某个索引的基本信息 GET http://localhost:9200/index_crud
5.3.3?根据 id 查看文档
GET http://localhost:9200/{索引名}/{类型名}/{id值}
### 根据 id 查看文档 GET http://localhost:9200/index_crud/_doc/eDSqUIoBUtENzhwJDloo Accept: application/json
5.4 修改文档
5.4.1 完全替换修改
请求格式
PUT http://localhost:9200/{索引名}/{类型名}/{id值}
### 修改文档 PUT http://localhost:9200/index_crud/_doc/eDSqUIoBUtENzhwJDloo Content-Type: application/json { "id": 2, "title": "修改了!" }
5.4.2 局部修改
请求格式
POST http://localhost:9200/{索引名}/{类型名}/{id值}/_ipdate
### 局部修改 POST http://localhost:9200/index_crud/_doc/eDSqUIoBUtENzhwJDloo/_update Content-Type: application/json { "doc": { "title": "局部修改了!" } }
5.5 删除相关
5.5.1 根据 ID 删除文档
请求格式
注:如果不存在,响应 404 状态码
DELETE http://localhost:9200/{索引名}/{类型名}/{id值}
### 根据 ID 删除文档 DELETE http://localhost:9200/index_crud/_doc/eDSqUIoBUtENzhwJDloo
5.5.2 删除索引
请求格式
注:如果不存在,响应 404 状态码
DELETE http://localhost:9200/{索引名}
### 删除索引 DELETE http://localhost:9200/index_crud
5.6 自定义索引及其类型
5.6.1?概念
- 可以自定义数据类型,灵活使用,不会全部默认 text 和 long?
- 需要在 text 类型中添加?"analyzer": "ik_max_word" 进行中文分词处理
5.6.2?请求格式
### 自定义索引及其数据类型 PUT http://localhost:9200/{索引名} Content-Type: application/json { "mappings": { "properties": { "{字段名1}": { "type": "{类型名1}" ????????"analyzer": "ik_max_word" }, "{字段名2}": { "type": "{类型名3}" } ???????........ } } }
5.6.3?代码实例
### 自定义索引及其数据类型 PUT http://localhost:9200/index_search Content-Type: application/json { "mappings": { "properties": { "id": { "type": "long" }, "title": { "type": "text", "analyzer": "ik_max_word" }, "description": { "type": "keyword" }, "sort": { "type": "long" }, "price": { "type": "long" } } } }
### 添加文档--测试数据1
PUT http://localhost:9200/index_search/_doc/1
Content-Type: application/json{
? "id": 1,
? "title": "散装龙井",
? "description": "好喝又实惠",
? "sort": 160,
? "price": 300
}### 添加文档--测试数据2
PUT http://localhost:9200/index_search/_doc/2
Content-Type: application/json{
? "id": 2,
? "title": "大红袍大礼包",
? "description": "性价比之王",
? "sort": 190,
? "price": 688
}### 添加文档--测试数据3
PUT http://localhost:9200/index_search/_doc/3
Content-Type: application/json{
? "id": 3,
? "title": "龙井礼盒",
? "description": "送礼必备",
? "sort": 130,
? "price": 888
}### 添加文档--测试数据4
PUT http://localhost:9200/index_search/_doc/4
Content-Type: application/json{
? "id": 4,
? "title": "精装陈年老普洱",
? "description": "收藏珍品",
? "sort": 150,
? "price": 988
}### 添加文档--测试数据5
PUT http://localhost:9200/index_search/_doc/5
Content-Type: application/json{
? "id": 5,
? "title": "精装铁观音大礼包",
? "description": "家中常备",
? "sort": 160,
? "price": 700
}
5.7 简单搜索
5.7.1 概念
"_score": 2.005562, 对搜索的结果进行评分,关系越大分越高,例如“精装”找到“散装”,看起来不相干,但是有点联系也会被搜索到
5.7.2?请求格式
GET http://localhost:9200/{索引名}/_search?q={字段名:查询关键字}
5.7.3?实例
### 根据关键字执行简单的搜索
GET http://localhost:9200/index_search/_search?q=title:精装
5.8 高级搜索
5.8.1 单条件搜索
自定义query,在query属性下配置match属性,在match属性下再配置搜索的字段名与关键字
###?单条件搜索
GET http://localhost:9200/index_search/_search Content-Type: application/json { "query": { "match": { "title": "龙井" } } }
5.8.2?多条件搜索
多条件搜索:自定义query,在query属性下配置bool属性,在bool属性下再配置must,must的值是数组,数组元素就是匹配规则,除了must以外,还可以是should 和?must_not
- must下的多个规则必须同时匹配才能被搜索到
- should只需要匹配其中任意1个条件即可
- must_not,即不允许是某种规则
must >>> AND
should >>> OR
must_not >>> !=
###?多条件搜索
GET http://localhost:9200/index_search/_search
Content-Type: application/json{
? "query": {
? ? "bool": {
? ? ? "should": [
? ? ? ? {
? ? ? ? ? "match": {
? ? ? ? ? ? "title": "龙井"
? ? ? ? ? }
? ? ? ? },
? ? ? ? {
? ? ? ? ? "match": {
? ? ? ? ? ? "description": "送礼必备"
? ? ? ? ? }
? ? ? ? }
? ? ? ]
? ? }
? }
}
5.8.3?指定字段搜索
在query同级配置_source,此属性的值是数组,用于指定字段列表
### 指定字段搜索
GET http://localhost:9200/index_search/_search
Content-Type: application/json{
? "query": {
? ? "match_all": {}
? },
? "_source": [
? ? "id",
? ? "title",
? ? "price"
? ]
}
5.8.4?指定排序搜索
指定排序,在query同级配置sort属性,在sort属性中配置排序规则,默认根据字段值升序排列,也可以配置order属性来指定规则,取值为desc时为降序
### 指定排序搜索
GET http://localhost:9200/index_search/_search
Content-Type: application/json{
? "query": {
? ? "match_all": {}
? },
? "sort": [
? ? {
? ? ? "sort": {},
? ? ? "id": {
? ? ? ? "order": "desc"
? ? ? }
? ? }
? ]
}
5.8.5?分页搜索
分页搜索,在query同级配置from和size属性
表示从第一条数据开始查3条
### 高级搜索
GET http://localhost:9200/index_search/_search
Content-Type: application/json{
? "query": {
? ? "match_all": {}
? },
? "sort": [
? ? {
? ? ? "id": {}
? ? }
? ],
? "from": 1,
? "size": 3
}
5.8.6?高亮搜索
- 高亮显示,在query同级配置highlight属性进行配置,在highlight中配置fields属性,可以指定对应的字段
- 执行搜索后,匹配的结果中会出现同级的highlight数据,其中搜索的关键字默认会被<em>标签框住,后续在客户端拿到此结果后可以对<em>标签设计CSS样式
- 或者,在配置高亮搜索时,还可以在highlight标签下配置pre_tags和post_tags属性,以替换<em>和</em>标签
###?高亮搜索
GET http://localhost:9200/index_search/_search
Content-Type: application/json{
? "query": {
? ? "match": {
? ? ? "title": "精装"
? ? }
? },
? "highlight": {
? ? "fields": {
? ? ? "title": {}
? ? },
? ? "pre_tags": "<font style='color: red;'>",
? ? "post_tags": "</font>"
? }
}
6 代码实现
6.1 添加依赖
<spring-boot.version>2.7.14</spring-boot.version><!-- Spring Boot支持Elasticsearch --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <version>${spring-boot.version}</version> </dependency>
6.2?实现流程
6.2.1 自定义数据访问接口
继承第一个的 Repository<T,ID>
继承 CrudRepository?
package com.example.adminarea.dao.search; import com.example.adminarea.pojo.vo.RoomSearchVO; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository public interface RoomSearchRepository extends CrudRepository<RoomSearchVO,Long> { }
6.2.2?定义文档数据的Java类
使用ES时的Java类可以完全自由设计,只需要包含显示在“搜索结果中的属性(最终显示在客户端界面中的属性)”及“执行搜索时需要匹配的属性”即可。
注:与ES中的文档对应的Java类,并不需要与实体类、用于Redis的相关类保持一致!
- 索引名必须小写
- 时间必须格式,不然会查询不了
package com.example.adminarea.pojo.vo; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.io.Serializable; import java.time.LocalDateTime; @Data @Document(indexName = "room") // 索引名 public class RoomSearchVO implements Serializable { @Id private Long id; // 中文分词 @Field(type = FieldType.Text,analyzer = "ik_smart") private String name; private Integer floor; private Long roomTypeId; private Integer isBooked; private Integer isLive; @Field(type = FieldType.Date,format = {},pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @Field(type = FieldType.Date,format = {},pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; }
6.2.3 相关操作 API
6.2.4?操作测试
package com.example.adminarea.RoomSearchRepositoryTest; import com.example.adminarea.dao.search.RoomSearchRepository; import com.example.adminarea.pojo.vo.RoomSearchVO; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.time.LocalDateTime; import java.util.Optional; @SpringBootTest public class RoomSearchRepositoryTest { @Autowired RoomSearchRepository repository; @Test void xxx() { RoomSearchVO roomSearchVO = new RoomSearchVO(); roomSearchVO.setId(1L); roomSearchVO.setName("分词测试房间-1"); roomSearchVO.setFloor(1); roomSearchVO.setRoomTypeId(1L); roomSearchVO.setCreateTime(LocalDateTime.now()); roomSearchVO.setUpdateTime(LocalDateTime.now()); RoomSearchVO save = repository.save(roomSearchVO); System.out.println("写入完成 = " + save); } @Test void findById() { Optional<RoomSearchVO> optional = repository.findById(1L); RoomSearchVO roomSearchVO = optional.get(); System.out.println("roomSearchVO = " + roomSearchVO); } }
6.2.5 实现搜索
继承 ElasticsearchRepository 接口,其他的操作不受影响
package com.example.adminarea.dao.search; import com.example.adminarea.pojo.vo.RoomSearchVO; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface RoomSearchRepository extends ElasticsearchRepository<RoomSearchVO,Long> { List<RoomSearchVO> queryByName(String name); }
测试
6.2.6 自定义搜索条件
6.2.6.1 添加方法
6.2.6.2?测试方法
6.2.7 分页搜索
6.2.7.1 添加方法
6.7.1.2 测试方法
@Test void queryByName() { String name = "测试"; // 0 是 第一页 Integer pageNum = 0; Integer pageSize = 3; // 创建一个排序规则,按照 createTime 字段降序排序 Sort sort = Sort.by(Sort.Direction.DESC, "createTime"); PageRequest pageRequest = PageRequest.of(pageNum,pageSize,sort); SearchPage<RoomSearchVO> searchPage = repository.queryByName(name,pageRequest); System.out.println("分页数据 = " + searchPage); System.out.println("------------------------------------"); SearchHits<RoomSearchVO> searchHits = searchPage.getSearchHits(); System.out.println("searchHits = " + searchHits); System.out.println("------------------------------------"); List<SearchHit<RoomSearchVO>> hits = searchHits.getSearchHits(); for (SearchHit<RoomSearchVO> hit : hits) { System.out.println(hit); RoomSearchVO roomSearchVO = hit.getContent(); System.out.println(roomSearchVO); } }
6.2.8?高亮分页搜索
6.2.8.1 添加注解实现
// 高亮分页搜索 @Highlight(fields = {@HighlightField(name = "name")}, parameters = @HighlightParameters( preTags = "<font style='color: red;'>", postTags = "</font>")) SearchPage<RoomSearchVO> queryByName(String name, Pageable pageable);
6.2.8.2?测试方法
结果如下
6.3 项目实现流程
6.3.1 读取并写入 ES
6.3.1.1 读取数据
在 mapper 中定义方法
在 xml 中实现 mapper 接口
在 repository 中调用 mapper 接口
6.3.1.2?写入数据
- 在 service 中调用 repository 接口
- 自动装配
- 实现接口方法
注:如果数据过多,应该分批读取写入,采用 do-while 循环
- 采用计划任务进行自动构建、热加载
-
@EnableScheduling 添加这个注解开启计划任务
package com.example.adminarea.preload; import com.example.adminarea.service.RoomService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Slf4j @Component public class RoomSearchSchedule { @Autowired RoomService service; // fixedRate执行频率,以上一次开始 // fixedDelay执行间隔,以下一次开始 // cron表达式 <秒> <分钟> <小时> <日期> <月份> <星期> @Scheduled(fixedDelay = 60 * 60 * 1000) public void rebuildSearch() { log.info("重新构建ES搜索数据"); service.rebuildSearch(); } }
- 最终在 controller 中调用方法实现效果
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!