ES的各种查询构建(部分Java代码)

1. ES分词器

在学习查询语法之前有必要了解一下ES的分词器。因为这是ES搜索引擎最大的特点了,它查询速度之所以这么快也和这个有很大关系。但更重要的是,如果我们不了解ES会对你存储的索引文本或者查询query进行分词,后面的语法你将很难理解。

对于我们存入ES索引(Index)中的各个字段(Term),ES内部都会有一个分词器对其进行分词,然后将这个分词结果存储起来(可以把它记作分词库),方便未来的查询使用,并且查询时查询条件是和分词库做对比,并不是和原文做对比,这一点很重要!

这个分词器我们也可以直接调用,只要访问其_analyze即可:

 POST http://ip:prot/_analyze
{
  "analyzer": "standard",
  "text": "This is a test doc"
}

这里需要指定一个分词器,ES默认的分词器是standard,不过只支持英文分词,如果你用它来对中文进行分词的话会直接按字拆分,有一些中文分词器可以下载使用,像ik或者jieba之类的。

我们用上述的分词请求返回的结果如下:

{
	"tokens": [
		{
			"token": "this",
			"start_offset": 0,
			"end_offset": 4,
			"type": "<ALPHANUM>",
			"position": 0
		},
		{
			"token": "is",
			"start_offset": 5,
			"end_offset": 7,
			"type": "<ALPHANUM>",
			"position": 1
		},
		{
			"token": "a",
			"start_offset": 8,
			"end_offset": 9,
			"type": "<ALPHANUM>",
			"position": 2
		},
		{
			"token": "test",
			"start_offset": 10,
			"end_offset": 14,
			"type": "<ALPHANUM>",
			"position": 3
		},
		{
			"token": "doc",
			"start_offset": 15,
			"end_offset": 18,
			"type": "<ALPHANUM>",
			"position": 4
		}
	]
}

可以看到我们的句子就被分词器这样做好了分词,还有偏移量之类的信息。

这里只是带大家看一下ES是如何做到分词的,我们平时查询的话自己是用不到分词的,这些分词都是在保存索引时ES自动分好存储起来的。

2. ES查询

2.1 单字段查询

单字段查询就类似于SQL语句里只有一个条件的查询,比如说

select * from user where id = 1

2.1.1 match查询

ES的查询有一个很大的特点就是分词。所以大家在使用ES的过程中脑子要始终有这么一个意识,你要查找的text是通过分词器分过词的,所以你去匹配的实际上是一个个被分词的片段。而你搜索的query也有可能会被分词,match就是一种会将你搜索的query进行分词的查询方法(但term不会对查询条件分词)。我们结合例子来看!

比如我们要查询的索引结构如下:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "王大",
        "num": 20
    }
}

_index代表索引名称,_id代表该条数据唯一id,_source代表该条数据具体的结构。

这里我们通过bookName字段来查询。

输入query语句如下:

GET http://ip:prot/textbook/_search
{
  "query": {
    "match": {
      "bookName":"test"
    }
  }
}

该条语句代表用match方式搜索索引为textbook中bookName可以匹配到test的语句。因为:

"bookName": "This is a test doc"

原文被分词器分词后包含test这个词语,所以可以正常被匹配出来。

这个例子比较简单,我们换个复杂一点的例子:

GET http://ip:prot/textbook/_search
{
  "query": {
    "match": {
      "bookName":"my test"
    }
  }
}

大家认为这个能否被匹配出来呢?

原文中根本就没有my这个词语,那怎么被匹配出来?但实际上是可以匹配出来的。

原因是match查询里,会对你查询的query也进行分词,也就是会将你的"my test"进行分词,得到my与test两个词语,然后用这两个词语分别去匹配文本,发现虽然my匹配不到,但是test可以匹配到,所以依然可以查出来。这个和我们传统的搜索方式确实存在差异,大家要注意。

那这种搜索方式存在的价值是什么呢?其实还有蛮大用处的。比如我们的ES库存储的是很多的英文好词好句,然后用户想提高自己的英文写作,因此想搜索出一些比较好的表达加在自己的文章中,那这个时候对于用户来讲,严格的匹配方式大概率什么都搜不到,但是像match这样的搜索方式便非常合适。例如有个好句是这样的:

If at first you don't succeed, try again.

然后用户用下面的方式搜索:

If you don't success

用match就可以很好的匹配出来。

2.1.2 match_phrase查询

既然match的限制比较小,那如果我们需要这个限制更强一点用什么方式呢?match_phrase便是一个比较不错的选择。match_phrase和match一样也是会对你的搜索query进行分词,但是,不同的是它不是匹配到某一处分词的结果就算是匹配成功了,而是需要query中所有的词都匹配到,而且相对顺序还要一致,而且默认还是连续的,如此一来,限制就更多了。我们还是举个例子。比如还是刚刚的索引数据:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "王大",
        "num": 20
    }
}

如果我们还用刚刚的方式搜索:

GET http://ip:prot/textbook/_search
{
  "query": {
    "match_phrase": {
      "bookName":"my test"
    }
  }
}

这次是匹配不到结果的。那么怎样才能匹配到结果呢?只能是搜索原文中的连续字串:

GET http://ip:prot/textbook/_search
{
  "query": {
    "match_phrase": {
      "bookName":"is a test"
    }
  }
}

这样是可以匹配到结果的。但是如此一来限制可能太大了一点,所以官方还给了一个核心参数可以调整搜索的严格程度,这个参数叫slop,我们举个例子:

GET http://ip:prot/textbook/_search
{
  "query": {
    "match_phrase": {
      "bookName":{
        "query":"is test",
        "slop":1
      }
    }
  }
}

比如我们将slop置为1,然后搜索"is test",虽然is test中间省略了一个词语"a",但是在slop为1的情况下是可以容忍你中间省略一个词语的,也可以搜索出来结果。以此类推,slop为2就可以省略两个词语了。大家可以根据自己的实际情况进行调整。

另外我们可以发现,如果在搜索时添加了辅助参数(比如slop)我们搜索格式的层级要往下扩展一层,之前的

"bookName":"my test"

要改为:

"bookName":{
    "query":"is test",
    "slop":1
}

我们注意一下就好了。

2.1.3 multi_match查询

有了前面的基础,multi_match比较好理解。实际上就是可以从多个字段中去寻找我们要查找的query:

GET http://ip:prot/textbook/_search
{
  "query": {
    "multi_match": {
        "query" : "王大",
        "fields" : ["bookName", "author"]
    }
  }
}

比如这里我们是从bookName和author两个字段里去寻找老坛,虽然bookName没有,但是author可以匹配到,那也可以找到数据。所以本质上就是对bookName和author分别做了一次match:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "王大",
        "num": 20
    }
}

2.1.4 term查询(等值查询)

term查询也是比较常用的一种查询方式,它和match的唯一区别就是match需要对query进行分词,而term是不会进行分词的,它会直接拿query整体和分词的原文(分词库)进行匹配。

还要特别注意查询字段的类型,如果字段是文本类型,有两种选择:text类型、keyword类型

  • text类型(文本类型)会使用默认分词器分词,也就是存入的数据会先进行分词,然后将分完词的词组存入索引,当然你也可以为他指定特定的分词器。
  • keyword类型(关键字类型),那么默认就不会对其进行分词,原样存储。当一个字段需要按照精确值进行过滤、排序、聚合等操作时, 就应该使用keyword类型。keyword类型检索,直接被存储为了二进制,检索时我们直接匹配,不匹配就返回false。所以精确匹配可以用keyword。

而对于term这个查询,term不会对查询条件分词,不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配,也就是精确查找.

一般在代码里使用基本都是使用term,match使用的很少

GET http://ip:prot/textbook/_search
{
  "query": {
    "term": {
      "bookName": "This is a test doc"
    }
  }
}

当我们用这种方式进行搜索时,明明要搜索的和被搜索的文本一模一样,确就是搜不出来。**这就是因为我们去搜的实际上并不是原文本身,而是被分词的原文,在原文被分好的每一个词语里,没有一个词语是:“This is a test doc”,那自然是什么都搜不到了。**所以在这种情况下就只能用某一个词进行搜索才可以搜到:

GET http://ip:prot/textbook/_search
{
  "query": {
    "term": {
      "bookName": "This"
    }
  }
}

这里说明下:我们用的是ES默认的分词器standard,所以"This is a test doc"这个字符串的分词结果是:This 、is、a、 test、doc。但如果换一个分词器比如说:ix_max_word,此时分词器里就会有这个词:“This is a test doc”,在这种情况下我们用term就可以查到。

2.1.5 terms查询(多值查询)

多条件查询类似Mysql里的IN查询

terms查询事实上就是多个term查询取并集:

GET http://ip:prot/textbook/_search
{
  "query": {
    "terms": {
      "bookName": ["This", "is"]
    }
  }
}

结果:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "老大",
        "num": 20
    }
}

再看例子:

GET http://ip:prot/textbook/_search
{
  "query": {
    "terms": {
      "bookName": ["This", "my"]
    }
  }
}

结果还是可以查询出来的,my不匹配,但This匹配。

2.1.6 fuzzy查询

fuzzy是ES里面的模糊搜索,它可以借助term查询来进行理解。fuzzy和term一样,也不会将query进行分词,但是不同的是它在进行匹配时可以容忍你的词语拼写有错误,至于容忍度如何,是根据参数fuzziness决定的。fuzziness默认是2,也就是在默认情况下,fuzzy查询容忍你有两个字符及以下的拼写错误。即如果你要匹配的词语为test,但是你的query是text,那也可以匹配到。这里无论是错写多写还是少写都是计算在内的。我们同样还是举例说明。

对于索引数据:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "王大",
        "num": 20
    }
}

如果查询语句为:

GET http://ip:prot/textbook/_search
{
  "query": {
    "fuzzy": {
      "bookName":"text"
    }
  }
}

这时肯定是用text来匹配原文中的每一个词,发现text和test最为接近,但是有一个字符的差异,在默认fuzziness为2的情况下,依然可以匹配出来。

当然这个fuzziness是可以调的,比如:

GET http://ip:prot/textbook/_search
{
  "query": {
    "fuzzy": {
      "bookName":{
        "value":"texts",
        "fuzziness":1
      }
    }
  }
}

在容忍度为1的情况下,如果你想查texts就查不到结果了。

2.1.7 prefix-前缀查询

前缀查询类似于SQL中的模糊查询。

select * from user where user_name like '王%'

ES查询就是:

{
	"query": {
		"prefix": {
			"author": {
				"value": "王",
			}
		}
	}
}

要注意几点:

  • 不计算相关度评分

  • 性能较差

  • 前缀搜索匹配的是分词后的词项

  • 前缀搜索没有缓存

  • 前缀搜索尽可能把前缀长度设置的更长

2.1.8 wildcard-通配符查询

通配符查询,与前缀查询类似,都属于模糊查询的范畴,但通配符显然功能更强。

类似于SQL:

select * from user where user_name like '王%二'

ES语句:

{
	"query": {
		"wildcard": {
			"author": {
				"value": "王*二",
			}
		}
	}
}

2.1.9 range查询(范围查询)

range查询时对于某一个数值字段的大小范围查询,比如我这里特意所加的num字段就是这个时候派上用场的。range的语法设计到了一些关键字:

  1. gte:大于等于
  2. gt:大于
  3. lt:小于
  4. lte:小于等于
GET http://ip:prot/textbook/_search
{ 
  "query": {
    "range": { 
      "num": { 
          "gte":20, 
          "lt":30 
      } 
    }
  } 
}

比如这样的条件就是去查找字段num大于等于20小于30的数据

那我们的数据便可以被查询到:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "王大",
        "num": 20
    }
}

2.1.10 排序和分页

排序和分页的条件是和query平级去写的

GET http://ip:prot/textbook/_search
{
    "query":{
        "match":{
            "bookName":"王大"
        }
    },
    "from":0,
    "size":100,
    "sort":{
        "num":{
            "order":"desc"
        }
    }
}

这里关于分页的语句是:

"from":0,
"size":100,

它代表的意思是按照页容量为100进行分页,取第一页。

关于排序的语句是:

"sort":{
    "num":{
        "order":"desc"
    }
}

它需要指定一个字段,然后根据这个字段进行升序或降序。这里我们根据num来进行降序排序,如果想升序就把order的值改为asc就好了。

2.2 多字段查询

多字段查询就类似于SQL语句里有多个条件的查询,比如说

select * from user where user_name = "王" and (age = 10 or age = 20) 

2.2.1 bool查询

bool查询是上面查询的一个综合,它可以用多个上面的查询去组合出一个大的查询语句,它也有一些关键字:

  1. must:代表且的关系,也就是必须要满足该条件 (类似于and)
  2. should:代表或的关系,代表符合该条件就可以被查出来 (类似于or)
  3. must_not:代表非的关系,也就是要求不能是符合该条件的数据才能被查出来 (类似于not)

来看一个图:

在这里插入图片描述

精度控制:

所有 must 语句必须匹配,所有 must_not 语句都必须不匹配,但有多少 should 语句应该匹配呢?默认情况下,没有 should 语句是必须匹配的,只有一个例外:那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。

我们可以通过 minimum_should_match 参数控制需要匹配的 should 语句的数量,它既可以是一个绝对的数字,又可以是个百分比:

例如有这样一个查询:

GET http://ip:prot/textbook/_search
{
    "query":{
        "bool":{
            "must":[
            	{
            		"match":{
                    	"bookName":"王大"
                	}
            	}
            ],
            "should":[
            	{
            		"term":{
                    	"author":"王大"
                	}
            	},
            	{
					"range":{
                    	"num":{
                        	"gt":20
                    	}
                	}
				}
            ],
            minimum_should_match": "1"
        }
    }
}

这里就要求must里面的match是必须要符合的,但是should里面的两个条件就可以符合一条即可。

再来看一个复杂查询:

先看一个sql,这里随意写一个

select 
	*
from
	persons
where 
	sex = '女'
and
	age between 30 and 40
and 
	sect != '明教'
and 
	(address = '峨眉山' OR skill = '暗器')

ES语句就是:

GET /person/_search
{
	"query": {
		"bool": {
			"must": [
				{
					"term": {
						"sex": {
							"value": "女",
							"boost": 1.0
						}
					}
				},
				{
					"range": {
						"age": {
							"from": 30,
							"to": 40,
							"include_lower": true,
							"include_upper": true,
							"boost": 1.0
						}
					}
				}
			],
			"must_not": [
				{
					"term": {
						"sect.keyword": {
							"value": "明教",
							"boost": 1.0
						}
					}
				}
			],
			"should": [
				{
					"term": {
						"address.keyword": {
							"value": "峨眉山",
							"boost": 1.0
						}
					}
				},
				{
					"term": {
						"skill.keyword": {
							"value": "暗器",
							"boost": 1.0
						}
					}
				}
			],
			"adjust_pure_negative": true,
			"minimum_should_match": "1",
			"boost": 1.0
		}
	}
}

这里用了一个 “skill.keyword" 这样的字段,不明白的可以看这篇文章:ES中字符串keyword和text类型区别

用Java构建这个查询条件:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构建查询语句
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
        .must(QueryBuilders.termQuery("sex", "女"))
        .must(QueryBuilders.rangeQuery("age").gte(30).lte(40))
        .mustNot(QueryBuilders.termQuery("sect.keyword", "明教"))
        .should(QueryBuilders.termQuery("address.keyword", "峨眉山"))
        .should(QueryBuilders.rangeQuery("power.keyword").gte(50).lte(80))
        .minimumShouldMatch(1);  // 设置should至少需要满足几个条件

// 将BoolQueryBuilder构建到SearchSourceBuilder中
searchSourceBuilder.query(boolQueryBuilder);

2.2.1 filter查询

bool查询里有张图,可以看到filter是bool下面的一个选项,和must类似。

query和filter的区别:query查询的时候,会先比较查询条件,然后计算分值,最后返回文档结果;而filter是先判断是否满足查询条件,如果不满足会缓存查询结果(记录该文档不满足结果),满足的话,就直接缓存结果,filter不会对结果进行评分,能够提高查询效率

方式一,单独使用:

单独使用时,filter与must基本一样,不同的是filter不计算评分,效率更高

{
	"query": {
		"bool": {
			"filter": [
				{
					"term": {
						"sex": {
							"value": "男",
							"boost": 1.0
						}
					}
				}
			],
			"adjust_pure_negative": true,
			"boost": 1.0
		}
	}
}

方式二,和must、must_not同级,相当于子查询:
select * from (select * from persons where sect = '明教')) a where sex = '女';

ES语句:

{
	"query": {
		"bool": {
			"must": [
				{
					"term": {
						"sect.keyword": {
							"value": "明教",
							"boost": 1.0
						}
					}
				}
			],
			"filter": [
				{
					"term": {
						"sex": {
							"value": "女",
							"boost": 1.0
						}
					}
				}
			],
			"adjust_pure_negative": true,
			"boost": 1.0
		}
	}
}

java:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构建查询语句
searchSourceBuilder.query(QueryBuilders.boolQuery()
        .must(QueryBuilders.termQuery("sect.keyword", "明教"))
        .filter(QueryBuilders.termQuery("sex", "女"))
);

方式三,将must、must_not置于filter下,这种方式是最常用的:
{
	"query": {
		"bool": {
			"filter": [
				{
					"bool": {
						"must": [
							{
								"term": {
									"sect.keyword": {
										"value": "明教",
										"boost": 1.0
									}
								}
							},
							{
								"range": {
									"age": {
										"from": 20,
										"to": 35,
										"include_lower": true,
										"include_upper": true,
										"boost": 1.0
									}
								}
							}
						],
						"must_not": [
							{
								"term": {
									"sex.keyword": {
										"value": "女",
										"boost": 1.0
									}
								}
							}
						],
						"adjust_pure_negative": true,
						"boost": 1.0
					}
				}
			],
			"adjust_pure_negative": true,
			"boost": 1.0
		}
	}
}

java:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构建查询语句
searchSourceBuilder.query(QueryBuilders.boolQuery()
        .filter(QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("sect.keyword", "明教"))
                .must(QueryBuilders.rangeQuery("age").gte(20).lte(35))
                .mustNot(QueryBuilders.termQuery("sex.keyword", "女")))
);

2.3 聚合查询

2.3.1 最值、平均值、求和

案例:查询最大年龄、最小年龄、平均年龄。

SQL:

select max(age) from persons;

ES:

GET /person/_search
{
	"aggregations": {
		"max_age": {
			"max": {
				"field": "age"
			}
		}
	}
}

Java:

@Autowired
private RestHighLevelClient client;

@Test
public void maxQueryTest() throws IOException {
	// 聚合查询条件
    AggregationBuilder aggBuilder = AggregationBuilders.max("max_age").field("age");
    SearchRequest searchRequest = new SearchRequest("person");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    // 将聚合查询条件构建到SearchSourceBuilder中
    searchSourceBuilder.aggregation(aggBuilder);
    System.out.println("searchSourceBuilder----->" + searchSourceBuilder);

    searchRequest.source(searchSourceBuilder);
    // 执行查询,获取SearchResponse
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
    System.out.println(JSONObject.toJSON(response));
}

使用聚合查询,结果中默认只会返回10条文档数据(当然我们关心的是聚合的结果,而非文档)。返回多少条数据可以自主控制:

GET /person/_search
{
	"size": 20,
	"aggregations": {
		"max_age": {
			"max": {
				"field": "age"
			}
		}
	}
}

而Java中只需增加下面一条语句即可:

searchSourceBuilder.size(20);

与max类似,其他统计查询也很简单:

AggregationBuilder minBuilder = AggregationBuilders.min("min_age").field("age");
AggregationBuilder avgBuilder = AggregationBuilders.avg("min_age").field("age");
AggregationBuilder sumBuilder = AggregationBuilders.sum("min_age").field("age");
AggregationBuilder countBuilder = AggregationBuilders.count("min_age").field("age");

2.3.2 去重查询

案例:查询一共有多少个门派。

SQL:

select count(distinct sect) from persons;

ES:

{
	"aggregations": {
		"sect_count": {
			"cardinality": {
				"field": "sect.keyword"
			}
		}
	}
}

java:

@Test
public void cardinalityQueryTest() throws IOException {
	// 创建某个索引的request
    SearchRequest searchRequest = new SearchRequest("person");
    // 查询条件
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    // 聚合查询
    AggregationBuilder aggBuilder = AggregationBuilders.cardinality("sect_count").field("sect.keyword");
    searchSourceBuilder.size(0);
    // 将聚合查询构建到查询条件中
    searchSourceBuilder.aggregation(aggBuilder);
    System.out.println("searchSourceBuilder----->" + searchSourceBuilder);

    searchRequest.source(searchSourceBuilder);
    // 执行查询,获取结果
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
    System.out.println(JSONObject.toJSON(response));
}

2.3.3 分组聚合

2.3.3.1 单条件分组

案例:查询每个门派的人数

SQL:

select sect,count(id) from mytest.persons group by sect;

ES:

{
	"size": 0,
	"aggregations": {
		"sect_count": {
			"terms": {
				"field": "sect.keyword",
				"size": 10,
				"min_doc_count": 1,
				"shard_min_doc_count": 0,
				"show_term_doc_count_error": false,
				"order": [
					{
						"_count": "desc"
					},
					{
						"_key": "asc"
					}
				]
			}
		}
	}
}

java:

SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(0);
// 按sect分组
AggregationBuilder aggBuilder = AggregationBuilders.terms("sect_count").field("sect.keyword");
searchSourceBuilder.aggregation(aggBuilder);
2.3.3.2 多条件分组

案例:查询每个门派各有多少个男性和女性

SQL:

select sect,sex,count(id) from mytest.persons group by sect,sex;

ES:

{
	"aggregations": {
		"sect_count": {
			"terms": {
				"field": "sect.keyword",
				"size": 10
			},
			"aggregations": {
				"sex_count": {
					"terms": {
						"field": "sex.keyword",
						"size": 10
					}
				}
			}
		}
	}
}

2.3.4 过滤聚合

前面所有聚合的例子请求都省略了 query ,整个请求只不过是一个聚合。这意味着我们对全部数据进行了聚合,但现实应用中,我们常常对特定范围的数据进行聚合,例如下例。

案例:查询明教中的最大年龄。 这涉及到聚合与条件查询一起使用。

SQL:

select max(age) from mytest.persons where sect = '明教';

ES:

GET /person/_search
{
	"query": {
		"term": {
			"sect.keyword": {
				"value": "明教",
				"boost": 1.0
			}
		}
	},
	"aggregations": {
		"max_age": {
			"max": {
				"field": "age"
			}
		}
	}
}

java:

SearchRequest searchRequest = new SearchRequest("person");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 聚合查询条件
AggregationBuilder maxBuilder = AggregationBuilders.max("max_age").field("age");
// 等值查询
searchSourceBuilder.query(QueryBuilders.termQuery("sect.keyword", "明教"));
searchSourceBuilder.aggregation(maxBuilder);

另外还有一些更复杂的查询例子。

案例:查询0-20,21-40,41-60,61以上的各有多少人。

SQL:

select 
	sum(case when age<=20 then 1 else 0 end) ageGroup1,
	sum(case when age >20 and age <=40 then 1 else 0 end) ageGroup2,
	sum(case when age >40 and age <=60 then 1 else 0 end) ageGroup3,
	sum(case when age >60 and age <=200 then 1 else 0 end) ageGroup4
from 
	mytest.persons;

ES:

{
	"size": 0,
	"aggregations": {
		"age_avg": {
			"range": {
				"field": "age",
				"ranges": [
					{
						"from": 0.0,
						"to": 20.0
					},
					{
						"from": 21.0,
						"to": 40.0
					},
					{
						"from": 41.0,
						"to": 60.0
					},
					{
						"from": 61.0,
						"to": 200.0
					}
				],
				"keyed": false
			}
		}
	}
}

结果:

"aggregations" : {
  "age_avg" : {
    "buckets" : [
      {
        "key" : "0.0-20.0",
        "from" : 0.0,
        "to" : 20.0,
        "doc_count" : 3
      },
      {
        "key" : "21.0-40.0",
        "from" : 21.0,
        "to" : 40.0,
        "doc_count" : 13
      },
      {
        "key" : "41.0-60.0",
        "from" : 41.0,
        "to" : 60.0,
        "doc_count" : 4
      },
      {
        "key" : "61.0-200.0",
        "from" : 61.0,
        "to" : 200.0,
        "doc_count" : 1
      }
    ]
  }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值