六.Docker安装

1.Docker中安装ElasticSearch

1.1下载ElasticSearch和kibana

#存储和检索数据
docker pull elasticsearch:7.6.2
#可视化检索数据
docker pull kibana:7.6.2
Shell

1.2配置

#创建config目录
mkdir -p /mydata/elasticsearch/config目录
#创建data目录
mkdir -p /mydata/elasticsearch/data
#写入配置
echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml
#保证权限问题
chmod -R 777 /mydata/elasticsearch/
Shell

1.3启动ElasticSearch

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e  "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-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.6.2 
Shell

如果显示下图,则启动成功

1.4设置开机启动ElasticSearch

docker update elasticsearch --restart=always
Shell

1.5启动kibana

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://`自己的主机地址`:9200 -p 5601:5601 -d kibana:7.6.2
Shell

如果显示下图,则启动成功

1.6设置开机启动kibana

docker update kibana  --restart=always
Shell

2.初步探索

2.1 _cat命令

  • GET/_cat/nodes:查看所有节点

如:http://主机IP:9200/_cat/nodes

127.0.0.1 15 96 2 0.02 0.08 0.07 dilm * ecc0cd748420

注:*表示集群中的主节点

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

如:http://主机IP:9200/_cat/health

1651039692 06:08:12 elasticsearch green 1 1 3 3 0 0 0 0 - 100.0%

注:green表示健康值正常

  • GET/_cat/master:查看主节点

如:http://主机IP:9200/_cat/master

6yNoV8kzSzKNZMZphXRdFw 127.0.0.1 127.0.0.1 ecc0cd748420
  • GET/_cat/indicies:查看所有索引 ,等价于MySQL数据库的show databases;

如:http://主机IP:9200/_cat/indicies

green open .kibana_task_manager_1   MTE_79x8S9itpEK34-zVsQ 1 0 2 0   34kb   34kb
green open .apm-agent-configuration ypUWMXNcRtmpjBWR-ji-EQ 1 0 0 0   283b   283b
green open .kibana_1                Ax4hZXYhSMafWC7IkwzWwQ 1 0 7 0 31.2kb 31.2kb

2.2索引一个文档(保存)

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

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

{
 "name":"John Doe"
}
JSON

请求地址: http://主机IP:9200/customer/external/1

得到如下图结果,则是请求成功

{
    "_index": "customer",   //表明该数据在哪个数据库下;
    "_type": "external",    //表明该数据在哪个类型下;
    "_id": "1",             //表明被保存数据的id;
    "_version": 1,          //被保存数据的版本;
    "result": "created",    //这里是创建了一条数据,如果重新put一条数据,则该状态会变为updated,并且版本号也会发生变化。
}
JSON

POST方式 customer/external/1;在customer索引下的external类型下保存1号数据

  • 添加数据的时候,不指定ID,会自动的生成id,并且类型是新增
  • 再次使用POST插入数据,仍然是新增的
  • 添加数据的时候,指定ID,会使用该id,并且类型是新增
  • 再次使用POST插入数据,类型为updated

注意

PUT方式和POST方式的区别在于,PUT方式后面必须带ID。否则会出现下图错误

2.3查询文档

GET /customer/external/1

请求地址: http://主机IP:9200/customer/external/1

{
    "_index": "customer",  //在哪个索引
    "_type": "external",   //在哪个类型
    "_id": "1",            //记录id
    "_version": 1,         //版本号
    "_seq_no": 0,          //并发控制字段,每次更新都会+1,用来做乐观锁
    "_primary_term": 1,    //同上,主分片重新分配,如重启,就会变化
    "found": true,         //找到了这个数据
    "_source": {           //真正的内容
        "name": "Jhon Doe"
    }
}
JSON

通过 http://主机IP:9200/customer/external/1?if_seq_no=0&if_primary_term=1 ,当序列号匹配的时候,才进行修改,否则不修改。

修改失败时,如下图显示:

2.4更新文档

2.4.1POST更新文档,带有_update

请求地址:http://主机IP:9200/customer/external/1/_update

//一定要带上doc
{
    "doc":{
        "name": "Jhon Doe"
    }
}
JSON

当重复更新一样的数据时

{ "_index": "customer", "_type": "external", "_id": "1", "_version": 3, //版本也不增加 "result": "noop", //此时显示的noop,表示没有做任何操作 "_shards": { "total": 0, "successful": 0, "failed": 0 }, "_seq_no": 3, //序列号不改变 "_primary_term": 1 }
JSON

2.4.2POST更新文档,不带_update

请求地址:http://主机IP:9200/customer/external/1

//不用带上doc
{
 "name": "Jhon Doe"
}
JSON

当重复更新一样的数据时,表示不会检查元数据,每次都做更新

{ "_index": "customer", "_type": "external", "_id": "1", "_version": 4, //版本增加 "result": "updated", //显示的updated,表示做更新操作 "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 4, //序列号改变 "_primary_term": 1 }
JSON

此时不带_update等同于PUT操作

无论是PUT请求还是POST请求,更新同时增加属性,都直接往里面增加就行,但是带有_update时,还是必须带上doc

2.5删除文档&索引

DELETE customer/external/1
DELETE customer
  • 删除文档

请求地址:http://主机IP:9200/customer/external/1

删除成功时是,效果如下:

此时再查询时:

  • 删除索引:

请求地址:http://主机IP:9200/customer

再次查询时:

3.检索

3.1search Api

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

  • 通过REST request uri 发送搜索参数 (uri +检索参数);
  • 通过REST request body 来发送它们(uri+请求体);

uri +检索参数

uri+请求体进行检索

Postman不支持此操作,所以用

http://主机IP:5601/app/kibana#/dev_tools/console方式请求

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

3.2Query DSL

3.2.1基本语法格式

Elasticsearch提供了一个可以执行查询的Json风格的DSL。这个被称为Query DSL,该查询语言非常全面。

一个查询语句的典型结构

QUERY_NAME:{
   ARGUMENT:VALUE,
   ARGUMENT:VALUE,...
}
JSON

如果针对于某个字段,那么它的结构如下:

{
  QUERY_NAME:{
     FIELD_NAME:{
       ARGUMENT:VALUE,
       ARGUMENT:VALUE,...
      }   
   }
}
JSON
GET bank/_search { "query": { "match_all": {} }, "from": 0, "size": 5, "sort": [ { "account_number": { "order": "desc" } } ] }
JSON

query定义如何查询;

  • match_all查询类型【代表查询所有的所有】,es中可以在query中组合非常多的查询类型完成复杂查询;
  • 除了query参数之外,我们可也传递其他的参数以改变查询结果,如sort,size;
  • from+size限定,完成分页功能;
  • sort排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;
3.2.2返回部分字段
GET bank/_search { "query": { "match_all": {} }, "from": 0, "size": 5, "sort": [ { "account_number": { "order": "desc" } } ], "_source": ["balance","firstname"] }
JSON
3.2.3 match匹配查询
  • 基本类型(非字符串),精确控制
GET bank/_search
{
  "query": {
    "match": {
      "account_number": "20"
    }
  }
}
JSON

match返回account_number=20的数据。

  • 字符串,全文检索,模糊检索
GET bank/_search
{
  "query": {
    "match": {
      "address": "kings"
    }
  }
}
JSON

全文检索,最终会按照评分进行排序,会对检索条件进行分词匹配。

3.2.4 match_phrase [短句匹配]

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

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

查处address中包含mill_road的所有记录,并给出相关性得分。

注:match_phrase、match和match的keyword的区别

文本字段的匹配,使用keyword,匹配的条件就是要显示字段的全部值,要进行精确匹配的。match_phrase是做短语匹配,只要文本中包含匹配条件,就能匹配到。

3.2.5 multi_math【多字段匹配】
GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill",
      "fields": [
        "state",
        "address"
      ]
    }
  }
}
JSON

state或者address中包含mill,并且在查询过程中,会对于查询条件进行分词。

3.2.6 bool用来做复合查询

复合语句可以合并,任何其他查询语句,包括符合语句。这也就意味着,复合语句之间
可以互相嵌套,可以表达非常复杂的逻辑。

must:必须达到must所列举的所有条件

GET bank/_search
{
   "query":{
        "bool":{
             "must":[
              {"match":{"address":"mill"}},
              {"match":{"gender":"M"}}
             ]
         }
    }
}
JSON

must_not:必须不是指定的情况

GET bank/_search { "query": { "bool": { "must": [ { "match": { "gender": "M" } }, { "match": { "address": "mill" } } ], "must_not": [ { "match": { "age": "38" } } ] } } }
JSON

should:应该达到should列举的条件,如果到达会增加相关文档的评分,并不会改变查询的结果。如果query中只有should且只有一种匹配规则,那么should的条件就会被作为默认匹配条件二区改变查询结果。

GET bank/_search { "query": { "bool": { "must": [ { "match": { "gender": "M" } }, { "match": { "address": "mill" } } ], "must_not": [ { "match": { "age": "18" } } ], "should": [ { "match": { "lastname": "Wallace" } } ] } } }
JSON

能够看到相关度越高,得分也越高。

3.2.7 Filter【结果过滤】

并不是所有的查询都需要产生分数,特别是哪些仅用于filtering过滤的文档。为了不计算分数,ElasticSearch会自动检查场景并且优化查询的执行。

GET bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } } ], "filter": { "range": { "balance": { "gte": "10000", "lte": "20000" } } } } } }
JSON

这里先是查询所有匹配address=mill的文档,然后再根据10000<=balance<=20000进行过滤查询结果,并不会计算相关性得分,能看到所有文档的 "_score" : 0.0。

3.2.8 term

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

使用term匹配查询

GET bank/_search
{
  "query": {
    "term": {
      "address": "mill Road"
    }
  }
}
JSON

一条也没有匹配到,而更换为match匹配时,能够匹配到32个文档

也就是说,全文检索字段用match,其他非text字段匹配用term

3.2.9 Aggregation(执行聚合)

聚合提供了从数据中分组提取数据的能力。最简单的聚合方法大致等于SQL的Group by和SQL聚合函数。在ElasticSearch中,执行搜索返回this(命中结果),并且同时返回聚合结果,把以响应中的所有hits(命中结果)分隔开的能力。这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API啦避免网络往返。

aggs:执行聚合。聚合语法如下:

"aggs":{
    "aggs_name这次聚合的名字,方便展示在结果集中":{
        "AGG_TYPE聚合的类型(avg,term,terms)":{}
     }
},
JSON

例:搜索address中包含mill的所有人的年龄分布以及平均年龄,但不显示这些人的详情

GET bank/_search { "query": { "match": { "address": "Mill" } }, "aggs": { "ageAgg": { "terms": { "field": "age", "size": 10 } }, "ageAvg": { "avg": { "field": "age" } }, "balanceAvg": { "avg": { "field": "balance" } } }, ## 表示查处的值不显示 "size": 0 }
JSON

例:按照年龄聚合,并且求这些年龄段的这些人的平均薪资

GET bank/_search { "query": { "match_all": {} }, "aggs": { "ageAgg": { "terms": { "field": "age", "size": 100 }, "aggs": { "ageAvg": { "avg": { "field": "balance" } } } } }, "size": 0 }
JSON

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

GET bank/_search { "query": { "match_all": {} }, "aggs": { "ageAgg": { "terms": { "field": "age", "size": 100 }, "aggs": { "genderAgg": { "terms": { "field": "gender.keyword" }, "aggs": { "balanceAvg": { "avg": { "field": "balance" } } } }, "ageBalanceAvg": { "avg": { "field": "balance" } } } } }, "size": 0 }
JSON

3.3 Mapping

3.3.1 创建映射:

创建索引并指定映射

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

输出:

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "my_index"
}    
JSON
3.3.2 查看映射
GET /my_index
JSON

输出:

{ "my_index" : { "aliases" : { }, "mappings" : { "properties" : { "age" : { "type" : "integer" }, "email" : { "type" : "keyword" }, "employee-id" : { "type" : "keyword", "index" : false }, "name" : { "type" : "text" } } }, "settings" : { "index" : { "creation_date" : "1588410780774", "number_of_shards" : "1", "number_of_replicas" : "1", "uuid" : "ua0lXhtkQCOmn7Kh3iUu0w", "version" : { "created" : "7060299" }, "provided_name" : "my_index" } } } }
JSON
3.3.3 添加新的字段映射
PUT /my_index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}
JSON

这里的 "index": false,表明新增的字段不能被检索,只是一个冗余字段。

3.3.4 更新映射

对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。

3.3.5 数据迁移

先创建新的索引的正确映射。然后使用如下方式进行数据迁移。

此方法是6.0以后的迁移方法

POST _reindex [固定写法]
{
  "source":{
      "index":"twitter"
   },
  "dest":{
      "index":"new_twitters"
   }
}
JSON

将旧索引的type下的数据进行迁移:

不限于版本

POST _reindex [固定写法]
{
  "source":{
      "index":"twitter",
      "type":"twitter"
   },
  "dest":{
      "index":"new_twitters"
   }
}
JSON

例:

## 不用type,老的数据可以迁移过。
POST _reindex
{
  "source":{
      "index":"bank",
      "type":"account"
   },
  "dest":{
      "index":"newbank"
   }
}
JSON

3.4 分词

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

例如:whitespace tokenizer遇到空白字符时分割文本。它会将文本“Quick brown fox!”分割为[Quick,brown,fox!]。

该tokenizer(分词器)还负责记录各个terms(词条)的顺序或position位置(用于phrase短语和word proximity词近邻查询),以及term(词条)所代表的原始word(单词)的start(起始)和end(结束)的character offsets(字符串偏移量)(用于高亮显示搜索的内容)。

ElasticSearch提供了很多内置的分词器,可以用来构建custom analyzers(自定义分词器)。

3.4.1 安装ik分词器

所有的语言分词,默认使用的都是“Standard Analyzer”,但是这些分词器针对于中文的分词,并不友好。为此需要安装中文的分词器。

  • 进入es容器内部plugin目录
[root@hadoop-104 ~]# docker exec -it elasticsearch /bin/bash
[root@0adeb7852e00 elasticsearch]# 
Shell
  • wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
[root@0adeb7852e00 elasticsearch]# pwd
/usr/share/elasticsearch
#下载ik7.6.2
[root@0adeb7852e00 elasticsearch]# wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
Shell
  • unzip 下载的文件
[root@0adeb7852e00 elasticsearch]# unzip elasticsearch-analysis-ik-7.6.2.zip -d ink Archive: elasticsearch-analysis-ik-7.6.2.zip creating: ik/config/ inflating: ik/config/main.dic inflating: ik/config/quantifier.dic inflating: ik/config/extra_single_word_full.dic inflating: ik/config/IKAnalyzer.cfg.xml inflating: ik/config/surname.dic inflating: ik/config/suffix.dic inflating: ik/config/stopword.dic inflating: ik/config/extra_main.dic inflating: ik/config/extra_stopword.dic inflating: ik/config/preposition.dic inflating: ik/config/extra_single_word_low_freq.dic inflating: ik/config/extra_single_word.dic inflating: ik/elasticsearch-analysis-ik-7.6.2.jar inflating: ik/httpclient-4.5.2.jar inflating: ik/httpcore-4.4.4.jar inflating: ik/commons-logging-1.2.jar inflating: ik/commons-codec-1.9.jar inflating: ik/plugin-descriptor.properties inflating: ik/plugin-security.policy [root@0adeb7852e00 elasticsearch]# #移动到plugins目录下 [root@0adeb7852e00 elasticsearch]# mv ik plugins/
Shell
  • rm -rf *.zip
[root@0adeb7852e00 elasticsearch]# rm -rf elasticsearch-analysis-ik-7.6.2.zip 
Shell
3.4.2 测试分词器

默认

POST _analyze
{
  "analyzer": "standard",
  "text": "中软国际"
}
JSON

安装好ik分词器:

## 1
POST _analyze
{
  "analyzer": "ik_smart",
  "text": "中软国际"
}

## 2
GET _analyze
{
   "analyzer": "ik_max_word", 
   "text":"中软国际"
}
JSON
3.4.3 自定义词库

修改/usr/share/elasticsearch/plugins/ik/config中的IKAnalyzer.cfg.xml
/usr/share/elasticsearch/plugins/ik/config

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict"></entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <entry key="remote_ext_dict">http://192.168.137.14/es/fenci.txt</entry> 
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
XML

修改完成后,需要重启elasticsearch容器,否则修改不生效。

更新完成后,es只会对于新增的数据用更新分词。历史数据是不会重新分词的。如果想要历史数据重新分词,需要执行。

http://主机IP/es/fenci.txt,这个是nginx上资源的访问路径

在运行下面实例之前,需要安装nginx(安装方法见安装nginx),然后创建“fenci.txt”文件,内容如下:

echo "樱桃萨其马,带你甜蜜入夏" > /mydata/nginx/html/fenci.txt 
Shell

测试效果:

GET my_index/_analyze
{
   "analyzer": "ik_max_word", 
   "text":"樱桃萨其马,带你甜蜜入夏"
}
JSON

输出结果:

{ "tokens" : [ { "token" : "樱桃", "start_offset" : 0, "end_offset" : 2, "type" : "CN_WORD", "position" : 0 }, { "token" : "萨其马", "start_offset" : 2, "end_offset" : 5, "type" : "CN_WORD", "position" : 1 }, { "token" : "带你", "start_offset" : 6, "end_offset" : 8, "type" : "CN_WORD", "position" : 2 }, { "token" : "甜蜜", "start_offset" : 8, "end_offset" : 10, "type" : "CN_WORD", "position" : 3 }, { "token" : "入夏", "start_offset" : 10, "end_offset" : 12, "type" : "CN_WORD", "position" : 4 } ] }
JSON

4.ElasticSearch -Rest-Client

4.1 9300: TCP

  • spring-data-elasticsearch:transport-api.jar;

    • springboot版本不同,ransport-api.jar不同,不能适配es版本
    • 7.x已经不建议使用,8以后就要废弃

4.2 9200: HTTP

  • jestClient: 非官方,更新慢;
  • RestTemplate:模拟HTTP请求,ES很多操作需要自己封装,麻烦;
  • HttpClient:同上;
  • Elasticsearch-Rest-Client:官方RestClient,封装了ES操作,API层次分明,上手简单;
    最终选择Elasticsearch-Rest-Client(elasticsearch-rest-high-level-client);