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 官方文档(英文)