首页 > 技术文章 > elasticsearch Query DSL

zenan 2019-06-04 14:01 原文

紧接上次入门篇,这一篇主要记录elasticsearch的结构化查询 Query DSL.
结构化查询是一种灵活的,多表现形式的查询语言。 Elasticsearch在一个简单的JSON接口中用结构化查询来展现Lucene绝大多数能力。 你应当在你的产品中采用这种方式进行查询。它使得你的查询更加灵活,精准,易于阅读并且易于debug。


single API

PUT /spider/ # 创建索引
POST /spider/ # 并不能用于创建索引,报错
PUT /spider/movies/1/ # 插入document,索引为1,若数据存在,则为覆盖更新
POST /spider/movies/ # 插入文档,随机生成索引
HEAD /spider/ # 用于验证 索引或document是否存在,若存在则返回200状态码,不存在则返回404.
HEAD /spider/movies/1/
DELETE /spider/ # 删除索引
DELETE /spider/movies/1/ # 删除文档

查询与过滤

首先我们要明确查询和过滤各自的优缺点,以及适用场景。

一条过滤语句会询问每个文档的字段值是否包含着特定值,查询语句会询问每个文档的字段值与特定值的匹配程度如何。这一点较好区分,一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且 按照相关性对匹配到的文档进行排序。 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。

性能上的差异

使用过滤语句得到的结果集 -- 一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。

查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗时,并且查询结果也不可缓存。
幸亏有了倒排索引,一个只匹配少量文档的简单查询语句在百万级文档中的查询效率会与一条经过缓存 的过滤语句旗鼓相当,甚至略占上风。 但是一般情况下,一条经过缓存的过滤查询要远胜一条查询语句的执行效率。
过滤语句的目的就是缩小匹配的文档结果集,所以需要仔细检查过滤条件。

适用场景

原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句


查询、过滤语句

term过滤

term主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型):

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

terms 过滤

terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:

{
    "terms": {
        "tag": [ "Java", "Python", "Php" ]
        }
}

range过滤

range过滤允许我们按照指定范围查找一批数据

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

exists 和 missing 过滤

exists 和 missing 过滤可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的IS_NULL条件

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

bool 过滤

bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含一下操作符:

  1. must :: 多个查询条件的完全匹配,相当于 and。
  2. must_not :: 多个查询条件的相反匹配,相当于 not。
  3. should :: 至少有一个查询条件匹配, 相当于 or。
{
    "bool": {
        "must":     { "term": { "folder": "inbox" }},
        "must_not": { "term": { "tag":    "spam"  }},
        "should": [
                    { "term": { "starred": true   }},
                    { "term": { "unread":  true   }}
        ]
    }
}

bool 查询

bool 查询与 bool 过滤相似,用于合并多个查询子句。不同的是,bool 过滤可以直接给出是否匹配成功, 而bool 查询要计算每一个查询子句的 _score

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

bool嵌套查询

{
  "bool" : {
    "should" : [
      { "term" : {"productID" : "KDKE-B-9947-#kL5"}}, 
      { "bool" : { 
        "must" : [
          { "term" : {"productID" : "JODL-X-1937-#pV7"}}, 
          { "term" : {"price" : 30}} 
        ]
      }}
    ]
  }
}

match_all 查询

使用match_all 可以查询到所有文档,是没有查询条件下的默认语句。

{
    "match_all": {}
}

match 查询

match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析match一下查询字符

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

multi_match 查询

multi_match查询允许你做match查询的基础上同时搜索多个字段

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

match_phrase

短语查询,full text search 是一个词组,意味着三个词的位置是连续且有顺序

{
    "match_phrase": {
        "title":    "full text search",
    }
}

设置slop词组间隔

{
    "match_phrase": {
        "title": {
            "query": "full text search",
            "slop":1
        }
    }
}

phrase_prefix 查询

与词组中最后一个词条进行前缀匹配。

{
  "query": {
    "match_phrase_prefix": {
      "title": {
        "query": "秦时"
      }
    }
  },
  "from":0,
  "size":5
}

regexp查询

通配符查询

{
    "query": {
        "regexp": {
            "title": "W[0-9].+" 
        }
    }
}

过滤查询

查询语句和过滤语句可以放在各自的上下文中,filtered已弃用,用bool代替

{
  "query": {
    "bool": {
      "must": {"match": {"text": "quick brown fox"} },
      "filter": {"term": {"status": "published"} }
    }
  }
  "from":0, # 从0开始
  "size":10, # 显示条数
  "sort":{"publish_date" :{"order":"desc"}}
}

返回指定字段

{
    "_source":["id","name"],
    "query":{...}
}

推荐阅读