es是文档数据库
es8.x默认开启了security,无法0配置启动
配置:
cluster.name: 机器名称,同一集群名称相同,用来发现集群节点
Node.name: 节点名称,同一集群节点名称是唯一的,对应一个es实例
path.data:
path.log:
network.host: es对外提供服务的IP地址,默认会绑定本地回环地址
http.port: 对外提供服务的端口号,默认9200,如果已绑定会自动递增
transport.port: 节点间通信的端口号,默认9300,集群使用
首次启动之后,es会自动在yml配置文件中添加xpack securiy的配置,将它置为false,重新启动,才可以localhost:9200访问,返回Json,说明成功启动,json中有主机名、集群名
开发模式和生产模式:
开发模式:没有引导检查(启动时检查配置),默认是开发模式
生产模式:有引导检查,检查配置更严格
如果配置了非本地回环地址,能和其它节点形成集群,则进入生产模式
安装启动es
es不能root启动
useradd es
passwd es
chown -R es:es /home/es
./elasticesearch -d 后台启动,8.x启动后会打印密码(可以通过reset修改)、CA证书密钥、kibana访问token,需要保存。
启动后要通过https访问,账号是elastic
yml配置中将xpack.enable=false,则可以用http访问
集群
集群加入节点:在原集群的一个节点执行es-create-enrollment-token -s node创建token,在新节点执行这个token(bin/elasticsearch --enrollment-token )
单项目多节点:单个es目录,使用./es -Epath.data= -Epath.logs= -Enode.name= -Ecluster.name=
多项目多节点:多个es目录,占磁盘空间,独立配置,方便用配置文件修改,使用一个外部命令执行多个节点的启动,localhost:9200/_cat/nodes?v查看集群节点
安装启动kibana
如果修改了es端口号,需要修改kibana的es访问配置,es.hosts
启动后,访问localhost:5601,设置es的kibana token
进入devtools,可以执行查询脚本
学习方法
有所学有所不学:只学高频用到的,不要一下子都学完,而是先掌握大体框架,后续的用到时再看官方文档,分清学习的先后顺序,先把好拿的分拿到
定义
es是分布式文档系统,一行相当于一个文档(document),只能保存json。
是OLAP(联机分析处理)系统,不是OLTP(联机事务处理)
自带分布式,不依赖zk
es支持的搜索类型:
结构化搜索:可以确定数据结构类型的
非结构化搜索:不能确定数据结构类型
文本搜索
地理位置搜索:查询某点半径范围内的,多边形搜索,是否相交、相离
es支持的查询类型:
es擅长与不擅长:
最擅长从海量数据检索少量相关数据,但不擅长单词查询大量数据(单次导出大量数据)
擅长查询,不擅长写入,写入实时性不高,牺牲写入实时性来实现查询
不支持事务
概念
角色
- 主节点(active master):活跃的主节点,从候选主节点选出来的,作用:管理集群,应避免主节点做重负载的任务
- 候选节点(master eligeble):配置文件中配置的master-eligible,主节点发生故障时或第一次启动时,选举和被选举主的节点
- 数据节点(data node):做数据相关操作,保存、搜索,做大负载的工作,数据节点也可以投票(voting_only的数据节点),但不能被选举
- 预处理节点(ingest node):数据写入前的预处理操作,过滤
配置:
老的版本用node.master=true、node.data=true,
新版本:node.roles:[master,data],一个节点可以同时是多种角色,默认不配置表示具备所有的角色
索引
GET index_name 查询索引
索引的组成:
- 别名(alias):
- settings:索引设置,如分片和副本的数量
- mapping:映射,定义了索引的字段,字段类型、长度、分词器等
索引可以表示数据文件,也可以表示索引文件,比如倒排索引等
类型
是索引的下一级,7.0可以使用非_doc类型,推荐使用_doc类型,8.0只能用_doc类型
DELETE test_index
PUT /test_index/test_type/1{
...
}PUT /test_index/_doc/1{
...
}
8.0删除了类型的概念
文档
相当于一行,可以通过cat api查询
GET _cat/indices?v 查询所有索引
GET [indexName]/_doc/[id] 查询某个索引的某个文档id
元数据字段(meta data),带下划线的:
_index:索引名称
_id:文档id
_version:文档的版本号,每次修改版本加1
_seq_no
_primary_term
_source
found: 有数据为true
源数据(source data业务数据)字段:_source中的字段
集群
自动发现
默认是零配置自动发现的,会自动绑定本地回环地址,并扫描本地端口9300~9305上运行的其它节点,自动形成集群
network.host: 提供服务的IP地址,默认本地唤回地址,配置此项会导致开发模式转向生产模式,从而触发引导检查
network.publish.host:节点间通信的公网地址
http.port:服务端口号,默认9200~9299
transport.port:节点间通信的端口号,默认9300~9399
discovery.seed_hosts:候选节点列表(master-eligible),ip+port
cluster.initial_master_nodes:首次启动时的候选节点,只有第一次启动需要,之后可以删除,如果配置了network.host,此项必须配置
m:master, d:data, v: voting only经投票节点
作业:5 nodes,3候选,2master role,1进投票和data,2data, 2m, 1mdv,2d
分片:索引分为多个分片,每个分片保留一部分数据,p
副本:分片的备份,一个分片对应多个副本,r
分片号:分片和对应的副本分片号相同
集群健康状态:可以通过head、elasticvue插件查看,或者_cat/health?v,_cluster/health
绿色:所有分片都可用
黄色:至少有一个副本不可用,但所有主分片都可用
红色:有一个主分片不可用,数据不完整
集群故障诊断
_cat/indices 查询集群索引
_cluster/allocation/explain 诊断分片未分配的原因
分片
主分片(primary shard, P):可读写
副本分片(replica shard, R):只读,
作用:1、备份,容灾。2、提高并发读写能力
分片策略:7.0之前:默认5P, 1R;8.0:默认1P1R
每个分片都是一个Lucene实例
- 一个文档不可能同时存在于多个主分片,但可以同时存在于多个属于同一主分片的副本
- 主分片和副本不能存在同一节点(a)
- 同一主分片的多个副本不能存在于同一节点(b)
- a和b可以得出:分片号相同的分片(即数据相同的分片)不能存在于同一节点
- 节点加入和离开时会触发分片再均衡(shard rebalance),使分片尽量平均分布在每个节点,但是再均衡不能违反a和b,如果违反了,则会有未分配的分片
开发工具
es-head,es-vue,kibana
API
索引API
查询
GET _search,在hits.hits中有所有的索引数据,_index:索引名称,_id: 文档号,_source:业务数据
- 默认只会查前10条,GET _search?size=20
- took:查询毫秒数,time_out:是否超时
GET [索引名称]/_search,查某个索引的
GET [索引名称]/_search?from=9&size=100&timeout=10,分页查询,从第九个开始(默认是0,从第一条开始),查100条,timeout:查询超时时间
PUT [index_name]:创建索引,默认别名和映射为空
GET [index_name]:查询索引的别名(alias)、映射(mapping)、设置(setting),设置包括number_of_shards主分片数,副分片数
http
//创建索引,已存在则失败
PUT [index_name]
{
"settings":{
"number_of_shards":1,//主分片数
"number_of_relicas":1//每个主的副本数
}
}
//修改索引的设置,已打开的索引的主副分片数不能修改
PUT [index_name]/_settings
{
"number_of_shards":2
}
静态索引设置:只能在创建索引或索引关闭时设置,比如主分片数,每个索引的分片数上限是1024
动态索引设置:可以通过_setting API实时修改的,比如副分片数、index.refresh_interval(索引刷新频率,默认1s,写入后多久才能查到)、index.max_result_window(_search API查询的窗口数限制,from+size不能超过,默认10000,比如from=9990,size=11,则报错,size=10可以)
索引命名规范:
- 必须小写、不能特殊符号、不能中文、可以_ - +三种,不能超过255字节
- 应该小写英文字母下划线命名,不能驼峰
删除索引:DELETE [index_name]
判断索引是否存在:HEAD [index_name],返回404表示索引不存在,GET也可以
索引的不可变性:
- 索引名称创建后不能修改,如果要修改只能重建索引,代价较高,可以通过别名
- 打开状态的索引的主分片数量不能修改
- 字段类型不可变
GET [ind]/_count 查询索引文档数
reindex:通过指定源索引和目标索引,重新创建索引文档 ,源索引必须有文档,才会创建目标索引,并且只会复制文档,其它的不会复制,比如映射
http
POST _reindex
{
"source":{
"index":[source_index_name]
},
"dest":{
"index":[dest_idx]
}
}
文档API
创建文档
op_type:
- index: 文档id不存在则创建,存在则替换,是全量更新,将所有字段更新,未指定的字段会被删除
- create:文档id不存在则创建,存在则报错
均为写操作,都发生在P shard
http
//create
PUT /[idx_name]/_doc/[id]?op_type=create
{
"name":"",
"content":""
}
//简化写法,与上面的等价
PUT /[idx_name]/_create/[id]
{
"name":"",
"content":""
}
//index
PUT /[idx_name]/_doc/[id]?op_type=index
{
"name":"",
"content":""
}
//不加op_type相当于op_type=index
PUT /[idx_name]/_doc/[id]
{
"name":"",
"content":""
}
//自动生成id,不指定id即可,会生成一字符串格式的id
POST /[idx_name]/_doc
{
"name":"",
"content":""
}
GET [idx_name]/_search/
查询文档
GET [idx_name]/_doc/[id] 查询文档,found=false表示文档不存在
HEAD [idx_name]/_doc/[id] 检查文档是否存在
GET [idx_name]/_doc/[id]?source=false 只查询元字段(meta data),不查询源字段(source data,业务数据)
GET [idx_name]/_source/[id] 只查源字段
DELETE [idx_name]/_doc/[id] 删除某个文档
部分字段更新文档
- 部分更新字段:使用POST,uri使用_update,字段放在doc中。如果不放在doc中,或者uri中使用_doc,仍为全量更新
- 创建时不能加doc,否则会当成业务字段
http
//7.x,8.x不支持
POST [idx_name]/_doc/[id]/_update
{
"doc":{
"name":""
}
}
//只更新指定字段,未指定的字段不变
POST [idx_name]/_update/[id]
{
"doc":{
"name":""
}
}
批量
http
//批量查不同索引的文档
GET /_mget
{
"docs":[
{
"_index":"[idx_name]",
"_id":"[doc_id]",
"_source":false//只查元字段
},
{
"_index":"",
"_id":""
}
]
}
//批量查某个索引的文档
GET [idx_name]/_mget
{
"ids":["",""]
}
http
//批量写入的action和data必须各自只占一行,一共3行,action包括create和index、update、delete
POST _bulk
POST [idx_name]/_bulk
{"action":{"meta data"}} //必须只占一行
{"data"} //必须只占一行
//批量创建多个索引,并为每个索引添加文档
POST _bulk
{
"create":{
"_index":"[idx_name]",
"_id":"[doc_id]"
}
}
{
"name":""
"content":""
}
{
"create":{
"_index":"[idx_name1]",
"_id":"[doc_id]"
}
}
{
"name":""
"content":""
}
POST _bulk
{"create":{"_index":"test_bulk1","_id":1}}
{"name":"陈文捷","age":27}
{"create":{"_index":"test_bulk2","_id":1}}
{"name":"陈文捷2","age":28}
POST _bulk
{
"update":{
"_index":"[idx_name]",
"_id":"[doc_id]"
}
}
{
"doc":{
"name":""
"content":""}
}
POST _bulk
{
"delete":{
"_index":"[idx_name]",
"_id":"[doc_id]"
}
}
POST _bulk
{"create":{"_index":"test_bulk1","_id":1}}
{"name":"陈文捷","age":27}
{"create":{"_index":"test_bulk2","_id":1}}
{"name":"陈文捷2","age":28}
POST _bulk
{"update":{"_index":"test_bulk1","_id":1}}
{"doc":{"age":29}}
{"update":{"_index":"test_bulk2","_id":1}}
{"doc":{"age":29}}
POST _bulk
{"delete":{"_index":"test_bulk1","_id":1}}
{"delete":{"_index":"test_bulk2","_id":1}}
GET _mget
{
"docs": [
{
"_index": "test_bulk1",
"_id":"1"
},{
"_index":"test_bulk2",
"_id":"1"
}
]
}
action:
- create:创建,索引已存在则报错
- index:创建,存在则全量替换
- update: 部分字段更新
- delete:只需要action行,不需要data行
bulk api对json串有严格的要求,每个操作必须2个json串,2行,性能
delete by query
通过等值条件来删除
http
POST [idx]/_delete_by_query
{
"query":{
"term":{
"price":6999 //price字段是6999的删除
}
}
}
查询API
- _source:过滤结果集字段,相当于select f1,f2...,支持通配符"*"。可以直接指定一个数组,表示只查询满足条件的字段,也可以使用include和exclude,同时指定时,会先执行include过滤,再在include基础上执行exlude过滤。为false时,表示不查询源数据,只返回元字段
- 分页:根据from、size分页,前者指定分页偏移量,后者指定分页大小,默认是0和10,from+size默认不能超过10000,因为es是分布式数据库,分页时需要将排序后from+size条满足条件的数据拉取到一起返回,太大oom。如果需要偏移超过10000的分页,用search_after。
排序
- 使用sort指定排序字段,值是一个数组,其中的元素可以为字段名称、对象、_score、_doc
- 为字段名称时,默认升序;_score默认按照分值降序;_doc表示索引中存储的顺序(文档添加的顺序)
- 为对象时,对象中的key是字段名称,对应的值有order:asc/desc。
- mode:用于设置数组类型的字段取哪个元素的值来参与排序,min取最小值,max最大值,avg取平均值,sum取总和
- numeric_type:可以同时查询多个索引,逗号分割,GET idx1,idx2/_search,对其中名称相同,类型不同的数值类型的字段,可以通过numeric_type指定先转成什么类型,再排序,比如long和double可以统一转成long
- sort支持多个字段排序,前面相同的,使用后面的排序规则
查询
- 全文检索:无搜索条件边界,召回结果取决于相关性,而相关性计算无明确边界性条件,如同义词、谐音、别名、错别字、混淆词等均可成为相关性判断依据
- es实现的全文检索:包含2个过程:1、TEXT类型的字段插入时,会经过文档归一化器和分词器处理,得到多个词项,然后根据词项到包含该词项的文档的ID的映射,也就是倒排索引。2、搜索时,搜索字段也会经过分词器得到词项,然后在倒排索引中匹配。
- match:将搜索词分词成词项后,依次和文档中的词项匹配,如果只有query参数,可以省略它,operator: and/or, query查询词的每个词项匹配文档词项之间是and还是or关系,默认or,只要有一个查询词项匹配文档词项,该文档匹配,改成and时,所有查询词项都匹配文档词项时,文档匹配。
- math_phrase: 也会将搜索词分词,但和文档词项匹配时,搜索词项顺序必须和文档词项一致,并且对应的文档词项间隔的词项数必须小于等于slop,slop默认0。和match的区别:math_phrase查询结果集更小,相关度更高,match对搜索词的顺序和对应的文档词项间隔没有限制。
- term: 不会对搜索词进行任何处理,包括大小写、标点、分词等,和match的区别:match会对搜索词进行分词处理,适用于text,而term适用于非text类型字段,比如keyword,date,numeric。它们只决定是否对搜索词进行分词,而文档字段的分词取决于是否text类型
- 自动映射生成的text类型的字段会包含一个keyword类型的子字段,term查询时通过[field].[子字段名称]查询
- term查询时,大小写不匹配查询不到,text分词后的词项默认会转成小写
- term查询不会分词的字段时,相当于等值查询,如果字段有多个值(数组类型),等于其中一个值则文档匹配
- terms: 相当于in查询
组合查询
- 支持多条件查询,
- must和filter: 与
- should:其中至少minimum_should_match个条件满足时为true,否则false,minimum_should_match默认是1,所以should默认是或
- must_not:等价于must取反
- must和should会计算相关度评分,filter和must_not不会, score为0,如果不需要评分,应该将must替换为filter
- bool可以嵌套,从而嵌套条件,bool相当于括号
- bool默认逻辑关系是且, filter和must可以同时使用,将不需要计算评分的条件放到filter里,从而减少must需要计算的分值个数,比如按照价格范围和名称查询,价格范围查询不需要计算评分,那么可以将价格范围条件放到filter,名称查询放到must
- filter:忽略评分、缓存
http
GET test_index3/_search
{
"query": {
"bool": {
"should": [
{
"bool": {
"must": [
{
"term": {
"name": "chenwenjie"
}
},
{
"term": {
"age": 28
}
}
]
}
},
{
"bool": {
"must": [
{
"term": {
"name": "guojing"
}
},
{
"term": {
"age": 28
}
}
]
}
}
]
}
}
}
bash
GET test_index4/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"type": "shouji"
}
}
],
"filter": [
{
"match": {
"tags": "xingnengqiang"
}
}
]
}
}
}
桶聚合
- 定义:桶是所有文档的子集,符合桶条件的文档逻辑上放入对应的桶,桶条件通过聚合类型指定,放入桶时会自动计算桶中文档数
- 聚合通过aggs指定,需要自定义聚合名称,名称下一级key是聚合的类型,聚合类型:terms、range
- terms类型:桶是指定字段key的值
- size:聚合结果的个数,默认只返回前10条文档数最多的,没返回的数量是sum_other_doc_count
- 聚合是近似值
- text类型不能聚合
- shard_size(https://blog.csdn.net/UbuntuTouch/article/details/104141398):如果有多个主分片,聚合实际上分为2部,类似于map-reduce,先对每个分片单独聚合(map),再将分片聚合结果汇总聚合(reduce),reduce时,每个分片聚合结果最多取前文档数最多的shard_size个,从而减少分片数据传输的时间。shard_size默认大于size,为size * 1.5 + 10,如果等于size,如果某个聚合key数据倾斜严重,在某些分片文档很多,而在其他分片文档很少,那么reduce时,会导致文档数多的分片取了该key,而文档数少的分片没取,造成结果数量不对。适当调大shard_size可以使聚合结果更精确,但查询更慢
- show_term_doc_count_error:如果为true,聚合结果中的每个聚合key会显示doc_count_error_upper_bound,它是对应聚合key在每个分片中未被统计进shard_size的数量,如果大于0,说明对应的聚合key统计不准确,可以通过调大shard_size,直到为0
- range:通过from、to定义桶的范围,指定字段的值符合桶的范围的计入该桶
- 全局聚合过滤:可以在query的基础上进行聚合,外层的size分页大小不影响聚合,只影响hits.hits结果集的个数,hits.total.value是满足query条件的文档总数,但不超过10000,aggs中的size才是对聚合结果的取前n个
- aggs中的order:是对聚合结果进行排序,默认按照文档个数倒序,_count=desc,_term按照聚合key排序,还可以通过指定嵌套聚合名称,对其中的嵌套聚合结果进行排序,[嵌套聚合名称].value的value可以省略,其他不能省略,比如stats
http
GET test_index4/_search
{
"_source": false,
"aggs": {
"agg_type": {
"terms": {
"field": "type.keyword",
"shard_size": 20
}
}
}
}
http
GET test_index4/_search
{
"_source": false,
"aggs": {
"agg_type": {
"range": {
"field": "price",
"ranges": [
{
"to": 4000
},
{
"from": 4000,
"to": 6000
},
{
"from": 6000,
"to": 8000
},
{
"from": 8000
}
]
}
}
}
}
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"mmy": {
"terms": {
"field": "type.keyword",
"order": {
"_count": "asc",
"my1":"asc"
}
},
"aggs": {
"my1": {
"avg": {
"field": "price"
}
}
}
}
}
}
指标聚合
- 根据文档的字段计算指标,比如最大值、最小值、平均值等
http
GET test_index4/_search
{
"_source": false,
"aggs": {
"my_agg": {
"max": {
"field": "price"
}
},
"my_agg1":{
"min": {
"field": "price"
}
},
"my_agg2":{
"avg": {
"field": "price"
}
},
"my_agg3":{
"stats": {
"field": "price"
}
},
"my_agg4":{
"value_count": {
"field": "price"
}
},
"my_agg5":{
"sum": {
"field": "price"
}
}
}
}
管道聚合
- 将其他聚合结果作为输入,得到新的聚合结果,分为2种:1、Parent: 在父聚合的结果上进一步聚合。2、Sibling:在兄弟聚合的结果上聚合
- bucket_path:用于指定兄弟聚合的名称,可以通过">"指定兄弟聚合的子聚合,只能是相对路径,并且不能跳到上一级
- 场景:根据品牌对商品桶聚合,对每个桶计算价格平均数(父聚合),计算价格平均数最小的品牌(兄弟聚合)
- min_bucket是兄弟聚合
http
GET test_index4/_search
{
"_source": false,
"aggs": {
"my_agg": {
"terms": {
"field": "type.keyword",
"size": 10
},
"aggs": {
"my_agg1": {
"avg": {
"field": "price"
}
}
}
},
"my_agg2":{
"min_bucket": {
"buckets_path": "my_agg>my_agg1"
}
}
}
}
doc_values, index
- doc_values: 排序和聚合需要根据文档查找值,也就是正向索引,doc_values用于开启正向索引,非text和annotated_text类型的字段默认开启,因此只有text和annotated_text类型的字段不支持排序和聚合,如果某个字段不需要排序和聚合,可以将doc_values设为false,从而节约磁盘空间。对为false的字段排序或聚合会报错: Can't load fielddata on [name.keyword] because fielddata is unsupported on fields of type [keyword]. Use doc values instead.
- index:是否创建反向索引,默认true,为fasle的字段不能搜索,会报Cannot search on field [type] since it is not indexed.
- 反向索引是值到文档的映射,记录了值出现在哪些文档中;正向索引记录的文档中有哪些值,对过滤结果进行聚合时,过滤结果通常是一部分文档,需要根据这部分文档查找其中的值,反向索引需要扫描整个索引,不适合。
- text不能开启doc_values,报Failed to parse mapping [_doc]: [text] fields do not support doc values
- doc_value内部列式存储,便于压缩节约磁盘空间,比如数值类型字段,如果所有的值都相同或都为空,只需要设置标志位、记录一个值;如果都小于256,每个值只需要1字节;如果大于256并且有公约数,除以公约数再存储;如果没有公约数,以最小值为起始值,其他值只存储偏移量。字符串类型可以使用字符串常量池,相同字符串只存储一次,key是整数,又可以使用整数存储。
- doc_value存储在磁盘上,不会立即fsync刷盘,而是使用os page cache,所以es 堆内存不宜过大,使用了doc_value时,应当小于50%
- fielddata: 和doc_values一样,也是用于开启正向索引,但存储在堆内存中,并且只有text类型支持fielddata。
聚合filter
- 使用场景:需要多种条件的聚合结果,比如要返回所有商品的平均售价和电脑的平均售价。和全局聚合过滤的区别:query对所有聚合都生效,而filter只对单个聚合的生效,先执行filter再执行对应的聚合
- filters聚合:filter条件和聚合key相同时的桶聚合的简写形式,固定2层filters嵌套。
- 匿名filters:filters聚合内部的聚合名称可以省略,filters可以为数组类型
- other_bucket:显示不满足filters聚合条件的文档数,结果集默认key为_other_,可以通过other_bucket_key指定,匿名filters时为最后一个bucket
- 如果同时使用了query和聚合filter,会同时生效,关系为且
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"mmy": {
"avg": {
"field": "price"
}
},
"my1": {
"filter": {
"term": {
"type.keyword": "computer"
}
},
"aggs": {
"NAME1": {
"avg": {
"field": "price"
}
}
}
}
}
}
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"my":{
"filters": {
"filters": {
"NAME1": {
"term": {
"price": "4999"
}
},
"name2":{
"term": {
"type.keyword": "shouji"
}
}
}
}
}
}
}
GET test_index4/_search
{
"size": 0,
"aggs": {
"my": {
"filter": {
"term": {
"price": "4999"
}
},
"aggs": {
"my1": {
"terms": {
"field": "price"
}
}
}
},
"my2": {
"filter": {
"term": {
"type.keyword": "shouji"
}
},
"aggs": {
"my3": {
"terms": {
"field": "type.keyword"
}
}
}
}
}
}
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"my": {
"filters": {
"filters": [
{
"term": {
"price": "4999"
}
},
{
"term": {
"type.keyword": "shouji"
}
}
]
}
}
}
}
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"my": {
"filters": {
"other_bucket": true,
"filters": [
{
"term": {
"price": "4999"
}
},
{
"term": {
"type.keyword": "shouji"
}
}
]
}
}
}
}
global筛选器
- query筛选条件会对所有聚合生效,而指定了global筛选器的聚合不受query筛选条件的影响
- 如果指定了global筛选器,必须嵌套一层聚合。global筛选器只能用于顶层聚合,不能用于嵌套聚合,是一种只能用于顶层的聚合类型。
http
GET test_index4/_search
{
"size": 0,
"query": {
"term": {
"type.keyword": {
"value": "computer"
}
}
},
"aggs": {
"computer_bucket": {
"terms": {
"field": "type.keyword"
}
},
"all_bucket": {
"global": {},
"aggs": { //指定了global筛选器,必须嵌套一层聚合
"all_bucket1": {
"terms": {
"field": "type.keyword"
}
}
}
}
}
}
post_filter
- 先执行query,在执行聚合,最后执行post_filter,它只过滤hits中的结果,不影响聚合结果
- 使用场景:页面上对查询结果进一步过滤,但聚合统计的结果不变
- 应该和聚合同时使用,不应该替换filter,因为post_filter会在query之后单独执行,效率更低
- post_filter和聚合都是在query的基础上过滤或聚合的,它们之间没有关系
http
GET test_index4/_search
{
"query": {
"bool": {
"filter": {
"range": {
"price": {
"gte": 5000
}
}
}
}
},
"post_filter": {
"term": {
"name.keyword": "honor"
}
},
"aggs": {
"my":{
"terms": {
"field": "type.keyword",
"size": 10
}
}
}
}
top_hits
- 用于显示桶聚合内的文档详细字段信息,支持分页、_source查询指定字段、排序
- top_hits必须用在对应聚合的子聚合中,是一种聚合类型,是一种内层的嵌套聚合。
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"my":{
"terms": {
"field": "type.keyword"
},
"aggs": {
"NAME": {
"top_hits": {
"from": 1,
"size": 2,
"_source": {
"includes": ["name"]
},
"sort": [
{
"name.keyword": {
"order": "desc"
}
}
]
}
}
}
}
}
}
bucket_sort
- 对聚合结果进行排序、分页,和terms中定义order类似
http
GET test_index4/_search
{
"size": 0,
"aggs": {
"my":{
"terms": {
"field": "type.keyword"
},
"aggs": {
"NAME": {
"bucket_sort": {
"from": 0,
"size": 10,
"sort": [{
"_count":"asc"
}]
}
}
}
}
}
}
映射
用来定义索引的字段名称、类型、长度,以及如何存储和索引的
GET [idx]/_mapping 查询索引的映射,properties中包含字段定义
GET [idx]/_mapping/field/[field_name] 查询索引的某个字段的定义
子字段:fields
自动映射
写入文档时根据Json字段类型生成Mapping的字段类型
推断规则:
- null:不添加字段
- true/fasle: boolean
- 小数:float
- 整数:long
- object:Object
- 数组:取决于第一个元素的类型
- 字符串:日期格式:date,其它字符串:text+keyword
自动映射会尽可能使用宽字段类型,容易浪费存储空间,首次环境建议使用手工映射
类型无法修改,只能重建索引
ES字段可以包含子字段,比如text和keyword,
http
PUT test_dynamic_mapping
{
"mappings": {
"numeric_detection": true,
"dynamic_date_formats": ["yyyy-MM-dd HH:mm:ss"]
}
}
POST test_dynamic_mapping/_doc
{
"field1":false,
"field2":3.14,
"field3":52,
"field4":"abc",
"field5":"0.618",
"field6":"2023-05-07 15:00:10"
}
GET test_dynamic_mapping/_mapping
手动映射(explicit mapping)
创建索引时,指定mappings,来显示创建映射
mappings.properties是字段的定义
小技巧:可以使用自动映射创建后,复制mappings定义,删除索引,再重新创建,在自动映射的基础上修改
修改mappings: mappings中有些参数可以修改,比如fielddata;有些重要的参数不能修改,比如类型、分词器
http
PUT /[idx]
{
"mappings":{
"properties":{
"[field_name]":{
"[param_name]":"[param_val]"
}
}
}
}
PUT /[idx]
{
"mappings":{
"properties":{
"text_field":{
"type":"text",
"fields":{
"text_field_keyword":{
"type":"keyword"
}
}
},
"long_field":"long"
}
}
}
GET test_mapping/_mapping
//在已创建的索引上修改mapping
PUT test_dynamic_mapping/_mapping
{
"numeric_detection": false,
"dynamic_date_formats": [
"yyyy-MM-dd HH:mm:ss"
]
}
PUT test_index3/_mapping
{
"properties": {
"name": {
"type": "text"
}
}
}
字段数据类型(field data type)
字段类型分为2种:会被分词的(text)、不会的()
-
数字类型:
-
boolean:
-
keywords:包括keyword、constant keyword、wildcard,查询时只能完全匹配
-
dates
-
flattened:
-
Range:范围类型,包括上限和下限,gte、lte指定
-
text:文本数据类型,用于全文检索
-
completion:搜索补全,文本补全
-
geo_point:经纬度
-
geo_shape:多边形
array
- 其实没有专门的数组类型,任何类型的字段都可以包含0个或多个值,这些值的类型必须相同,动态映射时,数组元素类型取决于第一个添加的文档的第一个元素的类型,后面的元素和文档如果能强转成第一个元素的类型,则可以添加成功,否则文档添加失败
- 比如"tags": [ "abc",1 ]可以添加成功,因为1可以强转成"1","tags": [ 1,"abc" ]添加失败,"abc"不能转成数字。用_mapping api查看,仍然是数组元素的类型,而不是数组类型。
- 添加第一个文档时会进行类型推断,后面添加的文档的字段如果不能转换成第一次推断出来的类型,则报错
Text
- 默认会被分词为词项(term),并创建索引,用于全文检索
- 自动映射器会自动创建一个keyword类型的子字段
- analyzer:分词器,创建索引和查询时会执行分词器,search_analyzer: 专门指定查询时使用的分词器,默认和analyzer相同
Keyword
- 用于精确查询、聚合字段、和query.term查询。
- 可以指定ignore_above,query.term查询时,在查询结果中,不会查询出超过指定长度时的数据
- 查询时大小写敏感
- 和numeric类型的区别:keyword的term查询更快,numeric适合范围查询,不是所有数字类型都适合numeric类型,如果不需要范围查询,建议使用keyword
- keyword是一个类型族,包含多个类型:keyword, constant_keyword
- constant_keyword:
Date
- 默认格式是UTC时间格式,yyyy-MM-ddTHH:mm:ssZ,
- 可以通过字段属性format来指定格式,epoch_millis时间戳毫秒格式,epoch_second时间戳秒格式,可以指定多个,||分割
- 时区:yyyy-MM-ddTHH:mm:ss.sss+08:00 东八区,yyyy-MM-ddTHH:mm:ssZ 零时区
- 内部会统一转成时间戳存储
object
- 用于存储json对象,对应java中的一个对象
- 内部实际上还是key-value的形式保存,key是多层级字段,value是最后一层字段的值,比如"manager.name.first": "John"
- 手动映射时,可以通过properties指定对象内部字段的类型
映射参数(mapping parameter)
映射参数是mappings.properties.[field]中指定的参数
- analyzer:指定分词器,用于text类型的字段
- coerce:是否允许强制类型转换,比如字段类型定义的是interger,写入的是"1",coerce=false时,报错,为true是,写入成功,默认为true。可以在索引级别和字段级别设置,索引级别:settings.index.mapping.coerce
- copy_to:用于指定字段写入时复制到另一个字段,用于多字段搜索
http
"first_name":{
"type":"text",
"copy_to":"full_name"
},
"last_name":{
"type":"text",
"copy_to":"full_name"
},"full_name":{
"type":"text"
}
- enabled:当mappings.enabled=false时,当前索引的所有字段会存储,但不会创建倒排索引,query.match.[field]搜不到。如果对某个字段进行指定,则这个字段类型必须是object。为false的字段只会存储,不会解析和索引,因此根据该字段搜索不到文档。当把所有字段都enable设为false时,只能根据文档ID搜索文档。
- fields:用于为一个字段指定多个子字段,不同的子字段可以使用不同分词器。Text一般都有一个keyword类型的子字段,因为Text类型会分词和倒排索引,不支持排序,可以通过指定keyword子字段来支持排序。子字段不会继承父字段任何属性,可以独立指定映射参数。
http
"test_f":{
"type":"text",
"fields":{
"chinese":{
"type":"keyword",
"ignore_above":5//字段长度超过5时,query查不到
},
"english":{
"type":"text",
"analyzer":"english"
}
}
Post test_fields/_doc
{
"test_f":"english abcd"
}
GET test_fields/_search
{
"query":{
"match":{
"test_f.chinese":"english abcd"//搜索字段包含指定值的
}
}
}
- format:用于格式化,比如日期
- ignore_above:用于keyword类型,插入文档时,超过指定长度的字段值被忽略,忽略的意思是会被存储,查所有时能查出来,但不会建立索引,不能根据该字段搜到文档。可以理解为超长的时候,enable=false。
- index:用于关闭某个字段的倒排索引,true、false,关闭后query.match查不到,会报错,没有创建索引
- meta:用于字段的元字段,对es是透明的,es不对其做任何处理,用于业务上区分
- norms: 是否禁用评分,禁用可以优化性能
- null_value:为null值设置默认值
- properties:用于声明字段
- similarity:用于为字段设置评分算法,8.0后删除了classic算法,5.0~7.17默认是BM25,可选配置是boolean和classic(TF-IDF)
- store:用于为字段指定单独的存储区域,查询时可以通过store_fields指定查询store的字段, _source=true查询_source字段。字段分为2块存储区域,source和store
自动映射模板(Dynamic Templates)
由条件和映射组成,写入数据时,如果条件匹配则使用配置的mapping,否则使用自动映射器,为一类相同或相似特征的字段定义相同的映射
可以定义多个模板,每个模板包含一或多个条件和一个mapping
匹配条件:
- match_mapping_type
- match、unmatch
- path_match
定义
http
"dynamic_templates":[
{
"[template_name]":{
[match conditions]
"mapping":{...}
}
},{}...
]
匹配条件
match_mapping_type
用于匹配数据类型
http
//将数值类型的值的字段创建为integer类型,文本类型的值字段创建为keyword类型
//数值类型默认是long,将他映射为integer
PUT idx
{
"mappings":{
"dynamic_templates":[
{
"integers":{//模板名字
"match_mapping_type":"long",
"mapping":{
"type":"integer"
}
}
},
{
"strings":{
"match_mapping_type":"string",
"mapping":{
"type":"keyword"
}
}
}
]
}
}
match、unmatch
根据字段名称匹配
http
//字段类型是string,字段名称以num_开头,不以_text结尾的字段,映射为long
PUT idx
{
...
"[temp_name]":{
"match_mapping_type":"string",
"match":"num_*",
"unmatch":"*_text",
"mapping":{
"type":"long"
}
}
}
PUT ...
{
"num_interger":5,//类型不匹配
"num_long":"5",//匹配
"num_text":"foo"//名称不匹配
}
path_match、path_unmatch
用于对象类型字段的匹配,和match类似,但是它使用完整限定名的字段名来匹配
http
PUT ..
{
...
"full_name":{
"path_match":"name.*",
"path_unmatch":"*.middle",
"mapping":{
"type":"text",
"copy_to","full_name"//将字段拷贝到full_name,只对查询query可见,full_name本身使用自动映射器
}
}
}
PUT ...
{
"name":{
"first":"elastic",//匹配
"middle":"org",//不匹配
"last":"cn"//匹配
}
}
GET .../_search
{
"query":{
"match":{
"full_name":"elastic"//能搜索到,搜索org不能搜索到
}
}
}
模板变量
是一个占位符变量({name}、{dynamic_type}),可以根据字段的信息动态使用相应的值
- {name}:字段名称
- {dynamic_type}:字段匹配的类型
http
"named_analyzers":{
"match_mapping_type":"string",
"match":"*",
"mapping":{
"type":"text",
"analyzer":"{name}"//使用和字段名称一样的分词器,分词器未定义则报错
}
}
PUT ...
{
"english":"xxx x"//字段的分词器是english
}
"test":{
"match_mapping_type":"*",
"mapping":{
"type":"{dynamic_type}",//所有非文本类型的字段
"doc_values":false
}
}
分词器(Text Analysis)
检索:和简单的搜索不同,更注重相关性,为了满足用户需求
分词发生时期:
- Index Time(创建倒排索引的时期):源数据分词
- search time(搜索时期):对搜索词进行分词
倒排索引存储了词项和包含词项的文档ID的对应关系:
- Term Dic: 搜索内容分词后的词项
- Posting List:包含词项的文档ID
分词器的组成
字符过滤器(char_filter):用于处理单个字符
词项过滤器(filter)
切词器(tokenizer): 定义了分词器的切词规则
注意:源数据和倒排索引是分开存储的,分词器处理的是倒排索引,也就是词项,不会对源数据造成任何影响
http
"settings":{
"analysis":{
"char_filter":{},
"filter":{},
"tokenizer":{}
}
}
analyzer API
http
//查看分词结果,默认使用默认分词器,可以通过anayzer指定分词器
GET _analyze
{
"text":["What are you doing"],//测试文本
"analyzer":"standard"//默认分词器是standard,也可以指定为english
}
切词器(tokenizer)
http
GET _analyze
{
"text":["What are you doing"],
"tokenizer":"standard" //默认的切词器
}
词项过滤器(token filter)
用来处理切词器切词后的词项,如大小写转换,删除停用词和同义词处理
http
GET _analyze
{
"text":["What are you doing"],
"filter":["lowercase"]//使用词项过滤器,这里不会切词,将text整体作为一个词项
}
GET _analyze
{
"text":["What are you doing"],
"tokenizer":"standard",
"filter":["lowercase"]
}
GET _analyze
{
"text":["What are you doing"],
"tokenizer":"standard",
"filter":["stop"]//停用词,默认使用预制的停用词
}
//自定义停用词
PUT [idx]
{
"settings":{
"analysis":{
"filter":{
"my_filter":{//词项过滤器名
"type":"stop",
"stopwords":["www"],//内联的方式配置停用词,不能修改
"stopwords_path": "stop_words_test.txt"//外部文件配置停用词,可以是相对于config的路径,utf8编码,每个停用词占一行,这种方式可以动态修改停用词
"ignore_case":true
}
}
}
}
}
GET [idx]/_analyze//使用某个索引的自定义分词器
{
"tokenzier":"standard",
"fitler":["my_filter"],
"text":["What www WWW are you doing"]
}
同义词
- a,b,c=>d,e,f:分词时,a、b、c替换成d、e、f, 搜abc,会搜到d、e、f
- a,b,c,d:abcd等价,搜其中任何一个,其他的词项都能搜到,相当于a,b,c,d=>a,b,c,d的简写
定义方式:
- 内联:在synonyms中定义,修改需要重新创建索引
- 通过synonyms_path定义,同义词定义在文件中,可以动态修改
http
PUT [idx]
{
"settings":{
"analysis":{
"filter":{
"my_synonym":{
"type":"synonym",
"synonyms":["a,b,c=>d",]//内联的方式
}
}
}
}
}
GET [idx]/_analyze//使用某个索引的自定义分词器
{
"tokenzier":"standard",
"fitler":["my_synonym"],
"text":["a"]
}
PUT [idx]
{
"settings":{
"analysis":{
"filter":{
"my_synonym":{
"type":"synonym",
"synonyms_path":"anaylisis/syn.txt",//相对路径config目录,每行一个同义词规则
"expand":true//默认是true,只对逗号分割的停用词有效,为true时,a,b,c相当于a,b,c=>a,b,c,为false时a,b,c相当于b,c=>a
"lenient":false//同义词规则是否宽松,默认false,表示同义词中包含停用词时,索引创建失败;为true时,同义词中可以包含停用词,只是会自动去掉停用词
}
}
}
}
}
字符过滤器
分词之前的预处理,过滤无用字符,可以添加、修改、删除字符串
字符过滤器-》切词器-》词项过滤器
类型:
- html_strip:过滤html标签
- mapping:替换指定字符,如果替换后的字符是停用词,则切词后不会有替换后的字符;如果不是停用词,则切词后会有替换后的字符。替换的key和value都可以是字符串,如果有多个匹配的key,选择最长的。可以替换成空字符。定义方式:1、内联(mappings参数)。2、外部文件(mappings_path),相对于config目录,每个key value一行,utf8
- pattern_replace:正则替换,将符合正则的字符替换成指定字符
http
PUT [idx]
{
"settings":{
"analysis":{
"char_filter":{
"my_char_filter":{
"type":"html_strip",
"escaped_tags":["a"]//要保留的HTML标签
}
}
}
}
}
//analyzer API验证分词结果
GET idx/_analyze
{
//"tokenizer":"standard",//如果不用切词器,则将text作为一个完整的词项;
"char_filter":["my_char_filter"],
"text":["<p>I m so <a>happy</a>!</p>"]
}
PUT [idx]
{
"settings":{
"analysis":{
"char_filter":{
"my_char_filter1":{
"type":"mapping",
"mappings":[
"滚=>*","垃=>*","圾=>*",
"壹=>1","abc=>efgh","艹=>"
]
}
}
}
}
}
//analyzer API验证分词结果
GET idx/_analyze
{
//"tokenizer":"standard",
"char_filter":["my_char_filter1"],
"text":["你就是个垃圾!滚"]
}
PUT [idx]
{
"settings":{
"analysis":{
"char_filter":{
"my_char_filter1":{
"type":"pattern_replace",
"pattern":"""(\d{3}\d{4}(\d{4}))""",
"replacement":"$1****$2"
}
}
}
}
}
GET idx/_analyze
{
//"tokenizer":"standard",
"char_filter":["my_char_filter1"],
"text":"您的手机号是13516707749"
}
内置分词器
- standard:默认分词器,中文回逐字拆分
- whitespace:根据空白分割符分割,会保留大小写和时态,standard会转小写,而whitespace只会分割不转小写
- keyword:不做任何处理
- 语言分词器:english、...
http
GET _analyze
{
"analyzer": "standard" ,
"text":"您的手机号是"
}
自定义分词器
组件定义规则
- 切词器:一个分词器只能使用一个,但可以在analysis.tokenizer定义多个
- 词项过滤器:一个分词器可以使用0个或多个
- 字符过滤器:一个分词器可以使用0个或多个
分词器必须在索引中创建,analyzer api使用也必须加上索引名称
在索引中应用分词器:在settings中定义分词器,在mappings中将字段和分词器绑定,query.match全文检索时,自定义分词器生效,而文档查询查的是源数据,不变
analyzer和search_analyzer
- 前者是源数据的分词器,后者是搜索词的分词器
- 后者未指定时,默认等于前者
- 都未指定时,都为standard分词器
http
PUT idx
{
"settings":{
"analysis":{
"tokenizer":{
"my_tokenizer":{
"type":"pattern",
"pattern":[",.!?"]
}
},
"char_filter":{
"my_char_filter":{
"type":"html_strip",
"escaped_tags":["a"]//要保留的HTML表情
} ,
"my_mapping_char_filter":{
"type":"mapping",
"mappings":[
"滚=>*","垃=>*","圾=>*"//*是默认的停用词,分词结果中不会显示,也可以替换成其他的字符
]
}
},
"fitler":{
"my_fitler":{
"type":"stop",
"stopwords":["www"],
"ingore_case":true
}
},
"analyzer":{
"my_analyzer":{
"type":"custom",//可以省略
"tokenizer":"my_tokenizer"//只能定义一个
"char_filter":["my_char_filter","my_mapping_char_filter"]//可以0个或多个
"filter":["my_fitler","uppercase"]//可以0个或多个
}
}
}
},
mappings:{
properties:{
"title":{
"type":"text",
"analyzer":"my_analyzer"//title字段使用自定义分词器
}
}
}
}
GET idx/_search{
"query":{
match:{//match全文检索,会使用分词器,搜索词也会使用相同的分词器
"title":"www"
}
}
}
GET idx/_search/_doc
GET idx/_analyze
{
"analyzer": "my_analyzer" ,
"text":"asd垃圾sas滚df,<a>www</a>.baidu!com?<p>cn</p> easffes"
}
http
PUT idx{
mappings:{
properties:{
"title":{
"type":"text",
"analyzer":"my_analyzer",
"search_analyzer":"my_search_analyzer"
}
}
}
}
文档归一化器(normalizer)
大小写统一,时态转换,停用词(语气词、介词在大多数场景下无搜索意义)
作用:增加召回率,减少匹配次数
- 和分词器类似,也有字符过滤器和词项过滤器,但没有切词器
- 只能用于keyword类型的字段
- 不能被分词
- 搜索词也会使用字段的normalizer,不会分词
http
PUT idx{
"settngs":{
"analysis":{
"normalizer":{
"my_normalizer":{
"filter":["uppercase"]
}
}
}
}
"mappings":{
"properties":{
"title":{
"type":"keyword",
"normalizer":"my_normalizer"
},
"content":{
"type":"text",
"analyzer":"my_analyzer"
}
}
}
}
中文分词器
下载ik分词器zip,复制到plugins目录解压,需要删除zip,重启es
http
GET _analyze
{
"analyzer":"ik_max_word",
"text":["我学习 Elasticsearch 选择 Elastic开源社区"]
}
GET _analyze
{
"analyzer":"ik_smart",
"text":["中华人民共和国国歌"]
}
GET _analyze
{
"analyzer":"ik_max_word",
"text":["特斯拉比亚迪蒙迪欧霸道蒙迪欧大G"]//词库中没有的词分不出来
}
PUT idx{
"mappings":{
"properties":{
"title":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
PUT test/_doc
{
"title":"我爱中华人民。。。"
}
Get test/_search
{
"query":{
"match":{
"title":"中华"
}
}
}
GET _analyze
{
"analyzer":"ik",
"text":["奥利给yydsasd绝绝子"]
}
默认词库在config目录
IKAnalyzer.cfg.xml:配置字典文件和停用词文件
*.dic:预定义的中文词库,比如main.dic主词库,stopword.dic停用词库,quantifyer.dic计量单位,suffix.dic地理位置后缀,surname.dic姓氏
2种分词器:
- ik_max_word:最细粒度拆分,词项较多,每个有意义的词都会拆分
- ik_smart:最粗粒度
自定义本地词库:每行一个词项,修改IKAnalyzer.cfg.xml,ext.dic中配置自定义词库,相对路径config目录,可以分号分割多个,需要重启
远程词库热更新:IKAnalyzer.cfg.xml的remote_ext_dic,其中的值是url,需要自己定义http接口,body返回词库内容,header需要包含last-modified或Eflag,都是字符串类型,es会根据这2个值是否发生改变来判断是否更新词库,
本地停用词和远程停用词:ext_stopwords和remote_ext_stopwords
ik可能有bug,远程热更新的词库第一行需要换行符,第一个词项才能生效
本地词库和远程词库:优点是上手简单,缺点是直接操作磁盘文件,词项多时不便管理
mysql热更新
需要修改ik源码
Dictorynary.initial,loadMysqlExtDict, 读取jdbc-relead.properties,根据配置的url、username、password、刷新间隔、查询远程词库和停用词的SQL
误区
分词器(包括3个组件)只会影响倒排索引的结果,不影响源数据
同义词a,b,c,d=>s,建倒排索引时,都会替换成s,搜索时,用abcds任何一个搜索,搜索词也会替换成s,因此查询结果中abcds都包含,搜索结果是源数据
搜索
检索:根据相关性
请求上下文:查询的json对象
响应上下文:返回的JSON
相关度评分
用来对搜索结果进行排序,评分高的排序靠前
5.x之前默认TF/IDF,5.x之后默认BM2.5
查询DSL
http
PUT goods
{
mappings:
_source:
excludes:[]//定义mappings时,定义查询结果只要哪些字段
includes:[]
}
GET goods/_search
{
"_source":{
"includes":["name","tags"]//查询结果只包含哪些字段
"excludes":[]//查询结果排除哪些字段
}
}
GET goods/_search
{
"_source":{
"includes":"*"//支持通配符,t*
"excludes":[]//查询结果排除哪些字段
}
}