Elasticsearch查询和聚合基本语法

1.概述

Elasticsearch主要的查询语法包括URI查询和body查询,URI比较轻便快速,而body查询作为一种json的格式化查询,可以有许多限制条件。本文主要介绍结构化查询的query,filter,aggregate的使用,本文使用的ES版本为6.5.4,中文分词器使用的ik,安装和使用可以参考:
Elasticsearch 安装和使用
Elasticsearch中ik分词器的使用
在ES建立以下索引,并且导入数据

PUT /news
{
        "aliases": {
            "news": {}
        },
        "mappings":{
            "news": {
                "dynamic": "false",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "title": {
                        "analyzer": "ik_max_word",
                        "type": "text"
                    },
                    "summary": {
                        "analyzer": "ik_max_word",
                        "type": "text"
                    },
                    "author": {
                        "type": "keyword"
                    },
                    "publishTime": {
                        "type": "date"
                    },
                    "modifiedTime": {
                        "type": "date"
                    },
                    "createTime": {
                        "type": "date"
                    },
                    "docId": {
                        "type": "keyword"
                    },
                    "voteCount": {
                        "type": "integer"
                    },
                    "replyCount": {
                        "type": "integer"
                    }
                }
            }
        },
        "settings":{
            "index": {
                "refresh_interval": "1s",
                "number_of_shards": 3,
                "max_result_window": "10000000",
                "mapper": {
                    "dynamic": "false"
                },
                "number_of_replicas": 1
                }
                }
            }
        }
    }

2.查询

2.1 一个查询的例子

一个简单的查询例子如下,查询主要分为query和filter,这两种类型的查询结构都在query里面,剩下的sort标识排序,size和from用来翻页,_source用来指定召回document返回哪些字段。
查询请求:

GET /news/_search
{
  "query": {"match_all": {}}, 
  "sort": [
    {
      "publishTime": {
        "order": "desc"
      }
    }
  ],
  "size": 2,
  "from": 0,
  "_source": ["title", "id", "summary"]
}

返回结果:

{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 204,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "news",
        "_type" : "news",
        "_id" : "228",
        "_score" : null,
        "_source" : {
          "summary" : "据陕西高院消息,6月11日上午,西安市中级人民法院二审公开开庭宣判了陕西省首例“套路贷”涉黑案件——韩某某等人非法放贷一案,法院驳回上诉,维持原判。西安市中级人",
          "id" : 228,
          "title" : "陕西首例套路贷涉黑案宣判:团伙对借款人喷辣椒水"
        },
        "sort" : [
          1560245097000
        ]
      },
      {
        "_index" : "news",
        "_type" : "news",
        "_id" : "214",
        "_score" : null,
        "_source" : {
          "summary" : "网易娱乐6月11日报道6月11日,有八卦媒体曝光曹云金与妻子唐菀现身天津民政局办理了离婚手续。对此,网易娱乐向曹云金经纪人求证,得到了对方独家回应:“确实是离婚",
          "id" : 214,
          "title" : "曹云金承认已离婚:和平离婚 有人恶意中伤心思歹毒"
        },
        "sort" : [
          1560244657000
        ]
      }
    ]
  }
}

返回结果中took表示耗时,_shards表示分片信息,当前index有3个分片,并且3个分片都工作正常,hits表示命中的结果,total表示命中总数,max_score表示最大的分值,hits表示命中的具体document。
查询分为精确过滤(filter)和全文搜索(query)两种:精确过滤容易被缓存,因此它的执行速度非常快。

2.2 Filter

2.2.1 term

term 查找可以精确的找到符合条件的记录,其中的FIELD标识索引中的字段,VALUE表示需要查询的值。基本的查询语句如下:

{
  "term": {
    "FIELD": {
      "value": "VALUE"
    }
  }
}

比如,查询source为中新经纬的新闻,那么可以这么使用:

GET /news/_search
{
  "query": {"term": {
    "source": {
      "value": "中新经纬"
    }
  }}
}

2.2.2 bool

当需要多个逻辑组合查询的时候,可以使用bool来组各逻辑。bool可以包含

{
   "bool" : {
      "must" :     [],
      "should" :   [],
      "must_not" : [],
   }
}

must:搜索的结果必须匹配,类似SQL的AND
must_not: 搜索的结果必须不匹配,类似SQL的NOT
should: 搜索的结果至少匹配到一个,类似SQL的OR
当我们需要查source为中新经纬,并且id为4或者75的新闻,可以这样使用:

GET /news/_search
{
  "query": {
    "bool": {
    "must": [
    {"term": {
      "source": {
        "value": "中新经纬"
      }
    }}
  ],
  "should": [
    {"term": {
      "id": {
        "value": "4"
      }
    }},
    {"term": {
      "id": {
        "value": "75"
      }
    }}
  ],
  "minimum_should_match": 1
  }}
}

其中的minimun_should_match用来指定should内的条件需要匹配多少个,默认是0,0的情况下should内容只参与打分,不做倒排过滤。

2.2.3 terms

对于上面查找多个精确值的情况,可以使用terms,比如查找id是4或者75的文章,可以这么使用:

GET /news/_search
{
  "query": {"terms": {
    "id": [
      "4",
      "75"
    ]
  }}
}

2.2.4 range

对于需要用到范围的查询,可以使用range,range和term作用的位置相同,比如查找id从1到10的文章

GET /news/_search
{
  "query": {"range": {
    "id": {
      "gte": 1,
      "lte": 10
    }
  }}
}

其中:

  • gt: > 大于(greater than)
  • lt: < 小于(less than)
  • gte: >= 大于或等于(greater than or equal to)
  • lte: <= 小于或等于(less than or equal to)

2.2.5 exists

es中可以使用exists来查找某个字段存在或者不存在的document,比如查找存在author字段的文档,也可以在bool内配合should和must_not使用,就可以实现不存在或者可能存在的查询。

GET /news/_search
{
  "query": {
    "exists": {"field": "author"}
  }
}

2.3 Query

和filter的精确匹配不一样,query可以进行一些字段的全文搜索和搜索结果打分,es中只有类型为text的字段才可以被分词,类型为keyword虽然也是字符串,但只能作为枚举,不能被分词,text的分词类型可以在创建索引的时候指定。

2.3.1 match

当我们想要搜某个字段的时候可以使用match,比如查找文章中出现体育的新闻,可以这样查询

GET /news/_search
{
  "query": {
    "match": {"summary":"体育" }
  }
}

在match中我们还可以指定分词器,比如指定分词器为ik_smart对输入的词尽量分大颗粒,此时召回的就是含有进口红酒的document,如果指定分词器为ik_max_word则分出的词颗粒会比较小,会召回包含口红和红酒的document

{
    "match": {
      "name": {
        "query": "进口红酒",
        "analyzer": "ik_smart"
      }
    
    }
  }

对于query的文本有可能分出好几个词,这个时候可以用and连接,表示多个词都命中才被召回,如果用or连接,则类似should可以控制,至少命中多少个词才被召回。比如搜索包含体育新闻内容的新闻,下面这个查询只要包含一个体育或者新闻的document都会被召回

GET /news/_search
{
  "query": {
    "match": {
      "summary": {
        "query": "体育新闻",
        "operator": "or",
        "minimum_should_match": 1
      }
    }
  }
}

2.3.2 multi_match

当需要搜索多个字段的时候,可以使用multi_match进行查询,比如在title或者summary中搜索含有新闻关键词的document

GET /news/_search
{
  "query": {
    "multi_match": {
      "query": "新闻",
      "fields": ["title", "summary"]
    }
  }
}

2.4 组合查询

    有了全文搜索和过滤的这些字段,配合bool就可以实现复杂的组合查询
GET /news/_search
{
  "query": {"bool": {
    "must": [
      {"match": {
        "summary": {
          "boost": 1,
          "query": "长安"
        }
      }
      },
      {
        "term": {
          "source": {
            "value": "中新经纬",
            "boost": 2
          }
        }
      }
    ],
    "filter": {"bool": {
      "must":[
        {"term":{
          "id":75
        }}
        ]
    }}
  }}
}

上面请求bool中的must、must_not、should可以使用term,range、match。这些默认都是参与打分的,可以通过boost来控制打分的权重,如果不想要某些查询条件参与打分,可以在bool中添加filter,这个filter中的查询字段都不参与打分,而且查询的内容可以被缓存。

3.聚合

聚合的基本格式为:

GET /news/_search
{
  "size": 0,
  "aggs": {
    "NAME": {
      "AGG_TYPE": {}
    }
  }
}

其中NAME表示当前聚合的名字,可以取任意合法的字符串,AGG_TYPE表示聚合的类型,常见的为分为多值聚合和单值聚合

3.1 一个聚合的例子

GET /news/_search
{
 "size": 0, 
  "aggs": {
    "sum_all": {
      "sum": {
        "field": "replyCount"
      }
    }
  }
}

上面的例子表示查询当前库里面的replayCount的和,返回结果:

{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 204,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "sum_all" : {
      "value" : 390011.0
    }
  }
}

返回结果中默认会包含命中的document,所以需要把size指定为0,结果中的sum_all为请求中指定的名字。
Elasticsearch中的聚合类型主要分为Metrics和Bucket

3.2 Metrics

metrics主要是一些单值的返回,像avg、max、min、sum、stats等这些计算。

3.2.1 max

比如计算index里面最多的点赞数是多少,可以这样使用,max、avg、min、sum使用类似

GET /news/_search
{
  "size": 0,
  "aggs": {
    "max_replay": {
      "max": {
        "field": "replyCount"
      }
    }
  }
}

3.2.2 stats

常用的一些统计信息,可以用stats,比如查看某个字段的,总数,最小值,最大值,平均值等,比如查看document中新闻回复量的基本情况,stats就是统计的综合使用。比如请求如下:

GET /news/_search
{
 "size": 0, 
  "aggs": {
    "cate": {
      "stats": {
        "field": "replyCount"
      }
    }
  }
}

返回结果为:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 204,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "cate" : {
      "count" : 202,
      "min" : 0.0,
      "max" : 32534.0,
      "avg" : 1930.7475247524753,
      "sum" : 390011.0
    }
  }
}```

能返回基本的统计信息

3.3 Bucket

桶类似于sql里面的group by,使用Bucket会对内容进行分桶

3.3.1 terms

利用terms分桶之后,可以查看数据的分布,比如可以查看index中一共有多少个source,每个source有多少文章,size是用来指定返回最多的几个分类,可以这样使用:

GET /test_ratings_v1/_search
{
  "size": 0, 
  "aggs": {
    "myterms": {
      "terms": {
        "field": "productId",
        "size": 10
      }
    }
  }
}

表示对productId进行分桶,返回每个桶的个数。外层的size表示不返回命中的数据,只返回聚合结果

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 10002,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "myterms" : {
      "doc_count_error_upper_bound" : 69,
      "sum_other_doc_count" : 5701,
      "buckets" : [
        {
          "key" : 452966,
          "doc_count" : 1168
        },
        {
          "key" : 452734,
          "doc_count" : 608
        },
        {
          "key" : 453353,
          "doc_count" : 592
        },
        {
          "key" : 453231,
          "doc_count" : 522
        },
        {
          "key" : 453275,
          "doc_count" : 387
        },
        {
          "key" : 452639,
          "doc_count" : 273
        },
        {
          "key" : 453104,
          "doc_count" : 236
        },
        {
          "key" : 452679,
          "doc_count" : 180
        },
        {
          "key" : 453152,
          "doc_count" : 169
        },
        {
          "key" : 452640,
          "doc_count" : 165
        }
      ]
    }
  }
}

返回结果key表示productId,doc_count表示数目

3.3.2 range

除了按值进行聚合,还可以按范围进行聚合,比如,求rating的值3-4和小于3,大于4的统计,可以这样写

{
  "size": 0, 
  "aggs": {
    "myterms": {
      "range": {
        "field": "rating",
        "ranges": [
          {
            "from": 3,
            "to": 4
          },
          {
            "from": 4
          },
          {
            "to":3
          }
        ]
      }
    }
  }
}

得到返回结果:

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 10002,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "myterms" : {
      "buckets" : [
        {
          "key" : "*-3.0",
          "to" : 3.0,
          "doc_count" : 1101
        },
        {
          "key" : "3.0-4.0",
          "from" : 3.0,
          "to" : 4.0,
          "doc_count" : 1464
        },
        {
          "key" : "4.0-*",
          "from" : 4.0,
          "doc_count" : 7436
        }
      ]
    }
  }
}

可以看到小于3的有1101个,3-4的有1464个,而大于4的有7436个。

3.4 组合聚类

GET /news/_search
{
  "size": 0,
  "aggs": {
    "myterms": {
      "terms": {
        "field": "source",
        "size": 100
      },
      "aggs": {
        "replay": {
          "terms": {
            "field": "replyCount",
            "size": 10
          }
        },
        "avg_price": { 
            "avg": {
                  "field": "voteCount"
               }
            }
      }
    }
  }
}

上面代码首先对source分桶,在每个souce类型里面在对replayCount进行分桶,并且计算每个source类里面的voteCount的平均值
由于返回结果比较大,这里只给出返回的某一个桶结果:

{
          "key" : "中国新闻网",
          "doc_count" : 16,
          "avg_price" : {
            "value" : 1195.0
          },
          "replay" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 4,
            "buckets" : [
              {
                "key" : 0,
                "doc_count" : 3
              },
              {
                "key" : 1,
                "doc_count" : 1
              },
              {
                "key" : 5,
                "doc_count" : 1
              },
              {
                "key" : 32,
                "doc_count" : 1
              },
              {
                "key" : 97,
                "doc_count" : 1
              },
              {
                "key" : 106,
                "doc_count" : 1
              },
              {
                "key" : 133,
                "doc_count" : 1
              },
              {
                "key" : 155,
                "doc_count" : 1
              },
              {
                "key" : 156,
                "doc_count" : 1
              },
              {
                "key" : 248,
                "doc_count" : 1
              }
            ]
          }
        }

4.查询和聚合的组合使用

有了查询和聚合,我们就可以对查询的结果做聚合,比如我想查看summary中包含体育的新闻都是那些来源网站,就可以像下面这样查询:

GET /news/_search
{
 "size": 0, 
 "query": {"bool": {"must": [
   {"match": {
     "summary": "体育"
   }}
 ]}}, 
  "aggs": {
    "cate": {
      "terms": {
        "field": "source"
      }
    }
  }
}

5.总结

Elasticsearch的查询语法比较复杂和多样,这里只例举了常见的一些查询和聚合,详细可以参考官方文档和权威指南,权威指南由于是中文,阅读比较方便,但是是2.x的内容,官方文档有对应版本的内容,内容比较新,建议阅读官方文档。
Elasticsearch权威指南(中文)
Elasticsearch6.5 官方文档(英文)
更多精彩内容,请关注公众号

公众号二维码.jpg

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

推荐阅读更多精彩内容