六.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);
评论