Loading

尚硅谷--雷丰阳--ElasticSearch 7.4.2版本

简介:

ElasticSearch是一个分布式,RESTful风格的搜索和数据分析引擎


官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html


一、基本概念

1、index(索引)

动词:相当于MySql的insert

名词:相当于MySql的database


2、Type (类型)

在index中,可以定义一个或多个类型

类似于mysql的table,每种类型的数据放在一起


3、Document(文档)

保存在某个索引(index)下,某种类型(Type)的一个数据(Document),文档是json形式的,

Documnet类似于table中的一行

clipboard


4、倒排索引机制

es会维持一个倒排索引,当其存入一个数据时,会对该数据中的内容进行分词,然后该词语后

维持一个该文档的id

clipboard



二、ElasticSearch - Docker安装

1、下载镜像文件

docker pull elasticsearch:7.4.2
docker pull kibana:7.4.2


2、创建实例

创建文件夹和配置文件

mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host : 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml


创建容器

9200: restful请求的端口
9300:elasticsearch集群节点间通信端口
discovery.type=single-node  以单节点形式运行
ES_JAVA_OPTS 设置es运行的java虚拟机内存
-v :目录挂载
-d : 以守护进程方式运行
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx128m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2


【注意】:启动错误时,需要改一下 数据目录的权限 /mydata/elasticsearch/data

chmod 777 /mydata/elasticsearch/data

clipboard


访问 elasticsearch服务所在机器的 9200端口

http://192.168.25.129:9200/

clipboard


3、【补充】 docker 安装kibana

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.25.129:9200 -p 5601:5601 \
-d kibana:7.4.2

clipboard


访问kibana服务所在机器的5601端口

http://192.168.25.129:5601/

clipboard



三、初步检索

1、_cat

GET /_cat/nodes : 查看所有结点

GET /_cat/health : 查看es的健康状况

GET /_cat/master : 查看主节点

GET /_cat/indices :查看所有索引


2、索引一个文档

保存一个数据,保存在哪个索引的哪个类型下,指定用哪个唯一标识

PUT customer/external/1 : 在customer索引下的 external 类型下保存1号数据

PUT customer/external/1
{
    "name":"houchen"
}


返回数据:

{
  "_index" : "customer",  // 带_的为元数据  索引
  "_type" : "external",
  "_id" : "1",
  "_version" : 1, 
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}


【总结】

PUT 和 POST都可以进行新增操作

PUT必须要带id,id存在的话即修改,id不存在的话则是新增

带id的情况下,多次PUT/POST是修改

不带id的情况下,多次POST是新增


3、查询文档

GET /customer/external/1

结果:
{
  "_index" : "customer", //在哪个索引
  "_type" : "external", //在哪个类型
  "_id" : "1",          //记录id
  "_version" : 1,       //版本号
  "_seq_no" : 0,     //并发控制字段,每次更新就会+1,用来做乐观锁
  "_primary_term" : 1, //同上,主分片重新分配,会重启就会变化
  "found" : true,    //是否找到
  "_source" : {         
    "name" : "houchen"
  }
}

并发修改时,需要携带 _seq_no

clipboard


4、更新文档

带 _update的更新语句:会对比原来的数据,如果没有变化,则版本号不会更新

POST customer/external/1/_update
{
    "doc":{
        "name":"john Doew"
    }
}


不带_update的更新语句,会不断更新,叠加版本_version

POST customer/external/1
{
    "name":"john Doew"
}

PUT customer/external/1
{
    "name":"john Doew"
}


5、删除文档和索引

删除文档

DELETE customer/external/1

删除索引

DELETE customer


6、bulk批量API

POST /customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"111"}
{"index":{"_id":"2"}}
{"name":"222"}

clipboard



复杂操作:

POST /_bulk
{"delete":{"_index":"wehsite","_type":"blog","_id":"123"}}  //删除文档
{"create":{"_index":"wehsite","_type":"blog","_id":"123"}}  //创建文档
{"title":"My first blog post"}
{"index":{"_index":"wehsite","_type":"blog"}}  //索引文档
{"title":"My second blog post"}
{"update":{"_index":"wehsite","_type":"blog","_id":"123"}}
{"doc":{"title":"My second blog post"}}

clipboard


【导入官网测试数据】

post /bank/account/_bulk  
{
    {"index":{"_id":"1"}}
    {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
    {"index":{"_id":"6"}}
    {"account_number":6,"balance":5686,"firstname":"Hattie","lastname":"Bond","age":36,"gender":"M","address":"671 Bristol Street","employer":"Netagy","email":"hattiebond@netagy.com","city":"Dante","state":"TN"}
    {"index":{"_id":"13"}}
}

https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip




四、进阶检索

ES支持两种基本方式检索:

一个是通过使用 REST request URI来发送搜索参数(uri + 检索参数)

GET bank/_search?q=*&sort=account_number:asc


另一个是通过 REST request body 来发送他们 (uri + 请求体)

GET bank/_search
{
    "query":{
        "match_all":{}
     },
     "sort":[
        {
             "balance":{
                 "order":"desc"
             }
        } 
     ],
     "from":0,
     "size":5,
     "_source":["balance","firstname"]
}


1、match_all :查询所有

GET bank/_search
{
    "query":{
        "match_all":{}
     },
     "sort":[
        {
          "balance":"asc"
        } 
     ]
}


2、match查询

全文检索会按照评分进行排序,

首选需要对要查询的字符串进行分词,然后根据分词的结果进行匹配

最后根据得分进行排序

GET bank/_search
{
  "query": {
    "match": {
      "address": "kings"
    }
  }
}


3、match_phrase 【短语匹配】

将需要匹配的值,当成一个整体单词(不分词)进行检索

GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill lane"
    }
  }
}


4、multi_match 【多字段匹配】

也是分词查询

// 查询 adress或者city中包含 mill的数据
GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill",
      "fields": ["address","city"]
    }
  }
}


5 bool查询

GET bank/_search
{
  "query": {
    "bool": {
      "must":[
      {
        "match": {
          "gender": "F"
        }
      },
      {
        "match": {
          "address": "Mill"
        }
      }
    ]
    }
  }
}



6、filter 结果过滤

只用于对数据进行过滤,不会计算相关性得分

GET bank/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "age": {
            "gte": 10,
            "lte": 20
          }
        }
      }
    }
  }
}


7、term 查询

和match一样。匹配某个属性的值。全文检索用match,其他非text字段匹配用term

GET /bank/_search
{
  "query": {
    "term": {
      "balance": {
        "value": "32838"
      }
    }
  }
}


精确匹配:查询address等于132 Gunnison的文档

GET bank/_search
{
  "query": {
    "match": {
      "address.keyword": "132 Gunnison"
    }
  }
}

clipboard


8、aggregation (执行聚合)

聚合提供了从数据中分组和提取数据的能力。最简单的聚合大致等于SQL的聚合函数。

在ElasticSearch中,你有执行搜索返回hit,并且同时返回聚合结果,把一个响应中所有hits分隔开的

能力,这是非常强大且有效的。您可以执行查询和多个聚合并且在一次使用中得到各自的返回结果,使用一次简介和简化的api来避免网络往返


语法

"aggregations" : {
    "<aggregation_name>" : {   //聚合名称
        "<aggregation_type>" : {  //聚合类型
            <aggregation_body>    //聚合体
        }
        [,"meta" : {  [<meta_data_body>] } ]?
        [,"aggregations" : { [<sub_aggregation>]+ } ]?
    }
    [,"<aggregation_name_2>" : { ... } ]*
}


1)需求:搜索address中包含mail的所有人的年龄和平均年龄,但不显示这些人的详情

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    }
  },
  "aggs": {
    "ageagg": {
      "terms": {
        "field": "age",
        "size": 10
      }
    },
    "ageavg":{
      "avg": {
        "field": "age"
      }
    },
    "balanceavg":{
      "avg": {
        "field": "balance"
      }
    }
  }
}

clipboard


2)按照年龄聚合,并求这些年龄段的人的平均薪资

(聚合里面套聚合)

GET /bank/_search
{
  "query":{
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 10
      },
      "aggs": {
        "balanceAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0  ##过滤掉查询的结果
}

clipboard


3)查出所有的年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资,以及这个年龄段总体的平均薪资

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "agegg": {  //聚合年龄
      "terms": { 
        "field": "age",
        "size": 100
      },
      "aggs": {
         "genderagg":{  //在年龄的聚合结果上聚合性别
            "terms": {
              "field": "gender.keyword",
              "size": 2
            },
            "aggs": {
              "balanceagg": {  //xx岁 M性别的平均薪资
                "avg": {
                  "field": "balance"
                }
              }
            }
        },
          "agebalanceaavg":{  //xx岁的平均薪资
            "avg": {
              "field": "balance"
            }
          }
      }
    }
  }
}

clipboard



五、Mapping

1、ElasticSearch7去掉type的概念

关系数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。elasticsearch是基于Lucene开发的搜索引擎,而Es中不同type下名称相同的field最终咋Lucene中的处理方式是一样的

  • 两个不同type下的user-name,在es同一个索引下其实被认为是同一个field,你必须在不同的

type中定义相同的field映射。否则,不同type中的相同字段名称就会在处理时出现冲突的情况,导致

Lucene处理效率下降


2、查看某个索引的映射

GET bank/_mapping

clipboard

【注意】text属性的field,都会有一个keyword子属性


3、创建映射

PUT /my_index
{
  "mappings":{
    "properties": {
      "age":{"type":"integer"},
      "name":{"type": "text"},
      "email":{"type": "keyword"}
    }
  }
}

clipboard


4、添加新的映射字段

PUT /my_index/_mapping
{
  "properties":{
    "employ_id":{
      "type":"keyword",
      "index":false   //控制该字段能不能被检索
    }
  }
}


5、修改映射&

不能修改映射,因为这会导致已经存入的数据所拥有的索引规则不能用,只能新建一个索引和映射关系,并将旧的数据迁移过去


6、数据迁移

数据迁移:

POST _reindex
{
    "source":{
        "index":"bank"
    },
    "dest":{
        "index":"newbank"
    }
}

如果是老版本的,包含type的

POST _reindex
{
    "source":{
        "index":"bank",
        "type":"account"
    },
    "dest":{
        "index":"newbank"
    }
}


六、分词

1、简介

一个tokenizer(分词器)接收一个字符流,将之分隔为独立的tokens(词元,通常是独立的单词),然后输出tokens流


2、如何查看一句话的分词结果:

POST _analyze
{
    "analyzer":"standard"  //指定分词器:现在使用的是标准分词器
    "text":"我爱北京天安门"
}

不得不说啊:标准分词器真是low爆了:

clipboard



3、安装ik分词器 (可以对中文进行分词)

ik分词器的github地址:

https://github.com/medcl/elasticsearch-analysis-ik

clipboard

我的es是 7.4.2版本

clipboard


1)由于elasticsearch容器启动时,指定了文件挂载:-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins

所以,只需要将ik分词器下载到 /mydata/elasticsearch/plugins,并进行解压

clipboard


2)进入elasticsearch容器内部

docker exec -it fe25cc93c122 /bin/bash

clipboard


3)使用ik分词器,查看分词结果

clipboard


4、自定义扩展词库

网络上的流行新词,ik分词器则不支持,此时就需要扩展词库

clipboard



七、ElasticSearch-Rest-Client

1、ElasticSearch Client

1)、9300:TCP

spring-data-elasticsearch:transport-api.jar

springboot版本不同,transport-api.jar不同。不能适配es版本

7.x已经不建议使用,8以后就要废弃


2)、9200:HTTP

JestClient:非官方更新慢

RestTemplate: 模拟发送HTTP请求,ES的很多操作需要自己封装,麻烦

HttpClient: 同上

Elasticsearch-Rest-Client:官方的RestClient,封装了ES操作,API层次分明,上手简单

最终选择 lasticsearch-Rest-Client(elasticsearch-rest-high-level-client)

https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html


2、保存

@RunWith(SpringRunner.class)
@SpringBootTest
class EsApplicationTests {
 
    @Autowired
    private RestHighLevelClient client;
 
    /**
     * 测试存储数据到es
     * 更新也可以
     */
    @Test
    void indexData() throws IOException {
        IndexRequest indexRequest = new IndexRequest("users");
        indexRequest.id("1");
        User user = new User("zhangsan", "男", 18);
        String jsonString = JSON.toJSONString(user);
        indexRequest.source(jsonString, XContentType.JSON);
 
        //执行操作
        IndexResponse index = client.index(indexRequest, ElasticSearchConfig.COMMON_OPTIONS);
 
        //提取有用的操作数据
        System.out.println(index);
    }
 
    @Data
    static class User{
        private String userName;
        private String gender;
        private Integer age;
 
        public User(String userName, String gender, Integer age) {
            this.userName = userName;
            this.gender = gender;
            this.age = age;
        }
    }
}


3、复杂检索

posted @ 2021-08-02 07:35  青岑  阅读(619)  评论(0编辑  收藏  举报