SpringBoot整合Elasticsearch

115 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

请添加图片描述

简介

Elaticsearch,简称为 ES,ES 是一个开源的高扩展的==分布式全文检索引擎==,它可以近乎==实时的存储、检索数据==;本身扩展性很好,可以扩展到上百台服务器,能够快速处理 PB 级别(大数据时代)的数据。

ES 的核心 Lucene 是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。是一套用于全文检索和搜寻的开源程式库,由 Apache 软件基金会支持和提供,使用的是一种称为倒排索引的结构,采用Lucene倒排索作为底层,这种结构适用于快速的全文搜索。

Lucene 提供了一个简单强大的接口,能够做全文索引和搜寻。然而 Lucene 非常复杂,想要使用它就需要深入了解检索的相关知识来理解它是如何工作的。

而 ES 使用 java 开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的==RESTful API==来隐藏 Lucene 的复杂性,从而让全文搜索变得简单 。Elasticsearch 用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

Elasticsearch 是面向文档,关系型数据库(Relational DB)和 Elasticsearch客观的对比!一切都是 json!

Relational DBElasticsearch
数据库(database)索引(indices)
表(tables)types
行(rows)documents
字段(columns)fields

在 Elasticsearch(集群)中可以包含多个索引(数据库) ,每个索引中可以包含多个类型(表) ,每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。

Elasticsearch:9200

下载

  1. 进入Elasticsearch 官网,官网地址:www.elastic.co/cn/elastics…

    image-20211223103354229

  2. 点击下载

    image-20211223103438411

  3. 选择系统(以 Windows 为例)

    image-20211223103652144

  4. 等待下载完成

    image-20211223103732532

  5. 下载完成

    image-20211223103815128

安装

  1. 将下载的压缩包进行解压,解压之后即可使用。

    image-20211223104347742

  2. 目录结构

    • bin:启动文件

    • config:配置文件

      • log4j2.properties:日志配置文件
      • jvm.options:java 虚拟机相关的配置
      • elasticsearch.yml:Elasticsearch 的配置文件;默认 9200 端口!跨域。
    • jdk:环境

    • lib:相关 JAR 包

    • logs:日志

    • modules:功能模块

    • plugins:插件

启动

  1. 双击 bin 目录下的 elasticsearch.bat 文件。

    image-20211223111109262

  2. 等待启动完成

    image-20211223111557940

    默认通信地址:127.0.0.1:9300

    默认公开地址:127.0.0.1:9200

  3. 浏览器访问 127.0.0.1:9200

    image-20211223112207578

    name:主机名字。

    cluster_name:集群名字,默认为 elasticsearch。

    cluster_uuid:集群 uuid。

    version:版本。、

    tagline:你知道的,为了搜索。

ES Head:9100

可查看 Elasticsearch 中的信息插件。

注:需要安装 node.js 环境

安装

  1. Github 下载地址:github.com/mobz/elasti…

    image-20211223113829880

  2. 解压 elasticsearch-head-master.zip 压缩包

    image-20211223113925250

  3. 进入 cmd,运行命令

    已经下载的不用再执行第一条命令。

    git clone git://github.com/mobz/elasticsearch-head.git
    cd elasticsearch-head
    npm install
    

启动

  1. cmd 中启动 elasticsearch-head

    运行以下命令。

    npm run start
    

    image-20211223160519418

  2. 在 elasticsearch.yml 中配置跨域

    image-20211223160118878

    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
  3. 访问 http://localhost:9100/,点击连接,即可显示节点信息。

    image-20211223160331625

Kibana:5601

Elasticsearch 的可视化平台,需要先启动 Elasticsearch。

下载

  1. Kibana 官网下载地址:www.elastic.co/cn/kibana/

    image-20211223161715894

  2. 点击下载 Kibana

    image-20211223161749595

  3. 选中对应版本下载即可

    image-20211223161835424

  4. 等待下载完成

    image-20211223161903660

  5. 下载完毕之后解压即可。解压非常慢。

    image-20211223162347992

启动

  1. 双击 bin 目录下的 Kibana.bat 文件

    image-20211223162523727

  2. 等待的时间有可能需要几分钟,根据电脑配置而定。

    image-20211223164413123

  3. 浏览器访问 http://localhost:5601/

    image-20211223163508635

汉化

  1. config 目录下的 kibana.yaml 文件

    image-20211223164300053

  2. 重新启动 Kibana,访问 http://localhost:5601/

    image-20211223164533479

IK 分词器插件

IK 分词器可以对中文进行一个分词操作的插件。即把一段中文或者别的划分成一个个的关键字,在搜索的时候会将搜索关键词进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词(不使用用IK分词器的情况下)。

IK 分词器默认提供了两个分词算法:(ik_smart 和 ik_max_word),其中 ik_smart 为最少切分,ik_max_word 为最细粒度划分!

下载

  1. 下载:Github 下载地址:github.com/medcl/elast…

    要下载和 Elasticsearch 一样的版本。

    image-20211223184622315

  2. 下载完毕之后,放入到 Elasticsearch 的 plugins 目录下即可。

    image-20211223184753814

    image-20211223184822144

  3. 重启 Elasticsearch,可以看到 ik 插件被加载。

    image-20211223185014486

  4. 在 Kibana 中进行测试

    GET _analyze
    {
      "analyzer": "ik_smart",
      "text": "蜘蛛侠"
    }
    GET _analyze
    {
      "analyzer": "ik_max_word",
      "text": "蜘蛛侠"
    }
    

    image-20211223191423591

自定义词典

在 IK 的 config 的目录中创建自定义文件 my.dic

image-20211223220819332

添加文本,保存退出。

image-20211223220944783

在 IKAnalyzer.cfg.xml 中配置自己的扩展字典。

image-20211223221147939

重启 ES,

image-20211223221533895

访问 Kibana 可以查看 “蜘蛛侠” 被分为一个词。

image-20211223221643559

如果我们需要自定义分词即在 dic 文件中进行配置即可。

Rest 风格说明

Rest 风格是一种软件架构风格,不是一种标准,它只是提供了一组设计原则约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁更有层次更易于实现缓存等机制。

字段数据类型

主要字段类型如下:

  • 字符串类型:text、keyword

    • text:支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;text类型的最大支持的字符长度无限制,适合大字段存储;
    • keyword:不进行分词,直接索引、支持模糊、支持精确匹配,支持聚合、排序操作。keyword类型的最大支持的长度为——32766个 UTF-8 类型的字符,可以通过设置 ignore_above 指定自持字符长度,超过给定长度后的数据将不被索引,无法通过 term 精确匹配检索返回结果。
  • 数值型:long、Integer、short、byte、double、float、half float、scaled float

  • 日期类型:date

  • 布尔类型:boolean

  • 二进制类型:binary

注:如果自己的文档字段没有指定,那么es就会给我们默认配置字段类型!

基本命令

methodurl地址描述
PUT(创建,修改)localhost:9200/索引名称/类型名称/文档id创建文档(指定文档id)
POST(创建)localhost:9200/索引名称/类型名称创建文档(随机文档id)
POST(修改)localhost:9200/索引名称/类型名称/文档id/_update修改文档
DELETE(删除)localhost:9200/索引名称/类型名称/文档id删除文档
GET(查询)localhost:9200/索引名称/类型名称/文档id查询文档通过文档ID
POST(查询)localhost:9200/索引名称/类型名称/文档id/_search查询所有数据

样例:创建索引

PUT /索引名/~类型名~/文档id
{请求体}
PUT /test1/type1/1
{
  "name": "蜘蛛侠",
  "age": 21
}

即创建一个索引 test1 ,文档类型名为 type1,文档 id 为 1。

image-20211223222913141

通过 ES Head 查看数据

image-20211223223314502

高亮显示

GET test/user/_search
{
  "query": {							//搜索匹配 name 包含"侠"
    "match": {
      "name": "侠"
    }
  },
  "highlight": {						//高亮
    "pre_tags": "<font color='red'>",	//html 前缀
    "post_tags": "</font>",				//html 后缀
    "fields": {							//属性
      "name": {}
    }
  }
}

image-20211224104519095

集成 SpringBoot

官方文档

  1. 点击链接进入官网:www.elastic.co/cn/,点击学习下面的…

    image-20211224112142954

  2. 找到并点击 Elasticsearch Clients

    image-20211224112358461

  3. 这里就是官方支持的文档了

    image-20211224112453948

测试 API

创建项目和 ES 配置类

  1. 新建 一个SpringBoot 项目,导入 Elasticsearch 和 fastjson 依赖。

    image-20211224124547776

  2. 编写 ElasticSearchClientConfig 配置类。

    @Configuration
    public class ElasticSearchClientConfig {
        //RestHighLevelClient对象
        @Bean
        public RestHighLevelClient restHighLevelClient(){
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(
                            //集群设置多个
                            new HttpHost("127.0.0.1", 9200, "http")));
            return client;
        }
    }
    
  3. 测试类中注入 RestHighLevelClient 对象。

@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;

创建索引

//创建索引
@Test
void createIndex() throws IOException {
    CreateIndexRequest request = new CreateIndexRequest("spring_test");
    CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
    System.out.println(createIndexResponse);
}

判断索引是否存在

//判断索引是否存在,存在返回 true,不存在 false
@Test
void isExistIndex() throws IOException {
    GetIndexRequest request = new GetIndexRequest("spring_test");
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    System.out.println(exists);
}

删除索引

//删除索引,删除成功,response.isAcknowledged()=true,否则为 false
@Test
void deleteIndex() throws IOException {
    DeleteIndexRequest request = new DeleteIndexRequest("spring_test");
    AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
    System.out.println(response.isAcknowledged());
}

添加文档

//测试添加文档(User 对象)
@Test
void addDoc() throws IOException {
    //创建对象
    User user = new User("Jie", 20);
    //创建请求
    IndexRequest request = new IndexRequest("spring_test");
    //创建规则
    request.id("1");
    request.timeout(TimeValue.timeValueSeconds(1));
    //将数据 json 格式放入请求
    request.source(JSON.toJSONString(user), XContentType.JSON);
    //客户端发送请求,获取响应的结果
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(response.status());
}

判断文档是否存在

//判断文档是否存在 get /index/doc/1
@Test
void isExistsDoc() throws IOException {
    //获取 GetRequest 请求,构造重载 index,type,id
    GetRequest request = new GetRequest("spring_test","1");
    //不获取返回的 _source 的上下文
    request.fetchSourceContext(new FetchSourceContext(false));
    request.storedFields("_none_");
    boolean exists = client.exists(request, RequestOptions.DEFAULT);
    System.out.println(exists);
}

获取文档信息

@Test
void getDoc() throws IOException {
    //获取 GetRequest 请求,构造重载 index,type,id
    GetRequest request = new GetRequest("spring_test","1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    //将文档内容作为字符串输出
    System.out.println(response.getSourceAsString());
    //响应信息是与命令是一样的
    System.out.println(response);
}

image-20211224172127072

更新文档信息

  1. 获取 UpdateRequest 请求
  2. 设置数据,将数据转为 JSON 格式。
  3. 客户端执行请求
//更新文档信息
@Test
void updateDoc() throws IOException {
    //获取 GetRequest 请求,构造重载 index,type,id
    UpdateRequest request = new UpdateRequest("spring_test","1");
    request.timeout("1s");
    //创建对象
    User user = new User("I'm Jie", 22);
    //将对象封装为 JSON 类型
    request.doc(JSON.toJSONString(user),XContentType.JSON);
    UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(response.status());
}

删除文档信息

  1. 获取 DeleteRequest 请求
  2. 客户端执行请求
//删除文档信息
@Test
void delDoc() throws IOException {
    //获取 GetRequest 请求,构造重载 index,type,id
    DeleteRequest request = new DeleteRequest("spring_test","1");
    request.timeout("1s");
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(response.status());
}

批量添加文档信息

  1. 获取 BulkRequest 请求
  2. 准备数据
  3. 批处理请求
    • 循环添加请求数据,将数据转为 JSON 格式。
  4. 客户端执行请求
//批量添加文档信息
@Test
void bulkRequest() throws IOException {
    BulkRequest request = new BulkRequest();
    request.timeout("10s");
    //准备数据
    ArrayList<User> users = new ArrayList<>();
    users.add(new User("test1",2));
    users.add(new User("test2",3));
    users.add(new User("test3",2));
    users.add(new User("test4",5));
    users.add(new User("test5",2));
    //批处理请求
    for (int i = 0; i < users.size(); i++) {
        request.add(
            new IndexRequest("spring_test")
            .id((i+1)+"")	//不设置 id,默认随机 id
            .source(JSON.toJSONString(users.get(i)),XContentType.JSON));
    }
	//提交请求
    BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(response.status());
}

查询信息

  1. 获取 SearchRequest 请求
  2. 构建搜索条件
    • 设置查询条件(通过 SearchSourceBuilder 构造对应的条件)
  3. 客户端执行请求
  4. 解析响应结果
//查询
//SearchRequest 搜索请求
//SearchSourceBuilder 条件构造
//使用 QueryBuilders 创造条件
//HighlightBuilder 构建高亮
//TermQueryBuilder 精确查询
//MatchAllQueryBuilder 匹配所有
@Test
void search() throws IOException {
    //请求
    SearchRequest search = new SearchRequest("spring_test");
    //构建搜索条件
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    /*查询条件,可以使用 QueryBuilders 工具类实现*/
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("age", 2);
    sourceBuilder.query(termQueryBuilder);
    //设置时间
    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    search.source(sourceBuilder);
    SearchResponse response = client.search(search, RequestOptions.DEFAULT);
    System.out.println(response);
    System.out.println(JSON.toJSONString(response.getHits()));
    System.out.println("===============================");
    for (SearchHit hit : response.getHits().getHits()) {
        System.out.println(hit.getSourceAsMap());
    }
}

高亮字段

  1. 获取 SearchRequest 请求
  2. 设置搜索条件
    1. 设置查询条件
    2. 设置高亮
  3. 执行查询
  4. 客户端执行请求
  5. 解析查询结果
    • 将高亮字段替换掉原来的字段(查询出来的高亮字段和源字段不是同一个,所以需要用高亮后的字段替换源字段)
//高亮字段
@Test
void highLight() throws IOException {
    //请求
    SearchRequest search = new SearchRequest("spring_test");
    //构建搜索条件
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "test1");
    sourceBuilder.query(termQueryBuilder);
    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    //高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("name");     				//设置高亮字段
    highlightBuilder.requireFieldMatch(false);   		//取消多个高亮
    highlightBuilder.preTags("<font color='red'>");     //前缀
    highlightBuilder.postTags("</font>");   			//后缀
    sourceBuilder.highlighter(highlightBuilder);
    //执行搜索
    search.source(sourceBuilder);
    SearchResponse response = client.search(search, RequestOptions.DEFAULT);
    //解析结果
    ArrayList<Map<String,Object>> list = new ArrayList<>();
    for (SearchHit hit : response.getHits().getHits()) {
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        HighlightField name = highlightFields.get("name");
        //获取原理的结果
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
        // 将高亮字段替换掉原来的字段
        if(name!=null){
            Text[] texts = name.getFragments();
            String n_name="";
            for (Text text : texts) {
                n_name += text;
            }
            sourceAsMap.put("name",n_name);
        }
        list.add(sourceAsMap);
    }
}

火狐截图_2021-12-24T13-15-02.739Z