dream

一个菜鸟程序员的成长历程

0%

index template

index template

index template 可以帮助你设定 Mappings 和 Settins ,并按照一定的规则,自动匹配到新创建的索引上

  • 模板仅在一个索引被新创建时,才会产生作用。修改模板不会影响已经创建的索引
  • 可以设定多个索引模板,这些设置会根据一定的规则 合并到一起
  • 可以指定 Order的数值 控制 合并 的过程

实例

1
2
3
4
5
6
7
8
9
10
PUT _template/template_default
{
"index_patterns":["*"],
"order": 0,
"version": 1,
"settings":{
"number_of_shards":1,
"number_of_replicas":1
}
}

index templalte 的工作方式

当一个新索引被创建

  • 应用 ES 的默认 settins 和 mappings
  • 应用 order 数值低的 index template 的 settings mappings
  • 应用 order 数值高的 index template 的 settings mappings
  • 应用 用户 自定义的 settings mappings

Dynamic template

根据 ES 识别的数据类型,结合字段名称,来动态设定字段类型

  • 所有的字符串类型都设定成 keyword 或者关闭 keyword 字段
  • is 开头的字段都设置成 boolean
  • long_ 开头的都设置成 long 类型

Aggregation 聚合

ES 除了搜索以外提供了统计分析的功能

  • 实时性高
  • Hadoop

通过聚合可以得到数据的概览,是分析和总结全套的数据,而不是寻找单个文档

高性能,只需要一条语句,就可以从 ES 得到结果

  • 无需在客户端自己去实现分析逻辑

比如 Kibana 中的可视化报表就是使用 聚合 实现的

分类

Bucket Aggregation : 一些满足特定条件的集合,相当于 sql 里面的 group by 分组功能

Metric Aggregatino : 提供数学运算,比如最大值,最小值,平均值 相当于 sql 里面的 count(),max()等函数功能

Pipeline Aggregation: 可以二次聚合

Matrix Aggregation: 支持对多个字段的操作并提供一个结果矩阵

极客时间 ES 学习笔记

自定义Analyzer

多字段特性

实现精确匹配

  • 增加一个 keyword 字段

使用不同的analyzer

  • 不同语言
  • pinyin 字段的搜索
  • 还支持为 搜索 和 索引 指定不同的 analyzer

Exact Values vs Full Text

Exact Value 包括数字 / 日期 / 具体一个字符串(例如 Apple Store) 精确值 不需要分词

  • ES 中的 keyword

全文本,非结构化的文本数据 需要分词

  • ES 中的 text

自定义分词

当 ES 自带的分词器无法满足时,可以自定义分词器。通过自己组合不同的组件实现

  • Character Filter

  • Tokenizer

  • Token Filter

通过自己组合上面不同的组件,可以实现出不同的分词器效果。

Character Filter

在 Tokenizer 之前对文本进行处理。可以配置多个进行不同的文本处理。会影响 Tokenizer 的 position 和 offset 信息

下面是一些自带的 Character Filter

  • HTML strip 去除 html 标签
  • Mapping 字符串替换
  • Pattern replace 正则匹配替换

Tokenizer

将原始的文本按照一定的规则,切分为词

下面是一些ES内置的 Tokenizer

  • whitespace
  • standard
  • uax_url_email
  • pattern
  • keyword
  • path hierarchy

也可以用 JAVA 开发插件,实现自己的 Tokenizer

Token filter

将 Tokenizer 输出的单词 进行增加修改删除等操作

下面是ES 自带的

  • Lowercase 小写
  • stop 停止词
  • synonym 近义词

这几个操作简单来说就是

  • Character Filter 在分词前进行处理
  • Tokenizer 分词
  • Token Filter 分词后进行处理

自定义分词器

指令

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
PUT test_home
{
"settings":{
"analysis":{
"analyzer":{
my_analyzer:{ //自定义分词器
"type":"custom",
"char_filter":[
"test" //自定义 character filter
],
"tokenizer":"test_tokenizer", //自定义 tokenizer
"filter":[ //token filter
"lowercase",
"english_stop"
]
},
"tokenizer":{ //自定义 tokenizer
"test_tokenizer":{
"type":"pattern",
"pattern":"[?]"
}
},
"char_filter":{ //自定义 character filter -替换成_
"test":{
"type":"mapping",
"mappings":[
"-" => "_"
]
}
},
"filter":{ //设置 停止词
"english_stop":{
"type":"stop",
"stopwords":"_englsh_"
}
}
}
}
}
}

极客时间 ES 学习笔记

Analyzer分词

Analysis 和 Analyzer

Analysis - 文本分析是把全文本转换成一系列单词(term/token)的过程,也叫分词

Analysis 是通过 Analyzer 来实现的

  • 可使用 Elasticsearch 内置的分析器,或者按需制定分析器

除了在数据写入时转换词条,匹配 Query 语句时候也需要用相同的分析器对查询语句进行分析

Analyzer 的组成

分词器是专门处理分词的组件,由三部分组成

  • Character Filters (针对原始文本处理,例如去除html)
  • Tokenizer (按照规则切分为单词)
  • Token Filter(将切分的单词进行加工,小写,删除 stopwords,增加同义词)

例子:

Mastering Elasticsearch & Elasticsearch in Action 经过上面的步骤就会产生

  • master
  • elasticsearch
  • action

ES的内置分词器

  • Standard Analyzer - 默认分词器,按词切分,小写处理
  • Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
  • Stop Analyzer - 小写处理,停用词过滤(the a is)
  • WhiteSpace Analyzer - 按照空格切分,不转小写
  • Keyword Analyzer - 不分词,直接将输入当做输出
  • Patter Analyzer - 正则表达式,默认\W+(非字符分隔)
  • Language - 提供了30多种常见语言的分词器
  • Customer Analyzer 自定义分词器

使用 _analyzer API

直接指定 Analyzer 进行测试

指令

1
2
3
4
5
GET /_analyze
{
"analyzer":"standard",
"text":"Mastering Elasticsearch & Elasticsearch in Action"
}

结果

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
{
"tokens" : [
{
"token" : "mastering",
"start_offset" : 0,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "elasticsearch",
"start_offset" : 10,
"end_offset" : 23,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "elasticsearch",
"start_offset" : 25,
"end_offset" : 38,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "in",
"start_offset" : 39,
"end_offset" : 41,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "action",
"start_offset" : 42,
"end_offset" : 48,
"type" : "<ALPHANUM>",
"position" : 4
}
]
}

指定索引的字段进行测试

指令

1
2
3
4
5
POST test_home/_analyze
{
"field": "job_name",
"text":"Mastering Elasticsearch, Elasticsearch in Action"
}

结果

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
{
"tokens" : [
{
"token" : "mastering",
"start_offset" : 0,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "elasticsearch",
"start_offset" : 10,
"end_offset" : 23,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "elasticsearch",
"start_offset" : 25,
"end_offset" : 38,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "in",
"start_offset" : 39,
"end_offset" : 41,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "action",
"start_offset" : 42,
"end_offset" : 48,
"type" : "<ALPHANUM>",
"position" : 4
}
]
}

自定义分词进行测试

指令

1
2
3
4
5
6
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text":"Mastering Elasticsearch, Elasticsearch in Action"
}

结果

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
{
"tokens" : [
{
"token" : "mastering",
"start_offset" : 0,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "elasticsearch",
"start_offset" : 10,
"end_offset" : 23,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "elasticsearch",
"start_offset" : 25,
"end_offset" : 38,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "in",
"start_offset" : 39,
"end_offset" : 41,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "action",
"start_offset" : 42,
"end_offset" : 48,
"type" : "<ALPHANUM>",
"position" : 4
}
]
}

中文分词的难点

中文句子,切分成一个个词(不是一个个字)

英文字,单词有自然地空格进行切分

一句中文,在不同的上下文,有不同的理解

推荐的一些中文分词器:

极客时间 ES 学习笔记

Dynamic Mapping和常见字段类型

Mapping

Mapping 类似数据库中的 schema的定义,作用如下:

  • 定义索引中的字段的名称
  • 定义字段的数据类型,例如字符串,数字,布尔等
  • 字段,倒排索引的相关配置

Mapping 会把 JSON 文档映射成 Lucene 所需要的扁平格式

一个Mapping 属于一个索引的 Type

  • 每个文档都属于一个Type
  • 一个 Type 有一个 Mapping 定义
  • 7.0开始,不需要在 Mapping 定义中指定 Type信息

字段的数据类型

简单类型
  • Text/Keyword
  • Date
  • Interger/Floating
  • Boolean
  • IPv4 & IPv6
复杂类型
  • 对象类型
  • 嵌套类型
特殊类型

geo 是用于存储地理位置信息的类型

  • geo_point & geo_shape/ percolator

Dynamic Mapping

这个就是动态的Mapping,可以不需要手动指定,由 ES 动态识别数据类型,来生成对应的Mapping。

但是这样生成的 Mappping 不一定正确。

如果想要修改Mapping

  • 新增加字段
    • Dynamic 设为 true,一旦有新字段,Mapping会自动更新
    • Dynamic 设为 false,一旦有新字段,Mapping不会更新,新增加的字段无法被索引,但是_source里面有数据
    • Dynamic 设为 Strict,文档写入失败
  • 已有字段,一旦已经有数据就不支持修改了
    • Lucene实现的倒排索引,一旦生成就不能修改
  • 如果想要修改,需要Reindex API ,重建索引

原因:

  • 如果修改了字段的数据类型,会导致已被索引的数据无法被搜索
  • 但是如果是增加新的字段,就不会有这种情况

注意!!!:
如果Dynamic设为false,那么新增字段无法被索引,也就意味着不能拿这个字段去搜索,比如新增加一个name字段,那么无法搜索name这个字段,比如你想搜索name是张三的,是搜索不出来的。

极客时间 ES 学习笔记

RequestBody和Query DSL简介

通过RequestBody 实现搜索

参数:

  • q:指定查询语句,使用Query String Syntax
  • df:默认字段,不指定会对所有字段进行查询
  • sort:排序
  • from/size: 用来分页
  • Profile 可以查看查询是如何被执行的

这个方法的参数和URI是一样的,比如我们在kibana里面执行下面的指令:

1
2
3
4
5
6
7
GET test_home/_search
{
"profile": "true",
"from":0,
"size":10,
"sort":[{"id":"desc"}]
}

查询指定字段

这种方式还可以查询指定字段,通过_source参数来指定。支持通配符

指令

1
2
3
4
5
6
7
8
GET test_home/_search
{
"profile": "true",
"query":{
"match_all":{}
},
"_source":['job_name','id'] //指定字段
}

脚本字段

可以进行一些简单运算,拼接字符串等操作

指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET test_home/_search
{
"profile": "true",
"query":{
"match_all":{}
},
"script_fields":{
"new_field":{
"script":{
"lang":"painless" //使用这个脚本
"source": "doc['job_name'].value+'_hello'" //在职位名称后面拼接_hello
}
}
}
}

结果

1
2
3
4
5
"fields":{
"new_fields":[
"产品_hello"
]
}

query DSL

通过DSL查询产品怎么办呢,可以使用query参数

1
2
3
4
5
6
7
8
9
10
GET test_home/_search
{
"profile": "true",
"query":{
"match":{ //使用match 而不是 match_all
"job_name":"产品" //这里就相当于出现产 或者 品 都会搜出来 左边的 job_name 就是搜索的字段
}
},
"_source":['job_name','id'] //指定字段
}

如果想使用AND可以增加参数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
GET test_home/_search
{
"profile": "true",
"query":{
"match":{ //使用match 而不是 match_all
"job_name": { //这里变成一个对象 左边的 job_name 就是搜索的字段
"query":"产品", //这里是搜索内容
"operator":"AND" //这里指定AND OR NOT
}
}
},
"_source":['job_name','id'] //指定字段
}

短语搜索 match phrase

这里就是短语,单词搜索,等价于URI的phrase search

指令

1
2
3
4
5
6
7
8
9
10
11
12
13
GET test_home/_search
{
"profile": "true",
"query":{
"match_phrase":{ //使用match_phrase
"job_name": { //这里变成一个对象 左边的 job_name 就是搜索的字段
"query":"产品", //这里是搜索内容
"slop":1, //代表可以有一个错误
}
}
},
"_source":['job_name','id'] //指定字段
}

极客时间 ES 学习笔记

Search API

两种方法

URI Search

  • 通过Url参数来进行查询

url指定参数q=field:搜索内容

例子:
http://localhost:9200/{index}/_search?q={field}:搜索内容

url:
http://localhost:9200/test_home/_search?q=job_name:php

Request Body Search

  • 通过ES提供的JSON格式的DSL语句进行查询
语法 范围
/_search 集群上所有索引
/{index}/_search 对应index上的数据
/{index},{index}/_search 对应多个index上的数据
/test*/_search 所有test开头的index上的数据

支持GET POST两种方式

例子:

1
2
3
4
5
6
GET/POST test_home/_search
{
"query":{
"match_all": {}
}
}

搜索返回的结果

  • took: 查询花的时间
  • hits-total: 符合条件的总文档数
  • hints: 结果集
  • _score: 相关度评分

相关度

Infomation Retrieval

  • Precision(查准率) - 尽可能返回较少的无关文档
  • Recall (查全率) - 尽量返回较多的相关文档
  • Ranking - 是否能按相关度进行排序

极客时间 ES 学习笔记

URI Search

通过URI query 实现搜索

参数:

  • q:指定查询语句,使用Query String Syntax
  • df:默认字段,不指定会对所有字段进行查询
  • sort:排序
  • from/size: 用来分页
  • Profile 可以查看查询是如何被执行的

Query String Syntax

指定字段和泛查询

泛查询

q参数后面只跟着查询内容会对所有字段进行搜索,可以看到返回值里面profile-query-typeDisjunctionMaxQuerydescription(customer_name.keyword:产品 | (job_name:产 job_name:品) | job_name.keyword:产品 | (customer_name:产 customer_name:品) | MatchNoDocsQuery("failed [id] query, caused by number_format_exception:[For input string: "产品"]") | (city_names:产 city_names:品) | city_names.keyword:产品)

指令

1
2
3
4
GET test_home/_search?q=产品
{
"profile": "true"
}

指定字段

指定字段有两种方式,一种是df参数

指令

1
2
3
4
GET test_home/_search?q=产品&df=job_name
{
"profile": "true"
}

还有一种是q参数使用冒号来分隔

指令

1
2
3
4
GET test_home/_search?q=job_name:产品
{
"profile": "true"
}

指定字段的返回参数可以看到typeBooleanQuery,descriptionjob_name:产 job_name:品

term query & PhraseQuery

term query 是 or的关系,比如上面搜索产品,只要包含或者就都会搜索出来,可以用()小括号把搜索内容括起来

phrase query 是 and 的关系,需要再搜索内容上加双引号。这样只会搜索出产品的结果。

指令

1
2
3
4
GET test_home/_search?q=job_name:"产品"
{
"profile": "true"
}

boolean query

boolean query可以用AND,OR,NOT来操作
比如既要又要
指令

1
2
3
4
GET test_home/_search?q=job_name:(产 AND 品)
{
"profile": "true"
}

比如只有一个就行
指令

1
2
3
4
GET test_home/_search?q=job_name:(产 OR 品)
{
"profile": "true"
}

比如只有品没有产

指令

1
2
3
4
GET test_home/_search?q=job_name:(品 NOT 产)
{
"profile": "true"
}

范围查询

区间表示 []闭区间 {}开区间

  • year:[2019 TO 2.18]

算术符号

可以使用>,<,=等数学符号

通配符查询

通配符查询效率低,占用内存大,不建议使用。

正则表达式

可以用正则进行匹配搜索查询

  • job_name:[bt]oy

模糊匹配与近似查询

模糊查询允许用户输错查询关键字

比如上面的查询产品,用户打错了,输成了品阶。我们通过模糊匹配依旧可以查询出来内容.就是通过在查询内容后面输入~1代表允许1个字的错误

指令

1
2
3
4
GET test_home/_search?q=job_name:品阶~1
{
"profile": "true"
}

极客时间 ES 学习笔记

query string和simple query string

query string

类似URI query

指令

1
2
3
4
5
6
7
8
9
POST users/_search
{
"query":{
"query_string":{
"default_field":"job_name", //相当于URI的 df
"query":"产品"
}
}
}

还可以搜索多个字段

指令

1
2
3
4
5
6
7
8
9
POST users/_search
{
"query":{
"query_string":{
"query":"产品",
"fields":['job_name'] //搜索job_name是产品的
}
}
}

还可以直接在query里面使用AND,OR,NOT的操作符

指令

1
2
3
4
5
6
7
8
9
10
POST users/_search
{
"query":{
"query_string":{
"query":"产 OR 品",
"fields":['job_name'] //搜索job_name是产品的
}
}
}

simple query string

类似 query string ,但是会忽略错误的语法,同时只支持部分查询语法

  • 不支持AND OR NOT ,会当做字符处理
  • Term 之间默认的关系是OR,可以指定 operator
  • 支持 部分逻辑
    • +代替AND
    • |代替OR
    • -代替NOT

这里如果使用了+或者AND还有OR,那么会使用AND,而OR不生效。

指令

1
2
3
4
5
6
7
8
9
10
POST users/_search
{
"query":{
"query_string":{
"query":"产-品", //这里-代表OR
"fields":['job_name'] //搜索job_name是产品的
"default_operator": "OR" //指定默认操作符
}
}
}

极客时间 ES 学习笔记

正排索引和倒排索引

正排索引

正排索引就是通过索引id找到内容,比如通过书本的目录找到内容。

倒排索引

倒排索引就是把正排索引倒过来,通过内容找到索引id.

建立一个倒排索引

id 内容
1 php java go
2 php es
3 php 开发

把上面的内容可以建立成下面的倒排索引

内容 Count id
php 3 1:0,2:0,3:0
java 1 1:1
go 1 1:2
es 1 2:1
开发 1 3:1

倒排索引的核心组成

单词词典 记录所有文档的单词 记录单词到倒排列表的关联关系。
单词词典一般比较大,可以通过B+树或hash拉链法实现,以满足高性能的插入和查询

倒排列表记录了单词对应的文档结合,由倒排索引项组成

倒排索引项

  • 文档id
  • 词频 该单词在文档中出现的次数,用于相关性评分
  • 位置 单词在文档中分词的位置。用于语句搜索
  • 偏移 记录单词的开始结束位置,实现高亮显示

ES中的倒排索引

ES的JSON文档中的每个字段,都有自己的倒排索引
可以指定对某些字段不做索引

  • 优点:节省存储空间
  • 缺点:字段无法被搜索

极客时间 ES 学习笔记

显式mapping

设置mapping

指令

1
2
3
4
5
6
7
8
9
10
PUT {index}
{
"mappings":{
"properties":{ //设置mapping
"job_name":{
"type":"text"
}
}
}
}

自定义Mapping

可以参考 API 手册,纯手写

也可以复制现有的动态Mapping:

  • 创建一个临时的index,写入一些样本数据
  • 通过访问 Mapping API 获得该临时文件的动态 Mapping 定义
  • 修改后,使用该配置创建你的索引
  • 删除临时索引

index

index 控制当前字段是否被索引。默认为true,如果设置成false,就不会被索引,不能被搜索。

优点:不会建立倒排索引,更节省内存空间
缺点:该字段不能被搜索

指令

1
2
3
4
5
6
7
8
9
10
11
PUT {index}
{
"mappings":{
"properties":{ //设置mapping
"job_name":{
"type":"text",
"index":false //这里设置false,这个字段就不能被搜索
}
}
}
}

index options

有四种不同级别的 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类型默认记录 positions ,其他默认为 docs

记录内容越多,占用存储空间越大。

指令

1
2
3
4
5
6
7
8
9
10
11
PUT {index}
{
"mappings":{
"properties":{ //设置mapping
"job_name":{
"type":"text",
"index_options":"offsets" //这里设置index_options
}
}
}
}

null value

如果需要对 NULL 值进行搜搜,那么只有 Keyword 类型支持设定 null_value

指令

1
2
3
4
5
6
7
8
9
10
11
PUT {index}
{
"mappings":{
"properties":{ //设置mapping
"job_name":{
"type":"text",
"null_value":"NULL" //这里设置null_value
}
}
}
}

copy_to

copy_to 相当于以前的 _all 。可以满足一些特定的搜索需求。copy_to 将字段的数值拷贝到目标字段,实现类似_all的作用。copy_to的目标字段不会出现在_source中

指令

1
2
3
4
5
6
7
8
9
10
11
PUT {index}
{
"mappings":{
"properties":{ //设置mapping
"job_name":{
"type":"text",
"copy_to":"fullName" //这里设置copy_to
}
}
}
}

数组类型

ES不提供专门的数组类型。但是任何字段,都可以包含多个相同类型的数值。

指令

1
2
3
4
5
6
7
PUT test_home/_doc/1
{
"job_name":"php",
"city":[
"北京市","上海市"
]
}

可以看到 city 就是一个数组。但是如果查看 mapping 会发现 city 是一个 text/Keyword 类型。

极客时间 ES 学习笔记