在ES中最重要的操作就是查询,上篇文章"ES中文档的基本操作"中,我们讨论了ES中对文档的一些常用操作,对于查询只是简单的描述了使用文档id进行查询的场景,然而在工作过程中,更多的场景是一些复杂的条件检索。接下来的这篇文章,我们就一起学习一下ES中的一些常用的检索方式。

什么是全文

ES相比其他数据库,比较擅长的是全文检索,在介绍全文检索前,我们有必要了解一下什么是全文,以及和全文相对应的精确值。

精确值:如它们听起来那样精确。例如日期或者用户 ID,但字符串也可以表示精确值,例如用户名或邮箱地址。对于精确值来说,Foo 和 foo 是不同的,2014 和 2014-09-15 也是不同的。

全文:是指文本数据(通常以人类容易识别的语言书写),例如一个推文的内容或一封邮件的内容。

查询分类

在ES中根据查询对象的类型可以分为:全文检索精确值查询

全文检索: 主要针对字段类型为String的数据进行检索。String类型的数据,在写入到ES中的过程中,会被分析器分析,分词,然后建立倒排索引,在进行查询的时候,也会对查询关键语句进行分词,然后对满足任意分词的文档进行返回。

精确查询: 主要针对非String类型的数据进行查询,也就是等值查询,只有完全包含被查询关键词的文档文档才会被返回。

按照查询条件和查询返回结果的匹配程度,查询可以分为:带评分的查询和不带评分的查询:

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

查询: 在进行全文查询时,和不评分的查询类似,也要去判断这个文档是否匹配,同时它还需要判断这个文档匹配的有多好(匹配程度如何)。匹配程度使用score值作为评判依据。 此查询的典型用法是用于查找以下文档:查找与 full text search 这个词语最佳匹配的文档,包含 run 这个词,也能匹配 runs 、 running 、 jog 或者 sprint。

查询常用语法

match: 可以进行全文检索也可以进行精确查询,因为此次查询是全文检索还是精确查询,取决于被查询的字段的类型。如果被查询的字段是全文字段,则进行全文查询,也即使对查询字符串进行分词处理后,在进行查询。如果被查询的字段是精确字段,则进行精确查询。

term: 查询只进行精确查询,也即是不对查询字符串进行分词处理,将查询字符串作为一个整体进行查询。

terms: 是对term进行了扩展,terms可以对一个字段进行多值查询,也就是查询字符串是一个数组,可以实现一次查询多个查询字符串的效果,而term一次只能查询一个查询字符串。

复杂查询,多条件联合查询:

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

"must":     { "match": { "title": "how to make millions" }}

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

"must_not": { "match": { "tag":   "spam" }}

should: 如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。也就是说,should 的条件不会影响查询返回的结果的数量,只会影响返回结果的score值。形如:

"should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ]

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

"filter": {
          "range": { "date": { "gte": "2014-01-01" }} 
        }

bool:使用bool可以组合多个查询,被组合的条件之间是与的关系。

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

总结来说,查询可以按照两个维度进行划分:是否进行分词,是否支持评分。

match 对于字符串域进行查询,支持分词和评分。而对于非字符串域,支持评分,不支持分词。

term 对于字符串域进行查询,不支持分词,支持评分,对于非字符串域,支持评分,不支持分词。

filter 对于字符串域进行查询,支持分词不支持评分,对于非字符串域,不支持评分,不支持分词。

查询验证

对于比较复杂的查询,查询语句可能会比较复杂,对于其中可能存在的语法错误,查询执行流程可能会比较难以理解,对于这个问题,ES提供了两个辅助校验的api:_validate 和 explain。

_validate 主要用来验证查询是否合法

GET /megacorp/employee/_validate/query
{
  "query":{
    "bool" : {
      "must":{"match" : {"about" : "rock climbing"}},
      "should": [ 
        {"match":{"age":15}},
        {"match": {"first_name": "John"}}
      ]
    }
  }
}

响应结果如下:

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

字段 vaild用来标识 验证是否通过。

_explain: 主要用来洞察内部查询流程,类似于mysql的explain。

如下,对about进行全文检索,会将"rock climbing"进行分词处理,分成"rock"和"climbing",然后对两个词进行查询。

GET /megacorp/employee/_validate/query?explain
{
  "query":{
    "match" : {"about" : "rock climbing"}
  }
}

响应结果如下:

{
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "valid" : true,
  "explanations" : [
    {
      "index" : "megacorp",
      "valid" : true,
      "explanation" : "+(about:rock about:climbing) #*:*"
    }
  ]
}

2.使用term进行精确查找,不对"rock climbing"进行分词处理,而是作为一个整体进行查询:

GET /megacorp/employee/_validate/query?explain
{
  "query":{
    "term": {"about" : "rock climbing"}
  }
}

响应结果如下:

{
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "valid" : true,
  "explanations" : [
    {
      "index" : "megacorp",
      "valid" : true,
      "explanation" : "+about:rock climbing #*:*"
    }
  ]
}

3.filter查询不对查询返回结果进行评分优化,但是会对查询字符串进行分词处理。

GET /megacorp/employee/_validate/query?explain
{
  "query":{
    "bool": {
      "filter": {"match": {"about" : "rock climbing"}}
    }
  }
}

响应结果如下:

{
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "valid" : true,
  "explanations" : [
    {
      "index" : "megacorp",
      "valid" : true,
      "explanation" : "+(#(about:rock about:climbing)) #*:*"
    }
  ]
}

从返回结果可以看出和1的差异在于filter的返回结果带有"#"。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐