zoukankan      html  css  js  c++  java
  • elasticsearch 基础 —— 请求体查询

    请求体查询

    简易 查询 —query-string search— 对于用命令行进行即席查询(ad-hoc)是非常有用的。 然而,为了充分利用查询的强大功能,你应该使用 请求体 search API, 之所以称之为请求体查询(Full-Body Search),因为大部分参数是通过 Http 请求体而非查询字符串来传递的。

    请求体查询 —下文简称 查询—不仅可以处理自身的查询请求,还允许你对结果进行片段强调(高亮)、对所有或部分结果进行聚合分析,同时还可以给出 你是不是想找 的建议,这些建议可以引导使用者快速找到他想要的结果。

    空查询

    让我们以 最简单的 search API 的形式开启我们的旅程,空查询将返回所有索引库(indices)中的所有文档:

        GET /_search
        {}  

      这是一个空的请求体。

    只用一个查询字符串,你就可以在一个、多个或者 _all 索引库(indices)和一个、多个或者所有types中查询:

        GET /index_2014*/type1,type2/_search
        {}

    同时你可以使用 from 和 size 参数来分页:

        GET /_search
        {
          "from": 30,
          "size": 10
        }

    一个带请求体的 GET 请求?

    某些特定语言(特别是 JavaScript)的 HTTP 库是不允许 GET 请求带有请求体的。 事实上,一些使用者对于 GET 请求可以带请求体感到非常的吃惊。

    而事实是这个RFC文档 RFC 7231— 一个专门负责处理 HTTP 语义和内容的文档 — 并没有规定一个带有请求体的 GET 请求应该如何处理!结果是,一些 HTTP 服务器允许这样子,而有一些 — 特别是一些用于缓存和代理的服务器 — 则不允许。

    对于一个查询请求,Elasticsearch 的工程师偏向于使用 GET 方式,因为他们觉得它比 POST 能更好的描述信息检索(retrieving information)的行为。然而,因为带请求体的 GET 请求并不被广泛支持,所以 search API 同时支持 POST 请求:

        POST /_search
        {
          "from": 30,
          "size": 10
        }

    类似的规则可以应用于任何需要带请求体的 GET API。

    我们将在聚合 聚合 章节深入介绍聚合(aggregations),而现在,我们将聚焦在查询。

    相对于使用晦涩难懂的查询字符串的方式,一个带请求体的查询允许我们使用 查询领域特定语言(query domain-specific language) 或者 Query DSL 来写查询语句。

    查询表达式

    查询表达式(Query DSL)是一种非常灵活又富有表现力的 查询语言。 Elasticsearch 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分。在你的应用中,你应该用它来编写你的查询语句。它可以使你的查询语句更灵活、更精确、易读和易调试。

    要使用这种查询表达式,只需将查询语句传递给 query 参数:

        GET /_search
        {
            "query": YOUR_QUERY_HERE
        }

    空查询(empty search) —{}— 在功能上等价于使用 match_all 查询, 正如其名字一样,匹配所有文档:

        GET /_search
        {
            "query": {
                "match_all": {}
            }
        }

    查询语句的结构

    一个查询语句 的典型结构:

        {
            QUERY_NAME: {
                ARGUMENT: VALUE,
                ARGUMENT: VALUE,...
            }
        }

    如果是针对某个字段,那么它的结构如下:

        {
            QUERY_NAME: {
                FIELD_NAME: {
                    ARGUMENT: VALUE,
                    ARGUMENT: VALUE,...
                }
            }
        }

    举个例子,你可以使用 match 查询语句 来查询 tweet 字段中包含 elasticsearch 的 tweet:

        {
            "match": {
                "tweet": "elasticsearch"
            }
        }

        {
            "match": {
                "tweet": {
                    "query": "elasticsearch"
                }
            }
        }

    完整的查询请求如下:

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

        或

        {
            "query": {
                "match": {
                    "tweet": {
                        "query": "elasticsearch"
                    }
                }
            }
        }

    合并查询语句

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

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

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

    一条复合语句可以合并 任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。

    例如,以下查询是为了找出信件正文包含 business opportunity 的星标邮件,或者在收件箱正文包含business opportunity 的非垃圾邮件:

        {
            "bool": {
                "must": { "match":   { "email": "business opportunity" }},
                "should": [
                    { "match":       { "starred": true }},
                    { "bool": {
                        "must":      { "match": { "folder": "inbox" }},
                        "must_not":  { "match": { "spam": true }}
                    }}
                ],
                "minimum_should_match": 1
            }
        }

    到目前为止,你不必太在意这个例子的细节,我们会在后面详细解释。最重要的是你要理解到,一条复合语句可以将多条语句 — 叶子语句和其它复合语句 — 合并成一个单一的查询语句。

    查询与过滤

    Elasticsearch 使用的查询语言(DSL) 拥有一套查询组件,这些组件可以以无限组合的方式进行搭配。这套组件可以在以下两种情况下使用:过滤情况(filtering context)和查询情况(query context)。

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

    • created 时间是否在 2013 与 2014 这个区间?
    • status 字段是否包含 published 这个单词?
    • lat_lon 字段表示的位置是否在指定点的 10km 范围内?

    当使用于 查询情况 时,查询就变成了一个“评分”的查询。和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有 _多好_(匹配程度如何)。 此查询的典型用法是用于查找以下文档:

    • 查找与 full text search 这个词语最佳匹配的文档
    • 包含 run 这个词,也能匹配 runs 、 running 、 jog 或者 sprint
    • 包含 quick 、 brown 和 fox 这几个词 — 词之间离的越近,文档相关性越高
    • 标有 lucene 、 search 或者 java 标签 — 标签越多,相关性越高

    一个评分查询计算每一个文档与此查询的 _相关程度_,同时将这个相关程度分配给表示相关性的字段 `_score`,并且按照相关性对匹配到的文档进行排序。这种相关性的概念是非常适合全文搜索的情况,因为全文搜索几乎没有完全 “正确” 的答案。

    自 Elasticsearch 问世以来,查询与过滤(queries and filters)就独自成为 Elasticsearch 的组件。但从 Elasticsearch 2.0 开始,过滤(filters)已经从技术上被排除了,同时所有的查询(queries)拥有变成不评分查询的能力。

    然而,为了明确和简单,我们用 "filter" 这个词表示不评分、只过滤情况下的查询。你可以把 "filter" 、 "filtering query" 和 "non-scoring query" 这几个词视为相同的。

    相似的,如果单独地不加任何修饰词地使用 "query" 这个词,我们指的是 "scoring query" 。

    性能差异

    过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取,所以有各种各样的手段来优化查询结果。

    相反,评分查询(scoring queries)不仅仅要找出 匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。

    多亏倒排索引(inverted index),一个简单的评分查询在匹配少量文档时可能与一个涵盖百万文档的filter表现的一样好,甚至会更好。但是在一般情况下,一个filter 会比一个评分的query性能更优异,并且每次都表现的很稳定。

    过滤(filtering)的目标是减少那些需要通过评分查询(scoring queries)进行检查的文档。

    如何选择查询与过滤

    通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。

    最重要的查询

    虽然 Elasticsearch 自带了很多的查询,但经常用到的也就那么几个。我们将在 深入搜索 章节详细讨论那些查询的细节,接下来我们对最重要的几个查询进行简单介绍。

    match_all 查询

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

        { "match_all": {}}

    它经常与 filter 结合使用--例如,检索收件箱里的所有邮件。所有邮件被认为具有相同的相关性,所以都将获得分值为 1 的中性 `_score`。

    match 查询

    无论你在任何字段上进行的是全文搜索还是精确查询,match 查询是你可用的标准查询。

    如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串:

        { "match": { "tweet": "About Search" }}

    如果在一个精确值的字段上使用它, 例如数字、日期、布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值:

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

    对于精确值的查询,你可能需要使用 filter 语句来取代 query,因为 filter 将会被缓存。接下来,我们将看到一些关于 filter 的例子。

    不像我们在 轻量 搜索 章节介绍的字符串查询(query-string search), match 查询不使用类似 +user_id:2 +tweet:search 的查询语法。它只是去查找给定的单词。这就意味着将查询字段暴露给你的用户是安全的;你需要控制那些允许被查询字段,不易于抛出语法异常。

    multi_match 查询

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

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

    range 查询

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

        {
            "range": {
                "age": {
                    "gte":  20,
                    "lt":   30
                }
            }
        }

    被允许的操作符如下:

    gt

    大于

    gte

    大于等于

    lt

    小于

    lte

    小于等于

    term 查询

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

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

    term 查询对于输入的文本不 分析 ,所以它将给定的值进行精确查询。

    terms 查询

    terms 查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件:

        { "terms": { "tag": [ "search", "full_text", "nosql" ] }}

    和 term 查询一样,terms 查询对于输入的文本不分析。它查询那些精确匹配的值(包括在大小写、重音、空格等方面的差异)。

    exists 查询和 missing 查询

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

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

    这些查询经常用于某个字段有值的情况和某个字段缺值的情况。

    组合多查询

    现实的查询需求从来都没有那么简单;它们需要在多个字段上查询多种多样的文本,并且根据一系列的标准来过滤。为了构建类似的高级查询,你需要一种能够将多查询组合成单一查询的查询方法。

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

    must

    文档 必须 匹配这些条件才能被包含进来。

    must_not

    文档 必须不 匹配这些条件才能被包含进来。

    should

    如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。

    filter

    必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。

    由于这是我们看到的第一个包含多个查询的查询,所以有必要讨论一下相关性得分是如何组合的。每一个子查询都独自地计算文档的相关性得分。一旦他们的得分被计算出来, bool 查询就将这些得分进行合并并且返回一个代表整个布尔操作的得分。

    下面的查询用于查找 title 字段匹配 how to make millions 并且不被标识为 spam 的文档。那些被标识为 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" }}}
                ]
            }
        }

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

    增加带过滤器(filtering)的查询

    如果我们不想因为文档的时间而影响得分,可以用 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" }}  
                }
            }
        }

      range 查询已经从 should 语句中移到 filter 语句

    通过将 range 查询移到 filter 语句中,我们将它转成不评分的查询,将不再影响文档的相关性排名。由于它现在是一个不评分的查询,可以使用各种对 filter 查询有效的优化手段来提升性能。

    所有查询都可以借鉴这种方式。将查询移到 bool 查询的 filter 语句中,这样它就自动的转成一个不评分的 filter 了。

    如果你需要通过多个不同的标准来过滤你的文档,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" }}
                      ]
                  }
                }
            }
        }

      将 bool 查询包裹在 filter 语句中,我们可以在过滤标准中增加布尔逻辑

    通过混合布尔查询,我们可以在我们的查询请求中灵活地编写 scoring 和 filtering 查询逻辑。

    constant_score 查询

    尽管没有 bool 查询使用这么频繁,constant_score 查询也是你工具箱里有用的查询工具。它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。

    可以使用它来取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。

        {
            "constant_score":   {
                "filter": {
                    "term": { "category": "ebooks" }  
                }
            }
        }

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


    验证查询

    查询可以变得非常的复杂,尤其 和不同的分析器与不同的字段映射结合时,理解起来就有点困难了。不过 validate-query API 可以用来验证查询是否合法。

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

    以上 validate 请求的应答告诉我们这个查询是不合法的:

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

    理解错误信息

    为了找出 查询不合法的原因,可以将 explain 参数 加到查询字符串中:

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

     explain 参数可以提供更多关于查询不合法的信息。

    很明显,我们将查询类型(match)与字段名称 (tweet)搞混了:

        {
          "valid" :     false,
          "_shards" :   { ... },
          "explanations" : [ {
            "index" :   "gb",
            "valid" :   false,
            "error" :   "org.elasticsearch.index.query.QueryParsingException:
                         [gb] No query registered for [tweet]"
          } ]
        }

    理解查询语句

    对于合法查询,使用 explain 参数将返回可读的描述,这对准确理解 Elasticsearch 是如何解析你的 query 是非常有用的:

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

    我们查询的每一个 index 都会返回对应的 explanation ,因为每一个 index 都有自己的映射和分析器:

        {
          "valid" :         true,
          "_shards" :       { ... },
          "explanations" : [ {
            "index" :       "us",
            "valid" :       true,
            "explanation" : "tweet:really tweet:powerful"
          }, {
            "index" :       "gb",
            "valid" :       true,
            "explanation" : "tweet:realli tweet:power"
          } ]
        }

    从 explanation 中可以看出,匹配 really powerful 的 match 查询被重写为两个针对 tweet 字段的 single-term 查询,一个single-term查询对应查询字符串分出来的一个term。

    当然,对于索引 us ,这两个 term 分别是 really 和 powerful ,而对于索引 gb ,term 则分别是 realli 和 power 。之所以出现这个情况,是由于我们将索引 gb 中 tweet 字段的分析器修改为 english分析器。

  • 相关阅读:
    Docker 安装 Logstash
    Docker 安装 Kibana
    go命令行参数
    小程序 表单提交
    小程序picker日历
    小程序,验证码倒计时效果
    flex图片垂直居中
    小程序单选框样式
    wechat-wxparse 使用
    小程序Banner跳转
  • 原文地址:https://www.cnblogs.com/gmhappy/p/11864064.html
Copyright © 2011-2022 走看看