学习地址为极客时间《Elasticsearch核心技术与实战》,这只是我做的笔记,仅供参考;
三、Elasticsearch
入门
1. Elasticsearch
基本概念
1.1 文档
-
Elasticsearch
是面向文档的,<font size = 3 color = red>文档是所有可搜索数据的最小单位</font>- 日志文件中的日志项
- 一本电影的具体信息/一张唱片的详细信息
-
MP3
播放器里的一首歌/一篇PDF
文档中的具体内容
- 文档会被序列化成
Json
格式,保存在Elasticsearch
中-
Json
对象由字段组成 - 每个字段都有对应的字段类型(字符串/数值/布尔/日期/二进制/范围类型)
-
- 每个文档都有一个
UniqueID
- 你可以自己指定
ID
- 或者通过
Elasticsearch
自动生成
- 你可以自己指定
1.1.1 Json文档
- 一篇文档包含了一系列的字段。类似数据库表中一条记录
-
Json
文档,格式灵活,不需要预先定义格式- 字段的类型可以指定或者通过
Elasticsearch
自动推算 - 支持数据/支持嵌套
- 字段的类型可以指定或者通过
1.1.2 文档元数据
{
"index" : "movies",
"_type" : "_doc",
"_id" : "1",
"_score" : 14.69302,
"_source" : {
"year" : 1995,
"@version" : "1",
"genre" : [
"Adventure",
"Animation",
"Children",
"Comedy",
"Fantasy"
],
"id" : "1",
"title" : "Toy Story"
}
},
- 元数据,用于标注文档的相关信息
-
_index
: 文档所属的索引名 -
_type
: 文档所属的类型名 -
_id
: 文档唯一ID
-
_source
: 文档的原始JSON
数据 -
_all
: 整合所有字段内容到该字段,已被废除 -
_version
: 文档的版本信息 -
_score
: 相关性打分
-
1.2 索引
{
"movies" : {
"settings" : {
"index" : {
"creation_date" : "1604218204918",
"number_of_shards" : "1",
"number_of_replicas" : "1",
"uuid" : "F9-uy1bUTemm1Hs_LaDMQQ",
"version" : {
"created" : "7010099"
},
"provided_name" : "movies"
}
}
}
}
-
index
: 索引是文档的容器,是一类文档的结合-
index
体现了逻辑空间的概念:每个索引都有自己的Mapping
定义,用于定义包含文档的字段名和字段类型 -
Shard
体现了物理空间的概念:索引中的数据分散在Shard
中
-
- 索引的
Mapping
与Setting
-
Mapping
定义文档字段的类型 -
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
的区别-
Elasticsearch
-
Schemaless
:数据组织更加灵活 - 相关性:算相关度
- 高性能全文检索
-
-
RDBMS
- 事务
Join
-
1.3 REST API
1.4 kibana
上索引管理
//查看索引相关信息
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
的实例- 本质上就是一个<font size = 3 color = red>
Java
进程</font> - 一台机器上可以运行多个
Elasticsearch
进程,但是生产环境一般建议一台机器上只运行一个Elasticsearch
实例
- 本质上就是一个<font size = 3 color = red>
- 每一个节点都有名字,通过配置文件配置,或者启动时候
-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 查看集群的健康状况
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
名,约定都用_doc
-
Create
:如果ID已经存在,会失败 -
Index
:如果ID不存在,创建新的文档。否则,先删除现有的文档,再创建新的文档,版本会增加 -
Update
:文档必须已经存在,更新只会对相应字段做增量修改
2.1.1 Create
一个文档
- 支持自动生成文档
Id
和指定文档Id
两种方式 - 通过调用
post users/_doc
- 系统会自动生成
document Id
- 系统会自动生成
- 使用
HTTP PUT user/_create/1
创建时,URI
中显示指定_create
,此时如果该id
的文档已经存在,操作失败
//create document. 自动生成 _id
POST users/_doc
{
"user" : "Mike",
"post_date" : "2020-11-15T19:38:42",
"message" : "trying out Kibana"
}
//create document. 指定Id。如果id已经存在,报错
PUT users/_doc/1?op_type=create
{
"user" : "Mike",
"post_date" : "2020-11-15T19:38:42",
"message" : "trying out Elasticsearch"
}
2.1.2 Get
一个文档
- 找到文档,返回
HTTP 200
- 文档元信息
- _index/_type/
- 版本信息,同一个
Id
的文档,即使被删除,Version
号也会不断增加 -
_source
中默认包含了文档的所有原始信息
- 文档元信息
- 找不到文档,返回
HTTP 404
//Get document by Id
GET users/_doc/1
2.1.3 Index
文档
//Index,如果文档已经存在,就会删除旧文档,写入新文档,版本号会+1
PUT users/_doc/1
{
"user" : "Mike"
}
-
Index
和Create
不一样的地方:如果文档不存在,就索引新的文档。否则现有文档会被删除,新的文档被索引。版本信息+1
2.1.4 Update
文档
-
Update
方法不会删除原来的文档,而是实现真正的数据更新 -
Post
方法/Payload
需要包含再doc
中
//Update,在原文档的基础上增加字段
POST users/_update/1/
{
"doc":{
"post_date" : "2020-11-15T19:59:42",
"message" : "trying out Elasticsearch"
}
}
2.2 批量操作
2.2.1 Bulk API
- 在一次
REST
请求中,<font size = 3 color = red>重新建立网络连接是非常损耗性能的</font> - 支持在一次
API
调用中,对不同的索引进行操作 - 支持四种类型操作
Index
Create
Update
Delete
- 可以在
URI
中指定Index
,也可以在请求的Payload
中进行 - <font size = 3 color = red>操作中单挑操作失败,并不会影响其他操作</font>
-
返回结果包含了每一条操作执行的结果
//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
-
批量操作,可以减少网络连接所产生的开销,提高性能
// mget 操作
GET /_mget
{
"docs":[
{
"_index":"test",
"_id":"1"
},
{
"_index":"test",
"_id":"2"
}
]
}
2.2.3 批量查询-msearch
### msearch 操作
POST kibana_sample_data_ecommerce/_msearch
{}
{"query" : {"match_all" : {}},"size":1}
{"index" : "kibana_sample_data_flights"}
{"query" : {"match_all" : {}},"size":2}
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
进行测试
# 直接指定Analyzer进行测试
GET _analyze
{
"analyzer": "standard",
"text":"Mastering Elasticsearch,elasticsearch in Action"
}
5.2.2 指定索引的字段进行测试
# 指定索引的字段进行测试
POST users/_analyze
{
"field":"message",
"text":"Mastering Elasticsearch"
}
5.2.3 自定义分词器进行测试
# 自定义分词器进行测试
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Mastering Elasticsearch"
}
5.2 Standard Analyzer
分词器(<font size = 3 color = red>Elasticsearch
的默认分词器</font>)
- 默认分词器
- 按词切分
- 小写处理
5.3 Simple Analyzer
- 按照非字母切分,非字母的都被去除
-
小写处理
# 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
输出
# 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+
,非字符的符号进行分割
# Pattern Analyzer
GET _analyze
{
"analyzer": "pattern",
"text":"2 runing Quick brown-foxes leap over lazy dogs in the summer evening"
}
5.6 Language Analyzer
-
对不同国家的语言进行分词
# 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
- 需要安装
plugin
Elasticsearch-plugin install analysis-icu
- 提供了
Unicode
的支持,更好的支持亚洲语言
POST _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
键值对
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
实现搜索
GET /movies/_search?q=2012&df=title&sort=year:desc&from=o&size=10&timeout=1s
{
"profile":true
}
-
q
:指定查询语句,使用Query String Syntax
(<font color = red size = 3>Query String Syntax</font>) -
df
:默认字段,不指定时,会对所有字段进行查询 -
Sort
:排序/from
和size
用于分页 -
Profile
:可以查看查询时如何被执行的
5.6.1 指定字段查询 VS 范查询
-
q=title:2012
/q=2012
# 指定字段查询 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
查询(<font size = 3 color =red>注意:要使用Phrase
查询,需要将内容用引号引起来</font>),还要求前后顺序保持一致;意思是这两个词都要包含 - 分组与引号
-
title:(Beautiful AND Mind)
分组 title:"Beautiful Mind"
-
# 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_not
-
title:(+matrix -reloaded)
-
# 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:>2010
year:(>2010 && <=2018)
year:(+>2010 +<=2018)
# 所有的电影年份必须>=1980
GET /movies/_search?q=year:>=1980
{
"profile": "true"
}
5.6.5 通配符查询、正则表达式、模糊匹配与近似查询
- 通配符查询(通配符查询效率低,占用内存大,不建议使用。特别是放在最前面)
-
?
代表 1 个字符,*
代表 0 或多个字符title:mi?d
title:be*
-
- 正则表达式
title:[bt]oy
- 模糊匹配与近似查询
title:befutifl~1
title:“lord rings”~2
# 通配符查询
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 排序
- 最好在"数字型"与"日期型"字段上排序
-
因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值
//对日期排序
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"]
//source filstering
POST kibana_sample_data_ecommerce/_search
{
"_source": ["order_date"],
"query": {
"match_all": {}
}
}
5.7.5 脚本字段
-
用例:订单中有不同的汇率,需要结合汇率对订单价格进行排序
//脚本字段
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
)
//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
查询
//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 <font size = 3 color = red>几种查询方式的总结</font>
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
-
PUT /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 <font color = red>es match、match_phrase、query_string和term的区别</font>
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/Keyword
Date
Integer/Floating
Boolean
IPv4 & 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 |
数组 | 由第一个非空数值的类型所决定 |
空值 | 忽略 |
//写入文档,查看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 |
NO |
Mapping 被更新 |
YES |
NO |
NO |
- 当
dynamic
被设置成false
时候,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃 - 当设置成
Strict
模式时候,数据写入直接出错
#默认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
,该字段不可被搜索
DELETE 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 id
-
freqs
:记录doc id
和term frequencies
-
positions
:记录doc id
/term frequencies
/term position
-
offsets
:记录doc id
/term frequencies
/term position
/character offects
-
-
Text
类型默认记录postions
,其他默认为docs
-
记录内容越多,占用存储空间越大
7.4 null_value
- 需要对
null
值实现搜索 - 只有
keyword
类型支持设定Null_Value
#设定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
中
#设置 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
中不提供专门的数组类型。但是任何字段,都可以包含多个相同类型的数值
#数组类型
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
的<font size = 3 color = red>子字段</font>
- 增加一个
- 使用不同的
analyzer
- 不同语言
- pingyin字段的搜索
- 还支持为搜索和索引指定不同的
analyzer
- 厂商名字实现精确匹配
8.2 Exact Values
(精确值) VS Full Text
(全文本)
-
Exact values vs Full Text
-
Exact Value
:包括数字/日期/具体一个字符串(例如:"Apple Store")-
Elasticsearch
中的keyword
-
- 全文本,非结构化的文本数据
-
Elasticsearch
中的text
-
-
8.2.1 Exact Values
不需要被分词(Exact Values和Full Text最大的区别)
-
Elasticsearch
为每一个字段创建一个倒排索引-
Exact Value
在索引时,不需要做特殊的分词处理
-
8.3 自定义分词
- 当
Elasticsearch
自带的分词器无法满足时,可以自定义分词器。通过组合不同的<font size = 3 color = red>组件</font>实现(下面这三个组件是Analyzer
的组成,可看上面4.2)Character Filter
Tokenizer
Token Filter
8.3.1 Character Filters
- 在
Tokenizer
之前对文本进行处理,例如增加删除及替换字符。可以配置多个Character Filters
。会影响Tokenizer
的position
和offset
信息 - 一些自带的
Character Filter
-
HTML strip
: 去除html
标签 -
Mapping
: 字符串替换 -
Pattern replace
: 正则匹配替换
-
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
内置的Tokenizers
-
whitespace
/standard
/uax_url_email
/pattern
/keyword
/path hierarchy
-
- 可以用
java
开发插件,实现自己的Tokenizer
# Tokenizer
POST _analyze
{
"tokenizer": "path_hierarchy",
"text": "/user/ymruan/a/b/c/d/e"
}
8.3.3 Token Filters
- 将
Tokenizer
输出的单词(term
),进行增加,修改,删除 - 自带的
Token Filters
-
Lowercase
/stop
/synonym
(添加近义词)
-
# 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
# 创建一个模板
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
开头的字段都设置成boolean
-
long_
开头的都设置成long
类型
- 所有的字符串类型都设定成
9.2.1 Dynamic Template
-
Dynamic Template
是定义在某个索引的Mapping
中 -
Template
有一个名称 - 匹配规则是一个数组
- 为匹配到字段设置
Mapping
GET 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
:一些列满足特定条件的文档的集合(<font size = 3 color = red>类似Group BY</font>) -
Metric Aggregation
:一些数学运算,可以对文档字段进行统计分析(最大值、最小值、平均值) -
Pipeline Aggregation
:对其他的聚合结果进行二次聚合 -
Matrix Aggregation
:支持多个字段的操作并提供一个结果矩阵
10.3 Bucket
& Metric
-
Bucket
: 可以理解为SQL
中的Group
-
Metric
: 可以理解为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
-