Elasticsearch 学习笔记

2023-12-13 09:21:30

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
  1. text 类型在处理时,默认被分词
  2. keyword 类型在被处理,默认不会分词
  • 数值:byte、sort、integer、float、double等
  • 布尔:boolean
  • 日期:data

2 环境配置

2.1?安装

注:父级文件夹不能有中文和空格

https://mirrors.huaweicloud.com/elasticsearch/7.6.2/elasticsearch-7.6.2-windows-x86_64.ziphttps://mirrors.huaweicloud.com/elasticsearch/7.6.2/elasticsearch-7.6.2-windows-x86_64.zipicon-default.png?t=N7T8https://mirrors.huaweicloud.com/elasticsearch/7.6.2/elasticsearch-7.6.2-windows-x86_64.zip

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.zipicon-default.png?t=N7T8https://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?概念

  1. 可以自定义数据类型,灵活使用,不会全部默认 text 和 long?
  2. 需要在 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

  1. must下的多个规则必须同时匹配才能被搜索到
  2. should只需要匹配其中任意1个条件即可
  3. 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的相关类保持一致!

  1. 索引名必须小写
  2. 时间必须格式,不然会查询不了
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 中调用方法实现效果

文章来源:https://blog.csdn.net/qq_53394871/article/details/132735758
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。