ES入门系列 — 5 查询语法

简单写一个ES系列,希望对NewSql、检索有兴趣的同学可以快速入手,争取在7月份完成该系列文章

 

ES入门系列

1、Query String查询

# 查询在 tweet 类型中 tweet 字段包含 elasticsearch 单词的所有文档
GET /_all/tweet/_search?q=tweet:elasticsearch
# 上面等同于
GET /_all/tweet/_search?q=+name:john +tweet:mary
# 下面语句搜索返回包含 mary 的所有文档。对 _all 字段进行查找,除非设置特定字段,否则查询字符串就使# # 用 _all 字段进行搜索
GET GET /_search?q=mary

 

当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all 字段进行索引。例如,当索引这个文档时:

{
    "tweet":    "However did I manage before Elasticsearch?",
    "date":     "2014-09-14",
    "name":     "Mary Jones",
    "user_id":  1
}

这就好似增加了一个名叫 _all 的额外字段:

"However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1"

注意:刚开始开发一个应用时,_all 字段是一个很实用的特性。之后,你会发现如果搜索时用指定字段来代替 _all 字段,将会更好控制搜索结果。当 _all 字段不再有用的时候,可以将它置为失效


查询字符串搜索允许任何用户在索引的任意字段上执行可能较慢且重量级的查询,这可能会暴露隐私信息,甚至将集群拖垮。因为这些原因,不推荐直接向用户暴露查询字符串搜索功能,除非对于集群和数据来说非常信任他们。相反,我们经常在生产环境中更多地使用功能全面的 request body 查询API,除了能完成以上所有功能,还有一些附加功能。


 

2、请求体查询

Elasticsearch 使用的查询语言(DSL)拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。包括查询模式和过滤模式

查询模式:查询就变成了一个“评分”的查询。和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有 多好(匹配程度如何)。匹配程度越好评分越高,评分查询计算每一个文档与此查询的 相关程度,同时将这个相关程度分配给表示相关性的字段 _score,并且按照相关性对匹配到的文档进行排序。

过滤模式:查询被设置成一个“不评分”或者“过滤”查询。即,这个查询只是简单的问一个问题:“这篇文档是否匹配?”。回答也是非常的简单,yes 或者 no ,二者必居其一。

  • 查询差别:过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取。评分查询(scoring queries)不仅仅要找出匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。
  • 性能区别:由于倒排索引(inverted index),一个简单的评分查询在匹配少量文档时可能与一个涵盖百万文档的filter表现的一样好,甚至会更好。但是在一般情况下,一个filter 会比一个评分的query性能更优异,并且每次都表现的很稳定。
  • 选取:使用查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)

一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。

 

3 查询语句结构

3.1 空查询

GET /_search
{

}
# 等同于下面查询
GET /_search
{
    "query": {
        "match_all": {}
    }
}

 

3.2 简单查询结构

# 1、典型结构
{
    QUERY_NAME: {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}
# 2、如果针对某个字段
{
    QUERY_NAME: {
        FIELD_NAME: {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}

 

eg :

GET /_search
{
    "match": {
        "tweet": "elasticsearch"
    }
}

# 上面查询完整结构如下:
GET /_search
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    }
}

 

3.3 复合查询结构

查询语句(Query clauses) 就像一些简单的组合块,这些组合块可以彼此之间合并组成更复杂的查询。这些语句可以是如下形式:

  • 叶子语句(Leaf clauses) (就像 match 语句) 被用于将查询字符串和一个字段(或者多个字段)对比。
  • 复合(Compound) 语句 主要用于 合并其它查询语句。 比如,一个 bool 语句 允许在你需要的时候组合其它语句,无论是 must 匹配、 must_not 匹配还是 should 匹配,同时它可以包含不评分的过滤器(filters)

复合查询列子:

Get /_search
{
    "bool": {
        "must":     { "match": { "tweet": "elasticsearch" }},
        "must_not": { "match": { "name":  "mary" }},
        "should":   { "match": { "tweet": "full text" }},
        "filter":   { "range": { "age" : { "gt" : 30 }} }
    }
}

 

4 查询模式

所有查询会或多或少的执行相关度计算,但不是所有查询都有分析阶段。和一些特殊的完全不会对文本进行操作的查询(如 bool 或 function_score )不同,文本查询可以划分成两大家族:

基于词项的查询

如 term 或 fuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用 term 查询词项 Foo 只要在倒排索引中查找 准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分 _score 。

记住 term 查询只对倒排索引的词项精确匹配,这点很重要,它不会对词的多样性进行处理(如, foo 或 FOO )。这里,无须考虑词项是如何存入索引的。如果是将 ["Foo","Bar"] 索引存入一个不分析的( not_analyzed )包含精确值的字段,或者将 Foo Bar 索引到一个带有 whitespace 空格分析器的字段,两者的结果都会是在倒排索引中有 Foo 和 Bar 这两个词。

基于全文的查询

像 match 或 query_string 这样的查询是高层查询,它们了解字段映射的信息:

  • 如果查询 日期(date) 或 整数(integer) 字段,它们会将查询字符串分别作为日期或整数对待。
  • 如果查询一个( not_analyzed )未分析的精确值字符串字段,它们会将整个查询字符串作为单个词项对待。
  • 但如果要查询一个( analyzed )已分析的全文字段,它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表

 

4.1 match_all 查询

match_all 查询简单的匹配所有文档,在没有指定查询方式时,它是默认的查询

{
    "match_all": { }
}

 

4.2 match 查询

match 查询是你可用的标准查询。如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串;如果在一个精确值的字段上使用它,例如数字、日期、布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值。

# 全文字符串
{ "match": { "tweet": "About Search" }}
# 精确值,例如数字、日期、布尔或者一个 not_analyzed 字符串字段
{ "match": { "age":    26           }}
{ "match": { "date":   "2014-09-01" }}
{ "match": { "public": true         }}
{ "match": { "tag":    "full_text"  }}

 

4.3 match_all 查询

match_all 查询可以在多个字段上执行相同的 match 查询

{
    "multi_match": {
        "query":    "full text search",
        "fields":   [ "title", "body" ]
    }
}

 

4.4 range 查询

range 查询找出那些落在指定区间内的数字或者时间

  • gt :大于
  • gte :大于等于
  • lt :小于
  • lte :小于等于
{
    "range": {
        "age": {
            "gte":  20,
            "lt":   30
        }
    }
}

 

4.5 term 查询

term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串

{ "term": { "age":    26           }}
{ "term": { "date":   "2014-09-01" }}
{ "term": { "public": true         }}
{ "term": { "tag":    "full_text"  }}

注意:term 和 terms 是 包含(contains) 操作,而非 等值(equals) (判断)。说明一下:terms 查询好比是 term 查询的复数形式,多个term查询词项,或关系,只有命中其中一个term查询项,及满足

#比如下面查询体
{ "term" : { "tags" : "search" } }
#可以匹配到下面两个文档,因为term或者terms是查询是基于包含关系
{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] } 

 

4.6 existing 查询 和 missing 查询

exists 查询和 missing 查询被用于查找那些指定字段中有值 (exists) 或无值 (missing) 的文档。这与SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本质上具有共性

{
    "exists":   {
        "field":    "title"
    }
}

 

5 过滤模式

5.1 bool 查询

你可以用 bool 查询来实现你的需求。这种查询将多查询组合在一起,成为用户自己想要的布尔查询。它接收以下参数:

  • must:文档 必须 匹配这些条件才能被包含进来。
  • must_not:文档 必须不 匹配这些条件才能被包含进来。
  • should:如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
  • filter必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。

记住:如果没有 must 语句,那么至少需要能够匹配其中的一条 should 语句。但如果存在至少一条 must 语句,则对 should 语句的匹配没有要求。

下面的查询用于查找 title 字段匹配 how to make millions 并且tag不为 spam 的文档。那些tag为 starred 或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 两者 都满足,那么它排名将更高

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ]
    }
}

上面例子,如果我们不想因为文档的时间而影响得分,可以用 filter 语句来重写上面的例子

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
          "range": { "date": { "gte": "2014-01-01" }} 
        }
    }
}

上面例子,如果你需要通过多个不同的标准来过滤你的文档,bool 查询本身也可以被用做不评分的查询。简单地将它放置到 filter 语句中并在内部构建布尔逻辑

{
    "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" }}
              ]
          }
        }
    }
}

 

5.2 constant_score 查询

constant_score 查询也是你工具箱里有用的查询工具。它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。可以使用它来取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助

# term 查询被放置在 constant_score 中,转成不评分的 filter。这种方式可以用来取代只有 filter 语句的 bool 查询
{
    "constant_score":   {
        "filter": {
            "term": { "category": "ebooks" } 
        }
    }
}

 

6 查询精度控制

6.1 精度控制operator 

match 查询还可以接受 operator 操作符作为输入参数,默认情况下该操作符是 or,也可以改成and 

#下面查询必须同时命中brown、dog才会命中查询
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": {      
                "query":    "BROWN DOG!",
                "operator": "and"
            }
        }
    }
}

 

6.2 精度控制minimum_should_match 

match 查询支持 minimum_should_match 最小匹配参数,这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,更常用的做法是将其设置为一个百分数。minimum_should_match是向下取整,eg: minimum_should_match=75%,查询词项3个,则需要命中 3 * 75% = 2.25 ==> 向下取整等于2,既需要命中2个此项

#下面查询需要命中2个词项才认为命中
GET /my_index/my_type/_search
{
  "query": {
    "match": {
      "title": {
        "query": "quick brown dog",
        "minimum_should_match": "75%"
      }
    }
  }
}

 

7 权重控制boost

boost 可以用来控制任何查询语句的相对的权重,默认值1。boost 参数被用来提升一个语句的相对权重( boost 值大于 1 )或降低相对权重( boost 值处于 0 到 1 之间),但是这种提升或降低并不是线性的,换句话说,如果一个 boost 值为 2 ,并不能获得两倍的评分 _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 
                    }
                }}
            ]
        }
    }
}

 

 

8 分析及验证

8.1 查询分析

#假设mytype类型的title字段,使用默认的 standard 标准分析器,返回词项 foxes
GET /my_index/_analyze
{
  "field": "my_type.title",   
  "text": "Foxes"
}
#假设mytype类型的english_title字段,使用默认的 english 标准分析器,返回词项 fox
GET /my_index/_analyze
{
  "field": "my_type.english_title",   
  "text": "Foxes"
}

 

8.2 validate 验证

req:

GET /gb/tweet/_validate/query
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}

resp:

{
  "valid" :         false,
  "_shards" : {
    "total" :       1,
    "successful" :  1,
    "failed" :      0
  }
}

 

8.3 explain参数

对于不合法查询,explanations 给出不合法的具体信息;对于合法查询,使用 explain 参数将返回可读的描述,我们查询的每一个 index 都会返回对应的 explanation ,因为每一个 index 都有自己的映射和分析器。

eg1 不合法查询:

GET /gb/tweet/_validate/query?explain 
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}

# 下面返回告诉:我们将查询类型(match)与字段名称 (tweet)搞混了
{
  "valid" :     false,
  "_shards" :   { ... },
  "explanations" : [ {
    "index" :   "gb",
    "valid" :   false,
    "error" :   "org.elasticsearch.index.query.QueryParsingException:
                 [gb] No query registered for [tweet]"
  } ]
}

eg2 合法查询:

GET /_validate/query?explain
{
   "query": {
      "match" : {
         "tweet" : "really powerful"
      }
   }
}

# 下面返回,从 explanation 中可以看出,匹配 really powerful 的 match 查询被重写为两个针对 tweet # 字段的 single-term 查询,一个single-term查询对应查询字符串分出来的一个term。
# 当然,对于索引 us ,这两个 term 分别是 really 和 powerful ,而对于索引 gb ,term 则分别是     # realli 和 power 。之所以出现这个情况,是由于我们将索引 gb 中 tweet 字段的分析器修改为 english # 分析器。
{
  "valid" :         true,
  "_shards" :       { ... },
  "explanations" : [ {
    "index" :       "us",
    "valid" :       true,
    "explanation" : "tweet:really tweet:powerful"
  }, {
    "index" :       "gb",
    "valid" :       true,
    "explanation" : "tweet:realli tweet:power"
  } ]
}

 

==========

待更新

==========

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章