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