Elasticsearch入门教程:Elasticsearch查询
Posted lonelyxmas
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch入门教程:Elasticsearch查询相关的知识,希望对你有一定的参考价值。
原文:Elasticsearch入门教程(五):Elasticsearch查询(一)
// 准备数据
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": ["批评小明"]
}
- 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
_search
注意:_search即支持GET也支持POST
GET /_search // 空查询:查询所有索引下的所有文档
GET /{index}/_search // 查询某个索引下的前10条文档
GET /{index}/{type}/_search // 查询某个索引下某个类型的前10条文档
- 1
- 2
- 3
- 4
// 返回结果
took字段表示该操作的耗时(单位为毫秒),
timed_out字段表示是否超时,
_shards:在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个
total:返回记录数,本例是2条。
max_score:最高的匹配程度,本例是1.0。
hits:返回的记录组成的数组。
返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。
多索引,多类型
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
// 示例
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
查询字符串
查询字符串:就是在_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 游标查询,保持游标查询窗口一分钟
// 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
- 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
查询表达式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来达到分页的效果
// 使用DSL查询肯能条件很多,比较复杂,可以通过使用验证API来检查一个查询是否有效
GET /{index}/{type}/_validate/query?explain
{
"query": {
...
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
数据准备
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中包含“女装”的文档
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"
}
}
}
- 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
- 96
- 97
- 98
- 99
- 100
- 101
- 102
match一般用于用引号括起来的值,如文本,日期,如果将match用于布尔或者数字等数据类型就变成精确匹配了,而不是全文检索或者模糊匹配了
如果在一个精确值的字段上使用match,例如数字、日期、布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值:
match_all
简单的匹配所有文档
GET /shop/goods/_search
{
"query": {
"match_all": {}
}
}
// 上面简写成这样,效果是一样的
GET /shop/goods/_search
{}
// 同样也可以去掉{},更加简洁
GET /shop/goods/_search
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
match_phrase
对于短语或者段落进行精确匹配,
// 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
}
}
}
}
- 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
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 。
GET /shop/goods/_search
{
"query": {
"match_phrase_prefix": {
"name": "女装冬季外套"
}
}
}
// slop:让相对词序位置不那么严格
// 注意上面使用的是“女装冬季外套”,而现在使用的是“套女装冬季”, slop:允许每个词放宽的间隔
// 尽管词语的顺序不正确,查询仍然能匹配,因为我们为它设置了足够高的slop值使匹配时的词序有更大的灵活性。
GET /shop/goods/_search
{
"query": {
"match_phrase_prefix": {
"name": {
"query": "外套女装冬季",
"slop": 10
}
}
}
}
- 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
prefix 查询存在严重的资源消耗问题,短语查询的这种方式也同样如此。前缀 a 可能会匹配成千上万的词,这不仅会消耗很多系统资源,而且结果的用处也不大。
索引时输入即搜索
## 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"
}
}
}
}
- 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
multi_match
多个字段上使用相同的值作为查询条件
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)
}
}
- 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
constant_score 常量分数
通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。
它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。可以使用它来取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助. 这种方式可以用来取代只有 filter 语句的 bool 查询
GET /shop/goods/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"price": 268.00
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
查询置于 filter 语句内不进行评分或相关度的计算,所以所有的结果都会返回一个默认评分 1
prefix 前缀查询
前缀查询:要查询的字段必须没有分词
默认状态下, prefix 查询不做相关度评分计算,它只是将所有匹配的文档返回,并为每条结果赋予评分值 1 。它的行为更像是过滤器而不是查询。 prefix 查询和 prefix 过滤器这两者实际的区别就是过滤器是可以被缓存的,而查询不行。
prefix 查询或过滤对于一些特定的匹配是有效的,但使用方式还是应当注意。当字段中词的集合很小时,可以放心使用,但是它的伸缩性并不好,会对我们的集群带来很多压力。可以使用较长的前缀来限制这种影响,减少需要访问的量。
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"
}
}
}
}
- 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
wildcard 和 regexp
与 prefix 前缀查询的特性类似, wildcard 通配符查询也是一种底层基于词的查询,与前缀查询不同的是它允许指定匹配的正则式。它使用标准的 shell 通配符查询: ? 匹配任意字符, * 匹配 0 或多个字符。
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].+"
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
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
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"
}
}
- 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
term
精确查询:用于精确值匹配,对数值,日期,布尔,not_analyzed确切值字符串, term 查询对于输入的文本不 分析 ,所以它将给定的值进行精确查询。
GET /shop/goods/_search
{
"query": {
"term": {
"is_discount": {
"value": true
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
terms
同term查询,但是它允许指定多个匹配值,一般用于数组,如果字段包含其中的任何一个,都会返回文档, 类似于where tag IN (‘value1’, ‘value2’, ‘value3’) 例如:{ “terms”: { “tag”: [ “search”, “full_text”, “nosql” ] }}
GET /shop/goods/_search
{
"query": {
"terms": {
"colors": [
"白蓝红",
"绿色"
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
exists
对于相同类型的文档,可能有的文档有某个字段,有的文档没有某个字段,查找包含或者不包含某个字段的文档, 例如: {“exists”: { “field”: “title” }} ,exists 用于查找那些指定字段中是否有值 , 相当于SQL中的IS NOT NULL
null, [] (空数组)和 [null] 所有这些都是等价的,它们无法存于倒排索引中
这些查询经常用于某个字段有值的情况和某个字段缺值的情况。
// 存在某个字段
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" }
}
}
}
}
- 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
highlight:高亮
匹配的结果会被 HTML字符包裹住,需要指定匹配的字段,”highlight”: {“fields” : {“about” : {}}}
// 高亮: 匹配的结果会被 <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" : {}
}
}
}‘
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
sort
排序,可以指定一个或者多个字段排序,多个字段使用数组包围,例如:”sort”: { “date”: { “order”: “desc” }}
// 排序,默认情况下是按照相关性评分排序的,最相关的文档排在最前, 默认按_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,统计计算数组的指定值排序
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
对字符串排序,sort对字符串排序需要使用到原始值raw,正常情况下文本可能会使用分词器进行分词,而分词器会影响正常的排序,这时可以对同一个字段设置两种类型,对文本设置成text类型,并设置分词器,如果要搜索的话使用text类型字段,然后再为该字段设置一个原始值,该原始值raw字段的fields的数据类型设置为keyword,关键字数据类型是不分词的,不分词的字段可以作为字符串的排序
// 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"} } ]
}
- 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
from
偏移量,类似于SQL中limit中的offset
size
返回数量,类似于SQL中limit中的count,可以通过from,size来达到分页的效果
GET /shop/goods/_search
{
"from": 0,
"size": 20
}
- 1
- 2
- 3
- 4
- 5
游标查询Scroll
使用from, size做分页不能分页太深,太深对性能营销较大,一般获取前1000条数据就算深了,游标查询用于解决深度分页带来的性能问题。
scroll 查询可以用来对Elasticsearch有效地执行大批量的文档查询,而又不用付出深度分页那种代价。游标查询允许我们 先做查询初始化,然后再批量地拉取结果。 这有点儿像传统数据库中的 cursor 。游标查询会取某个时间点的快照数据,查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 ‘视图’ 一样。
深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。 游标查询用字段_doc来排序。 这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间,游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。 设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。
// 保持游标查询窗口一分钟
// 关键字 _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"
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
这个游标查询返回的下一批结果。 尽管我们指定字段 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 语句必须匹配。
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
// 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
}
}}
]
}
}
}
- 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
过滤查询:不需要评分,性能更好,对结果进行缓存
评分查询:需要评分,评分比较费力,不对结果进行缓存
如何选择查询与过滤? 通常的规则是,使用查询(query)语句来进行 全文搜索或者其它任何需要影响相关性得分的搜索。除此以外的情况都使用过滤(filters)。
aggs
聚合操作,类似SQL中的Group By, 支持的聚合类型有avg, min, max, sum, rang 等,也可以对地理位置进行聚合
如果要对一个字段进行聚合,要保证这个字段的fielddata设置为true
PUT /{index}/_mapping/{type}
{
"properties": {
"FILED": {
"type": "text",
"fielddata": true
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
// 统计数组中每个元素出现的次数,中文有问题,现在中文分词是按单个字分词的,估计需要指定对该字段不分词
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"
}
}
}
}
}
}
- 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
_mget
multi-get,通过docs数组作为参数指定多个doc来获取多个文档,每个doc可以分别指定索引、类型、id,文档或者api中必须包含index/type/id
// 路径中不包含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" ]
}
- 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
_bulk
批量操作:将多个操作封装成一个操作,一次执行多个动作(create,index, update以及delete),并返回每个执行结果.
可以通过_bulk来执行批量插入、批量更新等操作
POST _bulk的请求主体的格式稍微有些不同:
{ action: { metadata }}
{ request body }
{ action: { metadata }}
{ request body }
...
- 1
- 2
- 3
- 4
- 5
它通过换行符( )连接到一起,最后一行也要有 ,每行一定要以换行符( )结尾, 包括最后一行 。这些换行符被用作一个标记,可以有效分隔行,这些行不能包含未转义的换行符,因为他们将会对解析造成干扰
- 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 请求不是原子的: 不能用它来实现事务控制。每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求
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 }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
我的微信公众号:
以上是关于Elasticsearch入门教程:Elasticsearch查询的主要内容,如果未能解决你的问题,请参考以下文章