ElasticSearch(二)

六.Docker安装

1.Docker中安装ElasticSearch

1.1下载ElasticSearch和kibana

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

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/

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 

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

1.4设置开机启动ElasticSearch

docker update elasticsearch --restart=always

1.5启动kibana

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

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

1.6设置开机启动kibana

docker update kibana  --restart=always

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"
}

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

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

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

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"
    }
}

通过 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"
    }
}

当重复更新一样的数据时

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

2.4.2POST更新文档,不带_update

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

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

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

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

此时不带_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"}
  ]
}

3.2Query DSL

3.2.1基本语法格式

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

一个查询语句的典型结构

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

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

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

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"]
}
3.2.3 match匹配查询
  • 基本类型(非字符串),精确控制
GET bank/_search
{
  "query": {
    "match": {
      "account_number": "20"
    }
  }
}

match返回account_number=20的数据。

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

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

3.2.4 match_phrase [短句匹配]

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

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

查处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"
      ]
    }
  }
}

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

3.2.6 bool用来做复合查询

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

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

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

must_not:必须不是指定的情况

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

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"
          }
        }
      ]
    }
  }
}

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

3.2.7 Filter【结果过滤】

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

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

这里先是查询所有匹配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"
    }
  }
}

一条也没有匹配到,而更换为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)":{}
     }
},

例:搜索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
}

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

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

例:查出所有年龄分布,并且这些年龄段中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
}

3.3 Mapping

3.3.1 创建映射:

创建索引并指定映射

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

输出:

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

输出:

{
    "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"
            }
        }
    }
}
3.3.3 添加新的字段映射
PUT /my_index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

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

3.3.4 更新映射

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

3.3.5 数据迁移

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

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

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

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

不限于版本

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

例:

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

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]# 
  • 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
  • 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/
  • rm -rf *.zip
[root@0adeb7852e00 elasticsearch]# rm -rf elasticsearch-analysis-ik-7.6.2.zip 
3.4.2 测试分词器

默认

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

安装好ik分词器:

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

## 2
GET _analyze
{
   "analyzer": "ik_max_word", 
   "text":"中软国际"
}
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>

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

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

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

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

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

测试效果:

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

输出结果:

{
  "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
    }
  ]
}

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);

评论

暂无

添加新评论