公号:码农充电站pro
主页:https://codeshellme.github.iohtml
DSL(Domain Specific Language)查询也叫作 Request Body 查询,它比 URI 查询更高阶,能支持更复杂的查询。git
默认状况下,查询按照算分排序,返回前 10 条记录。github
ES 也支持分页,分页使用 from-size:算法
示例:shell
POST /index_name/_search { "from":10, "size":20, "query":{ "match_all": {} } }
ES 是一个分布式系统,数据保存在多个分片中,那么查询时就须要查询多个分片。数组
好比一个查询 from = 990; size = 10
,那么 ES 须要在每一个分片上都获取 1000 个文档:缓存
而后经过 Coordinating 节点汇总结果,最后再经过排序获取前 1000 个文档。app
这种方式,当页数很深的时候,就会占用不少内存,从而给 ES 集群带来很大的开销,这就是深度分页问题。less
所以,ES 为了不此类问题带来的巨大开销,有个默认的限制 index.max_result_window,from + size
必须小于等于 10000,不然就会报错。dom
好比:
POST index_name/_search { "from": 10000, # 报错 "size": 1, "query": { "match_all": {} } } POST index_name/_search { "from": 0, # 报错 "size": 10001, "query": { "match_all": {} } }
为了解决深度分页问题,ES 有两种解决方案:Search After 和 Scroll。
Search After 经过实时获取下一页的文档信息来实现,使用方法:
Search After 的方式不支持指定页数,只能一页一页的往下翻。
Search After 的原理:
示例:
# 插入一些数据 DELETE users POST users/_doc {"name":"user1","age":10} POST users/_doc {"name":"user2","age":11} POST users/_doc {"name":"user2","age":12} POST users/_doc {"name":"user2","age":13} # 第一次搜索 POST users/_search { "size": 1, # size 值 "query": { "match_all": {} }, "sort": [ {"age": "desc"} , {"_id": "asc"} # sort by id ] } # 此时返回的文档中有一个 sort 值 # "sort" : [13, "4dR-IHcB71-f4JZcrL2z"] # 以后的每一次搜索都须要用到上一次搜索结果的最后一个文档的 sort 值 POST users/_search { "size": 1, "query": { "match_all": {} }, "search_after": [ # 上一次搜索结果的最后一个文档的 sort 值放在这里 13, "4dR-IHcB71-f4JZcrL2z"], "sort": [ {"age": "desc"} , {"_id": "asc"} ] }
Scroll 经过建立一个快照来实现,方法:
Scroll Id
。Scroll 方式的缺点是,当有新的数据写入时,新写入的数据没法被查到(第一次创建快照时有多少数据,就只能查到多少数据)。
示例:
# 写入测试数据 DELETE users POST users/_doc {"name":"user1","age":10} POST users/_doc {"name":"user2","age":20} # 第一次查询前,先创建快照,快照存在时间为 5 分钟,通常不要太长 POST /users/_search?scroll=5m { "size": 1, "query": { "match_all" : {} } } # 返回的结果中会有一个 _scroll_id # 查询 POST /_search/scroll { "scroll" : "1m", # 快照的生存时间,这里是 1 分钟 "scroll_id" : "xxx==" # 上一次的 _scroll_id 值 } # 每次的查询结果都会返回一个 _scroll_id,供下一次查询使用 # 全部的数据被查完之后,再查询就得不到数据了
分页方式共 4 种:
ES 默认使用算分进行排序,咱们可使用 sort-processor(不须要再计算算分)来指定排序规则;能够对某个字段进行排序,最好只对数字型和日期型字段排序。
示例:
POST /index_name/_search { "sort":[{"order_date":"desc"}], # 单字段排序 "query":{ "match_all": {} } } POST /index_name/_search { "query": { "match_all": {} }, "sort": [ # 多字段排序 {"order_date": {"order": "desc"}}, {"_doc":{"order": "asc"}}, {"_score":{ "order": "desc"}} # 若是不指定 _score,那么算分为 null ] }
对 text 类型的数据进行排序会发生错误,能够经过打开 fielddata 参数(通常不建议这么作),来对 text 类型进行排序:
# 打开 text的 fielddata PUT index_name/_mapping { "properties": { "customer_full_name" : { # 字段名称 "type" : "text", "fielddata": true, # 打开 fielddata "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } } }
可使用 _source
设置须要返回哪些字段。示例:
POST /index_name/_search { "_source":["order_date", "xxxxx"], "query":{ "match_all": {} } }
_source
中可使用通配符,好比 ["name*", "abc*"]
。
可使用脚本进行简单的表达式运算。
POST /index_name/_search { "script_fields": { # 固定写法 "new_field": { # 新的字段名称 "script": { # 固定写法 "lang": "painless", # 固定写法 "source": "doc['order_date'].value+'hello'" # 脚本语句 } } }, "query": { "match_all": {} } }
查询会有相关性算分;过滤不须要进行算分,能够利用缓存,性能更好。
参考这里。
全文本(Full text)查询会对搜索字符串进行分词处理。
全文本查询有如下 9 种:
Match 查询是全文搜索的标准查询,与下面的几种查询相比,更增强大,灵活性也更大,最常使用。
Match 查询会先对输入字符串进行分词,而后对每一个词项进行底层查询,最后将结果合并。
例如对字符串 "Matrix reloaded" 进行查询,会查到包含 "Matrix" 或者 "reloaded" 的全部结果。
示例:
POST index_name/_search { "query": { "match": { "title": "last christmas" # 表示包含 last 或 christmas } } } POST index_name/_search { "query": { "match": { "title": { # 表示包含 last 且 包含 christmas,不必定挨着 "query": "last christmas", "operator": "and" } } } }
使用 match_phrase 关键字。示例:
POST index_name/_search { "query": { "match_phrase": { "title":{ "query": "one love" # "one love" 至关于一个单词 } } } } POST index_name/_search { "query": { "match_phrase": { "title":{ "query": "one love", "slop": 1 # "one" 和 "love" 之间能够有 1 个字符 } } } }
使用 query_string 关键字。示例:
POST index_name/_search { "query": { "query_string": { "default_field": "name", # 默认查询字段,至关于 URI 查询中的 df "query": "Ruan AND Yiming" # 可使用逻辑运算符 } } } # 多 fields 与 分组 POST index_name/_search { "query": { "query_string": { "fields":["name","about"], # 多个 fields "query": "(Ruan AND Yiming) OR (Java AND Elasticsearch)" # 支持分组 } } } POST index_name/_search { "query":{ "query_string":{ "fields":["title","year"], "query": "2012" } } }
使用 simple_query_string 关键字。
特色:
AND OR NOT
,会当成普通字符串。
AND
用 +
替代OR
用 |
替代NOT
用 -
替代OR
,能够指定 default_operator
来修改。示例:
# Simple Query 默认的 operator 是 OR POST index_name/_search { "query": { "simple_query_string": { "query": "Ruan AND Yiming", # 这里的 AND 会当成普通的字符串 "fields": ["name"] } } } POST index_name/_search { "query": { "simple_query_string": { "query": "Ruan Yiming", "fields": ["name"], "default_operator": "AND" } } } GET index_name/_search { "query":{ "simple_query_string":{ "query":"Beautiful +mind", "fields":["title"] } } }
一个字符串在多个字段中查询的状况,如何匹配最终的结果。(还有一个 dis-max 查询也是针对这种状况的)
一个字符串在多个字段中查询的状况,Multi-match 有 6 种处理方式,以下:
示例:
POST index_name/_search { "query": { "multi_match" : { # multi_match 查询 "query": "brown fox", # 查询字符串 "type": "best_fields", # 处理方式 "fields": [ "subject", "message" ], # 在多个字段中查询,fields 是一个数组 "tie_breaker": 0.3 } } }
Term 查询与全文本查询不一样的是,Term 查询不会对查询字符串进行分词处理,Term 查询会在字段匹配精确值。
Term 查询输入字符串做为一个总体,在倒排索引中查找匹配的词项,而且会计算相关性评分。
Term 查询包括如下 11 种:
结构化查询是对结构化数据的查询,可使用 Term 语句进行查询。
结构化数据有着固定的格式,包括:
结构化查询是对结构化数据的逻辑运算,运算结果只有“是”和“否”。
若是某个文档的指定字段包含某个确切值,则返回该文档。
下面举一个 term 查询的例子,首先插入一个文档:
POST /products/_bulk { "index": { "_id": 1 }} { "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }
该文档插入时,会使用默认的分词器进行分词处理。
使用 term 查询:
POST /products/_search { "query": { "term": { "desc": { # "value": "iPhone" # 会对 iPhone 精确匹配查询。 # 文档插入时,iPhone 变成了 iphone # 因此查 iPhone 查不到任何内容 "value":"iphone" # 查 iphone 能查到 } } } }
keyword 子字段
ES 默认会对 text 类型的数据创建一个 keyword 子字段,用于精确匹配,这称为 ES 的多字段属性。
keyword 子字段将原始数据原封不动的存储了下来。
能够经过 mapping 查看,以下所示:
"desc" : { # 字段名称 "type" : "text", # text 数据类型 "fields" : { "keyword" : { # keyword 子字段 "type" : "keyword", # keyword 子类型 "ignore_above" : 256 } } }
下面使用 keyword 子字段进行查询:
POST /products/_search { "query": { "term": { "desc.keyword": { # 在 desc 字段的 keyword 子字段中查询 "value": "iPhone" # 能查到 //"value":"iphone" # 查不到 } } } }
term 查询有算分:
POST index_name/_search { "query": { # 固定写法 "term": { # term 查询,固定写法 "avaliable": true # 查询 avaliable 字段的值为 true 的文档 } } }
若是不须要算分,可使用 constant_score 查询,示例:
POST index_name/_search { "query": { "constant_score": { # constant_score 查询,固定写法 "filter": { # 固定写法 "term": { # constant_score 包装一个 term 查询,就没有了算分 "avaliable": true } } } } }
range 查询中有几个经常使用的比较运算:
运算符 | 含义 |
---|---|
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
示例:
POST index_name/_search { "query": { # 固定写法 "range": { # range 查询 "age": { # 字段名称 "gte": 10, # 10 <= age <= 20 "lte": 20 } } } }
对于日期类型有几个经常使用的符号:
符号 | 含义 |
---|---|
y | 年 |
M | 月 |
w | 周 |
d | 天 |
H / h | 小时 |
m | 分钟 |
s | 秒 |
now | 如今 |
示例:
POST index_name/_search { "query" : { # 固定写法 "range" : { # range 查询 "date" : { # 字段名称 "gte" : "now-10y" # 10年以前 } } } }
exists 语句能够判断文档是否存在某个字段。
搜索存在某个字段的文档,示例:
POST index_name/_search { "query" : { "exists": { # 存在 date 字段的文档 "field": "date" } } }
搜索不存在某个字段的文档,须要使用布尔查询。
示例:
POST index_name/_search { "query": { "bool": { # 布尔查询 "must_not": { # 不存在 "exists": { # 不存在 date 字段的文档 "field": "date" } } } } }
terms 语句用于处理多值查询,至关于一个多值版的 term 语句,能够一次查询多个值。
示例:
POST index_name/_search { "query": { "terms": { # terms 查询 "productID.keyword": [ # 字段名称 "QQPX-R-3956-#aD8", # 多个值 "JODL-X-1937-#pV7" ] } } }
复合查询(Compound)可以包装其余复合查询或叶查询,以组合其结果和分数,更改其行为或者将查询转成过滤。
复合查询有如下 5 种:
bool 查询是一个或多个子查询的组合,共包含如下 4 种子句:
bool 查询的多个子句之间没有顺序之分,而且能够嵌套。
示例:
POST index_name/_search { "query": { "bool" : { "must" : { "term" : { "user.id" : "kimchy" } }, "filter": { "term" : { "tags" : "production" } }, "must_not" : { "range" : { "age" : { "gte" : 10, "lte" : 20 } } }, "should" : [ # 是一个数组 { "term" : { "tags" : "env1" } }, { "term" : { "tags" : "deployed" } } ], "minimum_should_match" : 1, "boost" : 1.0 } } }
boosting 查询会给不一样的查询条件分配不一样的级别(positive / negative),不一样的级别对算分有着不一样的印象,从而影响最终的算分。
positive 级别会对算分有正面影响, negative 级别会对算分有负面影响。
咱们可使用 boosting 查询给某些文档降级(下降算分),而不是将其从搜索结果中排除。
示例:
GET index_name/_search { "query": { "boosting": { # boosting 查询 "positive": { # positive 级别 "term": { # 匹配 apple 的会对算分有正面影响 "text": "apple" } }, "negative": { # negative 级别 "term": { # 匹配这个的会对算分有负面影响 "text": "pie tart fruit crumble tree" } }, "negative_boost": 0.5 # 降级的力度 } } }
constant_score 查询能够将查询转成一个过滤,能够避免算分(下降开销),并有效利用缓存(提升性能)。
示例:
POST /index_name/_search { "query": { "constant_score": { # constant_score 查询 "filter": { # 过滤器,固定写法 "term": { # 包装了一个 term 查询,将 term 查询转成了过滤 "productID.keyword": "XHDK-A-1293-#fJ3" } } } } }
一个字符串在多个字段中查询的状况,如何匹配最终的结果。(还有一个 Multi-match 查询也是针对这种状况的)
示例:
POST index_name/_search { "query": { "bool": { "should": [ # should 语句会综合全部的字段的分数,最终给出一个综合分数 { "match": { "title": "Brown fox" }}, { "match": { "body": "Brown fox" }} ] } } } POST index_name/_search { "query": { "dis_max": { # dis_max 语句不会综合全部字段的分数,而把每一个字段单独来看 "queries": [ # 最终结果是全部的字段中分数最高的 { "match": { "title": "Quick pets" }}, { "match": { "body": "Quick pets" }} ] } } }
function_score 查询能够在查询结束后,对每个匹配的文档进行从新算分,而后再根据新的算分进行排序。
它提供了如下 5 种算分函数:
首先插入测试数据:
DELETE blogs PUT /blogs/_doc/1 { "title": "About popularity", "content": "In this post we will talk about...", "votes": 0 } PUT /blogs/_doc/2 { "title": "About popularity", "content": "In this post we will talk about...", "votes": 100 } PUT /blogs/_doc/3 { "title": "About popularity", "content": "In this post we will talk about...", "votes": 1000000 }
查询示例1:
新的算分 = 老的算分 * 投票数
POST /blogs/_search { "query": { "function_score": { "query": { "multi_match": { # 该查询会有一个算分 "query": "popularity", "fields": [ "title", "content" ] } }, "field_value_factor": { # 最终的算分要乘以 votes 字段的值 "field": "votes" } } } }
上面这种算法当出现这两种状况的时候,会出现问题:
查询示例2,引入平滑函数:
新的算分 = 老的算分 * 平滑函数(投票数)
POST /blogs/_search { "query": { "function_score": { "query": { "multi_match": { "query": "popularity", "fields": [ "title", "content" ] } }, "field_value_factor": { "field": "votes", "modifier": "log1p" # 在原来的基础上加了一个平滑函数 } # 新的算分 = 老的算分 * log(1 + 投票数) } } }
平滑函数有下面这些:
查询示例3,引入 factor :
新的算分 = 老的算分 * 平滑函数(factor * 投票数)
POST /blogs/_search { "query": { "function_score": { "query": { "multi_match": { "query": "popularity", "fields": [ "title", "content" ] } }, "field_value_factor": { "field": "votes", "modifier": "log1p" , "factor": 0.1 } } } }
引入 factor 以后的算分曲线:
Boost Mode:
Max Boost 能够将算分控制在一个最大值。
示例:
POST /blogs/_search { "query": { "function_score": { "query": { "multi_match": { "query": "popularity", "fields": [ "title", "content" ] } }, "field_value_factor": { "field": "votes", "modifier": "log1p" , "factor": 0.1 }, "boost_mode": "sum", "max_boost": 3 } } }
示例:
POST /blogs/_search { "query": { "function_score": { "random_score": { # 将原来的查询结果随机排序 "seed": 911119 # 随机种子 } } } }
(本节完。)
推荐阅读:
欢迎关注做者公众号,获取更多技术干货。