我是靠谱客的博主 超帅草莓,这篇文章主要介绍Elasticsearch查询操作(一),现在分享给大家,希望可以做个参考。

复制代码
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
70
71
72
73
74
75
76
77
78
79
80
81
// 准备数据 PUT /shop/goods/1 { "name": "2017新款女装冬季外套ulzzang棉袄中长款棉衣韩版百搭棉服面包服", "desc": "", "price": 268, "quantity": 9267, "colors": [ "绿色", "黑色" ], "is_discount": true, "create_date": "2018-01-31 12:10:30", "ip_addr": "192.168.10.1", "location": "39.92733,116.39507", "merchant": { "id": 999, "shop_name": "阿依莉旗舰店" }, "params": [ { "id": 1, "label": "型号", "value": "A30566" }, { "id": 2, "label": "品牌", "value": "阿依莉" } ], "activity": "买一送一" } PUT /shop/goods/2 { "name": "2018春季长袖t恤女加绒加厚圆领宽松套头毛衣女装韩版学生毛线衣", "price": 108, "quantity": 268, "colors": [ "白蓝红", "红白黑" ], "is_discount": false, "create_date": "2017-01-31 12:10:30", "ip_addr": "192.168.10.1", "location": "39.92733,116.39507", "merchant": { "id": 6666, "shop_name": "美特斯邦威旗舰店" }, "params": [ { "id": 1, "label": "型号", "value": "HWT8030" }, { "id": 2, "label": "品牌", "value": "美特斯邦威" } ] } PUT /account/users/1 { "username": "xiaoming", "nickname": "小明的老师", "age": 6, "height": 1.68, "birthday": "2017-01-31", "hobbies": ["吃", "喝", "嫖", "赌"] } PUT /accounts/users/1 { "username": "teacher", "nickname": "谁能求姐", "age": 6, "height": 1.68, "birthday": "2017-01-31", "hobbies": ["批评小明"] }

注意:_search即支持GET也支持POST

复制代码
1
2
3
4
5
6
7
GET /_search // 空查询:查询所有索引下的所有文档 GET /{index}/_search // 查询某个索引下的前10条文档 GET /{index}/{type}/_search // 查询某个索引下某个类型的前10条文档

// 返回结果
took字段表示该操作的耗时(单位为毫秒),
timed_out字段表示是否超时,
_shards:在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个
total:返回记录数,本例是2条。
max_score:最高的匹配程度,本例是1.0。
hits:返回的记录组成的数组。
返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。 

复制代码
1
2
3
4
5
6
7
8
9
多索引,多类型 GET /{index1},{index2}/_search // 查询多个索引下的文档 GET /{index*}/_search // 索引支持*号通配符 GET /{index*},{index*}/_search GET /_all/_search // 和GET /_search效果一样,_all:表示所有索引 GET /{index}/{type1},{type2}/_search
复制代码
1
2
3
4
5
6
7
8
9
10
// 示例 GET /_search GET /shop/_search GET /shop/goods/_search POST /shop/goods/_search GET /shop,account/_search GET /account*/_search GET /account*,sho*/_search GET /_all/_search GET /shop/goods,products/_search

查询字符串

查询字符串:就是在_search上使用GET参数的形式对search进行查询过滤,即将参数追加到路径上,此种方式有局限性,不够灵活

  • q: 通过加号+来指定查询中的某个字段必须包含某个值,默认是加号,通过减号-来指定文档中不能包含某个值,使用冒号将字段和值分隔,如 q=last_name:Smith,类似于关系型数据库中的where中的 like和not like 操作,
  • _source:用于指定要查询的字段,默认会返回文档中的所有字段,多个字段用逗号分隔
  • size: 设置search是返回的消息的条数,默认是10
  • from: 设置跳过的页数,默认是0,可以通过from和size来达到分页的目的
  • sort: 指定对那个字段进行什么排序,默认是按照相关性评分来降续排序的_score, 例如sort=date:desc&sort=_score
  • version: 版本号,用于更新和删除操作,当文档的版本等于指定的版本号时才能正常执行,否则会返回409 版本冲突 (乐观并发控制)
  • ersion_type=external 用于创建、修改、删除操作 外部版本号:当使用外部版本号是es会检查版本号是否比_version值小,如果小的话就正常执行,并将_version的值更改为外部的版本号, 例如version=5&version_type=external
  • timeout=10ms 指定超时时间,默认单位是毫秒ms,也可以指定秒s,例如1s
  • retry_on_conflict=n 参数来设置自动完成这项请求的次数,它的默认值是0,失败前重新尝试n次
  • scroll=1m 游标查询,保持游标查询窗口一分钟
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// q好像对中文查询不出啦???可以通过其他方式进行查询 // 查询字段包含的字符串q=filed:value, 类似于SQL中的where last_name like '%Smith%' GET /{index}/{type}/_search?q=last_name:Smith // 加号+:表示必须存在,每部分都是包含的意思, 类似于SQL中的where fist_name like '%join%' and like '%Smith%' // + 前缀表示必须与查询条件匹配。类似地, - 前缀表示一定不与查询条件匹配。 // 没有 + 或者 - 的所有其他条件都是可选的,类似于or——匹配的越多,文档就越相关 GET /{index}/{type}/_search?q=fist_name:join+last_name:Smith' // -号代表不包含, 类似于SQL中的 where name not like '%join%' GET /{index}/{type}/_search?q=-name:join // name包含join但tweet不包含jmary的, 类似于SQL中的 where name like '%join%' and tweet not like '%mary%' GET /{index}/{type}/_search?q=name:join+-tweet:mary // 查询所有索引所有类型所有字段中包含mary关键字的文档, _all:可以代表所有索引或者所有类型或者所有字段, // 每个文档都有一个隐形的_all字段,它的数据类型是文本型,它的值是以空格的形式将文档的所有字段的值拼接起来, 例如一个文档有三个值 age: 15, name:zhangsan, birthday:2018-09-15, 那么_all的值为“15 zhangsan 2018-09-15” // _all会被分词,如果字段中包含日期,例如2018-09-15会被分析成2018,09, 15 三个词条(token) GET /{index}/{type}/_search?q=_all:mary // 如果不指定字段则默认是_all字段, _all字段是String类型 GET /{index}/{type}/_search?q=mary POST /shop/goods/_search?_source=name,price POST /shop/goods/_search?timeout=10ms POST /shop/goods/_search?from=0&size=5 POST /shop/goods/_search?sort=create_date:desc&sort=_score

查询表达式DSL

使用查询领域特定语言(Query Domain Specific Language) 简称为DSL, 需要使用JSON作为主体,请求体查询

  • query

    • match:模糊匹配,类似于SQL中的like,示例{ “match” : { “field” : “keyword” }},
    • match_all:简单的匹配所有文档 {“match_all”: {}}
    • match_phrase: 对于短语或者段落进行精确匹配
    • match_phrase_prefix
    • multi_match: 在多个字段上反复执行相同查询
    • bool 用于表示复合语句,用于组合多个查询语句;将多查询组合成单一查询
      • must 必须匹配这些条件才能被包含进来,表示并且的关系
      • must_not 必须不 匹配这些条件才能被包含进来
      • should 如果满足这些语句中的任意语句,将增加 _score ,否则无任何影响。它们主要用于修正每个文档的相关性得分。
      • minimum_should_match:控制需要匹配的 should 语句的数量, 它既可以是一个绝对的数字,又可以是个百分比, 如 minimum_should_match: 2 或者 “minimum_should_match”: “75%”
      • filter 必须匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档, filter只是简单的数据过滤,并不影响评分,因filter不计算评分,所以性能更好,请尽可能多的使用过滤式查询
        • exists 查找包含某个字段的文档
    • range 范围,可以使用大于gt、大于等于gte、小于lt、小于等于lte作为查询条件,可用于数字、日期类型、字符串
    • term 精确查询,对数值,日期,布尔,not_analyzed确切值字符串
    • terms 指定多个匹配值,如果字段包含其中的任何一个,都会返回文档,类似于SQL中的IN 操作
    • constant_score: 恒定分数,它将一个不变的常量评分应用于所有匹配的文档
    • wildcard: 通配符, ? 匹配任意字符, * 匹配 0 或多个字符
    • fuzzy: 模糊查询
    • dis_max:分离 最大化查询(Disjunction Max Query)
    • nested : 用于操作嵌套类型
  • highlight 高亮,匹配的结果会被 HTML字符包裹住,需要指定匹配的字段

  • aggs 聚合操作,类似SQL中的Group By
  • sort 排序
  • from 偏移量,类似于SQL中limit中的offset
  • size 返回数量,类似于SQL中limit中的count,可以通过from,size来达到分页的效果
复制代码
1
2
3
4
5
6
7
// 使用DSL查询肯能条件很多,比较复杂,可以通过使用验证API来检查一个查询是否有效 GET /{index}/{type}/_validate/query?explain { "query": { ... } }

数据准备

DELETE /shop/goods/_all 删除所有文档

match 匹配查询

全文检索方式查询,类似于关系型数据库中的like操作,使用match时需要指定对那个字段进行全文检索,以及对应的关键字, 关键字可以指定一个也可以指定多个,指定多个时表示或者的关系 类似于SQL中的or的功能。语法:
{ "match" : { "field" : "keyword" }} 类似于SQL中的where filed like ‘%keyword%’
{ "match" : { "field" : "keyword1 keyword2" }} 类似于SQL中的where field like ‘%keyword1%’ or field like ‘%keyword2%’

示例:

// 查询name中包含“女装”的文档

复制代码
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
GET /shop/goods/_search { "query": { "match": { "name": "女装" } } } // 多词查询 :使用空格隔离多个单词 // 查询name中包含“外套” 或者or 包含 “休闲” 的文档,多个关键字使用空格分隔,如果不指定operator默认是or GET /shop/goods/_search { "query": { "match": { "name": "外套 休闲" } } } // "name": "外套 休闲" or 等价于bool中的should GET /shop/goods/_search { "query": { "bool": { "should": [ "match": { "name": "外套" }, "match": { "name": "休闲" }, ] } } } // 查询name中包含“外套” 并且 包含 “休闲” 的文档,多个关键字使用空格分隔 GET /shop/goods/_search { "query": { "match": { "name": "外套 休闲", "operator": "and" } } } // "name": "外套 休闲" and 等价于bool中的must GET /shop/goods/_search { "query": { "bool": { "must": [ "match": { "name": "外套" }, "match": { "name": "休闲" }, ] } } } // 多词查询中使用or只需要满足一个即可,要求太低,使用and又必须所有词项都必须满足,要求又太高, // 使用minimum_should_match折中一下,最小匹配:可以指定一个百分比, // 例如指定4个词项,一个是词项是25%,设置成50%就是至少要满足2个词项 GET /shop/goods/_search { "query": { "match": { "name": { "query": "春季 长袖 圆领 学生", "minimum_should_match": "50%" } } } } // 效果和上面一样,等价的 GET /shop/goods/_search { "query" : { "bool": { "filter": { "bool" : { "should" : [ { "term" : {"name" : "春季"}}, { "term" : {"name" : "长袖"}}, { "term" : {"name" : "圆领"}}, { "term" : {"name" : "学生"}} ], "minimum_should_match": 2 } } } } } // 查询日期 GET /shop/goods/_search { "query": { "match": { "create_date": "2018-01-31" } } }

match一般用于用引号括起来的值,如文本,日期,如果将match用于布尔或者数字等数据类型就变成精确匹配了,而不是全文检索或者模糊匹配了
如果在一个精确值的字段上使用match,例如数字、日期、布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值:

match_all

简单的匹配所有文档

复制代码
1
2
3
4
5
6
7
8
9
10
11
GET /shop/goods/_search { "query": { "match_all": {} } } // 上面简写成这样,效果是一样的 GET /shop/goods/_search {} // 同样也可以去掉{},更加简洁 GET /shop/goods/_search

match_phrase

对于短语或者段落进行精确匹配,

复制代码
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
// match如果对一小段文本进行查询,match首先对字段值进行分词,然后对分词进行匹配,相当于SQL中的name like '%女装%' or name like '%冬季%' or name like '%外套%' GET /shop/goods/_search { "query": { "match": { "name": "女装冬季外套" } } } GET /shop/goods/_search { "query": { "match_phrase": { "name": "女装冬季外套" } } } // slop:让相对词序位置不那么严格 // 注意上面使用的是“女装冬季外套”,而现在使用的是“套女装冬季”, slop:允许每个词放宽的间隔 // 尽管词语的顺序不正确,查询仍然能匹配,因为我们为它设置了足够高的slop值使匹配时的词序有更大的灵活性。 GET /shop/goods/_search { "query": { "match_phrase": { "name": { "query": "外套女装冬季", "slop": 10 } } } }

match_phrase_prefix

match_phrase_prefix与match_phrase是一样的,只是它允许在文本的最后一项中使用前缀匹配。

可以用于即时搜索(instant search) 或 输入即搜索(search-as-you-type),例如,如果用户输入 johnnie walker bl ,我们希望在它们完成输入搜索条件前就能得到:Johnnie Walker Black Label 和 Johnnie Walker Blue Label 。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
GET /shop/goods/_search { "query": { "match_phrase_prefix": { "name": "女装冬季外套" } } } // slop:让相对词序位置不那么严格 // 注意上面使用的是“女装冬季外套”,而现在使用的是“套女装冬季”, slop:允许每个词放宽的间隔 // 尽管词语的顺序不正确,查询仍然能匹配,因为我们为它设置了足够高的slop值使匹配时的词序有更大的灵活性。 GET /shop/goods/_search { "query": { "match_phrase_prefix": { "name": { "query": "外套女装冬季", "slop": 10 } } } }

prefix 查询存在严重的资源消耗问题,短语查询的这种方式也同样如此。前缀 a 可能会匹配成千上万的词,这不仅会消耗很多系统资源,而且结果的用处也不大。

索引时输入即搜索

复制代码
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
## edge_ngram 自定义过滤器、分词器 PUT /my_index { "settings": { "number_of_shards": 1, "analysis": { "filter": { "autocomplete_filter": { "type": "edge_ngram", "min_gram": 1, "max_gram": 20 } }, "analyzer": { "autocomplete": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "autocomplete_filter" ] } } } } } ## 测试分词 GET /my_index/_analyze { "analyzer": "autocomplete", "text": "quick brown" } PUT /my_index/_mapping/my_type { "my_type": { "properties": { "name": { "type": "text", "analyzer": "autocomplete", ## 使用自定义的分词器 "search_analyzer": "standard" ## 配置查询对应的分词器 } } } } POST /my_index/my_type/_bulk { "index": { "_id": 1 }} { "name": "Brown foxes" } { "index": { "_id": 2 }} { "name": "Yellow furballs" } ## 搜索 GET /my_index/my_type/_search { "query": { "match": { "name": { "query": "brown fo" } } } }

multi_match

多个字段上使用相同的值作为查询条件

复制代码
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
GET /shop/goods/_search { "query": { "multi_match": { "query": 268, "fields": ["price", "quantity"] } } } // 两者效果相同 GET /shop/goods/_search { "query": { "bool": { "should": [ { "term": { "price": { "value": 268 } } }, { "term": { "quantity": { "value": 268 } } } ] } } } // 查询字段名称的模糊匹配 // 字段名称可以用模糊匹配的方式给出:任何与模糊模式正则匹配的字段都会被包括在搜索条件中, 例如可以使用以下方式同时匹配 book_title 、 chapter_title 和 section_title (书名、章名、节名)这三个字段: { "multi_match": { "query": "Quick brown fox", "fields": "*_title" } } // 提升单个字段的权重 // 可以使用 ^ 字符语法为单个字段提升权重,在字段名称的末尾添加 ^boost ,其中 boost 是一个浮点数: // chapter_title 这个字段的 boost 值为 2 ,而*_title 字段的默认 boost 值为 1 { "multi_match": { "query": "Quick brown fox", "fields": [ "*_title", "chapter_title^2" ] (1) } }

constant_score 常量分数

通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。

它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。可以使用它来取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助. 这种方式可以用来取代只有 filter 语句的 bool 查询

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
GET /shop/goods/_search { "query": { "constant_score": { "filter": { "term": { "price": 268.00 } } } } }

查询置于 filter 语句内不进行评分或相关度的计算,所以所有的结果都会返回一个默认评分 1

prefix 前缀查询

前缀查询:要查询的字段必须没有分词

默认状态下, prefix 查询不做相关度评分计算,它只是将所有匹配的文档返回,并为每条结果赋予评分值 1 。它的行为更像是过滤器而不是查询。 prefix 查询和 prefix 过滤器这两者实际的区别就是过滤器是可以被缓存的,而查询不行。

prefix 查询或过滤对于一些特定的匹配是有效的,但使用方式还是应当注意。当字段中词的集合很小时,可以放心使用,但是它的伸缩性并不好,会对我们的集群带来很多压力。可以使用较长的前缀来限制这种影响,减少需要访问的量。

复制代码
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 { "mappings": { "address": { "properties": { "postcode": { "type": "keyword" } } } } } PUT /my_index/address/1 { "postcode": "W1V 3DG" } PUT /my_index/address/2 { "postcode": "W2F 8HW" } PUT /my_index/address/3 { "postcode": "W1F 7HW" } PUT /my_index/address/4 { "postcode": "WC1N 1LZ" } PUT /my_index/address/5 { "postcode": "SW5 0BE" } // prefix 查询 GET /my_index/address/_search { "query": { "prefix": { "postcode": { "value": "W1" } } } }

wildcard 和 regexp

与 prefix 前缀查询的特性类似, wildcard 通配符查询也是一种底层基于词的查询,与前缀查询不同的是它允许指定匹配的正则式。它使用标准的 shell 通配符查询: ? 匹配任意字符, * 匹配 0 或多个字符。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET /my_index/address/_search { "query": { "wildcard": { "postcode": "W?F*HW" } } } // 词必须以 W 开头,紧跟 0 至 9 之间的任何一个数字,然后接一或多个其他字符 GET /my_index/address/_search { "query": { "regexp": { "postcode": "W[0-9].+" } } }

wildcard 和 regexp 查询的工作方式与 prefix 查询完全一样,它们也需要扫描倒排索引中的词列表才能找到所有匹配的词,然后依次获取每个词相关的文档 ID ,与 prefix 查询的唯一不同是:它们能支持更为复杂的匹配模式。

这也意味着需要同样注意前缀查询存在性能问题,对有很多唯一词的字段执行这些查询可能会消耗非常多的资源,所以要避免使用左通配这样的模式匹配(如: *foo 或 .*foo 这样的正则式)。

数据在索引时的预处理有助于提高前缀匹配的效率,而通配符和正则表达式查询只能在查询时完成,尽管这些查询有其应用场景,但使用仍需谨慎。

prefix 、 wildcard 和 regexp 查询是基于词操作的,如果用它们来查询 analyzed 字段,它们会检查字段里面的每个词,而不是将字段作为整体来处理。


range

用于查询一个区间,一般用于日期和数值,支持 gt、gte、lt、lte, 示例{“range”: { “age”: { “gte”: 20, “lt”:30 }}} 类似于SQL中的 wher age >= 20 and age < 30

复制代码
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
GET /shop/goods/_search { "query": { "range": { "price": { "gt": 200, "lte": 500 } } } } // 日期类型 GET /shop/goods/_search { "query": { "range": { "create_date": { "gt" : "2014-01-01 00:00:00", "lt" : "2014-01-07 00:00:00" } } } } // 日期计算:过去一小时 GET /shop/goods/_search { "query": { "range": { "create_date": { "gt" : "now-1h" } } } } // 日期计算:早于 2014 年 1 月 1 日加 1 月(2014 年 2 月 1 日 零时) GET /shop/goods/_search { "query": { "range": { "create_date": { "gt" : "2014-01-01 00:00:00", "lt" : "2014-01-01 00:00:00||+1M" } } } } // 作用于字符串,性能相对较慢 "range" : { "title" : { "gte" : "a", "lt" : "b" } }

term

精确查询:用于精确值匹配,对数值,日期,布尔,not_analyzed确切值字符串, term 查询对于输入的文本不 分析 ,所以它将给定的值进行精确查询。

复制代码
1
2
3
4
5
6
7
8
9
10
GET /shop/goods/_search { "query": { "term": { "is_discount": { "value": true } } } }

terms

同term查询,但是它允许指定多个匹配值,一般用于数组,如果字段包含其中的任何一个,都会返回文档, 类似于where tag IN (‘value1’, ‘value2’, ‘value3’) 例如:{ “terms”: { “tag”: [ “search”, “full_text”, “nosql” ] }}

复制代码
1
2
3
4
5
6
7
8
9
10
11
GET /shop/goods/_search { "query": { "terms": { "colors": [ "白蓝红", "绿色" ] } } }

exists

对于相同类型的文档,可能有的文档有某个字段,有的文档没有某个字段,查找包含或者不包含某个字段的文档, 例如: {“exists”: { “field”: “title” }} ,exists 用于查找那些指定字段中是否有值 , 相当于SQL中的IS NOT NULL

null, [] (空数组)和 [null] 所有这些都是等价的,它们无法存于倒排索引中

这些查询经常用于某个字段有值的情况和某个字段缺值的情况。

复制代码
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
// 存在某个字段 GET /shop/goods/_search { "query": { "bool": { "filter": { "exists": { "field": "activity" } } } } } // 不存在某个字段 GET /shop/goods/_search { "query": { "bool": { "must_not": { "exists": { "field": "activity" } } } } } POST /my_index/posts/_bulk { "index": { "_id": "1" }} { "tags" : ["search"] } { "index": { "_id": "2" }} { "tags" : ["search", "open_source"] } { "index": { "_id": "3" }} { "other_field" : "some data" } { "index": { "_id": "4" }} { "tags" : null } { "index": { "_id": "5" }} { "tags" : ["search", null] } 以上文档集合中 tags 字段对应的倒排索引如下: Token DocIDs open_source 2 search 1,2,5 1. tags 字段有 1 个值。 2. tags 字段有 2 个值。 3. tags 字段缺失。 4. tags 字段被置为 null 。 5. tags 字段有 1 个值和 1 个 null 。 // 1,2,5 满足条件 GET /my_index/posts/_search { "query" : { "constant_score" : { "filter" : { "exists" : { "field" : "tags" } } } } }

highlight:高亮

匹配的结果会被 HTML字符包裹住,需要指定匹配的字段,”highlight”: {“fields” : {“about” : {}}}

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 高亮: 匹配的结果会被 <em></em> HTML字符包裹住: curl -i -H 'Content-Type: application/json' -XGET 'http://localhost:9200/megacorp/employee/_search' -d ' { "query" : { "match_phrase" : { "about" : "rock climbing" } }, "highlight": { "pre_tags" : ["<font color='red'>"], "post_tags" : ["</font>"], "fields" : { "about" : {} } } }'

sort

排序,可以指定一个或者多个字段排序,多个字段使用数组包围,例如:”sort”: { “date”: { “order”: “desc” }}

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 排序,默认情况下是按照相关性评分排序的,最相关的文档排在最前, 默认按_score排序的 // 多级排序,使用date, _score排序 GET /_search { "query" : { "bool" : { "must": { "match": { "tweet": "manage text search" }}, "filter" : { "term" : { "user_id" : 2 }} } }, "sort": [ { "date": { "order": "desc" }}, { "_score": { "order": "desc" }}, { "ids" : { "order": "asc", "mode": "min"}} ] } mode:一般用于数组,可以使用min 、 max 、 avg 或是 sum,统计计算数组的指定值排序

对字符串排序,sort对字符串排序需要使用到原始值raw,正常情况下文本可能会使用分词器进行分词,而分词器会影响正常的排序,这时可以对同一个字段设置两种类型,对文本设置成text类型,并设置分词器,如果要搜索的话使用text类型字段,然后再为该字段设置一个原始值,该原始值raw字段的fields的数据类型设置为keyword,关键字数据类型是不分词的,不分词的字段可以作为字符串的排序 

复制代码
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
// fields 多字段:相当于对同一个字段值起不同的名字,赋予这个字段不同的属性(如类型不同,分词器不同等) PUT /school { "mappings": { "students": { "properties": { "name": { "type": "text", "analyzer": "ik_max_word", "fields": { "raw": { "type": "keyword" } } } } } } } // _score 和 max_score 字段都是 null 。计算 _score 的花销巨大,通常仅用于排序; 我们并不根据相关性排序,所以记录 _score 是没有意义的。如果无论如何你都要计算 _score , 你可以将 track_scores 参数设置为 true GET /school/students/_search { "query": { "match": { "name": "abc" } }, "sort": [ { "name.raw": { "order": "desc" } } ] } // 强制计算评分 GET /school/students/_search { "track_scores": true, "query": { "match": { "name": "abc" } }, "sort": [ { "name.raw": { "order": "desc"} } ] }

from

偏移量,类似于SQL中limit中的offset

size

返回数量,类似于SQL中limit中的count,可以通过from,size来达到分页的效果

复制代码
1
2
3
4
5
GET /shop/goods/_search { "from": 0, "size": 20 }

游标查询Scroll

使用from, size做分页不能分页太深,太深对性能营销较大,一般获取前1000条数据就算深了,游标查询用于解决深度分页带来的性能问题。

scroll 查询可以用来对Elasticsearch有效地执行大批量的文档查询,而又不用付出深度分页那种代价。游标查询允许我们 先做查询初始化,然后再批量地拉取结果。 这有点儿像传统数据库中的 cursor 。游标查询会取某个时间点的快照数据,查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 ‘视图’ 一样。

深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。 游标查询用字段_doc来排序。 这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间,游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。 设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 保持游标查询窗口一分钟 // 关键字 _doc 是最有效的排序顺序。 GET /shop/goods/_search?scroll=1m { "query": {"match_all": {}}, "sort": ["_doc"], "size": 1000 } // 这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串 。 现在我们能传递字段 _scroll_id 到 _search/scroll { "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAMoFmQ0clZDNEg4UWYtYTBzVTg0QWRFdmcAAAAAAAADKRZkNHJWQzRIOFFmLWEwc1U4NEFkRXZnAAAAAAAAAyoWZDRyVkM0SDhRZi1hMHNVODRBZEV2ZwAAAAAAAAMrFmQ0clZDNEg4UWYtYTBzVTg0QWRFdmcAAAAAAAADLBZkNHJWQzRIOFFmLWEwc1U4NEFkRXZn", "took": 1, "timed_out": false, ... } // 查询接口获取下一批结果: GET /_search/scroll { "scroll": "1m", "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAKbFmQ0clZDNEg4UWYtYTBzVTg0QWRFdmcAAAAAAAACnBZkNHJWQzRIOFFmLWEwc1U4NEFkRXZnAAAAAAAAAp0WZDRyVkM0SDhRZi1hMHNVODRBZEV2ZwAAAAAAAAKeFmQ0clZDNEg4UWYtYTBzVTg0QWRFdmcAAAAAAAACnxZkNHJWQzRIOFFmLWEwc1U4NEFkRXZn" }

这个游标查询返回的下一批结果。 尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。 当查询的时候, 字段 size 作用于单个分片,所以每个批次实际返回的文档数量最大为 size * number_of_primary_shards.

注意游标查询每次返回一个新字段 _scroll_id。每次我们做下一次游标查询, 我们必须把前一次查询返回的字段 _scroll_id 传递进去。 当没有更多的结果返回的时候,我们就处理完所有匹配的文档了。

bool

布尔查询,这是一个很重要的查询,它可以将其它多种查询封装成一个大的查询,可以使用逻辑操作符(类似于sql中的and、not、or)来组装各个条件, 这是个复合过滤器(compound filter) ,它可以接受多个其他过滤器作为参数,并将这些过滤器结合成各式各样的布尔(逻辑)组合

用于表示复合语句,用于组合多个查询语句;将多查询组合成单一查询,bool可以放到query下面,也可以嵌套在某个子条件(must、should、must_not)里, 通过嵌套可以构造出更加复杂的过滤条件。

  • must: 必须匹配这些条件才能被包含进来。 表示并且的关系,与 SQL中的AND 等价,例如{"must": [{"match": {"desc": "xxx"}}, {"term": { "quantity": 999}}]},类似于SQL中的where desc like '%xxx%' and quantity = 999
  • must_not: 必须不匹配这些条件才能被包含进来, 是对must的取反操作,与SQL中的 != 或者<> 等价, where status != 0
  • should 至少有一个语句要匹配, 与 SQL中的 OR 等价,是一个数组,可以有多个值,如果满足这些语句中的任意语句,将增加_score的值, should的作用:or逻辑,如果满足条件评分_score更高
  • filter:必须匹配,以过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档, filter只是简单的数据过滤,并不影响评分
  • minimum_should_match

bool 查询会为每个文档计算相关度评分 _score ,再将所有匹配的 must 和 should 语句的分数 _score 求和,最后除以 must 和 should 语句的总数。

must_not 语句不会影响评分;它的作用只是将不相关的文档排除。

所有must语句必须匹配,所有must_not语句都必须不匹配,经测试至少有一个should需要匹配的(在should中写两个条件,每天条件都不满足条件,结果没有任何文档满足),当没有 must 语句的时候,至少有一个 should 语句必须匹配。

复制代码
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
GET /shop/goods/_search { "query": { "bool": { "must": [ {"term": { "quantity": { "value": 999 } }} ], "must_not": [ { "term": { "quantity": { "value": 9267 } } } ], "should": [ { "term": { "is_discount": { "value": true } } } ], "filter": { "range": { "price": { "gte": 7777, "lte": 9999 } } } } } }
复制代码
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// filter中也可以嵌套bool查询,这样内部的bool查询就不参与评分了 // bool中的must must_not should 等会计算评分的,如果不希望计算评分,可将bool放入到filter中,这样既不计算评分又可以使用bool中的逻辑 // 直接使用bool中的filter和将bool再次嵌入到filter中都不计算评分 GET /shop/goods/_search { "query": { "bool": { "must": { "match": { "title": "how to make millions" }}, "must_not": { "match": { "tag": "spam" }}, "should": [ { "match": { "tag": "starred" }} ], "filter": { "bool": { "must": [ { "range": { "date": { "gte": "2014-01-01" }}}, { "range": { "price": { "lte": 29.99 }}} ], "must_not": [ { "term": { "category": "ebooks" }} ] } } } } } // 就像我们能控制 match 查询的精度 一样,我们可以通过 minimum_should_match 参数控制需要匹配的 // should 语句的数量, 它既可以是一个绝对的数字,又可以是个百分比: // 这个查询结果会将所有满足以下条件的文档返回: // title 字段包含 "brown" AND "fox" 、 "brown" AND "dog" 或 "fox" AND "dog" 。 // 如果有文档包含所有三个条件,它会比只包含两个的文档更相关。 GET /my_index/my_type/_search { "query": { "bool": { "should": [ { "match": { "title": "brown" }}, { "match": { "title": "fox" }}, { "match": { "title": "dog" }} ], "minimum_should_match": 2 } } } // boost 参数被用来提升一个语句的相对权重( boost 值大于 1 )或降低相对权重( boost 值处于 0 到 1 之间), // 但是这种提升或降低并不是线性的,换句话说,如果一个 boost 值为 2 ,并不能获得两倍的评分 _score 。 // 通过指定 boost 来控制任何查询语句的相对的权重, boost 的默认值为 1 ,大于 1 会提升一个语句的相对权重。 // 更高的 boost 值为我们带来更高的评分 _score GET /_search { "query": { "bool": { "must": { "match": { "content": { "query": "full text search", "operator": "and" } } }, "should": [ { "match": { "content": { "query": "Elasticsearch", "boost": 3 } }}, { "match": { "content": { "query": "Lucene", "boost": 2 } }} ] } } }

过滤查询:不需要评分,性能更好,对结果进行缓存
评分查询:需要评分,评分比较费力,不对结果进行缓存
如何选择查询与过滤? 通常的规则是,使用查询(query)语句来进行 全文搜索或者其它任何需要影响相关性得分的搜索。除此以外的情况都使用过滤(filters)。

aggs

聚合操作,类似SQL中的Group By, 支持的聚合类型有avg, min, max, sum, rang 等,也可以对地理位置进行聚合

如果要对一个字段进行聚合,要保证这个字段的fielddata设置为true

复制代码
1
2
3
4
5
6
7
8
9
PUT /{index}/_mapping/{type} { "properties": { "FILED": { "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
22
23
24
25
26
27
28
29
30
31
32
// 统计数组中每个元素出现的次数,中文有问题,现在中文分词是按单个字分词的,估计需要指定对该字段不分词 GET /shop/goods/_search { "aggs": { "xxx": { "terms": { "field": "colors", "size": 10 } } } } // aggs可以嵌套在aggs中使用 // 嵌套聚合,分级汇总,在聚合中可以进行再聚合,意思是对分组的文档进行其他聚合,而不是对聚合结果进行处理 GET /shop/goods/_search { "aggs": { "xxx": { "terms": { "field": "colors", "size": 10 }, "aggs": { "yyy": { "avg": { "field": "price" } } } } } }

_mget

multi-get,通过docs数组作为参数指定多个doc来获取多个文档,每个doc可以分别指定索引、类型、id,文档或者api中必须包含index/type/id

复制代码
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
// 路径中不包含index、type、id GET /_mget { "docs" : [ { "_index" : "shop", "_type" : "goods", "_id" : 2 }, { "_index" : "account", "_type" : "users", "_id" : 1, "_source": "nickname" } ] } // 路径中包含index、type, 如果参数中没有指定index、type就使用路径中的,如果参数中明确指定了,就使用参数中的索引和类型 GET /{index}/{type}/_mget { "docs" : [ { "_id" : 2 }, { "_type" : "account", "_id" : 1 } ] } // 路径中指定了索引和类型,通过ids数组指定多个id值 GET /{index}/{type}/_mget' { "ids" : [ "2", "1" ] }

_bulk

批量操作:将多个操作封装成一个操作,一次执行多个动作(create,index, update以及delete),并返回每个执行结果.
可以通过_bulk来执行批量插入、批量更新等操作

POST _bulk的请求主体的格式稍微有些不同:

复制代码
1
2
3
4
5
6
7
{ action: { metadata }}n { request body }n { action: { metadata }}n { request body }n ...

它通过换行符(n)连接到一起,最后一行也要有n,每行一定要以换行符(n)结尾, 包括最后一行 。这些换行符被用作一个标记,可以有效分隔行,这些行不能包含未转义的换行符,因为他们将会对解析造成干扰

  • action 必须是以下选项之一:
    • create:如果文档不存在,那么就创建它, 相当于 PUT /{index}/{type}/{id}/_create
    • index:创建一个新文档或者替换一个现有的文档, 相当于 PUT /{index}/{type}/{id}
    • update:部分更新一个文档, 即局部更新文档中的个别字段,相当于 /{index}/{type}/{id}/_update
    • delete:删除一个文档,删除操作不需要指定请求体,delete操作没有请求体,它紧接着另一个行为;记得最后一个换行符
  • metadata 每个动作需要的参数,json格式的,应该指定被索引、创建、更新或者删除的文档的_index,_type和 _id

每个子请求都是独立执行,因此某个子请求的失败不会对其他子请求的成功与否造成影响。 如果其中任何子请求失败,最顶层的 error 标志被设置为 true ,并且在相应的请求报告出错误明细
这也意味着 bulk 请求不是原子的: 不能用它来实现事务控制。每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求

复制代码
1
2
3
4
5
6
7
8
9
10
11
POST /_bulk { "create": { "_index": "shop", "_type": "goods", "_id": 123 }} { "name": "My first blog post" } { "index": { "_index": "shop", "_type": "goods" }} { "name": "My second blog post" } { "update": { "_index": "shop", "_type": "goods", "_id": 123, "retry_on_conflict" : 3} } { "doc" : {"title" : "My updated blog post"} } { "delete": { "_index": "shop", "_type": "goods", "_id": 1234 }}

 

最后

以上就是超帅草莓最近收集整理的关于Elasticsearch查询操作(一)的全部内容,更多相关Elasticsearch查询操作(一)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部