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

1黃鷹發表於2020-04-02

概要

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架構社群

相關文章