Elasticsearch系列---聚合查詢(一)

概要

Elasticsearch的聚合查詢,跟數據庫的聚合查詢效果是一樣的,我們可以將二者拿來對比學習,如求和、求平均值、求最大最小等等。

基礎概念

bucket

數據分組,一些數據按照某個字段進行bucket劃分,這個字段值相同的數據放到一個bucket中。可以理解成Java中的Map<String, List>結構,類似於Mysql中的group by後的查詢結果。

metric:

對一個數據分組執行的統計,比如計算最大值,最小值,平均值等
類似於Mysql中的max(),min(),avg()函數的值,都是在group by後使用的。

案例

我們還是以英文兒歌爲案例背景,回顧一下索引結構:

PUT /music
{
  "mappings": {
      "children": {
        "properties": {
          "id": {
            "type": "keyword"
          },
          "author_first_name": {
            "type": "text",
            "analyzer": "english"
          },
          "author_last_name": {
            "type": "text",
            "analyzer": "english"
          },
          "author": {
            "type": "text",
            "analyzer": "english",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "content": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "language": {
            "type": "text",
            "analyzer": "english",
            "fielddata": true
          },
          "tags": {
            "type": "text",
            "analyzer": "english"
          },
          "length": {
            "type": "long"
          },
          "likes": {
            "type": "long"
          },
          "isRelease": {
            "type": "boolean"
          },
          "releaseDate": {
            "type": "date"
          }
        }
      }
  }
}

統計目前收錄的哪種語言的歌曲最多

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "song_qty_by_language": {
      "terms": {
        "field": "language"
      }
    }
  }
}

語法解釋:

  • size:0 表示只要統計後的結果,原始數據不展現
  • aggs:固定語法 ,聚合分析都要聲明aggs
  • song_qty_by_language:聚合的名稱,可以隨便寫,建議規範命名
  • terms:按什麼字段進行分組
  • field:具體的字段名稱

響應結果如下:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 5,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "song_qty_by_language": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "english",
          "doc_count": 5
        }
      ]
    }
  }
}

語法解釋:

  • hits: 由於請求時設置了size:0,hits就是空的
  • aggregations:聚合查詢的結果
  • song_qty_by_language:請求時聲明的名稱
  • buckets:根據指定字段查詢後得到的數據分組集合,[]內的是每一個數據分組,其中key爲每個bucket的對應指定字段的值,doc_count爲統計的數量。

默認按doc_count降序排序。

按語種統計每種歌曲的平均時長

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "lang": {
      "terms": {
        "field": "language"
      },
      "aggs": {
        "length_avg": {
          "avg": {
            "field": "length"
          }
        }
      }
    }
  }
}

這裏演示的是兩層aggs聚合查詢,先按語種統計,得到數據分組,再在數據分組裏算平均時長。

多個aggs嵌套語法也是如此,注意一下aggs代碼塊的位置即可。

統計最長時長、最短時長等的歌曲

最常用的統計:count,avg,max,min,sum,語法含義與mysql相同。

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "color": {
      "terms": {
        "field": "language"
      },
      "aggs": {
        "length_avg": {
          "avg": {
            "field": "length"
          }
        },
        "length_max": {
          "max": {
            "field": "length"
          }
        },
        "length_min": {
          "min": {
            "field": "length"
          }
        },
        "length_sum": {
          "sum": {
            "field": "length"
          }
        }
      }
    }
  }
}

按時長分段統計歌曲平均時長

以30秒爲一段,看各段區間的平均值。

histogram語法位置跟terms一樣,作範圍分區,搭配interval參數一起使用
interval:30表示分的區間段爲[0,30),[30,60),[60,90),[90,120)

段的閉合關係是左開右閉,如果數據在某段區間內沒有,也會返回空的區間。

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "sales_price_range": {
      "histogram": {
        "field": "length",
        "interval": 30
      },
      "aggs": {
        "length_avg": {
          "avg": {
            "field": "length"
          }
        }
      }
    }
  }
}

這種數據的結果可以用來生成柱狀圖或折線圖。

按上架日期分段統計新歌數量

按月統計

date histogram與histogram語法類似,搭配date interval指定區間間隔
extended_bounds表示最大的時間範圍。

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "sales": {
      "date_histogram": {
        "field": "releaseDate",
        "interval": "month",
        "format": "yyyy-MM-dd",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "2019-10-01",
          "max": "2019-12-31"
        }
      }
    }
  }
}

interval的值可以天、周、月、季度、年等。我們可以延伸一下,比如統計今年每個季度的新發布歌曲的點贊數量

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "sales": {
      "date_histogram": {
        "field": "releaseDate",
        "interval": "quarter",
        "format": "yyyy-MM-dd",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "2019-01-01",
          "max": "2019-12-31"
        }
      },
      "aggs": {
        "lang_qty": {
          "terms": {
            "field": "language"
          },
          "aggs": {
            "like_sum": {
              "sum": {
                "field": "likes"
              }
            }
          }
        },
        "total" :{
          "sum": {
            "field": "likes"
          }
        }
      }
    }
  }
}

帶上過濾條件

聚合查詢可以和query搭配使用,相當於mysql中where與group by聯合使用

查詢條件
GET /music/children/_search
{
  "size": 0,
  "query": {
    "match": {
      "language": "english"
    }
  },
  "aggs": {
    "sales": {
      "terms": {
        "field": "language"
      }
    }
  }
}
過濾條件
GET /music/children/_search
{
  "size": 0,
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "language": "english"
        }
      }
    }
  },
  "aggs": {
    "sales": {
      "terms": {
        "field": "language"
      }
    }
  }
}

global bucket查詢

global:就是global bucket,會將所有的數據納入聚合scope,不受前面的query或filter影響。

global bucket適用於同時統計指定條件的數據與全部數據的對比,如我們創造的場景:指定作者的歌與全部歌曲的點贊數量對比。

GET /music/children/_search
{
  "size": 0,
  "query": {
    "match": {
      "author": "Jean Ritchie"
    }
  },
  "aggs": {
    "likes": {
      "sum": {
        "field": "likes"
      }
    },
    "all": {
      "global": {},
      "aggs": {
        "all_likes": {
          "sum": {
            "field": "likes"
          }
        }
      }
    }
  }
}

統計近2月,近1月的點贊數

aggs.filter針對是聚合裏的數據

bucket filter:對不同的bucket下的aggs,進行filter

類似於mysql的中having語法

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "recent_60d": {
      "filter": {
        "range": {
          "releaseDate": {
            "gte": "now-60d"
          }
        }
      },
      "aggs": {
        "recent_60d_likes_sum": {
          "sum": {
            "field": "likes"
          }
        }
      }
    },
    "recent_30d": {
      "filter": {
        "range": {
          "releaseDate": {
            "gte": "now-30d"
          }
        }
      },
      "aggs": {
        "recent_30d_likes_sum": {
          "avg": {
            "field": "likes"
          }
        }
      }
    }
  }
}

統計排序

默認按doc_count降序排序,排序規則可以改,order裏面可以指定aggs的別名,如length_avg,類似於mysql的order by cnt asc。

GET /music/children/_search
{
  "size": 0,
  "aggs": {
    "group_by_lang": {
      "terms": {
        "field": "language",
        "order": {
          "length_avg": "desc"
        }
      },
      "aggs": {
        "length_avg": {
          "avg": {
            "field": "length"
          }
        }
      }
    }
  }
}

小結

本篇主要介紹常用的聚合查詢,均以示例爲主,瞭解基本寫法後可以快速閱讀,有不好理解的地方,多與我們熟悉的數據庫查詢SQL作比較,謝謝。

專注Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區
可以掃左邊二維碼添加好友,邀請你加入Java架構社區微信羣共同探討技術
Java架構社區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章