【ES】ElasticSearch 结构化查询和过滤

[TOC]

一、DSL

在 ElasticSearch 中,提供了功能十分丰富、多种表现形式的查询语言—— DSL 查询。

Query DSL 又叫结构化查询,使用 JSON 格式的请求体 与 ElasticSearch 交互,使查询语句更灵活、更精确、更易读且易调试。

使用结构化查询,你需要传递 query 参数:

GET /_search
{
    "query": YOUR_QUERY_HERE
}

主要包含两种类型的查询语句:叶子查询语句复合查询语句

1.1、叶子查询语句

这种查询可以单独使用,针对指定的字段查询指定的值,例如:match, term, range 等。

一个叶子查询语句一般使用这种结构:

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

或指向一个指定的字段:

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

例如,可以使用 match 查询子句用来找寻在 tweet 字段中找寻包含 elasticsearch 的成员:

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

1.2、复合查询语句

这种查询可以合并其他的叶子查询或复合查询,从而实现非常复杂的查询逻辑。

例如,bool 子句允许合并其他的合法子句,mustmust_not 或者 should

{
    "bool": {
        "must":     { "match": { "tweet": "elasticsearch" }},
        "must_not": { "match": { "name":  "mary" }},
        "should":   { "match": { "tweet": "full text" }}
    }
}

二、Query DSL 和 Filter DSL

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

查询与过滤语句非常相似,但是它们由于使用目的不同而稍有差异。

2.1、Query DSL

在上下文查询语境中,查询语句会询问文档与查询语句的匹配程度,它会判断文档是否匹配并计算相关性评分(_score)的值。

例如:

  • 查找与 full text search 这个词语最佳匹配的文档
  • 查找包含单词 run,但是也包含runs, running, jogsprint的文档
  • 同时包含着 quick, brownfox--- 单词间离得越近,该文档的相关性越高
  • 标识着 lucene, searchjava--- 标识词越多,该文档的相关性越高

一条查询语句会计算每个文档与查询语句的相关性,然后给出一个相关性评分 _score,并且按照相关性对匹配到的文档进行排序。

2.2、Filter DSL

在上下文过滤语境中,查询语句主要解决文档是否匹配的问题,而不会在意匹配程度(相关性评分)。

例如:

  • created 的日期范围是否在 20132014 ?
  • status 字段中是否包含单词 "published" ?
  • lat_lon 字段中的地理位置与目标点相距是否不超过10km ?

2.3、比较

相关度:

  • filter —— 只根据搜索条件过滤出符合的文档,将这些文档的评分固定为1,忽略 TF/IDF 信息,不计算相关度分数;
  • query —— 先查询符合搜索条件的文档, 然后计算每个文档对于搜索条件的相关度分数,再根据评分倒序排序。

性能:

  • filter 性能更好,无排序 —— 不计算相关度分数,不用根据相关度分数进行排序,同时 ES 内部还会缓存(cache)比较常用的 filter 的数据 (使用bitset <0或1> 来记录包含与否);
  • query性能较差, 有排序 —— 要计算相关度分数, 要根据相关度分数进行排序, 并且没有cache功能。

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

在进行搜索时,常常会在查询语句中,结合查询和过滤来达到查询目的:

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

三、重要的查询过滤语句

3.1、match

match查询是一个标准查询,不管全文本查询还是精确查询基本上都要用到它。

如果使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析查询字符:

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

如果用match下指定了一个确切值,在遇到数字,日期,布尔值或者not_analyzed 的字符串时,它将搜索给定的值:

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

提示: 做精确匹配搜索时,最好用过滤语句,因为过滤语句可以缓存数据。

3.2、multi_match

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

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

3.3、match_phrase

短语查询,精确匹配。查询a red会匹配包含a red短语的,而不会进行分词查询,也不会查询出包含a 其他词 red这样的文档。

{
    "query": {
        "match_phrase": {
            "ad": "a red"
        }
    }
}

3.4、match_all

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

{
    "match_all": {}
}

此查询常用于合并过滤条件。 比如说需要检索所有的邮箱,所有的文档相关性都是相同的,所以得到的_score为1。

3.5、term

term主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(即不进行分词器分析,文档中必须包含整个搜索的词汇):

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

3.6、terms

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

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

3.7、range

range允许按照指定范围查找一批数据:

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

范围操作符包含:

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

3.8、exists

用于查找那些指定字段中有值或无值的文档。

指定title字段有值:

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

指定title字段无值:

{
    "query": {
        "bool": {
            "must_not": {
                "exists": {
                    "field": "group"
                }
            }
        }
    }
}

注:missing 查询无值已经被取消。

3.9、bool

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

  • must :: 多个查询条件的完全匹配,相当于 and
  • should :: 至少有一个查询条件匹配,相当于 or
  • must_not :: 多个查询条件的相反匹配,相当于 not,忽略相关性评分
  • filter:: 必须匹配,忽略相关性评分
POST /_search
{
    "query": {
        "bool" : {
            "must" : {
              "term" : { "last_name" : "smith" }
            },
            "filter": {
              "term" : { "info.interests" : "musics" }
            },
            "must_not" : {
              "range" : {
                "info.age" : { "gte" : 10, "lte" : 25 }
              }
            },
            "should" : [
              { "term" : { "full_name" : "john" } },
              { "term" : { "full_name" : "smith" } }
            ]
        }
    }
}

提示: 如果bool 查询下没有must子句,那至少应该有一个should子句。但是 如果有must子句,那么没有should子句也可以进行查询。

四、验证查询

查询语句可以变得非常复杂,特别是与不同的分析器和字段映射相结合后,就会有些难度。

validate API 可以验证一条查询语句是否合法。

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

请求的返回值说明这条语句是非法的:

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

想知道语句非法的具体错误信息,需要加上 explain 参数:

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

explain 参数可以提供语句错误的更多详情,很显然,这里把 query 语句的 match 与字段名位置弄反了。

五、参考资料

ES 权威指南

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容