es中的查询请求有两种方式,一种是简易版的查询,另外一种是使用JSON完整的请求体,叫做结构化查询(DSL)。
由于DSL查询更为直观也更为简易,所以大都使用这种方式。
DSL查询是POST过去一个json,由于post的请求是json格式的,所以存在很多灵活性,也有很多形式。
这里有一个地方注意的是官方文档里面给的例子的json结构只是一部分,并不是可以直接黏贴复制进去使用的。一般要在外面加个query为key的机构。
match
最简单的一个match例子:
查询和"我的宝马多少马力"这个查询语句匹配的文档。
{ "query": { "match": { "content": { "query": "我的宝马多少马力" } } } } |
上面的查询匹配就会进行分词,比如"宝马多少马力"会被分词为"宝马 多少 马力", 所有有关"宝马 多少 马力", 那么所有包含这三个词中的一个或多个的文档就会被搜索出来。
并且根据lucene的评分机制(TF/IDF)来进行评分。
match_phrase
比如上面一个例子,一个文档"我的保时捷马力不错"也会被搜索出来,那么想要精确匹配所有同时包含"宝马 多少 马力"的文档怎么做?就要使用 match_phrase 了
{ "query": { "match_phrase": { "content": { "query": "我的宝马多少马力" } } } } |
完全匹配可能比较严,我们会希望有个可调节因子,少匹配一个也满足,那就需要使用到slop。
{ "query": { "match_phrase": { "content": { "query": "我的宝马多少马力", "slop": 1 } } } } |
multi_match
如果我们希望两个字段进行匹配,其中一个字段有这个文档就满足的话,使用multi_match
{ "query": { "multi_match": { "query": "我的宝马多少马力", "fields": ["title", "content"] } } } |
但是multi_match就涉及到匹配评分的问题了。
我们希望完全匹配的文档占的评分比较高,则需要使用best_fields
{ "query": { "multi_match": { "query": "我的宝马发动机多少", "type": "best_fields", "fields": [ "tag", "content" ], "tie_breaker": 0.3 } } } |
意思就是完全匹配"宝马 发动机"的文档评分会比较靠前,如果只匹配宝马的文档评分乘以0.3的系数
我们希望越多字段匹配的文档评分越高,就要使用most_fields
{ "query": { "multi_match": { "query": "我的宝马发动机多少", "type": "most_fields", "fields": [ "tag", "content" ] } } } |
我们会希望这个词条的分词词汇是分配到不同字段中的,那么就使用cross_fields
{ "query": { "multi_match": { "query": "我的宝马发动机多少", "type": "cross_fields", "fields": [ "tag", "content" ] } } } |
term
term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
{ "query": { "term": { "content": "汽车保养" } } } |
查出的所有文档都包含"汽车保养"这个词组的词汇。
使用term要确定的是这个字段是否“被分析”(analyzed),默认的字符串是被分析的。
拿官网上的例子举例:
mapping是这样的:
PUT my_index { "mappings": { "my_type": { "properties": { "full_text": { "type": "string" }, "exact_value": { "type": "string", "index": "not_analyzed" } } } } } |
PUT my_index / my_type / 1 { "full_text": "Quick Foxes!", "exact_value": "Quick Foxes!" } |
其中的full_text是被分析过的,所以full_text的索引中存的就是[quick, foxes],而extra_value中存的是[Quick Foxes!]。
那下面的几个请求:
GET my_index / my_type / _search { "query": { "term": { "exact_value": "Quick Foxes!" } } } |
请求的出数据,因为完全匹配
GET my_index / my_type / _search { "query": { "term": { "full_text": "Quick Foxes!" } } } |
请求不出数据的,因为full_text分词后的结果中没有[Quick Foxes!]这个分词。
bool查询
filter:不参与评分的快速查询
GET _search { "query": { "bool": { "filter": { "term": { "content": "宝马" } } } } } |
range:范围过滤
GET _search { "post_filter":{ "range":{ "price":{"gt":35, "lt": 50} } } } |
exists:字段有值
查询price有值的, null也不行 GET _search { "post_filter": { "exists": { "field": "price" } } } 查询price有值的, null也不行 GET _search { "query": { "bool": { "filter": { "exists": { "field": "price" } } } } } |
bool联合查询: must,should,must_not
如果我们想要请求"content中带宝马,但是tag中不带宝马"这样类似的需求,就需要用到bool联合查询。
联合查询就会使用到must,should,must_not三种关键词。
这三个可以这么理解
must: 文档必须完全匹配条件
should: should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should
must_not: 文档必须不匹配条件
比如上面那个需求:
{ "query": { "bool": { "must": { "term": { "content": "宝马" } }, "must_not": { "term": { "tags": "宝马" } } } } } |
聚合查询:
filtered查询
如果你想要找到所有售价高于10000美刀的车,同时也对这些车计算其平均价格,那么可以使用一个filtered查询:
GET _search?search_type=count { "query" : { "filtered": { "filter": { "range": { "price": { "gte": 10000 } } } } }, "aggs" : { "single_avg_price": { "avg" : { "field" : "price" } } } } |
从本质上而言,使用filtered查询和使用match查询并无区别,正如我们在上一章所讨论的那样。该查询(包含了一个过滤器)返回文档的一个特定子集,然后聚合工作在该子集上。
过滤桶(Filter Bucket)
如果你只想过滤聚合结果呢?假设我们正在创建针对汽车交易的搜索页面,我们想要根据用户搜索内容来展示对应结果。但是我们也想通过包含上个月出售的汽车的平均价格(匹配搜索的汽车)来让页面更加丰富。
此时我们不能使用简单的作用域,因为有两个不同搜索条件。搜索结果必须要匹配ford,但是聚合结果必须要匹配ford以及售出时间为上个月。
为了解决这一问题,我们使用一个名为filter的特殊桶。通过制定一个过滤器,当文档匹配了该过滤器的规则时,它就会被添加到桶中。
以下是得到的查询:
GET _search?search_type=count { "query":{ "match": { "make": "ford" } }, "aggs":{ "recent_sales": { "filter": { "range": { "sold": { "from": "now-1M" } } }, "aggs": { "average_price":{ "avg": { "field": "price" } } } } } } |
因为过滤器桶和任何其它桶以相似的方式工作,你可以任意地将其它桶和指标包含在其中。所有的嵌套组建都会”继承”该过滤器。从而使你能够根据需要对聚合中的内容进行过滤。
后置过滤器(Post Filter)
目前,我们有了用于过滤搜索结果和聚合的过滤器(filtered查询),也有了用于过滤聚合中某一部分的过滤器(filter桶)。
你也许会好奇,“是否有一种过滤器只过滤搜索结果,而不过滤聚合呢?”这个问题的答案就是使用post_filter。
它是搜索请求内能够接受一个过滤器作为参数的顶层元素。该过滤器会在查询执行完毕后生效(后置因此得名:在查询执行之后运行)。正因为它在查询执行后才会运行,所以它并不会影响查询作用域 - 因此就不会对聚合有所影响。
我们可以利用这一行为在搜索条件中添加额外的过滤器,而不影响用户界面中类似于类别分面(Categorical Facets)的元素。让我们设计另一个针对汽车交易的搜索页面。该页面允许用户对汽车进行搜索,同时还能够根据颜色进行过滤。颜色通过聚合提供:
GET _search?search_type=count { "query": { "match": { "make": "ford" } }, "post_filter": { "term" : { "color" : "green" } }, "aggs" : { "all_colors": { "terms" : { "field" : "color" } } } } |
post_filter元素是一个顶层元素,只会对搜索结果进行过滤。
查询部分呢用来找到所有ford汽车。然后我们根据一个terms聚合来得到颜色列表。因为聚合是在查询作用域中进行的,得到的颜色列表会反映出ford汽车的各种颜色。
最后,post_filter会对搜索结果进行过滤,只显示绿色的ford汽车。这一步发生在执行查询之后,因此聚合是不会被影响的。
这一点对于维持一致的用户界面而言是非常重要的。假设一个用户在界面上点击了一个分类(比如,绿色)。期望的结果是搜索结果被过滤了,而用户界面上的分类选项是不会变化的。如果你使用了一个filtered查询,用户界面上也立即会对分类进行更新,此时绿色就变成了唯一的选项 - 这显然不是用户想要的!
只有当你需要对搜索结果和聚合使用不同的过滤方式时才考虑使用post_filter。有时一些用户会直接在常规搜索中使用post_filter。
不要这样做!post_filter会在查询之后才会被执行,因此会失去过滤在性能上帮助(比如缓存)。
post_filter应该只和聚合一起使用,并且仅当你使用了不同的过滤条件时。
选择合适类型的过滤 - 搜索结果(Search Hits),聚合(Aggregations),或两者 - 通常都取决于你的用户界面的行为。过滤器的选择(或者组合)取决于你想要如何向用户展示结果数据。
A filtered query affects both search results and aggregations.filtered查询会影响搜索结果和聚合。
filter桶只影响聚合。
post_filter只影响搜索结果。
关于排序、分页查询
GET _search { "query": { "match_all": {} }, "sort": [ { "age": { "order": "desc" } } ], "from": 2, "size": 1 } |