ElasticSearch命令参考
目录
ElasticSearch 是一款强大的、开源的、分布式的搜索与分析引擎,简称 ES,它提供了实时搜索与聚合分析两大功能。
0,ES 与关系型数据库类比
ElasticSearch | 关系型数据库 |
---|---|
索引 | 表 |
文档 | 行 |
字段 | 列 |
Mapping / 映射 | 表定义 |
DSL | SQL |
ES 与关系型数据库的区别:
区别 | ElasticSearch | 关系型数据库 |
---|---|---|
索引方式 | 倒排索引 | B-Tree 结构 |
事务支持 | 不支持 | 支持 |
扩展方式 | 天生支持分片,易扩展 | 需借助第三方组件进行分库分表,不易使用 |
查询速度 | 为搜索而生,具有实时性 | 随着数据量的增大,搜索变慢 |
Mapping 的特点:
- 用于定义文档中字段的类型
- 文档中某字段的类型一旦确定就不能更改,因为 ES 会根据相应的类型,去建立索引信息
- 字段的类型虽然不能更改,但是 Mapping 中可以增加字段
1,查看集群信息
# 查看 ES 集群信息
> curl http://localhost:9200/
# 查看索引 mappings
> curl -XGET host:port/$index_name/_mappings?pretty
# 查看索引 settings
> curl -XGET host:port/$index_name/_settings?pretty
# 查看 ES 中安装的插件
> elasticsearch-plugin list
# 安装插件
> elasticsearch-plugin install analysis-icu
GET /_cat/plugins?v
查看 ES 中的插件GET /_cluster/health
查看集群的健康状态GET /_cat/nodes?v
查看所有的节点
2,查看索引信息
GET /_cat/indices?v
查看所有的索引信息GET /_cat/indices?v&health=green
查看状态为 green 的索引GET /_cat/indices?v&s=docs.count:desc
按照文档个数排序GET /_cat/indices?v&h=i,tm&s=tm:desc
查看索引使用的内存大小GET /_cat/indices/index_prefix*?v&s=index
查看所有的索引名以 index_prefix 为前缀的索引GET /_cat/indices/index_prefix*?pri&v&h=health,index,pri,rep,docs.count,mt
查看指定索引的指定信息
3,创建索引
每个索引都可以定义自己的 Mappings 和 Settings:
- Mappings:用于设置文档字段的类型。
- Settings:用于设置不同的数据分布。
1,创建索引
索引操作:
PUT $index_name
创建索引- 可加入 Mappings 和 Settings 信息(也可不加,采用默认设置)
- 不可重复创建,否则会报错
PUT $index_name/_mapping
创建索引,只有 mapping 信息PUT $index_name/_settings
创建索引,只有 settings 信息
HEAD $index_name
:查看索引是否存在,通过 http 响应的状态码查看- 状态码 200:表示索引存在
- 状态码 404:表示索引不存在
GET $index_name
查看指定索引相关信息(Mappings 和 Settings)GET $index_name/_mapping
只查看 mappingGET $index_name/_settings
只查看 settings
DELETE $index_name
删除整个索引,要谨慎使用!- 当删除的 index_name 存在时,会删除整个索引内容
- 当删除的 index_name 不存在时,ES 会返回 404 错误
POST $index_name/_close
关闭索引- 索引被关闭后,将不再能被使用,直到被打开
POST $index_name/_open
打开索引
创建索引:
PUT $index_name
{
"mappings":{
"properties":{
"col_1":{
"type":"text"
},
"col_2":{
"type":"keyword"
},
...
}
},
"settings":{
"number_of_shards":5, # 主分片数,不可更改
"number_of_replicas":2 # 副本分片数,默认为 0
}
}
虽然 ES 可以自动推断字段的数据类型,但最好还是自定义 mapping,因为,ES 自推断的类型,有可能并不是你想要的。
增加索引字段
虽然索引中的字段的类型是不能改变的,但是可以为索引增加字段。
# 为索引增加字段 col_3,类型为 keyword
POST $index_name/_mapping
{
"properties": {
"col_3": {
"type":"keyword"
}
}
}
其实字段类型是否能够修改,分两种情况:
- 对于新增字段
- 如果
mappings._doc.dynamic
为ture
,当有新字段写入时,Mappings 会自动更新。 - 如果
mappings._doc.dynamic
为false
,当有新字段写入时,Mappings 不会更新;新增字段不会建立倒排索引,但是信息会出现在_source
中。 - 如果
mappings._doc.dynamic
为strict
,当有新字段写入时,写入失败。
- 如果
- 对于已有字段
- 字段的类型不允许再修改。因为如果修改了,会导致已有的信息无法被搜索
- 如果希望修改字段类型,需要
Reindex
重建索引
dynamic
有 3 种取值,使用下面 API 可以修改 dynamic
的值:
PUT $index_name/_mapping
{
"dynamic": false/true/strict
}
2,重建索引
有时候我们需要重建索引,比如以下情况:
- 索引的 mappings 发生改变:比如字段类型或者分词器等发生更改。
- 索引的 settings 发生改变:比如索引的主分片数发生更改。
- 集群内或集群间需要做数据迁移。
ES 中提供两种重建 API:
- Update by query:在现有索引上重建索引
- 改变现有 mappings 时,使用该操作
- Reindex:在其它索引上重建索引
- 先创建一个新的索引 mappings
- 再使用 Reindex 迁移数据
# Reindex API
POST _reindex
{
"source": { # 指定原有索引
"index": "blogs"
},
"dest": { # 指定目标索引
"index": "blogs_new"
}
}
4,文档相关操作
文档的删除与更新:
- ES 中文档的删除操作不会马上将其删除,而是会将其标记到 del 文件中,在后期合适的时候(比如 Merge 阶段)会真正的删除。
- ES 中的文档是不可变更的,更新操作会将旧的文档标记为删除,同时增加一个新的字段,并且文档的 version 加 1。
1,查看文档
GET $index_name/_count
查看索引中的文档数量POST $index_name/_search
查看指定索引的前10条文档- POST Body 是
{}
- POST Body 是
GET $index_name/_doc/$id
获取指定索引中的指定文档
查询性能分析:
GET /$index_name/_search
{
"profile":"true", # 开启性能分析
"query": {
...
}
}
评分分析(对某个文档的匹配详情分析):
GET /$index_name/_explain/$doc_id
{
"query": {
...
}
}
2,写入文档
POST $index_name/_doc
不指定 ID 写入文档- 总是会插入新的文档,文档数加 1
PUT $index_name/_doc/$id?op_type=XXX
指定 ID 写入文档op_type=create
- 相当于
PUT $index_name/_create/$id
- 当 id 不存在时,会插入新的文档,文档数加 1
- 当 id 存在时,报错,不会插入新文档
- 相当于
op_type=index
- 相当于
POST/PUT $index_name/_doc/$id
- 当 id 不存在时,会插入新的文档,文档数加 1
- 当 id 存在时,会覆盖之前的,并且 version 会加 1,文档数不增加
- 相当于
3,更新文档
-
POST $index_name/_update/$id
更新指定文档的内容。更新的内容要放在 doc 字段中,否则会报错- 当 id 不存在时:报错,不更新任何内容
- 当 id 存在时:
- 如果更新的字段与原来的相同,则不做任何操作
- 如果更新的字段与原来的不同,则更新原有内容,并且 version 会加 1
- 实际上 ES 中的文档是不可变更的,更新操作会将旧的文档标记为删除,同时增加一个新的字段,并且文档的 version 加 1
-
POST $index_name/_update_by_query
根据条件更新文档
条件更新的语法:
POST $index_name/_update_by_query
{
"query":{ # 更新条件
...
},
"script":{ # 更新操作
...
}
}
upsert 操作,表示 update 与 insert
- 当目标文件存在时,则进行更新操作,否则进行插入操作。
格式如下:
POST /$index_name/_update/$id
{
"doc":{ # $id 存在时,进行更新操作
"col_1":"xxx",
"col_2":"yyy"
},
"upsert" { # $id 不存在时,进行插入操作
"col_1":"mmm",
"col_2":"nnn"
}
}
4,删除文档
DELETE $index_name/_doc/$id
删除指定文档- 当删除的 id 存在时,会删除该文档
- 当删除的 id 不存在时,ES 会返回 not_found
POST $index_name/_delete_by_query
根据条件删除文档
条件删除格式如下:
POST $index_name/_delete_by_query
{
"query":{ # 更新条件
...
}
}
5,并发控制
同一个资源在多并发处理的时候,会发生冲突的问题。
传统数据库(比如 MySQL)会采用锁的方式,在更新数据的时候对数据进行加锁,来防止冲突。
而 ES 并没有锁,而是将并发问题交给了用户处理。
在 ES 中可以采用两种方式:
- 内部版本控制(ES 自带的 version):在 URI 中使用 if_seq_no 和 if_primary_term
- 外部版本控制(由用户指定 version):在 URI 中使用 version 和 version_type=external
5,批量操作
- Bulk 批量操作
- Mget 批量读取
- Msearch 批量查询
注意在使用批量操作时,数据量不宜过大,避免出现性能问题,建议在 5~15M。
1,Bulk 操作
批量操作指的是,在一次 API 调用中,对不同的索引进行多次操作。
-
每次操作互不影响,即使某个操作出错,也不影响其他操作。
-
返回的结果中包含了所有操作的执行结果。
Bulk 操作支持 Index,Create,Update,Delete
,使用 POST 方法:
POST /_bulk
一般请求体的内容写在文件中,文件名以 .json
结尾。
批量写入的格式:
{"index":{"_index":"$index_name", "_id":"1"}} # 索引名称及 id
{...} # 索引内容
{"index":{"_index":"$index_name", "_id":"2"}} # 索引名称及 id
{...} # 索引内容
如果没有写 _id
字段,则 _id
由 ES 自动生成。
批量更新的格式:
{"update":{"_index":"$index_name", "_id":"1"}} # 索引名称及 id
{"doc":{...}} # 索引内容
{"update":{"_index":"$index_name", "_id":"2"}} # 索引名称及 id
{"doc":{...}} # 索引内容
批量删除的格式:
{"delete":{"_index":"$index_name", "_id":"1"}} # 索引名称及 id
{"delete":{"_index":"$index_name", "_id":"2"}} # 索引名称及 id
注意,批量 update
和 delete
时,必须指定 _id
字段。
一般使用 curl
命令写入:
# movies.json 是文件名
curl -s -XPOST "host:port/_bulk?pretty" -H "Content-Type: application/json" --data-binary "@movies.json"
2,Mget 读取
Mget 一次读取多个文档的内容,设计思想类似 Bulk 操作。
语法格式如下:
GET _mget
{
"docs" : [
{"_index" : "$index_name1", "_id" : "1"},
{"_index" : "$index_name2", "_id" : "2"}
]
}
# 也可以在 URI 中指定索引
GET $index_name/_mget
{
"docs" : [
{"_id" : "1"},
{"_id" : "2"}
]
}
# 用 _source 字段设置返回内容
GET _mget
{
"docs" : [
{"_index" : "$index_name1", "_id" : "1"},
{"_index" : "$index_name2", "_id" : "2", "_source" : ["f1", "f2"]}
]
}
3,Msearch 查询
格式如下:
POST $index_name1/_msearch
{} # 索引名称,不写的话就是 URI 中的索引
{"query" : {"match_all" : {}},"size":1}
{"index" : "index_name2"} # 改变了索引名称
{"query" : {"match_all" : {}},"size":2}
# URI 中也可以不写索引名称,此时请求体里必须写索引名称
POST _msearch
{"index" : "index_name1"} # 索引名称
{"query" : {"match_all" : {}},"size":1}
{"index" : "index_name2"} # 索引名称
{"query" : {"match_all" : {}},"size":2}
6,常见错误码
当我们的请求发生错误的时候,ES 会返回相应的错误码,常见的错误码如下:
错误码 | 含义 |
---|---|
429 | 集群过于繁忙 |
4XX | 请求格式错误 |
500 | 集群内部错误 |
7,数据类型
1,简单类型
- keyword:是不进行切分的字符串类型,主要用于对文档的过滤,排序,聚合
- 经常用于描述姓名,产品类型,用户 ID,URL,状态码等
- 查询这种类型,一般使用 term 查询
- 注意:对 keyword 类型使用 match 查询,是不会命中文档的
- text:可进行分割的字符串类型
- 如果希望某字段可以进行模糊查询(match),则可使用该类型
- 对该类型的查询,一般使用 match 查询(先进行分词,再匹配)
- 数值类型:一般用于对文档进行过滤,排序,聚合
- long,interger,short,byte
- double,float
- half_float,scaled_float,unsigned_long
- 对于这类数据类型,一般使用 term,range 查询
- boolean:有 true 和 false 两种值
- 该类型一般使用 term 查询
- date:日期类型
- ES 中可用如下三种形式表示 date 类型
- 格式化的日期字符串
- 秒级别的整型(从 1970.1.1 到现在)
- 毫秒级的长整型(从 1970.1.1 到现在)
- ES 中 date 的默认格式是
strict_date_option_time || epoch_millis
- strict_date_option_time 支持的格式有:
- yyyy-MM-dd,yyyyMMdd
- yyyyMMddHHmmss,yyyy-MM-ddTHH:mm:ss
- yyyy-MM-ddTHH:mm:ss.SSS,yyyy-MM-ddTHH:mm:ss.SSSZ
- 等
- epoch_millis 表示 1970.1.1 到现在的毫秒数
- strict_date_option_time 支持的格式有:
- 在写入数据时用什么格式,在查询时就用什么格式
- 一般使用 range 查询对 date 类型进行搜索
- ES 中可用如下三种形式表示 date 类型
注意,date 类型默认不支持我们常用的 yyyy-MM-dd HH:mm:ss
格式,如果需要使用该格式,则需要在 mapping 中定义:
{
"mappings": {
"properties": {
"create_time": {
"type":"date", # 日期类型
"format": "yyyy-MM-dd HH:mm:ss" # 指定格式
}
}
}
}
对 date 类型定义好了格式,就需要使用相应的格式进行写入和查询。
精确值与全文本
ES 中有精确值(Exact Values)与全文本(Full Text)之分,上面说到的数据类型,text 类型属于全文本类型,除了 text 之外,其它都是精确值。
- 对于精确值,ES 在建立索引和进行搜索时,都不会对文本进行分词分析
- term,terms,range,exists,bool 查询都属于精确值匹配
- 对于全文本,ES 在建立索引和进行搜索时,都会对文本进行分词分析
- 全文本类型适用于 match 查询
2,复杂类型
- 数组类型:没有特殊的定义方式
- 在写入时用中括号
[]
括起来就行,它在 mapping 中显示的还是元素的类型,而没有数组类型 - ES 中的空数组称为 missing field
- 写入空数组时,将字段值置为
[]
就行,例如"col_name":[]
- 对于数组类型的查询规则,遵循其元素的查询规则
- 在写入时用中括号
- 对象类型:和数组一样,对象类型也没有特殊的定义方式
- 在写入文档时,直接写入对象格式就行,ES 会自动将其转化为对象类型
- 对于对象类型的查询规则,遵循其元素的查询规则
- geo_point:地理类型
3,多字段
有时,对于同一个字段,需要有不同的索引方式,这时候就需要用到多字段。
比如下面的 mapping:
{
"mappings": {
"properties": {
"col_name": { # 自定义字段名称
"type": "text", # 字段类型
"fields": { # ES 关键字,用于定义多字段
"col_name_keyword": { # 自定义多字段名称
"type": "keyword" # 字段类型
}
}
}
}
}
}
对于上面的 mapping,ES 在建立索引时,会将 col_name 字段建立两份索引,分别是 text 类型和 keyword 类型。
4,nested 嵌套对象
直接上案例。
一个 movies 索引,其中 actors 是一个对象格式:
DELETE my_movies
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"properties" : {
"first_name" : { "type" : "keyword" },
"last_name" : { "type" : "keyword" }
}
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : { "type" : "keyword", "ignore_above" : 256 }
}
}
}
}
}
写入一条电影信息,注意 actors 写入的是数组:
POST my_movies/_doc/1
{
"title":"Speed",
"actors":[
{ "first_name":"Keanu", "last_name":"Reeves" },
{ "first_name":"Dennis", "last_name":"Hopper" }
]
}
查询电影:
POST my_movies/_search
{
"query": {
"bool": {
"must": [
{"match": {"actors.first_name": "Keanu"}},
{"match": {"actors.last_name": "Hopper"}}
]
}
}
}
这条查询语句居然查出了 _id 为 1 的文档。
那么问题出现了,我们查询的条件是 first_name 是 Keanu,并且 last_name 是 Hopper;显然 _id 为 1 的文档,并不满足这样的条件,那问什么能将其查询出来呢?
实际上 ES 在存储数组的时候,会将 _id 为 1 的文档,存成下面的样子:
{
"title": "Speed",
"actors.first_name": ["Keanu", "Dennis"],
"actors.last_name": ["Reeves", "Hopper"]
}
因此,我们的查询语句就搜索出了 _id 为 1 的文档。
那么如何解决这个问题呢(让那个查询语句查不出 _id 为 1 的文档)?
这时可以使用 nested 嵌套对象。
- nested 数据类型,使得对象数组中的对象是一个整体。
使用方式如下:
DELETE my_movies
# 创建 Nested 对象 Mapping
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"type": "nested", # nested 类型
"properties" : {
"first_name" : {"type" : "keyword"},
"last_name" : {"type" : "keyword"}
}},
"title" : {
"type" : "text",
"fields" : {"keyword":{"type":"keyword","ignore_above":256}}
}
}
}
}
然后写入文档,之后用下面的查询方式查询:
# Nested 查询
POST my_movies/_search
{
"query": {
"bool": {
"must": [
{ "match": {"title": "Speed"}},
{ "nested": { # 指定 nested 查询
"path": "actors", # 指定 nested 类型的字段名称
"query": { # 普通的 query 语句
"bool": {
"must": [
{"match": { "actors.first_name": "Keanu" }},
{"match": { "actors.last_name": "Hopper" }}
]
}
}
}
}
]
}
}
}
这样的查询语句时查不出我们刚写入的文档的(要的就是这个效果)。
nested 嵌套对象的聚合分析
格式如下:
# Nested Aggregation
POST my_movies/_search
{
"size": 0,
"aggs": {
"actors_aggs": { # 自定义聚合名称
"nested": { # 指定是 nested 类型聚合
"path": "actors" # 指定 nested 类型的字段名
},
"aggs": { # es 关键字,指定具体的聚合
"actor_name": { # 自定义聚合名称
"terms": { # 指定 terms 聚合
"field": "actors.first_name" # 指定聚合的字段
}
}
}
}
}
}
注意,对 nested 嵌套对象进行普通的聚合是不起作用的:
# 普通 aggregation 不工作
POST my_movies/_search
{
"size": 0,
"aggs": {
"actors_aggs": {
"terms": {
"field": "actors.first_name"
}
}
}
}
8,文档的高级查询
指定查询范围
通过 URI 可以指定在哪些索引中进行查询,有下面几种格式:
/_search
:在所有的索引中进行搜索。/$index_name/_search
:在 index_name 索引中进行搜索。/$index1,$index2/_search
:在 index1 和 index2 索引中进行搜索。/$index*/_search
:在所有的以 index 为前缀的索引中进行搜索。
ES 提供了丰富的查询功能:
- 进行完全匹配的 term 查询(不进行分词)
- 按照范围匹配的 range 查询(不进行分词)
- 进行分词匹配的 match 查询(进行分词)
一个查询模板:
GET /$index_name/_search
{
"query": { # 查询条件
...
},
"_source": [...], # 指定返回的字段
"sort":[ # 排序方式
{"col_name": {"order": "desc"}},
...
],
"from": 0, # 起始 id
"size": 10 # 页大小
}
为了不给 ES 造成较大负担,ES 使用 max_result_window
规定 from + size
的最大值,默认情况下为 10000。
修改该属性的方法(一般无需修改):
PUT /$index_name/_settings
{
"index": {
"max_result_window": 20000
}
}
为了解决深度分页问题,ES 有两种解决方案:Search After 和 Scroll。
不同分页方式的使用场景:
- 普通查询(不使用分页):需要实时获取顶部的部分文档。
- From-Size(普通分页):适用于非深度分页。
- Search After:需要深度分页时使用。
- Scroll:需要全部文档,比如导出全部数据。
1,match_all 查询
查看索引的所有文档:
# 默认返回前 10 行
GET $index_name/_search
{
"query":{
"match_all":{}
}
}
match_all 查询不会对文档进行打分,每个文档均为 1.0 分。
2,term 查询
term 查询是结构化精准查询的主要查询方式,用于查询待查字段和查询值是否完全匹配,格式如下:
GET $index_name/_search
{
"query": {
"term": { # term 查询
"col_name": { # 字段名称
"value": "$xxx" # 要匹配的值
}
}
}
}
term 查询还有一种简写形式:
GET $index_name/_search
{
"query": {
"term": { # term 查询
"col_name": "$xxx" # key:value 形式
}
}
}
term 查询一般针对的数据类型有:
- keyword 类型
- 数值类型
- 布尔类型
- 日期类型:要使用 mapping 定义的时间格式
- 数组类型
3,terms 查询
terms 是 term 的扩展形式,用于查询一个或多个值与待查字段是否完全匹配,格式如下:
GET $index_name/_search
{
"query": {
"terms": { # terms 查询
"col_name": [ # 字段名称,是一个数组
"$xxx", # 要匹配的值
"$yyy",
"$zzz",
...
]
}
}
}
terms 查询一般针对的数据类型有:
- keyword 类型
- 数值类型
- 布尔类型
- 日期类型:要使用 mapping 定义的时间格式
- 数组类型
4,range 查询
range 表示范围查询,比较符号有:
- gt:大于
- lt:小于
- gte:大于等于
- lte:小于等于
格式如下:
GET $index_name/_search
{
"query":{
"range": {
"$col_name": {
# 一个或多个比较符号
"gte": 300, # 这里 300 加与不加双引号是一样的
"lte": 500
}
}
}
}
range 查询一般针对的数据类型有:
- 数值类型
- 日期类型:要使用 mapping 定义的时间格式
对于日期类型,ES 内置了几个常用的符号:
符号 | 含义 |
---|---|
y | 年 |
M | 月 |
w | 周 |
d | 天 |
H / h | 时 |
m | 分 |
s | 秒 |
now | 现在 |
示例:
POST $index_name/_search
{
"query" : {
"range" : {
"$col_name" : { # 字段名称
"gte" : "now-10y" # 10年之前
}
}
}
}
5,exists 查询
exists 用于查找某个字段不为空的文档,下列情况都表示不为空:
- 值存在,且不是
null
- 值不是空数组
[]
- 值是数组,但不是
[null]
格式如下:
GET $index_name/_search
{
"query":{
"exists": { # 查询 col_name 字段不为空的文档
"field": "$col_name"
}
}
}
搜索不存在某个字段的文档,需要使用布尔查询。
如下:
POST index_name/_search
{
"query": {
"bool": { # 布尔查询
"must_not": { # 不存在
"exists": { # 不存在 $field_name 字段的文档
"field": "$field_name"
}
}
}
}
}
6,bool 查询
布尔类型支持四种子查询:
- must:必须匹配,相当于逻辑与(and)
- should:可以匹配,相当于逻辑或(or)
- must not:必须不匹配,相当于逻辑非(not)
- filter:必须匹配过滤条件,不进行打分计算
- 相当于不打分的 must
这四种子查询都包含一个数组,可以把其他的 term 级别的查询及 bool 查询放入其中。
格式如下:
GET $index_name/_search
{
"query": {
"bool": {
"must/should/must_not/filter": [
{"term":{}},
{"terms":{}},
{"range":{}},
{"exists":{}},
{"bool":{}},
...
]
}
}
}
7,match 查询
match 查询表示全文搜索,主要针对 text 类型,比如一段对话,商品介绍等。
不同于结构化查询,全文搜索会对查询词进行分析(分词),然后再进行查询。
- 结构化查询关注的是数据是否匹配,一般用于精确匹配
- 全文搜索关注的是匹配的程度(算分),用于模糊匹配
格式如下:
GET $index_name/_search
{
"query": {
"match":{ # 先对 $xxx 进行分词,在去字段中匹配
"col_name": "$xxx"
}
}
}
operator 属性
- 该属性决定文档按照分词后的词集合进行“与”还是“或”匹配
- 比如分词时分成了 A 和 B
- operator 用于控制查询时 A 和 B之间的关系
- 其取值有
and
(与关系) 和or
(或关系)- A and B 表示包含 A 和 B,它们之间没有先后顺序
- A or B 表示包含 A 或包含 B,它们之间没有先后顺序
- 默认为
or
minimum_should_match 属性
- operator 与关系对于搜索要求过于苛刻,或关系又比较宽松
- 此时可以使用 minimum_should_match 来控制最小匹配度
语法如下:
POST index_name/_search
{
"query": {
"match": {
"$field_name": {
"query": "$xxx",
"operator": "or",
"minimum_should_match": 80%
}
}
}
}
8,multi_match 查询
multi_match 表示在多个字段中进行 match。
格式如下:
GET $index_name/_search
{
"query": {
"multi_match":{
"query": "$xxx",
"fields": [ # 在哪些字段中进行匹配
"col_1",
"col_2",
...
]
}
}
}
9,match_phrase 查询
match_phrase 用于搜索确切的短语或邻近的词语。
# 此时表示的含义是
# one 与 love 紧邻,且 one 在 love 前面
# slop 表示 one 与 love 之间可以有几个字符,默认为 1
POST $index_name/_search
{
"query": {
"match_phrase": {
"$field_name":{
"query": "one love",
"slop" : 1
}
}
}
}
10,constant score 查询
默认的查询都会计算算分,constant score 称为不计分查询,所有分数都是 1。boots 属性用于改变分值。
语法如下:
POST $index_name/_search
{
"query": {
"constant_score": { # constant_score 查询,固定写法
"boost": 2.0, # 改变分值,默认为 1
"filter": { # 固定写法
"term": { # constant_score 包装一个 term 查询,就没有了算分
"avaliable": true
}
}
}
}
}
11,function score 查询
function score 暂略。
9,聚合操作
聚合操作是 ES 提供的第二项重要功能,其使用 aggs 子句。
1,统计指标
常见的统计指标包含以下:
- avg:平均值
- max:最大值
- min:最小值
- sum:求和
- stats:同时计算上面 4 种指标
- 另外还会返回 count,表示参与计算的文档数量
- 值为空值的字段不会参与计算
- value_count:专门用于统计字段非空值的数量
- 注意:如果进行聚合的字段是数组类型,则 value_count 统计的是符合条件的所有文档中该字段数组中非空元素个数的总和,而不是数组的个数总和
- cardinality:去重统计,类似 MySQL 中的 distinct
格式如下:
GET $index_name/_search
{
"size": 0, # 聚合操作,size 一般设为 0,表示不需要返回文档信息
"aggs": { # 聚合操作
"my_agg": { # 自定义聚合名称
"avg/max/min/sum/stats/value_count": {
"field": "$col_name", # 要进行聚合的字段
"missing": 100 # 当字段为空时,用该值替代,不写该属性时,默认为 0
}
}
}
}
对于空值的处理
- 索引中,会有部分文档,其某些字段可能缺失,或为空值
- 可以使用 missing 来补充空值
多个统计指标示例:
# 多个 Metric 聚合,找到最低最高和平均工资
POST $index_name/_search
{
"size": 0, # size 为 0
"aggs": {
"max_salary": { # 自定义聚合名称
"max": { # 聚合类型
"field": "$salary" # 聚合字段
}
},
"min_salary": { # 自定义聚合名称
"min": { # 聚合类型
"field": "$salary" # 聚合字段
}
},
"avg_salary": { # 自定义聚合名称
"avg": { # 聚合类型
"field": "$salary" # 聚合字段
}
}
}
}
2,桶聚合
ES 中的桶聚合相当于 MySQL 中的 Group By 语句,将同一类的数据放入同一个“桶”中。
桶聚合可分为:
- 单维度桶聚合
- 多维度桶聚合
单维度桶聚合
单维度桶聚合有:
- terms 桶聚合:按照字段的实际完整值进行匹配分组,适用于能够精确匹配的数据类型,比如:
- keyword 类型
- bool 类型
- keyword 数组类型
- text 类型不适合 terms 桶聚合
- ranges 桶聚合:针对的是数值类型,表示按照数值范围进行分组,使用到两个关键字:
- from:起始数值
- to:终止数值
- 范围是 [from, to),包含 from,不包含 to
- histogram 直方图桶聚合 :用于区间分桶
- date_histogram 直方图桶聚合 :用于时间区间分桶
terms 聚合示例:
GET $index_name/_search
{
"size": 0,
"aggs": {
"my_agg": { # 自定义聚合名称
"terms": {
"field":"$col_name" # 按照 $col_name 字段进行聚合
# 相当于 MySQL 中的以字段进行 group by 分组
}
}
}
}
默认情况下,进行桶聚合时如果不指定统计指标,则聚合的是文档计数,该值以 doc_count 为 key 存储在每一个 bucket 返回值中。
ranges 聚合示例:
GET $index_name/_search
{
"size": 0,
"aggs": {
"my_agg": { # 自定义聚合名称
"range": {
"field":"$col_name", # 聚合的字段
"ranges":[ # 指定多个范围
{ "to": 200 }, # from 默认为 0
{ "from": 200, "to": 500 },
{
"from": 500, # to 默认为该字段最大值
"key":"可以使用 key 自定义名称", # 如果没有定义 key,ES 会自动生成
}
]
}
}
}
}
histogram 桶聚合示例:
# Salary Histogram
POST $index_name/_search
{
"size": 0,
"aggs": {
"salary_histrogram": { # 自定义聚合名称
"histogram": { # histogram 聚合
"field":"$salary", # 聚合的字段
"interval":5000, # 区间值
"extended_bounds":{ # 最大最小范围
"min":0,
"max":100000
}
}
}
}
}
date_histogram 桶聚合示例:
POST $movies/_search
{
"size": 0,
"query": {
"range": {
"$publish_time": {
"gte": "2009-06-01",
"lte": "2010-01-01"
}
}
},
"aggs": {
"$publish_time_histrogram": { # 自定义聚合名称
"date_histogram": { # date_histogram
"field":"$publish_time", # 聚合字段
"calendar_interval": "year", # 聚合间隔
"format": "yyyy-MM-dd", # 时间格式
"min_doc_count": 3, # 最小数量
"extended_bounds":{ # 最大最小范围
"min":"2009-01-01",
"max":"2010-01-01"
}
},
"aggs": { # 在 date_histogram 的基础上再进行指标聚合
"$total_time": {
"sum": {
"field": "$movie_time"
}
}
}
}
}
}
指定统计指标:在单维度桶聚合的基础上,指定统计指标
# 先以 $col_name1 字段进行桶聚合
# 在其基础上,再对 $col_name2 字段进行聚合
GET $index_name/_search
{
"size": 0,
"aggs": {
"my_agg1": {
"terms": { # 先进行桶聚合
"field": "$col_name1"
},
"aggs": { # 在桶聚合的基础上,再进行聚合
"my_agg2": {
"sum" : { # 求和
"field": "$col_name2"
}
}
}
}
}
}
多维度桶聚合
ES 支持多维度(多层)桶聚合,一层一层的嵌套,如果有统计指标的话,统计指标放在最内层。
例如:
GET $index_name/_search
{
"size": 0,
"aggs": {
"my_level_1": { # 第一层桶聚合
"terms": {
"field": "$col_name1"
},
"aggs": {
"my_level_2": { # 第二次桶聚合,在第一层的基础上,再进行桶聚合
"terms": {
"field":"$col_name2"
},
"aggs": { # 最内层,指定统计指标
"my_sum" : {
"sum" : { # 求和
"field": "$col_name"
}
}
}
}
}
}
}
}
3,先查询再聚合
上面介绍到的都是直接聚合的方式,也就是没有指定 query 子句,是对索引内的所有文档进行聚合。
在加上 query 子句后,参加聚合的文档,必须先匹配 query 查询,才会进行聚合。
GET $index_name/_search
{
"size": 0,
"query": { # query 子句
...
},
"aggs": { # 聚合子句
...
}
}
4,前过滤器
只有符合过滤器条件的文档才会进行聚合。
GET $index_name/_search
{
"size": 0,
"query": { # query 子句
...
},
"aggs": { # 聚合子句
"my_agg": {
"filter": { # 前过滤器,与 query 子句语法一样
...
},
"aggs": { # 真正进行的聚合操作
...
}
}
}
}
5,聚合排序
可以使用 order 子句自定义排序方式,可选的排序方式有:
- 按照文档数排序,使用 _count 关键字(默认排序方式)
- 按照聚合指标排序,使用相应聚合名称
- 按照分组名称排序,使用 _key 关键字
_count 排序:
GET $index_name/_search
{
"size": 0,
"aggs": {
"my_agg1": {
"terms": { # 先进行桶聚合
"field": "$col_name1",
"order": { # order 子句
"_count": "asc" # 以 my_agg2 的结果进行正序排序
}
},
"aggs": { # 在桶聚合的基础上,再进行聚合
"my_agg2": {
"sum" : { # 求和
"field": "$col_name2"
}
}
}
}
}
}
order 中可以有多个指标:
"order":[
{"_count":"asc"}, # 先按照文档数排序
{"_key":"desc"} # 如果文档数相同,再按照 key 排序
]
以聚合名称排序:
"order": { # order 子句
"my_aggs": "asc" # my_aggs 是自定义聚合名称
}
以分组的组名排序: 也就是引用 buckets 结果中的 key
"order": { # order 子句
"_key": "asc"
}
6,其它属性
桶聚合的其它常用属性还有:
- size:返回的桶的数量,默认是 10
- min_doc_count:返回的桶的最小文档数,默认是 1
"aggs": {
"my_aggs": {
"terms": {
"field":"$col_name",
"size": 1000,
"min_doc_count": 10
}
}
}
10,搜索建议
搜索建议就是在用户输入搜索关键词的过程中,系统进行自动补全提示。
在搜索时,用户每输入一个字符,前端就需要向后端发送一次查询请求对匹配项进行查询,因此对后端的响应速度要求很高。
如下是百度的搜索建议:
ES 中提供的搜索建议功能,使用的索引结构不是倒排索引,而是在内存中构建的 FST,这种数据结构对内存的使用较多,一定要注意。
在 ES 中,要想使用搜索建议,对应的字段类型需要定义为 completion 类型。
PUT $index_name
{
"mappings": {
"properties": {
"$field_name": { # 字段名称
"type": "completion" # 字段类型
}
}
}
}
使用时,是这样使用的:
GET $index_name/_search
{
"suggest": {
"my_suggest": { # 自定义建议名称
"prefix": "$用户的输入词",
"completion": {
"field":"$field_name"
}
}
}
}
文章作者 @码农加油站
上次更改 2022-03-29