Elasticsearch 之 DQL

查询语法

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 /product/_doc/1
{
"name" : "xiaomi phone",
"desc" : "shouji zhong de zhandouji",
"price" : 3999,
"tags": [ "xingjiabi", "fashao", "buka" ]
}
PUT /product/_doc/2
{
"name" : "xiaomi nfc phone",
"desc" : "zhichi quangongneng nfc,shouji zhong de jianjiji",
"price" : 4999,
"tags": [ "xingjiabi", "fashao", "gongjiaoka" ]
}


PUT /product/_doc/3
{
"name" : "nfc phone",
"desc" : "shouji zhong de hongzhaji",
"price" : 2999,
"tags": [ "xingjiabi", "fashao", "menjinka" ]
}

PUT /product/_doc/4
{
"name" : "xiaomi erji",
"desc" : "erji zhong de huangmenji",
"price" : 999,
"tags": [ "low", "bufangshui", "yinzhicha" ]
}

PUT /product/_doc/5
{
"name" : "hongmi erji",
"desc" : "erji zhong de kendeji",
"price" : 399,
"tags": [ "lowbee", "xuhangduan", "zhiliangx" ]
}

以后语句将忽略type:_doc

Query_String

1
2
#查询product索引下的所有type/doc
GET /product/_search
1
2
3
4
5
6
7
8
9
#设置时限查询
timeout:

(1) 设置:默认没有timeout,如果设置了timeout,那么会执行timeout机制。

(2) Timeout机制:假设用户查询结果有1W条数据,但是需要10s才能查询完毕
但是用户设置了1s的timeout,那么不管当前一共查询到了多少数据,都会在1s后ES将停止查询,并返回当前数据。

GET /_search?timeout=1s
1
2
#查询doc中包含xiaomi的所有doc
GET /product/_search?q=xiaomi
1
2
#查询name中包含xiaomi的所有doc
GET /product/_search?q=name:xiaomi

上面两者的区别:

q=xiaomi :将所有字段拼接成一个长字符串进行匹配

q=name:xiaomi :直接按照name进行匹配

1
2
#分页查询 每页2条数据 取第一页
GET /product/_search?from=0&size=2
1
2
#排序 使用排序的话,相关度分数将_score = null
GET /product/_search?sort=price:asc

Query DQL

1
2
3
4
5
6
7
//match_all 查询所有
GET /product/_search
{
"query":{
"match_all": {}
}
}

match 重点掌握语句!!!

1
2
3
4
5
6
7
8
9
//match 查询 name中包含“nfc” 如果多个单词会进行分词!!!
GET /product/_search
{
"query": {
"match": {
"name": "nfc"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//sort 搜索名称包含“nfc”并且价格由高到低排序
GET /product/_search
{
"query": {
"multi_match": {
"query": "nfc",
"fields": ["name","desc"]
}
},
"sort": [
{
"price": "desc"
}
]
}
1
2
3
4
5
6
7
8
9
10
//multi_match 据多个字段查询一个关键词,name和desc中包含"nfc"
GET /product/_search
{
"query": {
"multi_match": {
"query": "nfc",
"fields": ["name","desc"]
}
}
}
1
2
3
4
5
6
7
8
9
10
//_source 元数据:想要查询多个字段,例子中为只查询“name”和“price”字段
GET /product/_search
{
"query":{
"match": {
"name": "nfc"
}
},
"_source": ["name","price"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//分页(deep-paging):查询第一页(每页两条数据)
GET /product/_search
{
"query":{
"match_all": {}
},
"sort": [
{
"price": "asc"
}
],
"from": 0,
"size": 2
}

Full-text queries 全文检索

1
2
3
4
5
6
7
8
9
10
//query-term:查询条件 "nfc phone" 不会被分词,不会分成多个单词
//term和match的区别是match 会对查询条件进行分词
GET /product/_search
{
"query": {
"term": {
"name": "nfc phone"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
//组合查询
GET /product/_search
{
"query": {
"bool": {
"must": [
{"term":{"name":"nfc"}},
{"term":{"name":"phone"}}
]
}
}
}
1
2
3
4
5
6
7
8
9
//包含查询  类似于 SQL: where xxx in ();
GET /product/_search
{
"query": {
"terms": {
"name":["nfc","phone"]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//全文检索,一个单词进行分词,常用语句!!!
//等价SQL: name in (xiaomi,nfc,zhineng,phone)
GET /product/_search
{
"query": {
"match": {
"name": "xiaomi nfc zhineng phone"
}
}
}

//验证这个字符串的分词结果
GET /_analyze
{
"analyzer": "standard",
"text":"xiaomi nfc zhineng phone"
}
1
2
3
4
5
6
7
8
9
10
//短语搜索,和全文检索相反,会将给定的短语(phrase)当成一个完整的查询条件
//等价SQL: name contains("nfc phone")
GET /product/_search
{
"query": {
"match_phrase": {
"name": "nfc phone"
}
}
}

Query and filter

bool:可以组合多个查询条件,bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值。

must:必须满足

​ 子句(查询)必须出现在匹配的文档中,并将有助于得分。

filter:过滤器 不计算相关度分数,cache☆

​ 子句(查询)必须出现在匹配的文档中。但是不像 must查询的分数将被忽略。Filter子句在filter上下文中执行,这意味着计分被忽略,并且子句被考虑用于缓存。

should:可能满足 or

​ 子句(查询)应出现在匹配的文档中。

must_not:必须不满足 不计算相关度分数 not

​ 子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着计分被忽略,并且子句被视为用于缓存。由于忽略计分,0因此将返回所有文档的分数。

minimum_should_match

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//首先筛选name包含“xiaomi phone”并且价格大于1999的数据(不排序)   先执行filter筛选数据
//然后搜索name包含“xiaomi”and desc 包含“shouji”

GET /product/_search
{
"query": {
"bool":{
"must": [
{"match": { "name": "xiaomi"}},
{"match": {"desc": "shouji"}}
],
"filter": [
{"match_phrase":{"name":"xiaomi phone"}},
{"range": {
"price": {
"gt": 1999
}
}}
]
}
}
}
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
//bool多条件 name包含xiaomi 不包含erji 描述里包不包含nfc都可以,价钱要大于等于4999

GET /product/_search
{
"query": {
"bool":{
//name中必须不能包含“erji”
"must": [
{"match": { "name": "xiaomi"}}
],
//name中必须包含“xiaomi”
"must_not": [
{"match": { "name": "erji"}}
],
//should中至少满足0个条件,参见下面的minimum_should_match的解释
"should": [
{"match": {
"desc": "nfc"
}}
],
//筛选价格大于4999的doc
"filter": [
{"range": {
"price": {
"gt": 4999
}
}}
]
}
}
}

minimum_should_match:参数指定should返回的文档必须匹配的子句的数量或百分比。
如果bool查询包含至少一个should子句,而没有must或 filter子句,则默认值为1。否则,默认值为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//查询 name必须包含 "nfc" 且should中必须满足一个条件
GET /product/_search
{
"query": {
"bool":{
"must": [
{"match": { "name": "nfc"}}
],
"should": [
{"range": {
"price": {"gt":1999}
}},
{"range": {
"price": {"gt":3999}
}}
],
//表示should里的条件至少满足一个
"minimum_should_match": 1
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//这种情况下,should至少满足0个条件
GET /product/_search
{
"query": {
"bool": {
"filter": {
"bool": {
//价格必须大于1999或者大于3999
"should": [
{ "range": {"price": {"gt": 1999}}},
{ "range": {"price": {"gt": 3999}}}
],
"must": [
{ "match": {"name": "nfc"}}
]
}
}
}
}
}

Compound queries

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
//组合查询
//想要一台带NFC功能的 或者 小米的手机 但是不要耳机
//等价于SQL:
// SELECT * from product
// where (`name` like "%xiaomi%" or `name` like '%nfc%')
// AND `name` not LIKE '%erji%'

GET /product/_search
{
"query": {
"constant_score":{
"filter": {
"bool": {
"should":[
{"term":{"name":"xiaomi"}},
{"term":{"name":"nfc"}}
],
"must_not":[
{"term":{"name":"erji"}}
]
}
},
//给他的分数赋值 1.2 没什么 意义
"boost": 1.2
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//搜索一台xiaomi nfc phone或者一台满足 是一台手机 并且 价格小于等于2999
//等价于SQL:
GET /product/_search
{
"query": {
"constant_score": {
"filter": {
"bool":{
"should":[
{"match_phrase":{"name":"xiaomi nfc phone"}},
{
"bool":{
"must":[
{"term":{"name":"phone"}},
{"range":{"price":{"lte":"2999"}}}
]
}
}
]
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//高亮查询
GET /product/_search
{
"query" : {
"match_phrase" : {
"name" : "nfc phone"
}
},
"highlight":{
"fields":{
"name":{}
}
}
}



//返回结果:会多返回一段高亮信息
"highlight" : {
"name" : [
"<em>nfc</em> <em>phone</em>"
]
}

Deep paging问题

es

假设我要分页获取第5001~5050条数据时,由于数据是无序散落在各个shard分片中的,所以进行分页排序的时候,需要将各个shard分片进行排序,获取每个分片的【0 - 5050】条数据,然后进行合并,最后取出合适的50条数据,然后丢弃其他数据。

这种操作是十分损耗性能的,尽量避免深度分页查询,当你的数据超过1W,不要使用,返回结果不要超过1000个,500以下为宜。

通过使用Scroll search来避免部分分页查询,在查询中添加?scroll参数

1
2
3
4
5
6
7
8
9
//其中  1m 表示当前的scroll窗口有效期是1分钟
GET /product/_search?scroll=1m
{
"query":{
"match_all":{}
},
"sort":[{"price":"asc"}],
"size":2
}

通过这样查询,返回值会带上一个_scroll_id结果

es

当进行下一页时,直接通过上一次返回的scroll_id进行查询即可

1
2
3
4
5
6
GET /_search/scroll
{
//给scroll进行续命
"scroll" :"1m",
scroll_id:"xxxxxxxxxxxx"
}

他的缺点是只能下一页,没办法上一页,不适合实时查询

Filter缓存原理

es

当使用 term词项去倒排索引表进行搜索时,返回的一条条数据,filter会通过一个Bit数组存储,每个词项term对应一个bit数组,1表示匹配成功,0表示匹配失败。

计算多个filter条件的组合时,直接进行bit数组的与运算就能得出相应的结果,在一定条件下,filter会将查询的bit数组进行缓存。

Mapping

概念:mapping就是ES数据字段field的type元数据,ES在创建索引的时候,dynamic mapping会自动为不同的数据指定相应mapping,mapping中包含了字段的类型、搜索方式(exact value或者full text)、分词器等。

1
2
查看mapping
GET /product/_mappings
1
2
3
4
5
6
7
Dynamic mapping

“Elasticsearch”:text/keyword
123456 => long ?为什么不是integer
123.123 => double
true false => boolean
2020-05-20 => date

为啥price是long类型而不是integer?因为es的mapping_type是由JSON分析器检测数据类型,而Json没有隐式类型转换(integer=>long or float=> double),所以dynamic mapping会选择一个比较宽的数据类型。

1
2
3
搜索方式:
exact value 精确匹配:在倒排索引过程中,分词器会将field作为一个整体创建到索引中,
full text全文检索:分词、近义词同义词、混淆词、大小写、词性、过滤、时态转换等(normaliztion)

数据类型

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
【核心类型】:
数字类型:
long, integer, short, byte, double, float, half_float, scaled_float
在满足需求的情况下,尽可能选择范围小的数据类型。

2.字符串:string:
2.1 keyword:适用于索引结构化的字段,可以用于过滤、排序、聚合。
keyword类型的字段只能通过精确值(exact value)搜索到。
Id应该用keyword。

2.2 text:
当一个字段是要被全文搜索的,比如Email内容、产品描述,这些字段应该使用text类型。
设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。
text类型的字段不用于排序,很少用于聚合。
(解释一下为啥不会为text创建索引:字段数据会占用大量堆空间,尤其是在加载高基数text字段时。
字段数据一旦加载到堆中,就在该段的生命周期内保持在那里。
同样,加载字段数据是一个昂贵的过程,可能导致用户遇到延迟问题。这就是默认情况下禁用字段数据的原因)

2.3 有时,在同一字段中同时具有全文本(text)和关键字(keyword)版本会很有用:一个用于全文本搜索,另一个用于聚合和排序。

3.date(时间类型):exact value(精确匹配)
4.布尔类型:boolean
5.binary(二进制):binary
6.range(区间类型):integer_range、float_range、long_range、double_range、date_range

【复杂类型】:
1.Object:用于单个JSON对象
2.Nested:用于JSON对象数组

【地理位置】:
1.Geo-point:纬度/经度积分
2.Geo-shape:用于多边形等复杂形状

【特有类型】:
1.IP地址:ip 用于IPv4和IPv6地址
2.Completion:提供自动完成建议
3.Tocken_count:计算字符串中令牌的数量
4.Murmur3:在索引时计算值的哈希并将其存储在索引中
5.Annotated-text:索引包含特殊标记的文本(通常用于标识命名实体)
6.Percolator:接受来自query-dsl的查询
7.Join:为同一索引内的文档定义父/子关系
8.Rank features:记录数字功能以提高查询时的点击率。
9.Dense vector:记录浮点值的密集向量。
10.Sparse vector:记录浮点值的稀疏向量。
11.Search-as-you-type:针对查询优化的文本字段,以实现按需输入的完成
12.Alias:为现有字段定义别名。
13.Flattened:允许将整个JSON对象索引为单个字段。
14.Shape:shape 对于任意笛卡尔几何。
15.Histogram:histogram 用于百分位数聚合的预聚合数值。
16.Constant keyword:keyword当所有文档都具有相同值时的情况的 专业化。

【Array(数组)】:在Elasticsearch中,数组不需要专用的字段数据类型。
默认情况下,任何字段都可以包含零个或多个值,但是,数组中的所有值都必须具有相同的数据类型。

【ES 7新增】:
1.Date_nanos:date plus 纳秒
2.Features:
3.Vector:as
1
2
3
4
5
6
7
8
9
10
11
//手工创建mapping fields的mapping只能创建,无法修改
PUT /product
{
"mappings": {
"properties": {
"field": {
"mapping_parameter": "parameter_value"
}
}
}
}

Mapping parameters

index:是否对创建对当前字段创建索引,默认true,如果不创建索引,该字段不会通过索引被搜索到,但是仍然会在source元数据中展示

analyzer:指定分析器(character filter、tokenizer、Token filters)。

boost:对当前字段相关度的评分权重,默认1

coerce:是否允许强制类型转换 true “1”=> 1 false “1”=< 1

copy_to:拷贝字段值

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
//基本案例
PUT /product3
{
"mappings": {
"properties": {
"date": {
"type": "text"
},
"desc": {
"type": "text",
"analyzer": "english"
},
"name": {
"type": "text",
"index": "false",
"boost": 1
},
"price": {
"type": "Integer",
"coerce": false
},
"tags": {
"type": "text",
"index": "true"
},
"parts": {
"type": "object"
},
"partlist": {
"type": "nested"
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//copy_to案例
PUT copy_to
{
"mappings": {
"properties": {
"field1": {
"type": "text",
"copy_to": "field_all"
},
"field2": {
"type": "text",
"copy_to": "field_all"
},
"field_all": {
"type": "text"
}
}
}
}

doc_values:为了提升排序和聚合效率,默认true,如果确定不需要对字段进行排序或聚合,也不需要通过脚本访问字段值,则可以禁用doc值以节省磁盘空间(不支持text和annotated_text)

dynamic:控制是否可以动态添加新字段

1
2
3
4
5
6
true 新检测到的字段将添加到映射中。(默认)

false 新检测到的字段将被忽略。这些字段将不会被索引,因此将无法搜索,但仍会出现在_source返回的匹配项中。
这些字段不会添加到映射中,必须显式添加新字段。

strict 如果检测到新字段,则会引发异常并拒绝文档。必须将新字段显式添加到映射中

eager_global_ordinals:用于聚合的字段上,优化聚合性能。

Frozen indices(冻结索引):有些索引使用率很高,会被保存在内存中,有些使用率特别低,宁愿在使用的时候重新创建,在使用完毕后丢弃数据,Frozen indices的数据命中频率小,不适用于高搜索负载,数据不会被保存在内存中,堆空间占用比普通索引少得多,Frozen indices是只读的,请求可能是秒级或者分钟级。eager_global_ordinals不适用于Frozen indices

enable只用于mapping中的object字段类型。当设置为false时,其作用是使es不去解析该字段,并且该字段不能被查询和store,只有在_source中才能看到(即查询结果中会显示的_source数据)。设置enabled为false,可以不设置字段类型,默认为object。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PUT my_index{
"mappings": {
"enabled": false
}}

PUT my_index{
"mappings": {
"properties": {
"session_data": {
"type": "object",
"enabled": false
}
}
}}

fielddata:查询时内存数据结构,在首次用当前字段聚合、排序或者在脚本中使用时,需要字段为fielddata数据结构,并且创建正排索引保存到堆中。

fields:给field创建多字段,用于不同目的(全文检索或者聚合分析排序)

format:格式化

1
2
3
4
"date": {
"type": "date",
"format": "yyyy-MM-dd"
}

ignore_abovetext中的keyword长度,超过长度将被截断

ignore_malformed:忽略类型错误

1
2
3
4
5
6
7
8
9
10
11
12
13
PUT my_index{
"mappings": {
"properties": {
"number_one": {
"type": "integer",
"ignore_malformed": true
},
"number_two": {
"type": "integer"
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//虽然有异常 但是不抛出
PUT my_index/_doc/1
{
"text": "Some text value",
"number_one":"foo"
}

//数据格式不对
PUT my_index/_doc/2
{
"text": "Some text value",
"number_two": "foo"
}

index_options:控制将哪些信息添加到反向索引中以进行搜索和突出显示。仅用于text字段

Index_phrases:提升exact_value查询速度,但是要消耗更多磁盘空间

Index_prefixes:前缀搜索

1
2
min_chars:前缀最小长度,>0,默认2(包含)
max_chars:前缀最大长度,<20,默认5(包含)
1
2
3
4
"index_prefixes": {
"min_chars" : 1,
"max_chars" : 10
}

meta:附加元数据

norms:是否禁用评分(在filter和聚合字段上应该禁用)。

null_value:为null值设置默认值

1
"null_value": "NULL"

proterties:除了mapping还可用于object的属性设置

search_analyzer:设置单独的查询时分析

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
PUT my_index{
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
//倒排索引的分词器 默认 standard
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
},
"mappings": {
"properties": {
"text": {
"type": "text",
"analyzer": "autocomplete",
//搜索时的分词器 默认 standard
"search_analyzer": "standard"
}
}
}
}
1
2
3
4
PUT my_index/_doc/1
{
"text": "Quick Brown Fox"
}
1
2
3
4
5
6
7
8
9
10
GET my_index/_search{
"query": {
"match": {
"text": {
"query": "Quick Br",
"operator": "and"
}
}
}
}

similarity:为字段设置相关度算法,支持BM25、claassic(默认TF-IDF)、boolean

store:设置字段是否仅查询

聚合查询

语法:"aggs":{}

1
2
3
4
5
6
7
8
9
10
11
12
13
//每个tag产品的数量   "size":0, 不显示原始结果  
//使用text类型.keyword,提高效率
GET /product/_search
{
"aggs": {
"your_group_name": {
"terms": {
"field": "tags.keyword"
}
}
},
"size":0
}
1
2
3
4
5
6
7
8
9
10
11
//text默认不支持聚合,若想要支持,需要修改mapping的key属性:fielddata
//text直接做聚合,效率极低,不推荐!!!
PUT /product/_mapping
{
"properties": {
"tags": {
"type": "text",
"fielddata": true
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//价格大于1999的每个tag产品的数量
GET /product/_search
{
"query": {
"bool": {
"filter": [
{
"range": {"price": {"gt": 1999}}
}
]
}
},
"aggs": {
"tag_agg_group": {
"terms": {
"field": "tags.keyword"
}
}
},
"size": 0
}
1
2
3
4
//平均值语法
"avg": {
"field": "your_avg_key"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//价格大于1999的每个tag产品的平均价格
GET /product/_search
{
"aggs": {
"tag_agg_avg": {
"terms": {
"field": "tags.keyword",
"order": {
"avg_price": "desc"
}
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
},
"size":0
}
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
//按照千元机 1000以下  中端机[2000-3000) 高端机 [3000,∞)
GET /product/_search
{
"aggs": {
"tag_agg_group": {
"range": {
"field": "price",
"ranges": [
{
"from": 100,
"to": 1000
},
{
"from": 1000,
"to": 3000
},
{
"from": 3000
}
]
},
"aggs": {
"price_agg": {
"avg": {
"field": "price"
}
}
}
}
},
"size": 0
}

最后更新: 2020年12月13日 22:17

原始链接: https://midkuro.gitee.io/2020/12/09/elasticearch-dql/

× 请我吃糖~
打赏二维码