Elasticsearch查询

一、简介

Elasticsearch提供了基于JSON的完整查询DSL(特定于域的语言)来定义查询。 将查询DSL视为查询的AST(抽象语法树),它由两种子句组成:

  • 叶子查询子句
    叶子查询子句在特定字段中查找特定值,例如matchtermrange查询。 这些查询可以自己使用。
  • 复合查询子句
    复合(Compound) 语句 主要用于 合并其它查询语句。 比如,一个 bool 语句 允许在你需要的时候组合其它语句,无论是 must 匹配、 must_not 匹配还是 should 匹配,同时它可以包含不评分的过滤器(filters):

查询子句的行为会有所不同,具体取决于它们是在查询上下文中还是在过滤上下文中使用。

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

1.1、查询和过滤上下文

1.1.1、关联分数

默认情况下,Elasticsearch通过相关性得分对匹配的搜索结果进行排序,该得分衡量每个文档与查询的匹配程度。

相关性分数是一个正浮点数,在搜索API的_score元字段中返回。 _score越高,文档越相关。 虽然每种查询类型可以不同地计算相关性分数,但是分数计算还取决于查询子句是在查询上下文中还是在过滤上下文中运行。

1.1.2、查询

在查询上下文中,查询子句回答以下问题:“此文档与该查询子句的匹配程度如何?” 除了确定文档是否匹配之外,查询子句还计算_score元字段中的相关性得分。

只要将查询子句传递给查询参数(例如搜索API中的查询参数),查询上下文就有效。

1.1.3、过滤

在过滤上下文中,查询子句回答问题“此文档是否与此查询子句匹配?” 答案是简单的“是”或“否”,即不计算分数。 过滤器上下文主要用于过滤结构化数据,例如:

  • 此时间戳记是否在2015年至2016年的范围内?
  • 状态字段设置为"published"吗?
  • 常用过滤将由Elasticsearch自动缓存,以提高性能

每当将查询子句传递到过滤器参数(例如bool查询中的filtermust_not参数,constant_score查询中的filter参数或过滤聚合)时,过滤上下文即生效。

1.1.4、查询和过滤示例

以下是在搜索API的查询和过滤器上下文中使用的查询子句的示例。 此查询将匹配满足以下所有条件的文档:

  • title字段包含单词seach。
  • content字段包含单词elasticsearch。
  • status字段包含确切单词"published"。
  • publish_date字段包含从2015年1月1日开始的日期。
GET /_search
{
  "query": { 
    "bool": { 
      "must": [
        { "match": { "title":   "Search"        }},
        { "match": { "content": "Elasticsearch" }}
      ],
      "filter": [ 
        { "term":  { "status": "published" }},
        { "range": { "publish_date": { "gte": "2015-01-01" }}}
      ]
    }
  }
}

二、复合查询

复合查询就是将一些简单的查询组合在一起作为查询条件进行文档检索。

2.1、bool查询

默认查询,用于组合多个叶子或复合查询子句(mustshouldmust_notfilter子句)。 must和should子句的分数组合在一起-匹配的子句越多越好-而must_not和filter子句在过滤器上下文中执行。

现实的查询需求从来都没有那么简单;它们需要在多个字段上查询多种多样的文本,并且根据一系列的标准来过滤。为了构建类似的高级查询,你需要一种能够将多查询组合成单一查询的查询方法。你可以用 bool 查询来实现你的需求。这种查询将多查询组合在一起,成为用户自己想要的布尔查询。它接收以下参数:

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

bool查询采用的是“更好匹配”的方法,因此,每个匹配的mustshould子句的分数将加在一起,以提供每个文档的最终_score。

POST _search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user" : "kimchy" }
      },
      "filter": {
        "term" : { "tag" : "tech" }
      },
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
      "should" : [
        { "term" : { "tag" : "wow" } },
        { "term" : { "tag" : "elasticsearch" } }
      ]
    }
  }
}

2.2、boosting query

一种复合查询,分为positive子查询和negitive子查询,两者的查询结构都会返回。

  • positive子查询的score保持不变;
  • negetive子查询的score值将会根据negative_boost的值做相应程度的降低(score乘以negative_boost)。
GET /_search
{
    "query": {
        "boosting" : {
            "positive" : {
                "term" : {
                    "text" : "apple"
                }
            },
            "negative" : {
                 "term" : {
                     "text" : "pie tart fruit crumble tree"
                }
            },
            "negative_boost" : 0.5
        }
    }
}

2.3、constant_score查询

constant_score检索可以包装一个其它类型的查询,并返回匹配过滤器中的查询条件且具备相同评分的文档。

GET /shakespeare/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "text_entry": "apple"
        }
      },
      "boost": 1.2
    }
  }
}

上述查询条件会返回包含apple单词的文档,并且评分都是1.2

2.4、 dis_max查询

使用 dis_max 即分离最大化查询(Disjunction Max Query) 。分离(Disjunction)的意思是 或(or) 。
分离最大化查询(Disjunction Max Query)指的是: 将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回 :

PUT /my_index/my_type/1
{
    "title": "Quick brown rabbits",
    "body":  "Brown rabbits are commonly seen."
}

PUT /my_index/my_type/2
{
    "title": "Keeping pets healthy",
    "body":  "My quick brown fox eats rabbits on a regular basis."
}
查询:
{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

响应:
{
  "hits": [
     {
        "_id":      "1",
        "_score":   0.14809652,
        "_source": {
           "title": "Quick brown rabbits",
           "body":  "Brown rabbits are commonly seen."
        }
     },
     {
        "_id":      "2",
        "_score":   0.09256032,
        "_source": {
           "title": "Keeping pets healthy",
           "body":  "My quick brown fox eats rabbits on a regular basis."
        }
     }
  ]
}

查询:
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

响应:
{
  "hits": [
     {
        "_id":      "2",
        "_score":   0.21509302,
        "_source": {
           "title": "Keeping pets healthy",
           "body":  "My quick brown fox eats rabbits on a regular basis."
        }
     },
     {
        "_id":      "1",
        "_score":   0.12713557,
        "_source": {
           "title": "Quick brown rabbits",
           "body":  "Brown rabbits are commonly seen."
        }
     }
  ]
}

2.5、 function_score查询

function_score 查询 是用来控制评分过程的终极武器,它允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分 _score 的目的。

三、全文查询

全文查询使您可以搜索分析的文本字段,例如电子邮件的正文。 使用在索引期间应用于字段的同一分析器来处理查询字符串。

3.1、match_all 查询

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

{ "match_all": {}}

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

3.2、match 查询

match搜索会对用户给出的关键词进行解析,首先会将其进行分词处理,分词后查询语句中的任意一个词项被匹配,文档就会检索到。其子参数包含:

  • query(必填):搜索关键文本
  • analyzer(可选):分词器,用于将query中的文本转换为一个个词项
  • fuzziness(可选):使用编辑距离技术进行模糊匹配
  • prefix_length:模糊匹配的起始单词数
  • operator(默认为OR):布尔逻辑,用来解释query中的词项,可选的参数有OR、AND
  • minimum_should_match(可选):返回的文档需要匹配的最小子串数
GET /shakespeare/_search
{
  "query": {
    "match": {
      "text_entry": {
        "query": "apple eye", 
        "operator": "and"
      }
    }
  }
}

查询包含text_entry字段中包含单词apple和eye的文档。

详情参见:match

3.3、multi_match 查询

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

GET /shakespeare/_search
{
  "query": {
    "multi_match": {
        "query": "apple",
        "fields": ["text_entry", "speaker"]
    }
  }
}

上述查询语句会在text_entryspeaker字段中查询包含单词apple的文档。

3.4、 match_phrase搜索

match_phrase搜索会首先把query进行分词处理,分词器可以自定义,同时文档还需满足以下条件才能被匹配:

  • 分词后所有词项都要出现在该字段中
  • 字段中所有的词项顺序要一致
GET /shakespeare/_search
{
  "query": {
    "match_phrase": {
      "text_entry": {
        "query": "apple eye", 
        "operator": "and"
      }
    }
  }
}

只有eye在apple后面的文档才可以被搜索到。

注:
要执行短语搜索而不是匹配单个术语,请使用match_phrase而不是match。 例如,以下请求仅匹配包含短语mill lane的地址:

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

3.5、match_phrase_prefix搜索

和match_phrase搜索类似,只不过match_phrase_prefix支持词项的前缀匹配,例如:

GET /shakespeare/_search
{
  "query": {
    "match_phrase_prefix": {
      "text_entry": {
        "query": "app"
      }
    }
  }
}

那么只会查找出某个词项的前缀为app的文档。

3.6、match_bool_prefix搜索

match_bool_prefix搜索于Elasticsearch 7.0推出,它将输入的文本通过指定的分词器来处理多个term,然后基于这些term进行bool query,除了最后一个term使用前缀查询,其它都是使用term_query

GET /shakespeare/_search
{
  "query": {
    "match_bool_prefix": {
        "query": "away as nim",
    }
  }
}

上述示例相当于查询包含单词away、as,并且有单词以nim开头的文档。

四、词项搜索

全文搜索在查询前会分析query字符串,而==词项搜索一般用于对存储的词项进行精确搜索,通常用于一些结构化数据,例如数字、枚举类型、日期类型等。

4.1、term

最基本的词项搜索,用于检索出某个text字段中包含指定单词的文档。例如:

GET /shakespeare/_search
{
  "query": {
    "term": {
      "text_entry": {
        "value": "apple",
        "boost": 1.0
      }
    }
  }
}

上述请求可以查找出text_entry字段中包含单词apple的文档。
value参数为需要查找的词项;
boost用来减少或增加相关性系数,默认为1.0,大于1.0会增加其相关性,小于1.0会减少其相关性。

4.2、terms

terms可以用来查找text字段中包含一个或多个指定单词的文档,例如:

GET /shakespeare/_search
{
  "query": {
    "terms": {
      "text_entry": [
        "eye","apple"
      ]
    }
  }
}

上述请求会查找出text_entry字段中满足以下要求的文档:

  • 包含单词eye,不包含单词apple
  • 包含单词apple,不包含单词eye
  • 包含单词eye和apple

同样,terms也可以使用boost来减少或增加相关性系数。

4.3、regexp

regexp检索可以查找出包含满足正则表达式单词的文档,例如:

GET /shakespeare/_search
{
  "query": {
    "regexp": {
      "text_entry": "pa.*"
    }
  }
}

上述请求会查找出text_entry字段中包含以pa开头的单词的文档。
需要注意regexp检索不支持包含^(行的开头)或$(行的结尾)的正则表达式。

regexp检索还支持以下几个参数:

  • flags:启用正则表达式可选运算符
  • max_determinized_states :查询所需的最大自动机状态数,用于限制过于复杂的正则表达式,默认10000。

4.4、prefix

prefix检索可以查找出包含指定字符串开头的单词的文档,例如:

GET /shakespeare/_search
{
  "query": {
    "prefix": {
      "text_entry": {
        "value": "par"
      }
    }
  }
}

上述请求会查找出包含以par开头的单词的文档。

4.5、range

range检索可以用于查询指定字段中在指定范围内的文档,例如:

GET /shakespeare/_search
{
  "query": {
    "range": {
      "line_id": {
        "gte": 160,
        "lte": 170
      }
    }
  }
}

上述请求可以查找出line_id字段大于等于160,小于等于170的文档。

被允许的操作符如下:

  • gt :大于
  • gte :大于等于
  • lt:小于
  • lte:小于等于

4.6、wildcard

wildcard检索用于单词的通配符检索,相当于一个简化版本的regexp检索,?用于匹配一个任意字符,*号用于匹配0个或者多个任意字符,相当于正则的.*。例如:

GET /shakespeare/_search
{
  "query": {
    "wildcard": {
      "text_entry": {
        "value": "pre*"
      }
    }
  }
}

上述请求可以查找出text_entry字段中包含以pre开头的单词的文档。此外wildcard也支持boost参数,作用类似。

4.7、fuzzy

fuzzy检索用于词项的近似检索,例如applx可以检索出包含apple单词的文档,两个单词的相似度通过编辑距离算法(Levenshtein)确定。例如:

GET /shakespeare/_search
{
  "query": {
    "fuzzy": {
      "text_entry": "applx"
    }
  }
}

上述请求可以查找出包含近似applx单词的文档,例如包含apple、apply的文档等。
fuzzy查询效率不高,需要消耗的资源比较大。

五、验证查询

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

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

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

{
  "valid" : false
}

5.1、错误信息

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

GET /gb/_validate/query?explain 
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}
explain 参数可以提供更多关于查询不合法的信息。

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

{
  "valid" : false,
  "error" : "org.elasticsearch.common.ParsingException: no [query] registered for [tweet]"
}

5.2、理解查询语句

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

ET /_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 powerfulmatch 查询被重写为两个针对 tweet 字段的 single-term 查询,一个single-term查询对应查询字符串分出来的一个term

当然,对于索引 us ,这两个 term 分别是 reallypowerful ,而对于索引 gb ,term 则分别是 reallipower 。之所以出现这个情况,是由于我们将索引 gbtweet 字段的分析器修改为 english 分析器。

补充

一个带请求体的 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
}

你可能感兴趣的:(Elasticsearch)