ElasticSearch上

01. ElasticSearch简介

1.1 数据库存在的问题

目标:能够说出数据库查询存在的问题
在这里插入图片描述

问题一:查询title中包含 “手机” 的信息?

SELECT * FROM goods WHERE title LIKE '%手机%';

结论:如果使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低

问题二:查询title中包含‘华为手机’的信息?

SELECT * FROM goods WHERE title LIKE%华为手机%';

结论:关系型数据库提供的查询,功能太弱

1.2 倒排索引

目标:了解倒排索引的存储原理

使用新华字典查找汉字,先找到汉字的偏旁部首,再根据偏旁部首对应的目录(索引)找到目标汉字。

在这里插入图片描述

ES样例:

  • 文档0(编号0): we like java java java
  • 文档1(编号1): we like lucene lucene lucene

建立倒排索引的流程:
1)首先对所有数据的内容进行拆分,拆分成唯一的一个个词语(词条)
2)然后建立词条和每条数据的对应关系

(Term 词条)(Doc ID,Freq 频率)(Pos 位置)
we(0,1) (1,1)(0)(0)
like(0,1) (1,1)(1)(1)
java(0,3)(2,3,4)
lucene(1,3)(2,3,4)

小结

倒排索引:将每条数据中的内容进行分词,形成词条。然后记录词条和数据的唯一标识(id)的对应关系,形成的产物。

1.3 ElasticSearch存储和搜索原理

目标:能够说出ElasticSearch存储和搜索原理

假设数据库存在以下几条数据:

在这里插入图片描述

ES中存储及搜索原理图:

在这里插入图片描述

说明:ES的数据库我们称之为 index(索引库),每条数据我们称之为 document(文档),ES在存储文档的时候,会对它需要分词的字段内容进行切分,切分成一个个词条,再建立每个词条与文档唯一标识(id)的对应关系,即倒排索引。

我们再回过头看之前数据库存在的两个问题,通过ES是否能够解决:

  • 性能低:使用模糊查询,左边有通配符,不会走索引,会全表扫描,性能低

    ES解决方案:如果使用"手机"作为关键字查询,ES生成的倒排索引中,词条会排序,形成一颗树形结构,提升词条的查询速度。

  • 功能弱:如果以"华为手机" 作为条件,查询不出来数据
    ES解决方案:如果使用"华为手机"作为关键字查询,ES也可以对搜索的关键字进行分词,比如将华为手机拆分成"华为"、“手机”,然后根据两个词分词去倒排索引中进行查询,然后取结果的并集。

1.4 ElasticSearch概念

1.4.1 介绍

ElasticSearch是Java语言开发的,并作为Apache许可条款下的开放源码发布,基于Lucene实现,是一款分布式、高扩展、近实时的搜索服务,可以基于RESTful web接口进行操作。官网:https://www.elastic.co/

1.4.2 应用场景
  • 海量数据的查询

  • 日志数据分析

  • 实时数据分析

1.4.3 与数据库的区别

1)结构上的区别

在这里插入图片描述

索引库/索引(index)
ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。

映射(mapping)
mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构。

类型(type)
相当于关系型数据库中的表。在Elasticsearch7.X默认type为 _doc,
ES 5.x中一个index可以有多种type。
ES 6.x中一个index只能有一种type。
ES 7.x以后,将逐步移除type这个概念,现在的操作已经不再使用,默认_doc

文档(document)
Elasticsearch中的最小数据单元,常以json格式显示。相当于关系型数据库中的一行数据。

倒排索引
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,对应一个包含它的文档id列表。

2)功能上的区别

  • MySQL有事务性,而ElasticSearch没有事务性,所以数据安全性、一致性较低。
  • MySQL使用方便,DML语法丰富,ES相对较弱

在这里插入图片描述

ElasticSearch和MySql分工不同,MySQL负责存储数据,ElasticSearch负责海量数据的搜索。

1.5 小结

在这里插入图片描述

02. 安装ElasticSearch

2.1 安装ElasticSearch

请参考 资料\elasticsearch安装.md 进行安装

2.2 安装Kibana及Postman

请参考 资料\elasticsearch安装.md 进行安装

03. 脚本操作ES

3.1 RESTful风格介绍

REST(Representational State Transfer 表述性状态转移),是一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是RESTful。就是一种定义接口的规范。有以下特征:

  • 基于HTTP

  • 可以使用XML格式定义或JSON格式定义参数和返回值。

  • 每一个URI代表1种资源。

  • 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:

    • GET:用来获取资源
    • POST:用来新建资源(也可以用于更新资源)
    • PUT:用来新建资源
    • DELETE:用来删除资源

如果有一个/user资源

get: /user/1   ---查询
delete: /user/1   ---删除
post: /user  ---添加、修改
put: /user  ---添加

3.2 操作索引

  • 添加索引
PUT http://ip:端口/索引名称

在这里插入图片描述

  • 查询索引
# 查询单个索引信息
GET http://ip:端口/索引名称  
# 查询多个索引信息
GET http://ip:端口/索引名称1,索引名称2...   
# 查询所有索引信息 
GET http://ip:端口/_all 

在这里插入图片描述

  • 删除索引
DELETE http://ip:端口/索引名称  

在这里插入图片描述

  • 关闭索引
POST http://ip:端口/索引名称/_close

在这里插入图片描述

说明:当索引进入关闭状态,是不能添加文档的

  • 打开索引
POST http://ip:端口/索引名称/_open 

在这里插入图片描述

3.3 操作映射

3.3.1 数据类型

在这里插入图片描述

ES中包含两类数据类型:简单数据类型和复杂数据类型

1)简单数据类型

  • 字符串

    • text:会分词,不支持聚合

    • keyword:不会分词,将全部内容作为一个词条,支持聚合

      例如:有个文档(相当于数据库一条数据),其中一个字段的值是华为手机
      text: 华为、手机
      keyword: 华为手机
      

在这里插入图片描述

  • 数值

  • 布尔(boolean)

  • 二进制(binary)

  • 范围类型(integer_range, float_range, long_range, double_range, date_range)(了解即可)

  • 日期(date)

2)复杂数据类型

  • 数组 []:没有专用的array数据类型,任何一个字段的值,都可以被添加0个到多个,但要求他们的类型必须一致,当多个值存储到ES的某条数据的某字段时,数据类型会自动转化成数组类型

  • 对象 {}

在这里插入图片描述

3.3.2 kibana操作映射
  • 添加映射
# 创建索引
PUT person

# 查询索引
GET person

# 添加映射
PUT person/_doc/_mapping
{
  "properties": {
    "name": {
      "type": "keyword"
    },
    "age": {
      "type": "integer"
    }
  }
}
  • 查询映射
# 查询映射
GET person/_doc/_mapping
  • 创建索引并添加映射
# 创建索引并添加映射
PUT person2
{
  "mappings": {
    "_doc": {
      "properties": {
        "age": {
          "type": "integer"
        },
        "name": {
          "type": "keyword"
        }
      }
    }
  }
}
  • 添加字段(同添加映射),不支持修改字段
# 添加字段
PUT person/_doc/_mapping
{
  "properties": {
    "address": {
      "type": "text"
    }
  }
}

说明:在ES7.0之后,脚本操作不需要携带_doc

3.4 操作文档

目标:能够在kibana中操作文档

  • 添加文档,指定id
# 添加文档,指定id
PUT person/_doc/1
{
  "name": "张三",
  "age": 20,
  "address": "广东天河区"
}
  • 添加文档,不指定id(自动分配一个UUID作为id)
# 添加文档,不指定id
POST person/_doc
{
  "name": "李四",
  "age": 19,
  "address": "广东黄埔区"
}
  • 查询文档
# 根据id查询文档
GET person/_doc/1

# 查询所有文档
GET person/_doc/_search 
或
GET person/_search
  • 修改文档(同指定id添加文档,如果id已存在就是修改文档)
# 修改文档
PUT person/_doc/1
{
  "name": "张三222",
  "age": 22,
  "address": "广东天河区"
} 
  • 删除文档
# 删除文档
DELETE person/_doc/0bB_yHcByJaCSNWY-GM1

说明:在ES7.0之后,脚本操作不需要携带_doc

04. 分词器

4.1 分词器介绍

​ 分词器(Analyzer)是将一段文本,按照一定逻辑,拆分成多个词语的一种工具,如:华为手机 —> 华为、手、手机,ElasticSearch 内置分词器有以下几种:

  • Standard Analyzer - 默认分词器,按词切分,小写处理
  • Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
  • Stop Analyzer - 小写处理,停用词过滤(the,a,is)
  • Whitespace Analyzer - 按照空格切分,不转小写
  • Keyword Analyzer - 不分词,直接将输入当作输出
  • Patter Analyzer - 正则表达式,默认\W+(非字符分割)
  • Language - 提供了30多种常见语言的分词器

ES提供了一个接口给我们来验证分词效果,如下所示:

# 分词效果验证,若不指定analyzer默认使用standard分词器
GET _analyze
{
  "text": "我爱黑马程序员",
  "analyzer": "standard"
}

分词结果:

在这里插入图片描述

ElasticSearch 可以建立倒排索引时对字段内容进行分词、也可以在搜索时会搜索关键字进行分词。

ElasticSearch 内置分词器对中文很不友好,处理方式为:一个字一个词。

4.2 IK分词器介绍及安装

​ IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包,是一个基于Maven构建的项目,具有60万字/秒的高速处理能力,并且支持用户词典扩展定义。下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

​ 安装请参考 资料\ik分词器安装.md

4.3 查询文档-使用IK分词器

目标:能够在kibana中使用term查询文档

4.3.1 高级查询介绍
  • 词条查询:term
    词条查询不会对查询条件进行分词,只有当查询字符串和倒排索引中的词条完全匹配时才可以搜索到
4.3.2 term搜索
# term查询,查询的关键字必须和词条完全匹配
GET person/_search
{
  "query": {
    "term": {
      "address": {
        "value": "广东"
      }
    }
  }
}

查询结果如下:

在这里插入图片描述

可以发现查询结果为空,因为我们之前在添加文档时,并没有指定字段使用哪种分词器,它会默认使用standard单字分词器,将address的内容分成一个个字来和文档建立倒排索引。因此如果我们查询的关键字如果是"广"、"东"这样一个个字就可以查询到结果

# term查询,查询的关键字必须和词条完全匹配
GET person/_search
{
  "query": {
    "term": { 
      "address": {
        "value": "广"
      }
    }
  }
}

实际项目中,我们查询一般是按照一个个词来进行查询,所以如果使用standard分词器,查询效果并不好,因此我们在创建索引之初一般会指定需要分词的字段的分词器是ik分词器,我们删除person索引库并进行重建,脚本如下:

# 删除索引库
DELETE person

# 创建索引库并指定映射
PUT person
{
  "mappings": {
    "_doc": {
      "properties": {
        "age": {
          "type": "integer"
        },
        "name": {
          "type": "keyword"
        },
        "address":{
          "type": "text",
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

# 再重新添加文档
PUT person/_doc/1
{
  "name": "张三",
  "age": 20,
  "address": "广东天河区"
}

这个时候我们再来搜索关键字"广东" 就搜索到结果了,我们可以根据ES提供的分词验证接口,验证广东天河区的分出了哪些词,如下所示:

在这里插入图片描述

可以看出在存储数据的时候,广东天河区可以分出"广东"这个词,因此查询广东可以查询到结果。

05. ElasticSearch HighLevelAPI

5.1 springboot整合ES

1)搭建springboot工程,并添加依赖

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>cn.itcast</groupId>
    <artifactId>springboot_es</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--test启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>6.6.2</version>
        </dependency>
        
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>6.6.2</version>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>6.6.2</version>
        </dependency>
        
    </dependencies>

</project>

2)添加启动类和yml配置文件如下:
在这里插入图片描述

3)编写elasticsearch配置类

package cn.itcast.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * ES配置类
 *
 * @Author LK
 * @Date 2021/2/22
 */
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {

    private String host;

    private int port;

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(RestClient.builder(
                new HttpHost(
                        "192.168.211.129",
                        9200,
                        "http"
                )
        ));
        return restHighLevelClient;
    }
}

4)编写yml配置文件

elasticsearch:
  host: 192.168.211.129
  port: 9200

5)编写单元测试类

package cn.itcast;

import org.elasticsearch.client.RestHighLevelClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticTest {

    @Autowired
    private RestHighLevelClient client;

    @Test
    public void test(){
        System.out.println(client);
    }
    
}

6)项目结构

在这里插入图片描述

5.2 添加索引

1) 添加空索引

/**
 * 添加索引
 */
@Test
public void addIndex() throws Exception{
    // 1.使用client获取操作索引的对象
    IndicesClient indices = client.indices();
    // 2.执行操作,获取返回值
    CreateIndexRequest request = new CreateIndexRequest("itheima");
    CreateIndexResponse response = indices.create(request, RequestOptions.DEFAULT);
    // 3.判断结果
    System.out.println(response.isAcknowledged());
}

运行结果如下:

在这里插入图片描述

2) 添加索引并添加映射

/**
 * 创建索引并添加映射
 */
@Test
public void addIndexAndMapping() throws Exception{
    // 1.使用client获取操作索引的对象
    IndicesClient indices = client.indices();
    // 2.执行操作,获取返回值
    CreateIndexRequest request = new CreateIndexRequest("itcast");

    // 2.1 设置映射信息
    String mapping = "{\n" +
            "      \"_doc\" : {\n" +
            "        \"properties\" : {\n" +
            "          \"address\" : {\n" +
            "            \"type\" : \"text\",\n" +
            "            \"analyzer\" : \"ik_max_word\"\n" +
            "          },\n" +
            "          \"age\" : {\n" +
            "            \"type\" : \"integer\"\n" +
            "          },\n" +
            "          \"name\" : {\n" +
            "            \"type\" : \"keyword\"\n" +
            "          }\n" +
            "        }\n" +
            "      }\n" +
            "    }";
 	// 指定表名,以及映射的格式
    request.mapping("_doc", mapping, XContentType.JSON);

    CreateIndexResponse response = indices.create(request, RequestOptions.DEFAULT);

    // 3.判断结果
    System.out.println(response.isAcknowledged());
}

运行结果如下:

在这里插入图片描述

5.3 删除索引

/**
 * 删除索引
 */
@Test
public void deleteIndex() throws Exception{
    // 1.使用client获取操作索引的对象
    IndicesClient indices = client.indices();
    
    // 2.执行操作,获取返回值
    DeleteIndexRequest request = new DeleteIndexRequest("itheima");
    AcknowledgedResponse response = indices.delete(request, RequestOptions.DEFAULT);

    // 3.打印结果
    System.out.println(response.isAcknowledged());
}

5.4 添加文档

应用场景:添加、修改、删除文档作用就是同步数据库的数据到ES的索引库中

方式一

/**
 * 添加文档
 */
@Test
public void addDoc() throws Exception {
    // 1.构建文档数据
    Map<String, Object> data = new HashMap<String, Object>();
    data.put("name", "小kk");
    data.put("age", 20);
    data.put("address", "广州天河区");

    // 2.构建请求对象
    // 参数1-索引库名称,参数2-类型名称,参数3-主键id(不指定使用uuid)
    IndexRequest request = new IndexRequest("itcast", "_doc", "1").source(data);

    // 3.执行,获取返回结果
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);

    // 4.打印查询结果
    System.out.println(response.getId());
}

运行结果:

在这里插入图片描述

方式二

第一步:创建实体类

package cn.itcast.pojo;


public class Person {
    private String id;

    private String name;

    private int age;

    private String address;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

第二步:pom文件添加jackson-databind依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.2</version>
</dependency>

第三步:编写测试用例

/**
 * 添加文档,通过对象构建
 */
@Test
public void addDoc2() throws Exception{
    // 1.构建文档数据
    Person data = new Person();
    data.setId("2");
    data.setName("小zz");
    data.setAge(21);
    data.setAddress("广州黄埔区");

    // 1.1 将对象转为json
    String jsonStr = new ObjectMapper().writeValueAsString(data);

    // 2.构建请求对象
    // 参数1-索引库名称,参数2-类型名称,参数3-主键id(不指定使用uuid)
    IndexRequest request = new IndexRequest("itcast", "_doc", data.getId()).source(jsonStr, XContentType.JSON);

    // 3.执行,获取返回结果
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);

    // 4.打印结果
    System.out.println(response.getId());
}

运行结果:

在这里插入图片描述

5.5 修改、查询、删除文档

1)修改文档
/**
 * 修改文档,如果id存在便修改,如果不存在就添加
 */
@Test
public void updateDoc() throws Exception{
    // 1.构建文档数据
    Person data = new Person();
    data.setId("2");
    data.setName("小zzzz");
    data.setAge(22);
    data.setAddress("广州黄埔区");

    // 1.1 将对象转为json
    String jsonStr = new ObjectMapper().writeValueAsString(data);

    // 2.构建请求对象
    // 参数1-索引库名称,参数2-类型名称,参数3-主键id
    IndexRequest request = new IndexRequest("itcast", "_doc", data.getId()).source(jsonStr, XContentType.JSON);

    // 3.执行,获取返回结果
    IndexResponse response = client.index(request, RequestOptions.DEFAULT);

    // 4.打印结果
    System.out.println(response.getId());
}

以上执行完毕可将id为2的数据修改成data的数据

2)查询文档
/**
 * 根据id查询文档
 * @throws Exception
 */
@Test
public void findDocById() throws Exception {
    // 1.构建请求对象
    // 参数1-索引库名称,参数2-类型名称,参数3-主键id
    GetRequest request = new GetRequest("itcast", "_doc", "1");
    // 2.执行,获取返回结果
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    // 3.打印结果
    System.out.println(response.getSourceAsString());
}

运行结果:
在这里插入图片描述

3)删除文档
/**
 * 根据id删除
 * @throws Exception
 */
@Test
public void deleteDocById() throws Exception {
    // 1.构建请求对象
    // 参数1-索引库名称,参数2-类型名称,参数3-主键id
    DeleteRequest request = new DeleteRequest("itcast", "_doc", "1");
    // 2.执行结果,获取返回结果
    DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
    // 3.打印结果
    System.out.println(response.getId());
}

以上执行完毕可将id为1的数据进行删除

5.6 批量操作

Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

**应用场景:**ES索引库数据初始化的时候,可以将数据库的数据查询出来通过批量操作导入到索引库中

脚本操作

语法:

POST _bulk
{"action": {"metadata"}}
{"data"}

示例:

# 1.删除id为5的文档
# 2.添加id为6的文档
# 3.修改id为2的文档
POST _bulk
{"delete":{"_index":"person","_type":"_doc","_id":"5"}}  # 指定删除person索引库中_doc表的id为5的文档
{"create":{"_index":"person","_type":"_doc","_id":"6"}}  # 指定新增文档到person索引库的_doc表中,id为6
{"name":"六号","age":60,"address":"广州越秀区"}  # 新增文档的内容
{"update":{"_index":"person","_type":"_doc","_id":"2"}}  # 指定修改person索引库中_doc表的id为2的文档
{"doc": {"name":"二号"}} # 修改的内容

代码操作

1)编写单元测试用例

/**
 * 批量操作
 */
@Test
public void bulk() throws Exception{
    // 1.创建批量请求对象
    BulkRequest bulkRequest = new BulkRequest();

    // 2.循环添加请求对象
    for (int i = 10; i < 20; i++) {
        IndexRequest indexRequest = new IndexRequest("itcast", "_doc");
        // 2.1.构建文档数据
        Person data = new Person();
        data.setId(i + "");
        data.setName("张" + i);
        data.setAge(20);
        data.setAddress("广州黄埔区");

        // 2.2 将对象转为json
        String jsonStr = new ObjectMapper().writeValueAsString(data);
        indexRequest.source(jsonStr, XContentType.JSON);

        // 2.3 添加请求对象
        bulkRequest.add(indexRequest);
    }

    // 3.执行请求,获取返回结果
    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);

    // 4.打印执行状态,返回200代表执行成功
    System.out.println(response.status().getStatus());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值