• 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分析器。

  • 相关阅读:
    phpcms后台进入地址(包含No permission resources错误)
    phpmyadmin上传大sql文件办法
    ubuntu彻底卸载mysql
    Hdoj 2602.Bone Collector 题解
    一篇看懂词向量
    Hdoj 1905.Pseudoprime numbers 题解
    The Python Challenge 谜题全解(持续更新)
    Hdoj 2289.Cup 题解
    Hdoj 2899.Strange fuction 题解
    Hdoj 2199.Can you solve this equation? 题解
  • 原文地址:https://www.cnblogs.com/gmhappy/p/11864064.html
Copyright © 2020-2023  润新知