过滤器聚集通过定义一个或多个过滤器来过滤分桶,满足过速器条件的文档将 落入这个过滤器形成的桶中。过滤器聚集分为单桶和多桶两种,对应的聚集类型 自然就是 filter 和 filters。
POST /kibana_sample_data_flights/_search?size=0&filter_path=aggregations
{
"aggs" : {
"origin_cn": {
# 过滤出发为CN的 航班 才落到桶中
"filter": {
"term": {
"OriginCountry":"CN"
}
},
# 然后再 统计它们的平均票价
"aggs": {
"cn_ticket_price": {
"avg": {
"field": "AvgTicketPrice"
}
}
}
},
# 统计了所有航班 平均票
"avg_price": {
"avg": {
"field": "AvgTicketPrice"
}
}
}
}
结果为:
{
"aggregations" : {
# 平均票价
"avg_price" : {
"value" : 628.2536888148849
},
"origin_cn" : {
#出现地为中国的 平均票价
"doc_count" : 743,
"cn_ticket_price" : {
"value" : 601.1180918245585
}
}
}
}
多桶过滤:
POST /kibana_sample_data_flights/_search?size=0&filter_path=aggregations
{
"aggs" :{
"origin_cn_us":{
"filters": {
"filters":[
{ "term": {"OriginCountry": "CN"} },
{"term": {"OriginCountry": "US " } }
]
},
"aggs":{
"avg_ price":{
"avg": {
"field": "AvgTicketPrice"
}
}
}
}
}
}
忽略query的条件,统计所有
把缺失的字段 规到某个桶
有两种比较特殊的多桶型聚集,它们是 composite 聚集和 adjacency_matrix 聚集。
集可以将不同类型的聚集组合到一一起,它会从不同的聚集中 提取数据,并以笛卡尔乘积的形式组合它们,而每一个组合就会形成一个新桶。
查看平均票价与机场天气的对应关系:
POST /kibana_sample_data_flights/_search?filter_path=aggregations
{
"aggs" :{
"price_weather" : {
"composite" : {
#用sources,定义2个需要组合的聚集
"sources":[
{
"avg_price": {
##第一个聚集:针对 AvgTicketPrice 以 500 为间隔 分桶聚集
"histogram":{
"field": "AvgTicketPrice",
"interval":500,
#按avg_price排序
"order": "asc"
}
}
},
{
##第二聚集:个针对 OriginWeather 的 terms 聚集,按weather 排序
"weather":{
"terms": {"field": "OriginWeather","order":"asc"}
}
}
]
}
}
}
}
运行结果:
{
"aggregations" : {
"price_weather" : {
#它包含自前聚集结果中最后一个结果的 key。所以请求下一页聚集结果就可 以通过 after 和 size 参数值定
"after_key" : {
"avg_price" : 500.0,
"weather" : "Cloudy"
},
"buckets" : [
{
"key" : {
"avg_price" : 0.0,
"weather" : "Clear"
},
"doc_count" : 795
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Cloudy"
},
"doc_count" : 809
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Damaging Wind"
},
"doc_count" : 303
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Hail"
},
"doc_count" : 373
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Heavy Fog"
},
"doc_count" : 292
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Rain"
},
"doc_count" : 738
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Sunny"
},
"doc_count" : 744
},
{
"key" : {
"avg_price" : 0.0,
"weather" : "Thunder & Lightning"
},
"doc_count" : 357
},
{
"key" : {
"avg_price" : 500.0,
"weather" : "Clear"
},
"doc_count" : 1377
},
{
"key" : {
"avg_price" : 500.0,
"weather" : "Cloudy"
},
"doc_count" : 1365
}
]
}
}
}
管道聚集不是直接从索引中读取文档,而是在其他聚集的基础上再进行聚集 运算。
管道聚集都会 包含一个名为 buckets_path 的参数,用于指定访问其他桶中指标值的路径。
buckets_ path 参数的值由三部分组成,即聚集名称、指标名称和分隔符。
POST /kibana_sample_data_flights/_search?filter_path=aggregations
{
"aggs": {
"carriers": {
#terms分桶, 按字段Carrier 分10个桶
"terms":{
"field": "Carrier",
"size": 10
},
"aggs": {
"carrier_stat": {
# 将上面10个桶,按stats聚集
"stats": {
"field": "AvgTicketPrice"
}
}
}
},
"all_stat": {
# 对兄弟聚集carriers,基础上再聚集,对carrier_stat.avg 作avg_bucket平均值聚集
"avg_bucket": {
"buckets_path": "carriers>carrier_stat.avg"
}
}
}
}
结果:
{
"aggregations" : {
"carriers" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Logstash Airways",
"doc_count" : 3331,
"carrier_stat" : {
"count" : 3331,
"min" : 100.37113189697266,
"max" : 1199.72900390625,
"avg" : 624.5819742276625,
"sum" : 2080482.5561523438
}
},
{
"key" : "JetBeats",
"doc_count" : 3274,
"carrier_stat" : {
"count" : 3274,
"min" : 101.0330810546875,
"max" : 1199.642822265625,
"avg" : 627.4573726292857,
"sum" : 2054295.4379882812
}
},
{
"key" : "Kibana Airlines",
"doc_count" : 3234,
"carrier_stat" : {
"count" : 3234,
"min" : 100.14596557617188,
"max" : 1199.109130859375,
"avg" : 630.8681507004435,
"sum" : 2040227.5993652344
}
},
{
"key" : "ES-Air",
"doc_count" : 3220,
"carrier_stat" : {
"count" : 3220,
"min" : 100.0205307006836,
"max" : 1199.5123291015625,
"avg" : 630.235816375069,
"sum" : 2029359.3287277222
}
}
]
},
"all_stat" : {
"value" : 628.2858284831152
}
}
}
把父聚集的结果作为输入,然后再聚集
滑动窗口有2个: moving_avg:平均值, moving_fn:自定义计算
只能用于间隔聚集
POST /kibana_sample_data_flights/_search?filter_path=aggregations
{
"aggs": {
"day_price": {
#timestamp 字段按天将文档分桶。
"date_histogram": {
"field": "timestamp",
"interval": "day"
},
"aggs": {
"avg_price": {
#AvgTicketPrice 字段在 1 个桶内avg 聚集
"avg": {
"field": "AvgTicketPrice"
}
},
#使用滑动窗口做平均值平滑的管道聚集,按滑动窗口聚集
"smooth_price": {
#以对落在窗口内的父聚集结果做各种自定义的运算
"moving_fn": {
"buckets_path": "avg_price",
"window": 10,
# 无加权平均计算
"script":"MovingFunctions.unweightedAvg(values)"
}
}
}
}
}
}
目前我们学习的管道聚集会对父聚集结果中落在窗口内的多个桶做聚集运 算,而 bucket_script、 bucket_selector 、bucket_sort 这三个管道聚集则会针对
父聚集结果中的每一个桶做单独的运算。
POST /kibana_sample_data_flights/_search?filter_path=aggregations
{
"aggs": {
"date_price_diff": {
#时间间隔分桶,按天分桶
"date_histogram": {
"field": "timestamp", "fixed_interval": "1d"
},
"aggs": {
"stat_price_day": {
#按字段 AvgTicketPrice 指数聚集
"stats": {
"field": "AvgTicketPrice"}
},
#兄弟聚集
"diff":{
#管道聚集
"bucket_script": {
#将上一个聚集 结果 ,取出最大值和最小值
"buckets_path": {
"max_price":"stat_price_day.max",
"min_price":"stat_price_day.min"
},
#对最大值和最小值求相减
"script": "params.max_price - params.min_price"
}
},
"gt990": {
#selector聚集,同样取最大值和最小值,然后相减去,如果差值大于990的才能出现,否则不能出现
"bucket_selector": {
"buckets_path": {
"max_price":"stat_price_day.max",
"min_price":"stat_price_day.min"
},
"script": "params.max_price - params.min_price > 990"
}
},
"sort_by": {
#排序
"bucket_sort": {
"sort":[
{
#对diff结果排序--倒序
"diff":{
"order":"desc"
}
}
]
}
}
}
}
}
}
过滤出最大值和最小值差值大于 990的聚集,然后按差值 倒序排序。
文档和文档间的关系。通过某个 标识 还标识 文档间的 父子关系。类似 mysql表中如下结构:
用parent_id字段来标识 父部门Id,通过父部门Id可以找到子部门id,可以通过子部门Id , 找到它的父部门。
部门表:
dept_id dept_name parent_id
1 研发一部 0
2 研发二部 1
3 研发三部 2
数据与数据间有父子关系,es间也有这种关系,就是父子关系。需要在索引中定义一个字段 ,来标识这 种关系
定义文档父子关系
PUT employees
{
"mappings": {
"properties": {
"management":{
"type": "join",
"relations":{
"manager": "member"
}
}
}
}
}
PUT /employees/_doc/1
{
"name" : "tom",
"management":{ "name" : "manager" }
}
PUT /employees/_doc/2?routing=1
{
"name" : "smith",
"management":{ "name" : "member", "parent": "1" }
}
PUT /employees/_doc/3?routing=1
{
"name" : "john",
"management":{ "name" : "member", "parent": "1" }
}
#如果再添加 文档1 的父文档好像添加不了
PUT /employees/_doc/4?routing=1
{
"name" : "tom_parent",
"management":{ "name":"manager","children":"1"}
}
#查询,who has child, 根据子文档名字 smith,查看谁有这个child,即:根据子文档查询父文档
POST /employees/_search
{
"query": {
"has_child":{
"type": "member",
"query":{
"match":{ "name": "smith" }
}
}
}
}
#查询,who has parent, 根据父文档名字查看 子文档
POST /employees/_search
{
"query": {
"has_parent":{
"parent_type": "manager",
"query":{
"match":{ "name": "tom" }
}
}
}
}
# 查看parent_id=1的子文档有哪些
POST /employees/_search
{
"query": {
"parent_id": { "type": "member", "id":1 }
}
}
POST /employees/_search?filter_path=aggregations
{
"query":{
"term":{ "name":"tom" }
},
"aggs":{
"members":{
#children聚集:查看集合中 parent=tom的子文档个数
"children":{
"type": "member"
},
# 再次聚集,分别统计tom子文档
"aggs": {
"member_name":{
"terms":{ "field": "name.keyword", "size": 10 }
}
}
}
}
}
查询结果:
{
"aggregations" : {
#members 聚集结果:doc_cout=2,统计了name=tom的子文档个数
"members" : {
"doc_count" : 2,
#member_name聚集结果: 分别统计了name=tom的子文档个数
"member_name" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
#子文档john 的个数
"key" : "john",
"doc_count" : 1
},
{
#子文档smith 的个数
"key" : "smith",
"doc_count" : 1
}
]
}
}
}
}
POST /employees/_search?filter_path=aggregations
{
# 查询name = smith的文档
"query": {
"match":{ "name": "smith" }
},
"aggs": {
#parent聚集:根据子文档名字smith 找到父文档,统计它的个数
"who_is_manager":{
"parent":{
"type": "member"
},
#再次聚集,根据smith 找到父文档,根据父文档名字 分别统计 父文档个数
"aggs":{
"manager_name":{
"terms":{ "field" :"name.keyword" , "size": 10 }
}
}
}
}
}
查询结果:
{
"aggregations" : {
"who_is_manager" : {
"doc_count" : 1,
"manager_name" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "tom",
"doc_count" : 1
}
]
}
}
}
}
添加一个数组文档如下;
PUT colleges/_doc/2
{
"address":[
{ "country": "CN", "city":"BJ" },
{ "country" : "US", "city": "NY" }
],
"age":10
}
colleges 文档在实际存储时,会被拆解为“ address. country": [“CN.US”]” 和 address. city":[“BJ” ,“NY”]” 两个数组字段。这样一来,单个对象 内部,country 字段和 city 字段之间的匹配关系就丢失了。换句话说,使用 CN 与 NY 作为共同条件检索的文档时,上述文档也会被检索出来,这在逻辑上就出现 了错误:
POST colleges/_search
{
"query": {
"bool": {
"must":[
{"match": { "address.country": "CN"}},
{"match": {"address.city": "NY"}}
]
}
}
}
在示例中使用了 bool 组合查询,要求 country 字段为 CN 而 city 字段为 NY。 这样的文档显然并不存在,但由于数组中的对象被平铺为两个独立的数组字段, 文档仍然会被检索出来。
所以定义文档时,address字段需要定义为嵌套类型。
PUT colleges
{
"mappings": {
"properties": {
"address":{ "type": "nested" },
"age":{ "type":"integer" }
}
}
}
如果还是用上面那个查询语句查询,实际上还是有问题,条件正确不会被查询出来:
POST colleges/_search
{
"query": {
"bool": {
"must":[
{"match": { "address.country": "CN"}},
{"match": {"address.city": "BJ"}}
]
}
}
}
上面改成正确的条件也查不出来数据,
这是因为对 nested 类型字段的检索实际 上是对隐式文档的检索,在检索时必须要将检索路由到隐式文档上,所以必须使 用专门的检索方法。
POST /colleges/_search
{
"query":{
"nested":{
"path": "address",
"query": {
"bool":{
"must":[
{"match": {"address.country": "CN"}},
{"match": {"address.city": "BJ"}}
]
}
}
}
}
}
nested 聚集是一个单桶聚集,也是通过 path 参数指定 nested 字段的路径, 包含在 path 指定路径中的隐式文档都将落入桶中。所以 nested 字段保存数组的 长度就是单个文档落入桶中的文档数量,而整个文档落入桶中的数量就是所有文 档 nested 字段数组长度的总和。有了 nested 聚集,就可以针对 nested 数组中的 对象做各种聚集运算,例如:
POST /colleges/_search?filter_path=aggregations
{
"aggs": {
"nested_address":{
"nested":{ "path": "address" },
"aggs":{
"city_names": {
"terms":{ "field": "address.city.keyword", "size": 10 }
}
}
}
}
}
在示例中,nested_address 是一个 nested 聚集的名称,它会将 address 字段 的隐式文档归入一个桶中。而嵌套在 nested_address 聚集中的 city_names 聚集 则会在这个桶中再做 terms 聚集运算,这样就将对象中 city 字段所有的词项枚举 出来了。
reverse_nested 聚集用于在隐式文档中对父文档做聚集,所以这种聚集必须 作为 nested 聚集的嵌套聚集使用。
POST /colleges/_search?filter_path=aggregations
{
"aggs": {
"nested address": {
"nested":{ "path": "address" },
"aggs":{
"city names":{
"terms":{ "field": "address.city.keyword", "size": 10 },
"aggs": {
"avg_age_in_city":{
"reverse_nested": {},
"aggs": {
"avg_age": {
"avg": {"field": "age"}
}
}
}
}
}
}
}
}
}
Elasticsearch 在 Basic 授权中支持以 SQL 语句的形式检索文档,SQL 语句在 执行时会被翻译为 DSL 执行。从语法的角度来看,Elastisearch 中的 SQL 语句与 RDBMS 中的 SQL 语句基本一致
目前常见的 Elasticsearch Java API 有四类 client 连接方式:
一般4种, 用简单的查询可以用spring data elasticsearch,但如果复杂就不用了,那么最终推荐使用 RestClient, 那么ResttClient又2种。
Java REST Client 有 Low Level 和 High Level 两种:
因为 Low Level更基于原始,所以一般我们用 High Level 。
maven依赖的版本最好和es的版本一致。
java low level 的maven依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.7.0</version>
</dependency>
java high level的maven的依赖
<dependency>
<groupId>com.strapdata.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.7.0</version>
</dependency>