Elasticsearch URI search 查询语法整理

Elasticsearch URI 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/

posted on 2019-08-26 20:43  doraman  阅读(11126)  评论(0编辑  收藏  举报