Elasticsearch URI search
一、请求体查询与空查询
1. 请求体查询(request body search)
简单查询语句(lite)是一种有效的命令行adhoc查询。但是,如果你想要善用搜索,你必须使用请求体查询(request body search) API。之所以这么称呼,是因为大多数的参数以JSON格式所容纳而非查询字符串。
请求体查询(以下简称查询),并不仅仅用于处理查询,而且还可以高亮返回结果中的片段,并且给出帮助你的用户找寻最好结果的相关数据建议。
2. 空查询
我们以最简单的search API开始,空查询将会返回索引中所有的文档。
GET /_search
{}
同字符串查询一样,你可以查询一个,多个或_all索引(indices)或类型(types):
GET /index_2014*/type1,typ2/_search
{}
你可以使用from及size参数进行分页:
GET /_search
{
"from" : 30,
"size" : 10
}
携带内容的GET请求?
任何一种语言(特别是js)的HTTP库都不允许GET请求中携带交互数据。事实上,有些用户很惊讶GET请求中居然会允许携带交互数据。
真实情况是,http://tools.ietf.org/html/rfc7231#page-24[RFC 7231], 一份规定HTTP语义及内容的RFC中并未规定 GET 请求中允许携带交互数据! 所以,有些HTTP服务允许这种行为,而另一些(特别是缓存代理),则不允许这种行为。
Elasticsearch的作者们倾向于使用GET提交查询请求,因为他们觉得这个词相比POST来说,能更好的描述这种行为。然而,因为携带交互数据的GET请求并不被广泛支持,所以search API同样支持POST请求,类似于这样:
POST /_search
{
"from" : 30,
"size" : 10
}
这个原理同样应用于其他携带交互数据的GET API请求中。
相对于神秘的查询字符串方法,请求体查询允许我们使用结构化查询Query DSL(Query Domain Specific Language)
二、结构化查询
bool联合查询: must, should, must_not
must
: 文档必须完全匹配条件,选择多个即 且 的意思;should
: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should,选择多个即 且 的意思;must_not
: 文档必须不匹配条件,选择多个即 且 的意思;
如果我们想要请求"content中带宝马,但是tag中不带宝马"这样类似的需求,就需要用到bool联合查询。
样例
全文检索,模糊匹配,message.keyword 包含“Error”,项目名称不以“boe”开头的数据。
此处我们需要使用 bool 查询:
GET awsbpm_prod_log-2019.08.20/_search
{
"query":{
"bool":{
"must": [
{
"match_all":{
}
},
{
"wildcard":{
"message.keyword":"*Error*"
}
}
],
"must_not":[
],
"should":[
]
}
},
"from":11,
"size":20,
"sort":[
],
"aggs":{
}
}
高级检索语法:term, wildcard, prefix, fuzzy, range, query_string, text, missing
term
:严格匹配条件,所查询的字段内容要与填入查询框搜索值一致;wildcard
:通配符查询,* 表示全匹配,? 表示单一匹配,etc: aaa* 或者 a?b;prefix
:前缀匹配,搜索框如果输入aa,那么可能匹配到的字段值为 aab,aavb等;fuzzy min_similarity
:弹性模糊匹配,有两个搜索框,第一个搜索框为搜索匹配值,会自动纠错,比如输入 ggjk,那么可能会匹配到ggjo,第二个框为最小相似度,采用的算法是Damerau-Levenshtein(最佳字符串对齐)算法,不建议填写这个框,我到发稿前也是被搞的头皮发麻,等我完全吃透再更新;fuzzy max_expansions
:弹性模糊匹配,有两个搜索框,第一个搜索框为搜索匹配值,会自动纠错,比如输入 ggjk,那么可能会匹配到ggjo,第二个框是最大扩展匹配数,比如是1,那么ggjk只会随机模糊匹配到一种可能结果,即使它会出现2种或者更加多,也只会搜索一种;range
:范围查询,gt为大于,gte为大于等于,lt小于,lte小于等于,所搜索的字段值在两个搜索框标识数值
之间;query_string
:字符片段查询,如果是数字,则严格匹配数字,如果是字符串,则按照自身或者分片词匹配;text
:分片词查询,等确定后更新;missing
:查询没有定义该字段或者该字段值为null的数据。
1. term query - 索引词检索
(1) term query - 不分词检索
term query: 把检索串当作一个整体来执行检索, 即不会对检索串分词.
term是完全匹配检索, 要用在不分词的字段上, 如果某个field在映射中被分词了, term检索将不起作用.
所以, 不分词的field, 要在mapping中设置为不分词.
—— ES 5.x之后, 为每个text类型的字段新增了名为keyword的子字段, 是不分词的, 默认保留256个字符.
—— 可以使用keyword字段进行term检索.
示例:
GET shop/_search
{
"query": {
"term": {
"name.keyword": "Java编程思想"
}
}
}
(2) terms query - in检索
terms, 相当于多个term检索, 类似于SQL中in关键字的用法, 即在某些给定的数据中检索:
GET shop/_search
{
"query": {
"terms": {
"name.keyword": [
"Java编程思想", "Java并发编程的艺术"
]
}
}
}
2. prefix query - 前缀检索
prefix query, 就是前缀检索. 比如商品name中有多个以"Java"开头的document, 检索前缀"Java"时就能检索到所有以"Java"开头的文档.
—— 扫描所有倒排索引, 性能较差.
GET shop/_search
{
"query": {
"prefix": { "name": "java" }
}
}
3. wildcard query - 通配符检索
扫描所有倒排索引, 性能较差.
GET shop/_search
{
"query": {
"wildcard": { "name": "ja*" }
}
}
4. regexp query - 正则检索
扫描所有倒排索引, 性能较差.
GET shop/_search
{
"query": {
"regexp": { "name": "jav[a-z]*" }
}
}
5. fuzzy query - 纠错检索
fuzziness的默认值是2 —— 表示最多可以纠错两次.
说明: fuzziness的值太大, 将削弱检索条件的作用, 也就是说纠错次数太多, 就会导致限定检索结果的检索条件被改变, 失去了限定作用.
示例: 检索name中包含"Java"的文档, Java中缺失了一个字母a:
GET shop/_search
{
"query": {
"match": {
"name": {
"query": "Jav",
"fuzziness": 1,
"operator": "and"
}
}
}
}
6. boost评分权重 - 控制文档的优先级别
通过boost参数, 令满足某个条件的文档的得分更高, 从而使得其排名更靠前.
GET shop/_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "编程思想"} }
],
"should": [
{
"match": {
"name": {
"query": "艺术",
"boost": 2 // 提升评分权重
}
}
}
]
}
}
}
7. dis_max的用法 - best fields策略
(1) dis_max的提出
如果我们希望检索结果中 (检索串被分词后的) 关键字匹配越多, 这样的文档就越靠前, 而不是多个子检索中匹配少量分词的文档靠前.
⇒ 此时可以使用dis_max和tie_breaker.
tie_breaker的值介于0~1之间, Elasticsearch将 bool检索的分数 * tie_breaker的结果与dis_max的最高分进行比较, 除了取dis_max的最高分以外, 还会考虑其他的检索结果的分数.
(2) 使用示例
为了增加精准度, 常用的是配合boost、minimum_should_match等参数控制检索结果.
GET shop/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "name": "虚拟机" } },
{ "match": { "desc": "经典" } }
],
"tie_breaker": 0.2 // 对同时满足的文档的分值进行提升
}
}
}
GET shop/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"name": {
"query": "虚拟机",
"minimum_should_match": "50%",
"boost": 2
}
}
},
{
"match": {
"desc": {
"query": "经典",
"minimum_should_match": "50%",
"boost": 3
}
}
}
],
"tie_breaker": 0.3
}
}
}
三、复杂检索的使用范例
1. 多条件过滤 - 包含
检索出版时间在2012-07之后, 且至少满足下述条件中一个的文档:
a. 名称(name)中包含"并发";
b. 描述(desc)中包含"java";
c. 出版社(publisher)名称中不包含"电子".
GET shop/_search
{
"query": {
"bool": {
"filter": { // 按时间过滤
"range": {
"date": {"gte": "2012-07"}
}
},
"should": [ // 可匹配, 可不匹配
{
"match": { "name": "并发" }
},
{
"bool": {
"must": { // 必须匹配
"match": { "desc": "java" }
},
"must_not": { // 不能匹配
"match": { "publisher": "电子" }
}
}
}
],
"minimum_should_match": 1 // 至少满足should中的一个条件
}
},
// 自定义排序
"sort": [
{ "price": { "order": "desc" } }
]
}
注意: 排序的字段最好是数字, 或日期, 因为字符串字段会被分词, ES会通过分词后的某个词去排序, 结果难以预测.
2. 多条件拼接 - 包含+范围+排序
匹配检索: name中包含"java"却不包含"虚拟机";
范围检索: 价格大于50、小于80;
结果排序: 按照价格升序排序.
GET shop/_search
{
"query": {
"bool": {
"must": { // 必须匹配
"match": { "name": "java" }
},
"must_not": { // 必须不匹配
"match": { "name": "虚拟机" }
},
"filter": {
"range": {
"price": {
"gte": 40,
"lte": 80,
"boost": 2.0 // 设置得分的权重值(提升值), 默认是1.0
}
}
}
}
}
}
关于范围检索的使用, 请参考下篇文章: ES 22 - Elasticsearch对数值或日期类型进行范围检索
3. 定制检索结果的排序规则
(1) 默认排序规则:
ES默认是按检索结果的分值(_score)降序排列的.
某些情况下, 可能存在无实际意义的_score, 比如filter时所有_score的值都相同:
GET website/_search
{
"query": {
"bool": {
"filter": {
"term": {
"author_id": 5520 // 此时所有符合条件的_score都为0
}
}
}
}
}
// 或通过constant_score过滤:
GET website/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"author_id": 5520 // 此时所有符合条件的_score都为1
}
}
}
}
}
(2) 定制排序规则:
GET website/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"author_id": 5520
}
}
}
},
"sort": [
{
"post_date": { "order": "asc" }
}
]
}
四、Elasticsearch filter和query的不同
1. query和filter的本质区别?
query关注点:此文档与此查询子句的匹配程度如何?
filter关注点:此文档和查询子句匹配吗?
2、Query检索细化关注点
1)是否包含?
确定文档是否应该成为结果的一部分.
2)相关度得分多少?
除了确定文档是否匹配外,查询子句还计算了表示文档与其他文档相比匹配程度的_score。
3)得分越高,相关度越高。
更相关的文件,在搜索排名更高。
典型应用场景:
1)全文检索——这种相关性的概念非常适合全文搜索,因为很少有完全“正确”的答案。
举例如下:
文档中存在字段hotel_name:“上海浦东香格里拉酒店”
IK实际分词结果如下:
上海浦东,上海,浦东,香格里拉,格里,里拉,酒店。
也就是说,搜索以上关键词都能搜到:hotel_name:“上海浦东香格里拉酒店”的酒店。这些都是“相关”的。
但是搜索:“香格里” 是搜索不到结果的。
2)包含单词“run”, 但也匹配”runs”, “running”, “jog”或者”sprint”。(都是奔跑的意思)
3. filter过滤细化关注点
1)是否包含?
确定是否包含在检索结果中,回答只有“是”或“否”。
2)不涉及评分。
在搜索中没有额外的相关度排名。
3)针对结构化数据。
适用于完全精确匹配,范围检索。
参见官网举例:
以下场景适用于filter过滤检索:
举例1:时间戳timestamp 是否在2015至2016年范围内?
举例2:状态字段status 是否设置为“published”?
4)更快。
只确定是否包括结果中,不需要考虑得分。
为什么会更快?——经常使用的过滤器将被Elasticsearch自动缓存,以提高性能。
4. query和filter的性能不同
过滤查询(filter)是对集合包含/排除的简单检查,这使得它们计算速度非常快。 当至少有一个过滤查询是“稀疏”(仅有少量匹配的文档)时,可以利用各种优化,并且可以将缓存经常使用的filter过滤查询缓存在内存中以加快访问速度。
对比之下,query检索(评分查询)不仅要查找匹配的文档,还要计算每个文档的相关程度,这通常会使其比非评分文档更复杂。 另外,查询结果不可缓存。
由于倒排索引,只有几个文档匹配的简单评分查询(query检索)可能会比跨越数百万个文档的过滤器(filter过滤)表现得更好。 但是,一般来说,fiter过滤的性能将胜过评分查询(query检索)。
过滤(filter)的目标是减少必须由评分查询(query)检查的文档数量。
5. filter过滤怎么缓存呢?
Elasticsearch将创建一个文档匹配过滤器的位集bitset(如果文档匹配则为1,否则为0)。 随后用相同的过滤器执行查询将重用此信息。
每当添加或更新新文档时,位集bitset也会更新。
6. 使用场景
全文检索以及任何使用相关性评分的场景使用query检索。
除此之外的其他使用filter过滤器过滤。
7. query和filter实战
ebay在Elasticsearch使用经验中总结到:
Use filter context instead of query context if possible.
即:如果可能,请使用filter过滤器上下文而不是query查询上下文。
查询query和过滤器filter已合并(在ES1.X版本是分开的,存在filtered检索类型)。
ES高版本(2.X/5.X/6.x以后),任何查询子句都可以在“查询上下文query”中用作查询,并在“过滤器上下文filter”中用作过滤器。
举例:
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
参考资料:
Elasticsearch的高级检索语法 (包括term、prefix、wildcard、fuzzy、boost等)
https://www.cnblogs.com/shoufeng/p/11103913.html
Elasticsearch URI search
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
query-filter-context
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html#relevance-scores
Elasticsearch filter和query的不同
https://blog.csdn.net/laoyang360/article/details/80468757
queries_and_filters
https://www.elastic.co/guide/en/elasticsearch/guide/master/_queries_and_filters.html
How to monitor Elasticsearch performance
https://www.datadoghq.com/blog/monitor-elasticsearch-performance-metrics/
Elasticsearch Performance Tuning Practice at eBay
https://tech.ebayinc.com/engineering/elasticsearch-performance-tuning-practice-at-ebay/