[toc]
三、Elasticsearch入门
1. Elasticsearch基本概念
1.1 文档
Elasticsearch是面向文档的,文档是所有可搜索数据的最小单位- 日志文件中的日志项
- 一本电影的具体信息/一张唱片的详细信息
MP3播放器里的一首歌/一篇PDF文档中的具体内容
- 文档会被序列化成
Json格式,保存在Elasticsearch中Json对象由字段组成- 每个字段都有对应的字段类型(字符串/数值/布尔/日期/二进制/范围类型)
- 每个文档都有一个
UniqueID- 你可以自己指定
ID - 或者通过
Elasticsearch自动生成
- 你可以自己指定
1.1.1 Json文档
- 一篇文档包含了一系列的字段。类似数据库表中一条记录
Json文档,格式灵活,不需要预先定义格式- 字段的类型可以指定或者通过
Elasticsearch自动推算 - 支持数据/支持嵌套
- 字段的类型可以指定或者通过
1.1.2 文档元数据
1 | { |
- 元数据,用于标注文档的相关信息
_index: 文档所属的索引名_type: 文档所属的类型名_id: 文档唯一ID_source: 文档的原始JSON数据_all: 整合所有字段内容到该字段,已被废除_version: 文档的版本信息_score: 相关性打分
1.2 索引
1 | { |
index: 索引是文档的容器,是一类文档的结合index体现了逻辑空间的概念:每个索引都有自己的Mapping定义,用于定义包含文档的字段名和字段类型Shard体现了物理空间的概念:索引中的数据分散在Shard中
- 索引的
Mapping与SettingMapping定义文档字段的类型Setting定义不同的数据分布
1.2.1 索引的不同语义

- 名词:一个
Elasticsearch集群中,可以创建很多不同的索引 - 动词:保存一个文档到
Elasticsearch的过程也叫索引(indexing)ES中,创建一个倒排索引的过程
- 名词:一个
B树索引,一个倒排索引
1.2.2 Type(这个我也不知道是干什么的)

1.2.3 ES和RDBMS对比
| RDBMS | Elasticsearch |
|---|---|
| Table | index(Type) |
| Row | Document |
| Column | Filed |
| Schema | Mapping |
| SQL | DSL |
- 传统关系型数据库和
Elasticsearch的区别ElasticsearchSchemaless:数据组织更加灵活- 相关性:算相关度
- 高性能全文检索
RDBMS- 事务
Join
1.3 REST API

1.4 kibana上索引管理


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 movies
//查看索引的文档总数
GET movies/_count
//查看前10条文档,了解文档格式
POST movies/_search{
}
//_cat indices API
//查看indices
GET /_cat/indices/movies*?v&s=index
//查看状态未绿的索引
GET /_cat/indices?v&health=green
//按照文档个数排序
GET /_cat/indices?v&s=docs.count:desc
//查看具体的字段
GET /_cat/indices/movies*?pri&v&h=health,index,pri,rep,docs.count,mt
//How much memory is used per index?
GET /_cat/indices?v&h=i,tm&s=tm:desc
1.5 分布式系统的可用性和扩展性
1.5.1 高可用
- 服务可用性-运行有节点停止服务
- 数据可用性-部分节点丢失,不会丢失数据
1.5.2 可扩展性
- 请求量提升/数据的不断增长(将数据分布到所有节点上)
1.6 ES的分布式特性
1.6.1 ES的分布式架构的好处
- 存储的水平扩容
- 提高系统的可用性,部分节点停止服务,整个集群的服务不受影响
1.6.2 ES的分布式架构
- 不同的集群通过不同的名字来区分,默认名字
elasticsearch - 通过配置文件修改,或者在命令行中
-E cluster.name=node1进行设定 - 一个集群可以有一个或者多个节点
1.7 ES节点
- 节点是一个
Elasticsearch的实例- 本质上就是一个
Java进程 - 一台机器上可以运行多个
Elasticsearch进程,但是生产环境一般建议一台机器上只运行一个Elasticsearch实例
- 本质上就是一个
- 每一个节点都有名字,通过配置文件配置,或者启动时候
-E node.name=node1指定 - 每一个节点在启动之后,会分配一个
UID,保存在data目录下
1.8 Master-eligible nodes和Master Node
- 每个节点启动后,默认就是一个
Master eligible节点- 可以设置
node.master:false禁止
- 可以设置
Master-eligible节点可以参加选主流程,成为Master节点- 当第一个节点启动时候,它会将自己选举成
Master节点 - 每个节点上都保存了集群的状态,只有
Master节点才能修改集群的状态信息- 集群状态(Cluster State),维护了一个集群中,必要的信息
- 所有的节点信息
- 所有的索引和其相关的
Mapping和Setting信息 - 分片的路由信息
- 任意节点都能修改信息会导致数据的不一致性
- 集群状态(Cluster State),维护了一个集群中,必要的信息
1.8 Data Node & Coordinating Node
Data Node- 可以保存数据的节点,叫做
Data Node。负责保存分片数据。在数据扩展上起到了至关重要的作用
- 可以保存数据的节点,叫做
Coordinating Node- 负责接受
Client的请求,将请求分发到合适的节点,最终把结果汇集到一起 - 每个节点默认都起到了
Coordinating Node的职责
- 负责接受
1.9 其他节点类型
Hot和Warm Node- 不同硬件配置的
Data Node,用来实现Hot和Warm架构,降低集群部署的成本
- 不同硬件配置的
Machine Learing Node- 负责跑机器学习的
Job,用来做异常检测
- 负责跑机器学习的
Tribe Node- (5.3开始使用
Cross Cluster Serarch)Tribe Node连接到不同的Elasticsearch集群,并且支持将这些集群当成一个单独的集群处理
- (5.3开始使用
1.10 配置节点类型
- 开发环境中一个节点可以承担多种角色
- 生产环境中,应该设置单一的角色的节点(
dedicated node)
| 节点类型 | 配置参数 | 默认值 |
|---|---|---|
| master eligible | node.master | true |
| data | node.data | true |
| ingest | node.ingest | true |
| coordinating only | 无 | 每个节点默认都是 coordinating 节点。设置其他类型全部为false |
| machine learing | node.ml | true(需 enable x-pack) |
1.11 分片(Primary Shard & Replica Shard)
- 主分片,用以解决数据水平扩展的问题。通过主分片,可以将数据分布到集群内的所有节点之上
- 一个分片是一个运行的
Lucene的实例 - 主分片数在索引创建时指定,后续不允许修改,除非
Reindex
- 一个分片是一个运行的
- 副本,用以解决数据高可用的问题。分片时主分片的拷贝
- 副本分片数,可以动态的调整
- 增加副本数,还可以在一定程度上提高服务的可用性(读取的吞吐)
- 一个三节点的集群中,
blogs索引的分片分布情况- 思考:增加一个节点或改大主分片数对系统的影响?

- 思考:增加一个节点或改大主分片数对系统的影响?
1.12 分片的设定(主分片是在创建时设定的,后期不能改,除非reindex)
- 对与生产环境中分片的设定,需要提前做好容量规划
- 分片数设置过小
- 导致后续无法增加节点实现水平扩展
- 单个分片的数据量太大,导致数据重新分配耗时(数据倾斜)
- 分片数设置过大,
7.0开始,默认主分片设置成1,解决了over-sharding的问题- 影响搜索结果的相关性打分,影响统计结果的准确性
- 单个节点上过多的分片,会导致资源浪费,同时也会影响性能
- 分片数设置过小
1.13 查看集群的健康状况
1 | GET _cluster/health |


Green:主分片与副本都正常分配Yellow:主分片全部正常分配,有副本分片未能正常分配Red:有主分片未能分配- 例如:当服务器的磁盘容量超过
85%时,去创建了一个新的索引
- 例如:当服务器的磁盘容量超过
2. 文档的基本CRUD与批量操作
2.1 文档的CRUD
| 类型 | API |
|---|---|
| Index | PUT my_index/_doc/1{“user”:”mike”,”comment”:”xxxx”} |
| Create | PUT my_index/_create/1{“user”:”mike”,”comment”:”xxxx”} POST my_index/_doc(不指定ID,自动生成){“user”:”mike”,”comment”:”xxxx”} |
| Read | GET my_index/_doc/1 |
| Update | POST my_index/_update/1{“doc”:{“user”:”mike”,”comment”:”xxxx”}} |
| Delete | DELETE my_index/_doc/1 |
Type名,约定都用_docCreate:如果ID已经存在,会失败Index:如果ID不存在,创建新的文档。否则,先删除现有的文档,再创建新的文档,版本会增加Update:文档必须已经存在,更新只会对相应字段做增量修改
2.1.1 Create一个文档

- 支持自动生成文档
Id和指定文档Id两种方式 - 通过调用
post users/_doc- 系统会自动生成
document Id
- 系统会自动生成
- 使用
HTTP PUT user/_create/1创建时,URI中显示指定_create,此时如果该id的文档已经存在,操作失败

1 | //create document. 自动生成 _id |
2.1.2 Get一个文档

- 找到文档,返回
HTTP 200- 文档元信息
- _index/_type/
- 版本信息,同一个
Id的文档,即使被删除,Version号也会不断增加 _source中默认包含了文档的所有原始信息
- 文档元信息
- 找不到文档,返回
HTTP 404

1 | //Get document by Id |
2.1.3 Index文档
1 | //Index,如果文档已经存在,就会删除旧文档,写入新文档,版本号会+1 |
Index和Create不一样的地方:如果文档不存在,就索引新的文档。否则现有文档会被删除,新的文档被索引。版本信息+1



2.1.4 Update文档
Update方法不会删除原来的文档,而是实现真正的数据更新Post方法/Payload需要包含再doc中


1 | //Update,在原文档的基础上增加字段 |
2.2 批量操作
2.2.1 Bulk API

- 在一次
REST请求中,重新建立网络连接是非常损耗性能的 - 支持在一次
API调用中,对不同的索引进行操作 - 支持四种类型操作
IndexCreateUpdateDelete
- 可以在
URI中指定Index,也可以在请求的Payload中进行 - 操作中单挑操作失败,并不会影响其他操作
- 返回结果包含了每一条操作执行的结果

1
2
3
4
5
6
7
8
9
10
11
12//Bulk 操作
//执行两次,查看每次的结果
//执行第一次
POST _bulk
{"index":{"_index":"test","_id":"1"}}
{"field1":"value1"}
{"delete":{"_index":"test","_id":"2"}}
{"create":{"_index":"test2","_id":"3"}}
{"field1":"value3"}
{"update":{"_id":"1","_index":"test"}}
{"doc":{"field2":"value2"}}
2.2.2 批量读取-mget
- 批量操作,可以减少网络连接所产生的开销,提高性能


1 | // mget 操作 |
2.2.3 批量查询-msearch
1 | ### msearch 操作 |

2.2.4 常见错误返回
| 问题 | 原因 |
|---|---|
| 无法连接 | 网络故障或集群挂了 |
| 连接无法关闭 | 网路故障或节点出错 |
| 429 | 集群过于繁忙 |
| 4xx | 请求体格式有错 |
| 500 | 集群内部错误 |
3. 正排与倒排索引
3.1 正排

3.2 倒排索引
如果你想找某个词,在书中的具体页码,那正排就不能满足了,那我们可以建一个内容->文档Id的一种索引结构,就可以满足我们的需求了
3.2.1 倒排索引的核心组成
倒排索引包含两部分
- 单词词典(Term Dictionary),记录所有文档的单词,记录单词到倒排列表的关联关系
- 单词词典一般比较大,可以通过
B+树或哈希拉链法实现,以满足高性能的插入和查找
- 单词词典一般比较大,可以通过
- 倒排序列(Posting List)记录了单词对应的文档结合,由倒排索引项组成
- 倒排索引项(Posting)
- 文档Id
- 词频TF-该单词在文档中出现的次数,用于相关性评分
- 位置(Position)-单词在文档中分词的位置。用于语句搜索(phrase query)
- 偏移(Offset)-记录单词的开始结束位置,实现高亮显示
- 倒排索引项(Posting)
3.2.2 Elasticsearch的一个例子

3.2.3 Elasticsearch的倒排索引
Elasticsearch的Json文档中的每个字段,都有自己的倒排索引- 可以指定对某些字段不做索引
- 优点:节省存储空间
- 缺点:字段无法被搜索
4. 通过Analyzer进行分词
4.1 Analysis与Analyzer
Analysis:文本分析是把全文本转换一系列单词(term\token)的过程,也叫分词Analysis是通过Analyzer来实现的- 可使用
Elasticsearch内置的分析器/或者按需定制化分析器
- 可使用
- 除了在数据写入时转换词条,匹配
Query语句时候也需要用相同的分析器对查询语句进行分析

4.2 Analyzer的组成
- 分词器时专门处理分词的组件,
Analyzer由三部分组成Character Filters(针对原始文本处理,例如去除html)Tokenizer(按照规则切分为单词)Token Filter(将切分的单词进行加工,小写,删除stopword,增加同义词)

5. Elasticsearch的内置分词器
Standard Analyzer:默认分词器,按词切分,小写处理Simple Analyzer:按照非字母切分(符号被过滤),小写处理Stop Analyzer:小写处理,停用词过滤(the,a,is)Whitespace Analyzer:按照空格切分,不转小写Keyword Analyzer:不分词,直接将输入当作输出Patter Analyzer:正则表达式,默认\w+(非字符分割)Language:提供了30多种常用语言的分词器Customer Analyzer:自定义分词器
5.1 使用_analyzer API
5.1.1 直接指定Analyzer进行测试

1
2
3
4
5
6# 直接指定Analyzer进行测试
GET _analyze
{
"analyzer": "standard",
"text":"Mastering Elasticsearch,elasticsearch in Action"
}
5.2.2 指定索引的字段进行测试

1
2
3
4
5
6# 指定索引的字段进行测试
POST users/_analyze
{
"field":"message",
"text":"Mastering Elasticsearch"
}
5.2.3 自定义分词器进行测试

1
2
3
4
5
6
7# 自定义分词器进行测试
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Mastering Elasticsearch"
}
5.2 Standard Analyzer分词器(Elasticsearch的默认分词器)
- 默认分词器
- 按词切分
- 小写处理


5.3 Simple Analyzer
- 按照非字母切分,非字母的都被去除
- 小写处理


1
2
3
4
5
6# Simple Analyzer
GET _analyze
{
"analyzer": "simple",
"text":"2 runing Quick brown-foxes leap over lazy dogs in the summer evening"
}
5.3 Whitespace Analyzer
- 按照空格切分

5.4 Stop Analyzer
- 相比
Simple Analyzer - 多了
stop filter- 会把
the、a、is等修饰性词语去掉
- 会把
5.5 Keyword Analyzer
- 不分词,直接将输入当一个
term输出

1
2
3
4
5
6# Keyword Analyzer
GET _analyze
{
"analyzer": "keyword",
"text":"2 runing Quick brown-foxes leap over lazy dogs in the summer evening"
}
5.5 Pattern Analyzer
- 通过正则表达式进行分词
- 默认是
\w+,非字符的符号进行分割

1
2
3
4
5
6# Pattern Analyzer
GET _analyze
{
"analyzer": "pattern",
"text":"2 runing Quick brown-foxes leap over lazy dogs in the summer evening"
}
5.6 Language Analyzer
- 对不同国家的语言进行分词

1
2
3
4
5
6# Language Analyzer
POST _analyze
{
"analyzer": "english",
"text":"2 runing Quick brown-foxes leap over lazy dogs in the summer evening"
}
5.7 中文分词的难点
- 中文句子,切分成一个一个词(不是一个个字)
- 英文中,单词有自然的空格作为分隔
- 一句中文,在不同的上下文,有不同的理解
- 这个苹果,不大好吃/这个苹果,不大,好吃!
- 例子
- 他说的确实在理/这事的确定不下来
5.8 ICU Analyzer
- 需要安装
pluginElasticsearch-plugin install analysis-icu
- 提供了
Unicode的支持,更好的支持亚洲语言



1
2
3
4
5
6
7POST _analyze
{
"analyzer": "icu_analyzer",
"text":"他说的确实在理"
}
GET /movies/_search
5. Search API概述
URI Search- 在
URL中使用查询参数
- 在
Request Body Search- 使用
Elasticsearch提供的,基于JSON格式的更加完备的Query Domain Specific Language(DSL)
- 使用
5.1 指定查询的索引
| 语法 | 范围 |
|---|---|
| /_search | 集群上所有的索引(用_search来标明这是一个搜索的请求) |
| /index1/_search | 指定索引的名称为index1 |
| /index1,inde-2/_search | 可以指定多个索引,index1和index-2 |
| /index*/_search | 以index开头的索引 |
5.2 URI查询
- 使用
"q",指定查询字符串 - “query string syntax”,
KV键值对
1 | curl -XGET "http://elasticsearch:9200/kibana_sample_data_ecommerce/_search?q=customer_first_name:Eddie" |

5.3 Request Body查询

5.4 搜索的返回结果(Response)

5.5 相关性
5.5.1 搜索的相关性
- 搜索时用户和搜索引擎的对话
- 用户关心的时搜索结果的相关性
- 是否可以找到所有相关的内容
- 有多少相关的内容被返回了
- 文档的打分是否合理
- 结合业务需求,平衡结果排名
5.5.2 衡量相关性
Information Retrieval(计算机领域的一门学课)Precision(查准率): 尽可能返回较少的无关文档Recall(查全率):尽可能返回较多的相关文档Ranking:是否能够按照相关度进行排序
5.5.3 Precision and Recall

5.6 URI Search:通过URI query实现搜索
1 | GET /movies/_search?q=2012&df=title&sort=year:desc&from=o&size=10&timeout=1s |
q:指定查询语句,使用Query String Syntax(Query String Syntax)df:默认字段,不指定时,会对所有字段进行查询Sort:排序/from和size用于分页Profile:可以查看查询时如何被执行的
5.6.1 指定字段查询 VS 范查询
q=title:2012/q=2012





1
2
3
4
5
6
7
8
9
10
11# 指定字段查询 VS 范查询
# 指定字段查询
GET /movies/_search?q=2012&df=title
{
"profile":true
}
# 范查询
GET /movies/_search?q=2012
{
"profile":true
}
5.6.2 Term VS Phrase
Beautiful Mind等效于Beautiful OR Mind,意思是只要有一个包含,就会返回"Beautiful Mind",等效于Beautiful AND Mind。Phrase查询(注意:要使用Phrase查询,需要将内容用引号引起来),还要求前后顺序保持一致;意思是这两个词都要包含- 分组与引号
title:(Beautiful AND Mind)分组title:"Beautiful Mind"








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# Term VS Phrase
# Phrase 精确查找
GET /movies/_search?q=title:"Beautiful Mind"
{
"profile": "true"
}
GET /movies/_search?q=title:Beautiful Mind
{
"profile": "true"
}
# TermQuery 分组,Bool查询
GET /movies/_search?q=title:(Mind Beautiful)
{
"profile": "true"
}
TermQuery需要用()括号来把要查找的词括起来代表一个分组,不然就会变成上面的那种情况,两个Term在一起的时候默认是or的关系PhraseQuery就是需要包含所有词,且保证顺序不变
5.6.3 布尔操作和分组操作
AND/OR/NOT或者&&/||/!- 必须大写
title:(matrix NOT reloaded)
- 分组
+表示must-表示must_nottitle:(+matrix -reloaded)





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# TermQuery 分组,Bool查询
GET /movies/_search?q=title:(Mind Beautiful)
{
"profile": "true"
}
# AND
GET /movies/_search?q=title:(Mind AND Beautiful)
{
"profile": "true"
}
# NOT
GET /movies/_search?q=title:(Beautiful NOT Mind)
{
"profile": "true"
}
# +
GET /movies/_search?q=title:(Beautiful %2BMind)
{
"profile": "true"
}
5.6.4 范围查询和算数符号
- 范围查询
- 区间表示:
[]闭区间,{}开区间year:{2019 TO 2018}year:[* TO 2018]
- 区间表示:
- 算数符号
year:>2010year:(>2010 && <=2018)year:(+>2010 +<=2018)

1
2
3
4
5# 所有的电影年份必须>=1980
GET /movies/_search?q=year:>=1980
{
"profile": "true"
}
5.6.5 通配符查询、正则表达式、模糊匹配与近似查询
- 通配符查询(通配符查询效率低,占用内存大,不建议使用。特别是放在最前面)
?代表 1 个字符,*代表 0 或多个字符title:mi?dtitle:be*
- 正则表达式
title:[bt]oy
- 模糊匹配与近似查询
title:befutifl~1title:“lord rings”~2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 通配符查询
GET /movies/_search?q=title:b*
{
"profile": "true"
}
# 模糊匹配&近似匹配
GET /movies/_search?q=title:beautifl~1
{
"profile": "true"
}
GET /movies/_search?q=title:"lord Ring"~2
{
"profile": "true"
}
5.7 Request Body与Query DSL简介
5.7.1 Request Body Search
- 将查询语句通过
HTTP Request Body发送给Elasticcsearch Query DSL
5.7.2 分页
From从0开始,默认返回10个结果- 获取靠后的翻页成本较高

5.7.3 排序
- 最好在”数字型”与”日期型”字段上排序
- 因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值


1
2
3
4
5
6
7
8
9//对日期排序
POST kibana_sample_data_ecommerce/_search
{
"profile": "true",
"sort": [{"order_date": "desc"}],
"query": {
"match_all": {}
}
}
5.7.4 _source filtering
- 如果
_source没有存储,那就只返回匹配的文档的元数据 _source支持使用通配符:_source["name*,"desc"]

1
2
3
4
5
6
7
8//source filstering
POST kibana_sample_data_ecommerce/_search
{
"_source": ["order_date"],
"query": {
"match_all": {}
}
}
5.7.5 脚本字段
- 用例:订单中有不同的汇率,需要结合汇率对订单价格进行排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//脚本字段
GET kibana_sample_data_ecommerce/_search
{
"script_fields": {
"new_field": {
"script": {
"lang": "painless",
"source": "doc['order_date'].value+'_hello'"
}
}
},
"query": {
"match_all": {}
}
}
5.7.6 使用查询表达式:Match Query(我们之前学了Term Query,Phrase Query)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//matchQuery OR
POST movies/_search
{
"query": {
"match": {
"title": "last Christmas"
}
}
}
//matchQuery AND
POST movies/_search
{
"query": {
"match": {
"title": {
"query": "last Christmas",
"operator": "and"
}
}
}
}
5.7.7 短语搜索:Match Phrase查询


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//matchPhrase
POST movies/_search
{
"query": {
"match_phrase": {
"title": "one love"
}
}
}
//matchPhrase slop:1
POST movies/_search
{
"query": {
"match_phrase": {
"title": {
"query": "one love",
"slop": 1
}
}
}
}
5.8 几种查询方式的总结


5.9 Query String和Simple Query String查询
5.8.1 Query String Query
- 类似
URI Query


5.8.1 Simple Query String Query
- 类似
Query String,但是会忽略错误的语法,同时只支持部分查询语法 - 不支持
AND、OR、NOT,会当作字符串处理 Term之间默认的关系是OR,可以指定Operator- 支持部分逻辑
+代替AND|代替OR-代替NOT



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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54PUT /users/_doc/1
{
"name":"Ruan Yiming",
"about":"java,golang,node,swift,elasticsearch"
}
PUT /users/_doc/2
{
"name":"Li Yiming",
"about":"Hadoop"
}
# Query String
POST users/_search
{
"query": {
"query_string": {
"default_field": "name",
"query": "Ruan AND Yiming"
}
}
}
POST users/_search
{
"query": {
"query_string": {
"fields": ["name","about"],
"query": "(Ruan AND Yiming) OR (Hadoop)"
}
}
}
# Simple Query String 默认operator是 OR
POST users/_search
{
"query": {
"simple_query_string": {
"query": "Ruan AND Yiming",
"fields": ["name"]
}
}
}
POST users/_search
{
"query": {
"simple_query_string": {
"query": "Ruan Yiming",
"fields": ["name"],
"default_operator": "AND"
}
}
}
5.10 es match、match_phrase、query_string和term的区别
https://www.cnblogs.com/chenmz1995/p/10199147.html
6. Dynamic Mapping和常见字段类型
6.1 什么是Mapping
Mapping类似数据库中的schema的定义,作用如下- 定义索引中的字段的名称
- 定义字段的数据类型,例如字符串,数字,布尔……
- 字段,倒排索引的相关配置,(Analyzed or Not Analyzed,Analyzer)
Mapping会把Json文档映射成Lucene所需要的扁平格式- 一个
Mapping属于一个索引的Type- 每个文档都属于一个
Type - 一个
Type有一个Mapping定义 - 7.0开始,不需要在
Mapping定义中指定type信息
- 每个文档都属于一个
6.2 Elasticsearch字段的数据类型
- 简单类型
Text/KeywordDateInteger/FloatingBooleanIPv4 & IPv6
- 复杂类型:对象和嵌套对象
- 对象类型/嵌套类型
- 特殊类型
geo_point & geo_shape/percolator
6.3 什么是Dynamic Mapping
- 在写入文档时,如果索引不存在,会自动创建索引
Dynamic Mapping的机制,使得我们无需手动定义Mappings,Elasticsearch会自动根据文档信息,推算出字段的类型- 但是有时候推算的不对,例如地理位置信息
- 当类型如果设置不对时,会导致一些功能无法正常运行,例如
Range查询
6.4 类型的自动识别
JSON类型 |
Elasticsearch类型 |
|---|---|
| 字符串 | 匹配日期格式,设置成Date配置数字设置为 float或者long,该选项默认关闭设置为 Text,并且增加keyword子字段 |
| 布尔值 | boolean |
| 浮点数 | float |
| 整数 | long |
| 对象 | Object |
| 数组 | 由第一个非空数值的类型所决定 |
| 空值 | 忽略 |


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//写入文档,查看Mapping
PUT mapping_test/_doc/1
{
"firstName":"Chan",
"lastName":"Jackie",
"loginDate":"2018-07-24T10:29:48.103Z"
}
//查看Mapping
GET mapping_test/_mapping
//Delete index 删除索引
DELETE mapping_test
//dynamic mapping,推断字段的类型
PUT mapping_test/_doc/1
{
"uid":"123",
"isVip":false,
"isAdmin":"true",
"age":19,
"heigh":180
}
#查看Mapping
GET mapping_test/_mapping
6.5 能否更改Mapping的字段类型
- 两种情况
- 新增字段
Dynamic设为true时,一旦有新增字段的文档写入,Mapping也同时被更新Dynamic设为false,Mapping不会被更新,新增字段的数据无法被索引,但是信息会出现在_source中Dynamic设置成Strict,文档写入失败
- 对已有字段,一旦一句有数据写入,就不再支持修改字段定义
Lucene实现的倒排索引,一旦生成后,就不允许修改
- 如果希望改变字段类型,必须
Reindex API,重建索引
- 新增字段
- 原因
- 如果修改了字段的数据类型,会导致已被索引的属于无法被搜索
- 但是如果是增加新的字段,就不会有这样的影响
6.6 控制Dynamic Mappings

| | "true" | "false" | "strict"
—|—|—|—
文档可索引 | YES | YES | NO
字段可索引 | YES | NO | NOMapping被更新 | YES | NO | NO
- 当
dynamic被设置成false时候,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃 - 当设置成
Strict模式时候,数据写入直接出错








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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51#默认Mapping支持dynamic,写入的文档中加入新的字段
PUT dynamic_mapping_test/_doc/1
{
"newField":"someValue"
}
POST dynamic_mapping_test/_search
{
"query": {
"match": {
"newField": "someValue"
}
}
}
#修改dynamic的值为false
PUT dynamic_mapping_test/_mapping
{
"dynamic":false
}
#新增 anotherField字段,字段依旧可以存入ES
PUT dynamic_mapping_test/_doc/10
{
"anotherField":"someValue"
}
#但是,该字段不可以被搜索,因为dynamic的值被我们设置为了false
POST dynamic_mapping_test/_search
{
"query": {
"match": {
"anotherField": "anotherField"
}
}
}
#查看索引的Mapping文件
GET dynamic_mapping_test/_mapping
#修改dynamic的值为strict
PUT dynamic_mapping_test/_mapping
{
"dynamic":"strict"
}
#写入数据出错,HTTP Code 400
PUT dynamic_mapping_test/_doc/12
{
"lastField":"value"
}
7 显示Mapping设置与常见参数
7.1 如何显示定义一个Mapping

7.2 自定义Mapping的一些建议
- 可以参考
API手册,纯手写 - 为了减少输入的工作量,减少出错概率,可以依照以下步骤
- 创建一个临时的
index,写入一些样本数据 - 通过访问
Mapping API获得该临时文件的动态Mapping定义 - 修改后用,使用该配置创建你的索引
- 删除临时索引
- 创建一个临时的
7.3 控制当前字段是否被索引
Index:控制当前字段是否被索引。默认为true。如果设置成false,该字段不可被搜索


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
36DELETE dynamic_mapping_test
#设置 index 为 false
DELETE users
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text"
},
"lastName":{
"type": "text"
},
"mobile":{
"type": "text",
"index": false
}
}
}
}
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName":"Yiming",
"mobile":"123456789"
}
POST /users/_search
{
"query": {
"match": {
"mobile": "123456789"
}
}
}
7.3 index Options
- 对于倒排索引的建立,es提供了四种不同级别的
index Options配置,可以控制倒排索引记录的内容docs:记录doc idfreqs:记录doc id和term frequenciespositions:记录doc id/term frequencies/term positionoffsets:记录doc id/term frequencies/term position/character offects
Text类型默认记录postions,其他默认为docs- 记录内容越多,占用存储空间越大

7.4 null_value
- 需要对
null值实现搜索 - 只有
keyword类型支持设定Null_Value

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#设定Null_value
DELETE users
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text"
},
"lastName":{
"type": "text"
},
"mobile":{
"type": "keyword",
"null_value": "NULL"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName":"Yiming",
"mobile":null
}
GET users/_search
{
"query": {
"match": {
"mobile": "NULL"
}
}
}
7.5 copy_to设置
_all在7中被copy_to所替代- 满足一些特定的搜索需求
copy_to将字段的数值拷贝到目标字段,实现类似_all的作用copy_to的目标字段不出现在_source中

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#设置 Copy to
DELETE users
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text",
"copy_to": "fullName"
},
"lastName":{
"type": "text",
"copy_to": "fullName"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName":"Yiming"
}
GET users/_search
{
"query": {
"match": {
"fullName": {
"query": "Ruan Yiming",
"operator": "and"
}
}
}
}
当你索引一个文档的时候,它如果包含了
firstName和lastName,我们都会把这个值拷贝到fullName字段上去,当你要查询时,就可以用fullName来查询了
7.6 数组类型
Elasticsearch中不提供专门的数组类型。但是任何字段,都可以包含多个相同类型的数值


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#数组类型
PUT users/_doc/1
{
"name":"onebird",
"interests":"reading"
}
PUT users/_doc/1
{
"name":"twobirds",
"interests":["reading","music"]
}
POST users/_search
{
"query": {
"match_all": {}
}
}
GET users/_mapping
8. 多字段特性及Mapping中配置自定义Analyzer
8.1 多字段类型

- 多字段特性
- 厂商名字实现精确匹配
- 增加一个
keyword的子字段
- 增加一个
- 使用不同的
analyzer- 不同语言
- pingyin字段的搜索
- 还支持为搜索和索引指定不同的
analyzer
- 厂商名字实现精确匹配
8.2 Exact Values(精确值) VS Full Text(全文本)
Exact values vs Full TextExact Value:包括数字/日期/具体一个字符串(例如:”Apple Store”)Elasticsearch中的keyword
- 全文本,非结构化的文本数据
Elasticsearch中的text

8.2.1 Exact Values不需要被分词(Exact Values和Full Text最大的区别)
Elasticsearch为每一个字段创建一个倒排索引Exact Value在索引时,不需要做特殊的分词处理

8.3 自定义分词
- 当
Elasticsearch自带的分词器无法满足时,可以自定义分词器。通过组合不同的组件实现(下面这三个组件是Analyzer的组成,可看上面4.2)Character FilterTokenizerToken Filter
8.3.1 Character Filters
- 在
Tokenizer之前对文本进行处理,例如增加删除及替换字符。可以配置多个Character Filters。会影响Tokenizer的position和offset信息 - 一些自带的
Character FilterHTML strip: 去除html标签Mapping: 字符串替换Pattern replace: 正则匹配替换



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
36
37
38
39
40
41
42
43
44
45
46
47
POST _analyze
{
"tokenizer": "keyword",
"char_filter": ["html_strip"],
"text": "<b>hello world</b>"
}
//使用 char filter进行替换(将'-'替换为'_')
POST _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type":"mapping",
"mappings":["- => _"]
}
],
"text": "123-456,I-test! test-990 650-555-1234"
}
//char filter替换表情符号
POST _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type":"mapping",
"mappings": [":) => happy",":( => sad"]
}
],
"text": ["I am felling :)","Felling :( today"]
}
//正则表达式
GET _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type": "pattern_replace",
"pattern": "http://(.*)",
"replacement": "$1"
}
],
"text": ["http://www.elastic.co"]
}
8.3.2 Tokenizer
- 将原始的文本按照一定的规则,切分为词(
term or token) Elasticsearch内置的Tokenizerswhitespace/standard/uax_url_email/pattern/keyword/path hierarchy
- 可以用
java开发插件,实现自己的Tokenizer

1
2
3
4
5
6# Tokenizer
POST _analyze
{
"tokenizer": "path_hierarchy",
"text": "/user/ymruan/a/b/c/d/e"
}
8.3.3 Token Filters
- 将
Tokenizer输出的单词(term),进行增加,修改,删除 - 自带的
Token FiltersLowercase/stop/synonym(添加近义词)




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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69# Token Filters
#whitespace与stop
#按照空格切分,且将一些特定词过滤,比如 on in the ,这些词称之为stop,但是这里的第一个大写的The不会被去除
GET _analyze
{
"tokenizer": "whitespace",
"filter": ["stop"],
"text": ["The rain in Spain falls mainly on the plain."]
}
#加入 lowercase后,The被当成 stopword 删除
#先做了小写处理
GET _analyze
{
"tokenizer": "whitespace",
"filter": ["lowercase","stop"],
"text": ["The girls in China are playing this game!"]
}
# 如何自定义一个 analyzer 去满足自己特定的需求
DELETE my_index
PUT my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"typpe":"custom",
"char_filter": [
"emoticons"
],
"tokenizer": "punctuation",
"filter": [
"lowercase",
"english_stop"
]
}
},
"tokenizer": {
"punctuation":{
"type": "pattern",
"pattern": "[.,!?]"
}
},
"char_filter": {
"emoticons": {
"type":"mapping",
"mappings": [
":) => _happy_",
":( => _sad_"
]
}
},
"filter": {
"english_stop": {
"type":"stop",
"stopwords": "_english_"
}
}
}
}
}
POST my_index/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "I'm a :) person,and you?"
}
9. Index Template和Dynamic Template
9.1 什么是Index Template
- 随着时间的推移,你的集群上会有越来越多的索引,那比如说你的集群是用来做日志管理的,你会每天为这些日志生成一个新的索引,因为这样的方式可以让你的数据管理更加合理,另外集群会有一个更好的性能;
Index Template:帮助你设定Mappings和Settings,并按照一定的规则,自动匹配到新创建的索引之上- 模板仅在一个索引被创建时,才会产生作用。修改模板不会影响已创建的索引
- 你可以设定多个索引模板,这些设置会被
merge在一起 - 你可以指定
order的数值,控制merging的过程
9.1.1 Index Template的工作方式
- 当一个索引被创建时
- 应用
Elasticsearch默认的settings和mappings - 应用
order数值低的Index Template中的设定 - 应用
order高的Index Template中的设定,之前的设定会被覆盖 - 应用创建索引时,用户指定的
Settings和Mappings,并覆盖之前模板中的设定
- 应用
9.1.2 Demo
- 创建2个
Index Template - 查看根据名字查看
Template - 查看所有
templates,_tmplate/* - 创建一个临时索引,查看
replica和数据类型推断 - 将所有名字设为能与
Index Template匹配时,查看所生成的Index的Mappings和Settings








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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53# 创建一个模板
PUT _template/template_default
{
"index_patterns": ["*"],
"order" : 0,
"version" : 1,
"settings" : {
"number_of_shards" : 1,
"number_of_replicas" : 1
}
}
# 创建第二个模板
PUT /_template/template_test
{
"index_patterns": ["test*"],
"order" : 1,
"settings" : {
"number_of_shards" : 1,
"number_of_replicas" : 2
},
"mappings" : {
"date_detection" : false,
"numeric_detection" : true
}
}
# 查看template信息
GET /_template/template_default
GET /_template/temp*
# 写入新的数据,index以test开头
PUT testtemplate/_doc/1
{
"someNumber" : "1",
"someDate" : "2020/12/05"
}
GET testtemplate/_mapping
GET testtemplate/_settings
# 自己设定的settings的优先级高于template
PUT testmy
{
"settings": {
"number_of_replicas": 5
}
}
PUT testmy/_doc/1
{
"key" : "value"
}
GET testmy/_settings
9.2 什么是Dynamic Template
- 根据
Elasticseach识别的数据类型,结合字段名称,来动态设定字段类型- 所有的字符串类型都设定成
Keyword,或者关闭keyword字段 is开头的字段都设置成booleanlong_开头的都设置成long类型
- 所有的字符串类型都设定成
9.2.1 Dynamic Template

Dynamic Template是定义在某个索引的Mapping中Template有一个名称- 匹配规则是一个数组
- 为匹配到字段设置
Mapping


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
34GET my_index/_mapping
DELETE my_index
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"string_as_boolean": {
"match_mapping_type": "string",
"match" : "is*",
"mapping": {
"type": "boolean"
}
}
},
{
"string_as_keywords": {
"match_mapping_type" : "string",
"mapping" : {
"type" : "keyword"
}
}
}
]
}
}
PUT my_index/_doc/1
{
"firstName" : "Ruan",
"isVip" : "true"
}
GET my_index/_mapping
10. Elasticsearch聚合分析简介
10.1 什么是聚合?
Elasticsearch除了搜索之外,提供的针对ES数据进行统计分析的功能- 实时性高
Hadoop(T+1)
- 通过聚合,我们会得到一个数据的概览,是分析和总结全套的数据,而不是寻找单个文档
- 尖沙咀和香港岛的客房数量
- 不同的价格区间,可预定的经济型酒店和五星级酒店的数量
- 高性能,只需要一条语句,就可以从
Elasticsearch得到分析结果- 无需再客户端自己去实现分析逻辑
Kibana中大量的可视化报表,都是聚合分析来的
10.2 聚合的分类
Bucket Aggregation:一些列满足特定条件的文档的集合(类似Group BY)Metric Aggregation:一些数学运算,可以对文档字段进行统计分析(最大值、最小值、平均值)Pipeline Aggregation:对其他的聚合结果进行二次聚合Matrix Aggregation:支持多个字段的操作并提供一个结果矩阵
10.3 Bucket & Metric
Bucket: 可以理解为SQL中的GroupMetric: 可以理解为SQL中的Count,可以执行一系列的统计方法
10.3.1 Bucket

- 一些例子
- 杭州属于浙江/一个演员属于 男或女
- 嵌套关系:杭州属于浙江属于中国属于亚洲
Elasticsearch提供了很多类型的Bucket,帮助你用多种方式划分文档Term & Range(时间/年龄区间/地理位置)

10.3.2 Metric
Metric会基于数据集计算结果,除了支持在字段上进行计算,同样也支持在脚本(painless script)产生的结果之上进行计算- 大多数
Metric是数学计算,仅输出一个值min/max/sum/avg/cardinality
- 部分
metric支持输出多个数值stats/percentiles/percentile_ranks


