ElasticSearch 基本操作

基本操作

环境搭建

搭建Elasticsearch环境

下载docker镜像

1
docker pull elasticsearch:7.4.2

映射配置文件

1
2
3
4
5
6
7
8
9
# 配置映射文件夹
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data

# 设置文件夹权限任何用户可读可写
chmod 777 /mydata/elasticsearch -R

# 配置 http.host
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml

启动容器

1
2
3
4
5
6
7
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type"="single-node" \	 # 设置为单节点
-e ES_JAVA_OPTS="-Xms64m -Xmx128m" \ # 设置启动时ES的初始内存以及最大内存
-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

访问ES服务,http://82.157.127.173:9200/

得到相应体如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "name" : "de85ed684243",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "UeIP1PrXT2OFd7FlEEl3hQ",
  "version" : {
    "number" : "7.4.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96",
    "build_date" : "2019-10-28T20:40:44.881551Z",
    "build_snapshot" : false,
    "lucene_version" : "8.2.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

可以通过/_cat来获取节点信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 访问http://82.157.127.173:9200/_cat

# 属性列表
/_cat/allocation
/_cat/shards
/_cat/shards/{index}
/_cat/master
/_cat/nodes
/_cat/tasks
/_cat/indices
/_cat/indices/{index}
/_cat/segments
/_cat/segments/{index}
/_cat/count
/_cat/count/{index}
/_cat/recovery
/_cat/recovery/{index}
/_cat/health
/_cat/pending_tasks
/_cat/aliases
/_cat/aliases/{alias}
/_cat/thread_pool
/_cat/thread_pool/{thread_pools}
/_cat/plugins
/_cat/fielddata
/_cat/fielddata/{fields}
/_cat/nodeattrs
/_cat/repositories
/_cat/snapshots/{repository}
/_cat/templates

搭建Kibana环境

下载docker镜像

1
docker pull kibana:7.4.2

启动容器

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

访问Kibana服务,http://192.168.0.128:5601/

RESTful

一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

ES的基本REST命令:

Method Url 描述
PUT localhost:9200/索引名称/类型名称/文档id 创建文档(指定文档id)
GET localhost:9200/索引名称/类型名称/文档id 通过文档id查询文档
POST localhost:9200/索引名称/类型名称 创建文档(随机文档id)
POST localhost:9200/索引名称/类型名称/文档id/_update 修改文档
POST localhost:9200/索引名称/类型名称/_search 查询所有数据
DELETE localhost:9200/索引名称/类型名称/文档id 删除文档

CRUD

创建索引

在创建索引时,我们可以声明字段与数据类型的映射

请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
PUT /test0
{
    "mappings":{
        "properties":{
            "name":{
                "type":"text"
            },
            "author":{
                "type":"text"
            }
        }
    }
}

响应:

1
2
3
4
5
{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "test0"
}

即使如果我们没有配置类型,ES也会根据字段的内容来自行推导。

注意⚠️:由于索引具有不变性,我们只能进行追加而不能更改已经存在的映射字段,必须创建新的索引后进行数据迁移。

1
2
3
4
5
6
7
8
9
POST _reindex
{
  "source": {
    "index": "test0"
  },
  "dest": {
    "index": "test1"
 }
}

插入文档

PUT和POST都可以插入文档:

  • POST:如果不指定 id,自动生成 id。如果指定 id,则修改这条记录,并新增版本号。
  • PUT:必须指定 id,如果没有这条记录,则新增,如果有,则更新。

示例:在 test1 索引下的books类型中保存标识为 1 的文档。

请求:

1
2
3
4
5
PUT /test1/books/1
{
	"name":"three days to see",
  "author" : "Daniel Defoe"
}

响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "_index": "test",			//索引
    "_type": "book",			//类型
    "_id": "1",						//id
    "_version": 1,				//版本号
    "result": "updated",	//操作类型
    "_shards": {					//分片
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,					//并发控制字段,每次更新就会+1,用来做乐观锁
    "_primary_term": 1    //同上,主分片重新分配,如重启,就会变化
}

查询文档

示例:查询test1索引下的books类型中保存标识为 1 的文档的内容。

请求:

1
GET /test1/books/1

响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
    "_index": "test1",
    "_type": "books",
    "_id": "1",
    "_version": 1,
    "_seq_no": 0,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "name": "three days to see",
        "author": "Daniel Defoe"
    }
}

更新文档

使用POST命令,在ID后面加_update,并把需要修改的内容放入doc属性中

示例:更新test1 索引下的books类型中保存标识为 1 的文档的内容。

请求:

1
2
3
4
5
6
7
8
POST /test1/books/1/_update
{
	"doc" : {
		"name":"three days to see",
  	"author" : "Daniel Defoe",
  	"country" : "England"
	}
}

响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "_index": "test1",
    "_type": "books",
    "_id": "1",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

删除文档和索引

删除使用DELETE命令

示例:删除文档/test1/books/1

请求:

1
DELETE /test1/books/1

响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "_index": "test1",
    "_type": "books",
    "_id": "1",
    "_version": 3,
    "result": "deleted",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 2,
    "_primary_term": 1
}

示例:删除索引/test1

1
DELETE /test1

响应:

1
2
3
{
    "acknowledged": true
}

为了方便测试,可以从官网导入测试数据https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip

1
2
3
4
5
6
POST /test_data/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"}
....................

查询方式

ES支持两种查询方式,一种是直接在URL后加上参数,另一种是在URL后加上JSON格式的请求体。

示例:查找到收入最高的十条记录

URL + 参数

常用的参数如下

  • q:用于指定搜索的关键词。
  • from & size:类似于SQL中的offsetlimit
  • sort:对结果排序,默认为降序。
  • _source:指定想要返回的属性。
1
GET /test_data/_search?q=*&sort=balance:desc&from=0&size=10

URL + QueryDSL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
GET /test_data/_search
{
    "query": {
        "match_all" : {} 
    },
    "sort" : [{
    		"balance" : "desc"
    }],
    "from" : 0,
    "size" : 10
}

虽然URL+参数的写法非常简洁,但是随着逻辑的复杂化,其可读性也越来越差,所以通常都会使用URL + QueryDSL的格式。

match 匹配

match 匹配查询

无论你在任何字段上进行的是全文搜索还是精确查询,match 查询是你可用的标准查询。

对于not_analyzed的字段,match能做到精确查询,而对于analyzed的字段,match能做到匹配查询(全文搜索)。

示例:查找所有年龄为25岁的记录(精确查询)

请求:

1
2
3
4
5
6
7
8
GET /test_data/_search
{
    "query":{
        "match" : {
        	"age": 25
        } 
    }
}

示例:查询所有地址与976 Lawrence Street相关的记录(全文搜索)

请求:

1
2
3
4
5
6
7
8
GET /test_data/_search
{
    "query":{
        "match" : {
        	"address": "976 Lawrence Street"
        } 
    }
}

match_all 全部匹配

match_all 用于查询所有文档。在没有指定查询方式时,它是默认

示例:查询年龄最小的十条记录

请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
GET /test_data/_search
{
    "query": {
        "match_all" : {} 
    },
    "sort" : [{
    		"age" : "asc"
    }],
    "from" : 0,
    "size" : 10
}

match_phase 短语匹配

match_phase用于进行短语的匹配,它查询时并不是像term一样不进行分词直接查询,而是借助分析器返回的查询词的相对顺序以及偏移量来做判断——满足所有查询词且顺序完全相同的记录才会被匹配上。

示例:地址包含502 Baycliff Terrace的记录

请求:

1
2
3
4
5
6
7
8
GET /test_data/_search
{
    "query":{
        "match_phase" : {
        	"address": "502 Baycliff Terrace"
        } 
    }
}

multi_match 多字段匹配

multi_match 可以在多个字段上执行相同的 match 查询。

示例:查找city或address字段中包含Dixie或Street的记录。

请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
GET /test_data/_search
{
    "query":{
        "multi_match":{
            "query":"Dixie Street",
            "fields":[
                "city",
                "address"
            ]
        }
    }
}

term 精确查询

term直接在倒排索引中查询,也就是精确查找,不进行分词器分析,文档中必须包含整个搜索的词汇。

termmatch的区别:

  • match是经过分析处理的,查询词先被文本分析器处理后再进行查询。所以根据不同的文本分析器,分析出

的结果也会不同。

  • term是不经过分析处理的,直接去倒排索引查找精确的值。

由于text字段会被文本分析器处理,所以通常全文检索字段用match,其他非text字段(not_analyzed)匹配用term。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
GET /test_data/_search
{
  "query": {
    "term": {
      "address": "Street"
    }
  }
}

// 虽然文档中存在”702 Quentin Street“,但是由于文本分析器默认会转为小写,无法搜到任何数据

布尔查询(复合查询)

借助布尔查询可以实现如SQL中(and、or、!=)等逻辑条件的判断,并且可以合并任何其他查询语句,包括复合语句。复合语句之间可以相互嵌套,可以表达复杂的逻辑。

  • must(and):文档必须匹配这些条件才能被包含进来。(影响相关性得分)

  • must_not(not):文档必须不匹配这些条件才能被包含进来。(不影响相关性得分)

  • should(or):如果满足这些语句中的任意语句,将增加得分 。(用于修正相关性得分)

示例:查找年龄不等于18的地址包含Street的男性,且优先展示居住在30岁以上的的记录

请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
GET /test_data/_search
{
    "query":{
        "bool":{
            "must":[
                {
                    "match":{
                        "address":"Street"
                    }
                },
                {
                    "match":{
                        "gender":"M"
                    }
                }
            ],
            "must_not":[
                {
                    "match":{
                        "age":"18"
                    }
                }
            ],
            "should":[
                {
                    "range":{
                        "age":{
                            "gt":30
                        }
                    }
                }
            ]
        }
    }
}

Filter 过滤器

Filter通常搭配布尔查询一起使用,用于过滤出所有满足Filter的记录,不影响相关性得分。

示例:查找年龄在30~60之间的记录

请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
GET /test_data/_search
{
    "query":{
        "bool":{
            "filter":[
                {
                    "range":{
                        "age":{
                            "gte":30,
                            "lte":60
                        }
                    }
                }
            ]
        }
    }
}

Aggregations 聚合

要掌握聚合,你只需要明白两个主要的概念:

  • 桶(Buckets):满足特定条件的文档的集合
  • 指标(Metrics):对桶内的文档进行统计计算

翻译成SQL的形式来理解的话:

1
2
3
4
5
SELECT 
	COUNT(1),
  MAX(balance)
FROM table
GROUP BY gender;

桶在概念上类似于 SQL 的分组(GROUP BY,如上面的GROUP BY gender),而指标则类似于 COUNT()SUM()MAX() 等统计方法,如MAX(balance)

聚合的语法如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
"aggregations" : {
    "<聚合名称 1>" : {
        "<聚合类型>" : {
            <聚合体内容>
        }
        [,"元数据" : {  [<meta_data_body>] }]?
        [,"aggregations" : { [<sub_aggregation>]+ }]?
    }
    ["聚合名称 2>" : { ... }]*
}

示例:按照性别进行分组,计算平均年龄和最高收入

请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
GET /test_data/_search
{
    "query":{
        "match_all": {}
    },
    "aggs":{
        "group_gender":{
            "terms":{
                "field":"gender"
            },
            "aggs":{
                "avg_age":{
                    "avg":{
                        "field":"age"
                    }
                },
                "max_balance":{
                    "max":{
                        "field":"balance"
                    }
                }
            }
        }
    },
    "size":0
}
Built with Hugo
主题 StackJimmy 设计