Fork me on GitHub

Elasticsearch基础学习二

[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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"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 索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"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
  • 索引的MappingSetting
    • Mapping定义文档字段的类型
    • Setting定义不同的数据分布

1.2.1 索引的不同语义

image

  • 名词:一个Elasticsearch集群中,可以创建很多不同的索引
  • 动词:保存一个文档到Elasticsearch的过程也叫索引(indexing)
    • ES中,创建一个倒排索引的过程
  • 名词:一个B树索引,一个倒排索引

1.2.2 Type(这个我也不知道是干什么的)

image

1.2.3 ESRDBMS对比

RDBMS Elasticsearch
Table index(Type)
Row Document
Column Filed
Schema Mapping
SQL DSL
  • 传统关系型数据库和Elasticsearch的区别
    • Elasticsearch
      • Schemaless:数据组织更加灵活
      • 相关性:算相关度
      • 高性能全文检索
    • RDBMS
      • 事务
      • Join

1.3 REST API

image

1.4 kibana上索引管理

image
image

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 nodesMaster Node

  • 每个节点启动后,默认就是一个Master eligible节点
    • 可以设置 node.master:false禁止
  • Master-eligible节点可以参加选主流程,成为Master节点
  • 当第一个节点启动时候,它会将自己选举成Master节点
  • 每个节点上都保存了集群的状态,只有Master节点才能修改集群的状态信息
    • 集群状态(Cluster State),维护了一个集群中,必要的信息
      • 所有的节点信息
      • 所有的索引和其相关的MappingSetting信息
      • 分片的路由信息
    • 任意节点都能修改信息会导致数据的不一致性

1.8 Data Node & Coordinating Node

  • Data Node
    • 可以保存数据的节点,叫做Data Node。负责保存分片数据。在数据扩展上起到了至关重要的作用
  • Coordinating Node
    • 负责接受Client的请求,将请求分发到合适的节点,最终把结果汇集到一起
    • 每个节点默认都起到了Coordinating Node的职责

1.9 其他节点类型

  • HotWarm Node
    • 不同硬件配置的 Data Node,用来实现 HotWarm架构,降低集群部署的成本
  • Machine Learing Node
    • 负责跑机器学习的 Job,用来做异常检测
  • Tribe Node
    • (5.3开始使用Cross Cluster Serarch)Tribe Node 连接到不同的Elasticsearch集群,并且支持将这些集群当成一个单独的集群处理

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索引的分片分布情况
    • 思考:增加一个节点或改大主分片数对系统的影响?
      image

1.12 分片的设定(主分片是在创建时设定的,后期不能改,除非reindex

  • 对与生产环境中分片的设定,需要提前做好容量规划
    • 分片数设置过小
      • 导致后续无法增加节点实现水平扩展
      • 单个分片的数据量太大,导致数据重新分配耗时(数据倾斜)
    • 分片数设置过大,7.0 开始,默认主分片设置成1,解决了over-sharding的问题
      • 影响搜索结果的相关性打分,影响统计结果的准确性
      • 单个节点上过多的分片,会导致资源浪费,同时也会影响性能

1.13 查看集群的健康状况

1
GET _cluster/health

image
image

  • 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一个文档

image

  • 支持自动生成文档Id和指定文档Id两种方式
  • 通过调用post users/_doc
    • 系统会自动生成document Id
  • 使用HTTP PUT user/_create/1创建时,URI中显示指定_create,此时如果该id的文档已经存在,操作失败
    image
    image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//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一个文档

image

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

2.1.3 Index文档

1
2
3
4
5
//Index,如果文档已经存在,就会删除旧文档,写入新文档,版本号会+1
PUT users/_doc/1
{
"user" : "Mike"
}
  • IndexCreate不一样的地方:如果文档不存在,就索引新的文档。否则现有文档会被删除,新的文档被索引。版本信息+1
    image
    image
    image
    image

2.1.4 Update文档

  • Update方法不会删除原来的文档,而是实现真正的数据更新
  • Post方法 /Payload 需要包含再doc
    image
    image
    image
1
2
3
4
5
6
7
8
//Update,在原文档的基础上增加字段
POST users/_update/1/
{
"doc":{
"post_date" : "2020-11-15T19:59:42",
"message" : "trying out Elasticsearch"
}
}

2.2 批量操作

2.2.1 Bulk API

image

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

  • 批量操作,可以减少网络连接所产生的开销,提高性能
    image
    image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// mget 操作
GET /_mget
{
"docs":[
{
"_index":"test",
"_id":"1"
},
{
"_index":"test",
"_id":"2"
}
]
}

2.2.3 批量查询-msearch

1
2
3
4
5
6
### msearch 操作
POST kibana_sample_data_ecommerce/_msearch
{}
{"query" : {"match_all" : {}},"size":1}
{"index" : "kibana_sample_data_flights"}
{"query" : {"match_all" : {}},"size":2}

image

2.2.4 常见错误返回

问题 原因
无法连接 网络故障或集群挂了
连接无法关闭 网路故障或节点出错
429 集群过于繁忙
4xx 请求体格式有错
500 集群内部错误

3. 正排与倒排索引

3.1 正排

image

3.2 倒排索引

如果你想找某个词,在书中的具体页码,那正排就不能满足了,那我们可以建一个内容->文档Id的一种索引结构,就可以满足我们的需求了
image

3.2.1 倒排索引的核心组成

倒排索引包含两部分

  • 单词词典(Term Dictionary),记录所有文档的单词,记录单词到倒排列表的关联关系
    • 单词词典一般比较大,可以通过B+树哈希拉链法实现,以满足高性能的插入和查找
  • 倒排序列(Posting List)记录了单词对应的文档结合,由倒排索引项组成
    • 倒排索引项(Posting)
      • 文档Id
      • 词频TF-该单词在文档中出现的次数,用于相关性评分
      • 位置(Position)-单词在文档中分词的位置。用于语句搜索(phrase query)
      • 偏移(Offset)-记录单词的开始结束位置,实现高亮显示

3.2.2 Elasticsearch的一个例子

image

3.2.3 Elasticsearch的倒排索引

  • ElasticsearchJson文档中的每个字段,都有自己的倒排索引
  • 可以指定对某些字段不做索引
    • 优点:节省存储空间
    • 缺点:字段无法被搜索

4. 通过Analyzer进行分词

4.1 AnalysisAnalyzer

  • Analysis:文本分析是把全文本转换一系列单词(term\token)的过程,也叫分词
  • Analysis是通过Analyzer来实现的
    • 可使用Elasticsearch内置的分析器/或者按需定制化分析器
  • 除了在数据写入时转换词条,匹配Query语句时候也需要用相同的分析器对查询语句进行分析

image

4.2 Analyzer的组成

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

image

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进行测试

image

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

5.2.2 指定索引的字段进行测试

image

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

5.2.3 自定义分词器进行测试

image

1
2
3
4
5
6
7
# 自定义分词器进行测试
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Mastering Elasticsearch"
}

5.2 Standard Analyzer分词器(Elasticsearch的默认分词器)

  • 默认分词器
  • 按词切分
  • 小写处理

image
image

5.3 Simple Analyzer

  • 按照非字母切分,非字母的都被去除
  • 小写处理
    image
    image
    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

  • 按照空格切分
    image

5.4 Stop Analyzer

  • 相比Simple Analyzer
  • 多了stop filter
    • 会把the、a、is等修饰性词语去掉
      image

5.5 Keyword Analyzer

  • 不分词,直接将输入当一个term输出
    image
    image
    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+,非字符的符号进行分割
    image
    image
    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

  • 对不同国家的语言进行分词
    image
    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

  • 需要安装plugin
    • Elasticsearch-plugin install analysis-icu
  • 提供了Unicode的支持,更好的支持亚洲语言

image
image
image

1
2
3
4
5
6
7
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键值对
1
curl -XGET "http://elasticsearch:9200/kibana_sample_data_ecommerce/_search?q=customer_first_name:Eddie"

image

5.3 Request Body查询

image

5.4 搜索的返回结果(Response)

image

5.5 相关性

5.5.1 搜索的相关性

  • 搜索时用户和搜索引擎的对话
  • 用户关心的时搜索结果的相关性
    • 是否可以找到所有相关的内容
    • 有多少相关的内容被返回了
    • 文档的打分是否合理
    • 结合业务需求,平衡结果排名

5.5.2 衡量相关性

  • Information Retrieval(计算机领域的一门学课)
    • Precision(查准率): 尽可能返回较少的无关文档
    • Recall(查全率):尽可能返回较多的相关文档
    • Ranking:是否能够按照相关度进行排序

5.5.3 Precision and Recall

image

5.6 URI Search:通过URI query实现搜索

1
2
3
4
GET /movies/_search?q=2012&df=title&sort=year:desc&from=o&size=10&timeout=1s
{
"profile":true
}
  • q:指定查询语句,使用Query String Syntax(Query String Syntax)
  • df:默认字段,不指定时,会对所有字段进行查询
  • Sort:排序/fromsize 用于分页
  • Profile:可以查看查询时如何被执行的

5.6.1 指定字段查询 VS 范查询

  • q=title:2012 / q=2012

image
image

image
image
image

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 MindPhrase查询(注意:要使用Phrase查询,需要将内容用引号引起来),还要求前后顺序保持一致;意思是这两个词都要包含
  • 分组与引号
    • title:(Beautiful AND Mind)分组
    • title:"Beautiful Mind"

image
image

image
image
image
image
image
image

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_not
    • title:(+matrix -reloaded)
      image

image
image
image
image
image

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:>2010
    • year:(>2010 && <=2018)
    • year:(+>2010 +<=2018)

image

1
2
3
4
5
# 所有的电影年份必须>=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

image

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 BodyQuery DSL简介

  • 将查询语句通过HTTP Request Body发送给Elasticcsearch
  • Query DSL
    image

5.7.2 分页

  • From从0开始,默认返回10个结果
  • 获取靠后的翻页成本较高
    image

5.7.3 排序

  • 最好在”数字型”与”日期型”字段上排序
  • 因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值
    image
    image
    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"]
    image
    image
    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 脚本字段

  • 用例:订单中有不同的汇率,需要结合汇率对订单价格进行排序
    image
    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)

image
image

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查询

image
image

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 几种查询方式的总结

image
image

5.9 Query StringSimple Query String查询

5.8.1 Query String Query

  • 类似URI Query
    image
    image
    image

5.8.1 Simple Query String Query

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

image
image
image

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
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 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/Keyword
    • Date
    • Integer/Floating
    • Boolean
    • IPv4 & IPv6
  • 复杂类型:对象和嵌套对象
    • 对象类型/嵌套类型
  • 特殊类型
    • geo_point & geo_shape/percolator

6.3 什么是Dynamic Mapping

  • 在写入文档时,如果索引不存在,会自动创建索引
  • Dynamic Mapping的机制,使得我们无需手动定义Mappings,Elasticsearch会自动根据文档信息,推算出字段的类型
  • 但是有时候推算的不对,例如地理位置信息
  • 当类型如果设置不对时,会导致一些功能无法正常运行,例如Range查询
    image

6.4 类型的自动识别

JSON类型 Elasticsearch类型
字符串 匹配日期格式,设置成Date
配置数字设置为float或者long,该选项默认关闭
设置为Text,并且增加keyword子字段
布尔值 boolean
浮点数 float
整数 long
对象 Object
数组 由第一个非空数值的类型所决定
空值 忽略

image
image

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

image
| | "true" | "false" | "strict"
—|—|—|—
文档可索引 | YES | YES | NO
字段可索引 | YES | NO | NO
Mapping被更新 | YES | NO | NO

  • dynamic被设置成false时候,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃
  • 当设置成Strict模式时候,数据写入直接出错

image
image
image
image
image
image
image
image

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

image

7.2 自定义Mapping的一些建议

  • 可以参考API手册,纯手写
  • 为了减少输入的工作量,减少出错概率,可以依照以下步骤
    • 创建一个临时的index,写入一些样本数据
    • 通过访问Mapping API获得该临时文件的动态Mapping定义
    • 修改后用,使用该配置创建你的索引
    • 删除临时索引

7.3 控制当前字段是否被索引

  • Index:控制当前字段是否被索引。默认为true。如果设置成false,该字段不可被搜索
    image
    image
    image
    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
    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 idterm frequencies
    • positions:记录doc id/term frequencies/term position
    • offsets:记录doc id/term frequencies/term position/character offects
  • Text类型默认记录postions,其他默认为docs
  • 记录内容越多,占用存储空间越大
    image

7.4 null_value

  • 需要对null值实现搜索
  • 只有keyword类型支持设定Null_Value
    image
    image
    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设置

  • _all7中被copy_to所替代
  • 满足一些特定的搜索需求
  • copy_to将字段的数值拷贝到目标字段,实现类似_all的作用
  • copy_to的目标字段不出现在_source
    image
    image
    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"
    }
    }
    }
    }

当你索引一个文档的时候,它如果包含了firstNamelastName,我们都会把这个值拷贝到fullName字段上去,当你要查询时,就可以用fullName来查询了

7.6 数组类型

  • Elasticsearch中不提供专门的数组类型。但是任何字段,都可以包含多个相同类型的数值
    image
    image
    image
    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 多字段类型

image

  • 多字段特性
    • 厂商名字实现精确匹配
      • 增加一个keyword子字段
    • 使用不同的analyzer
      • 不同语言
      • pingyin字段的搜索
      • 还支持为搜索和索引指定不同的analyzer

8.2 Exact Values(精确值) VS Full Text(全文本)

  • Exact values vs Full Text
    • Exact Value:包括数字/日期/具体一个字符串(例如:”Apple Store”)
      • Elasticsearch中的keyword
    • 全文本,非结构化的文本数据
      • Elasticsearch中的text

image

8.2.1 Exact Values不需要被分词(Exact Values和Full Text最大的区别)

  • Elasticsearch为每一个字段创建一个倒排索引
    • Exact Value在索引时,不需要做特殊的分词处理

image

8.3 自定义分词

  • Elasticsearch自带的分词器无法满足时,可以自定义分词器。通过组合不同的组件实现(下面这三个组件是Analyzer的组成,可看上面4.2)
    • Character Filter
    • Tokenizer
    • Token Filter

8.3.1 Character Filters

  • Tokenizer之前对文本进行处理,例如增加删除及替换字符。可以配置多个Character Filters。会影响Tokenizerpositionoffset信息
  • 一些自带的Character Filter
    • HTML strip: 去除html标签
    • Mapping: 字符串替换
    • Pattern replace: 正则匹配替换

image
image
image

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内置的Tokenizers
    • whitespace/standard/uax_url_email/pattern/keyword/path hierarchy
  • 可以用java开发插件,实现自己的Tokenizer

image

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 Filters
    • Lowercase/stop/synonym(添加近义词)

image
image
image
image

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 TemplateDynamic Template

9.1 什么是Index Template

  • 随着时间的推移,你的集群上会有越来越多的索引,那比如说你的集群是用来做日志管理的,你会每天为这些日志生成一个新的索引,因为这样的方式可以让你的数据管理更加合理,另外集群会有一个更好的性能;
  • Index Template:帮助你设定MappingsSettings,并按照一定的规则,自动匹配到新创建的索引之上
    • 模板仅在一个索引被创建时,才会产生作用。修改模板不会影响已创建的索引
    • 你可以设定多个索引模板,这些设置会被merge在一起
    • 你可以指定order的数值,控制merging的过程
      image

9.1.1 Index Template的工作方式

  • 当一个索引被创建时
    • 应用Elasticsearch默认的settingsmappings
    • 应用order数值低的Index Template中的设定
    • 应用order高的Index Template中的设定,之前的设定会被覆盖
    • 应用创建索引时,用户指定的SettingsMappings,并覆盖之前模板中的设定

9.1.2 Demo

  • 创建2个Index Template
  • 查看根据名字查看Template
  • 查看所有templates,_tmplate/*
  • 创建一个临时索引,查看replica和数据类型推断
  • 将所有名字设为能与Index Template匹配时,查看所生成的IndexMappingsSettings

image
image
image
image
image
image
image
image

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开头的字段都设置成boolean
    • long_开头的都设置成long类型

9.2.1 Dynamic Template

image

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

image
image

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
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:一些列满足特定条件的文档的集合(类似Group BY
  • Metric Aggregation:一些数学运算,可以对文档字段进行统计分析(最大值、最小值、平均值)
  • Pipeline Aggregation:对其他的聚合结果进行二次聚合
  • Matrix Aggregation:支持多个字段的操作并提供一个结果矩阵

10.3 Bucket & Metric

  • Bucket: 可以理解为SQL中的Group
  • Metric: 可以理解为SQL中的Count,可以执行一系列的统计方法

10.3.1 Bucket

image

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

image

10.3.2 Metric

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

image
image