Elasticsearch Java API 的使用(13)—分組聚合之一
分組聚和不像度量聚合那樣通過欄位進行計算,而是根據文件建立分組。每個聚合都關聯一個標準(取決於聚合的型別),決定了一個文件在當前的條件下是否會“劃入”分組中。
換句話說,分組實際上定義了一個文件集。除了這些分組之外,分組聚和也會計算和返回“劃入”每個分組中文件的數量。
與度量聚合不同,分組聚合可以擁有子聚合。這些子聚合可以聚合由它們的“父”聚合建立分組。
分組集合有不同的型別,對應著不同的“分組”策略。有的策略定義一個分組(單分組聚合),有的策略定義固定數量的分組(多分組聚合),還有的策略在聚合執行過程中動態的建立分組。
- 日期直方圖聚合
- 時間範圍聚合
- 巢狀聚合
1、日期直方圖聚合
日期直方圖集合是一個多分組聚合,除了只能應用於日期的值之外,和“直方圖聚合”的作用一樣的。
自從Elasticsearch將日期型別作為內建型別之後,在日期上進行普通的直方圖聚合得到了很好的支援。但是這麼做會在精度上有所失真,這是因為時間間隔不是固定的(思考一下,一年或者一個月有多少天)。因為這個原因,我們需要對基於資料的時間進行忒別支持。從功能性的角度來說,最主要的不同就是可以通過日期/時間表達式指定間隔。
以一個月為間隔請求:
POST /sales/_search?size=0
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "month"
}
}
}
}
可用的時間間隔表示式:year, quarter, month, week, day, hour, minute, second(年份、季度、月、周、日、小時、分鐘、秒)。
時間值也可以通過由時間單元解析支援的縮寫來指定。注意:不支援分數的時間值,但是你可以通過轉移到另一個時間單位地址本(例如,1.5h可以被指定為90m)。
POST /sales/_search?size=0
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "90m"
}
}
}
}
1.1、時間鍵值:
本質上,日期表示為一個64位的時間戳數字,代表從紀元開始到現在的毫秒數。這些時間戳為分組的鍵返回。key_as_string是相同的時間戳轉換為格式化的日期字串,格式通過format引數指定。
Note:如果沒有指定格式,則它將使用欄位對映中指定的第一日期格式。
請求示例:
POST /sales/_search?size=0
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "1M",
"format" : "yyyy-MM-dd"
}
}
}
}
返回值:
{
...
"aggregations": {
"sales_over_time": {
"buckets": [
{
"key_as_string": "2015-01-01",
"key": 1420070400000,
"doc_count": 3
},
{
"key_as_string": "2015-02-01",
"key": 1422748800000,
"doc_count": 2
},
{
"key_as_string": "2015-03-01",
"key": 1425168000000,
"doc_count": 2
}
]
}
}
}
1.2、時間區間
日期時間以世界標準時區(UTC)儲存在Elasticsearch中。預設情況下,所有的分組和取整的操作也都是以世界標準時完成的。time_zone引數用於表示分組時需要使用不同的時間區間。
時間區間也可以被指定為一個ISO 8601 UTC偏移(如+ 01:00或08:00)或作為一個網際網路編碼資料庫中的時間區間識別符號,比如America/Los_Angeles。
考慮下面的例子:
PUT my_index/log/1?refresh
{
"date": "2015-10-01T00:30:00Z"
}
PUT my_index/log/2?refresh
{
"date": "2015-10-01T01:30:00Z"
}
GET my_index/_search?size=0
{
"aggs": {
"by_day": {
"date_histogram": {
"field": "date",
"interval": "day"
}
}
}
}
如果沒有指定時間區間,就會使用世界標準時間,結果就是這些文件會放置在同一天的分組裡,這個分組開始於2015年10月1日UTC午夜開始:
{
...
"aggregations": {
"by_day": {
"buckets": [
{
"key_as_string": "2015-10-01T00:00:00.000Z",
"key": 1443657600000,
"doc_count": 2
}
]
}
}
}
如果指定一個 - 01:00的時間分割槽,午夜時間就會比標準時間的午夜提前一個小時:
GET my_index/_search?size=0
{
"aggs": {
"by_day": {
"date_histogram": {
"field": "date",
"interval": "day",
"time_zone": "-01:00"
}
}
}
}
現在,第一份文件就會劃分入2015年9月30日的分組裡,第二份文件劃入2015年10月1日的分組了:
{
...
"aggregations": {
"by_day": {
"buckets": [
{
"key_as_string": "2015-09-30T00:00:00.000-01:00",
"key": 1443574800000,
"doc_count": 1
},
{
"key_as_string": "2015-10-01T00:00:00.000-01:00",
"key": 1443661200000,
"doc_count": 1
}
]
}
}
}
key_as_string值代表指定的時區中每一天的午夜。
Warning:當使用DST(夏時制)更改的時區時,接近這些更改發生時的桶的大小與所使用的間隔所期望的大小略有不同。例如,考慮在CET時間區DST開始:2016年3月27日凌晨兩點,時鐘轉了1小時,當地時間凌晨3點。當使用一天作為間隔時,那一天的桶蓋只會儲存23小時的資料,而不是其他桶通常的24小時。相同的時間間隔越短如12h是真的。在這裡,我們將在3月27日上午在DST的轉變發生只有一11h鬥。
1.3、偏移
offset引數通過指定正(+)或負偏移(-)時間來修改每個分組的時間起始值,比如1h代表一小時,1M代表一個月。
例如,檔用day作為時間間隔,每個分組的時間範圍是從午夜到午夜。設定offset引數為+6h,會修改每個分組的時間範圍從6點到6點。
PUT my_index/log/1?refresh
{
"date": "2015-10-01T05:30:00Z"
}
PUT my_index/log/2?refresh
{
"date": "2015-10-01T06:30:00Z"
}
GET my_index/_search?size=0
{
"aggs": {
"by_day": {
"date_histogram": {
"field": "date",
"interval": "day",
"offset": "+6h"
}
}
}
}
而不是一個單一的開始時間在午夜開始,上述請求組檔案轉換成開始時間從早上6點:
{
...
"aggregations": {
"by_day": {
"buckets": [
{
"key_as_string": "2015-09-30T06:00:00.000Z",
"key": 1443592800000,
"doc_count": 1
},
{
"key_as_string": "2015-10-01T06:00:00.000Z",
"key": 1443679200000,
"doc_count": 1
}
]
}
}
}
Note:開始時間偏移量在時間區間做出調整後是計算在內的。
1.4、返回鍵控
將鍵控標誌設定為true將與每個開始時間關聯唯一的字串鍵,並將範圍返回為雜湊而不是陣列:
POST /sales/_search?size=0
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "date",
"interval" : "1M",
"format" : "yyyy-MM-dd",
"keyed": true
}
}
}
}
返回值:
{
...
"aggregations": {
"sales_over_time": {
"buckets": {
"2015-01-01": {
"key_as_string": "2015-01-01",
"key": 1420070400000,
"doc_count": 3
},
"2015-02-01": {
"key_as_string": "2015-02-01",
"key": 1422748800000,
"doc_count": 2
},
"2015-03-01": {
"key_as_string": "2015-03-01",
"key": 1425168000000,
"doc_count": 2
}
}
}
}
}
Java API呼叫
匯入聚合定義類:
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
下面是一個如何建立聚合請求的示例:
AggregationBuilder aggregation = AggregationBuilders
.dateHistogram("agg")
.field("dateOfBirth")
.dateHistogramInterval(DateHistogramInterval.YEAR);
SearchRequestBuilder srb = client.prepareSearch(indexName)
.setTypes(typeName);
srb.addAggregation(aggregation);
SearchResponse response = srb.execute().actionGet();
Histogram agg = response.getAggregations().get("agg");
// For each entry
for (Histogram.Bucket entry : agg.getBuckets()) {
DateTime key = (DateTime) entry.getKey(); // Key
String keyAsString = entry.getKeyAsString(); // Key as String
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], date [{}], doc_count [{}]", keyAsString, key.getYear(), docCount);
}
或者如果你想設定10天的間隔:
AggregationBuilder aggregation = AggregationBuilders
.dateHistogram("agg")
.field("dateOfBirth")
.dateHistogramInterval(DateHistogramInterval.days(10));
earchRequestBuilder srb = client.prepareSearch(indexName)
.setTypes(typeName);
srb.addAggregation(aggregation);
SearchResponse response = srb.execute().actionGet();
Histogram agg = response.getAggregations().get("agg");
// For each entry
for (Histogram.Bucket entry : agg.getBuckets()) {
DateTime key = (DateTime) entry.getKey(); // Key
String keyAsString = entry.getKeyAsString(); // Key as String
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], date [{}], doc_count [{}]", keyAsString, key.getYear(), docCount);
}
2、時間範圍聚合
時間範圍聚合是一個專門用於時間型別資料的範圍聚合,與普通的範圍聚合最大的區別在於,from和to引數值可以使用日期數值表示式,也可以指定返回的響應中from和to欄位的日期格式。注意時間範圍聚合的每個範圍裡面包含from值但排除to值。
示例:
POST /sales/_search?size=0
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyy",
"ranges": [
{ "to": "now-10M/M" },
{ "from": "now-10M/M" }
]
}
}
}
}
說明:現在減去10個月,並跳轉到月份的開始
在上面的例子裡,我們建立了兩個範圍分組,第一個會包含日期在10個月之前的所有檔案,第二個會包含從10個月前到現在的所有檔案。
返回值:
{
...
"aggregations": {
"range": {
"buckets": [
{
"to": 1.4436576E12,
"to_as_string": "10-2015",
"doc_count": 7,
"key": "*-10-2015"
},
{
"from": 1.4436576E12,
"from_as_string": "10-2015",
"doc_count": 0,
"key": "10-2015-*"
}
]
}
}
}
2.1、時間格式
所有ASCII字母都保留為格式模式字母,其定義如下:
符號 | 含義 | 描述 | 示例 |
---|---|---|---|
G | era | text | AD |
C | century of era (>=0) | number | 20 |
Y | year of era (>=0) | year | 1996 |
x | weekyear | year | 1996 |
w | week of weekyear | number | 27 |
e | day of week | number | 2 |
E | day of week | text | Tuesday; Tue |
y | year | year | 1996 |
D | day of year | number | 189 |
M | month of year | month | July; Jul; 07 |
d | day of month | number | 10 |
a | halfday of day | text | PM |
K | hour of halfday (0~11) | number | 0 |
h | clockhour of halfday (1~12) | number | 12 |
H | hour of day (0~23) | number | 0 |
k | clockhour of day (1~24) | number | 24 |
m | minute of hour | number | 30 |
s | second of minute | number | 55 |
S | fraction of second | number | 978 |
z | time zone | text | Pacific Standard Time; PST |
Z | time zone offset/id | zone | -0800; -08:00; America/Los_Angeles |
‘ | escape for text | delimiter | ” |
模式字母的計數決定格式。
Text
如果模式字母的數目是4或更多,則使用完整形式;否則,如果可用,則使用縮寫或縮寫形式。
Number
最小位數。較短的數字是零填充到這個數額。
Year
年與weekyear場數值表示的特殊處理。例如,如果y的計數為2,則該年份將顯示為本世紀的零基年份,即兩位數。
Month
使用3或以上,使用text;否則使用number。
Zone
Z的輸出偏移沒有冒號,ZZ輸出偏移一個冒號,ZZZ或多個輸出區域ID。
Zone names
時區名稱(z)無法解析。
模式中的任何字元不在[a…z]或者[A..Z]的範圍內,將被視為引用文字。
例如,人物如:,,,“#和?將出現在結果時間文字,即使他們不接受單引號內。
2.2、時間範圍聚合中的時間區間
日期可以從另一個時區的時間轉換為UTC通過指定time_zone引數。
時間區間引數也適用於資料的數學表示式的舍入。舉個例子,在CET時間段開始時,你可以做以下幾件事:
POST /sales/_search?size=0
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"time_zone": "CET",
"ranges": [
{ "to": "2016/02/01" },
{ "from": "2016/02/01", "to" : "now/d" },
{ "from": "now/d" }
]
}
}
}
}
該日期將被轉換為2016-02-15t00:00:00.000 + 01:00。
now/d 將在CET時間段結束。
2.3、返回鍵控
將鍵控標誌設定為true將與每個開始時間關聯唯一的字串鍵,並將範圍返回為雜湊而不是陣列:
POST /sales/_search?size=0
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyy",
"ranges": [
{ "to": "now-10M/M" },
{ "from": "now-10M/M" }
],
"keyed": true
}
}
}
}
返回值:
{
...
"aggregations": {
"range": {
"buckets": {
"*-10-2015": {
"to": 1.4436576E12,
"to_as_string": "10-2015",
"doc_count": 7
},
"10-2015-*": {
"from": 1.4436576E12,
"from_as_string": "10-2015",
"doc_count": 0
}
}
}
}
}
還可以自定義每個範圍的鍵:
POST /sales/_search?size=0
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyy",
"ranges": [
{ "from": "01-2015", "to": "03-2015", "key": "quarter_01" },
{ "from": "03-2015", "to": "06-2015", "key": "quarter_02" }
],
"keyed": true
}
}
}
}
返回值:
{
...
"aggregations": {
"range": {
"buckets": {
"quarter_01": {
"from": 1.4200704E12,
"from_as_string": "01-2015",
"to": 1.425168E12,
"to_as_string": "03-2015",
"doc_count": 5
},
"quarter_02": {
"from": 1.425168E12,
"from_as_string": "03-2015",
"to": 1.4331168E12,
"to_as_string": "06-2015",
"doc_count": 2
}
}
}
}
}
Java API呼叫
匯入聚合定義類:
import org.elasticsearch.search.aggregations.bucket.range.Range;
下面是一個如何建立聚合請求的示例:
AggregationBuilder aggregation = AggregationBuilders
.dateRange("agg")
.field("dateOfBirth")
.format("yyyy")
.addUnboundedTo("1950") // from -infinity to 1950 (excluded)
.addRange("1950", "1960") // from 1950 to 1960 (excluded)
.addUnboundedFrom("1960"); // from 1960 to +infinity
SearchRequestBuilder srb = client.prepareSearch(indexName)
.setTypes(typeName);
srb.addAggregation(aggregation);
SearchResponse response = srb.execute().actionGet();
// sr is here your SearchResponse object
Range agg = sr.getAggregations().get("agg");
// For each entry
for (Range.Bucket entry : agg.getBuckets()) {
String key = entry.getKeyAsString(); // Date range as key
DateTime fromAsDate = (DateTime) entry.getFrom(); // Date bucket from as a Date
DateTime toAsDate = (DateTime) entry.getTo(); // Date bucket to as a Date
long docCount = entry.getDocCount(); // Doc count
logger.info("key [{}], from [{}], to [{}], doc_count [{}]", key, fromAsDate, toAsDate, docCount);
}
這基本上會產生:
key [*-1950], from [null], to [1950-01-01T00:00:00.000Z], doc_count [8]
key [1950-1960], from [1950-01-01T00:00:00.000Z], to [1960-01-01T00:00:00.000Z], doc_count [5]
key [1960-*], from [1960-01-01T00:00:00.000Z], to [null], doc_count [37]
3、巢狀聚合
巢狀聚合是一種特殊的單分組聚合,可以聚合巢狀的文件。
例如,假設我們有一個產品索引,每個產品來自不同的經銷商——有著不同的價格。索引的對映如下:
{
...
"product":{
"properties":{
"resellers":{
"type":"nested",
“properties”:{
"name":{"type":"string"},
"price":{"type":"double"}
}
}
}
}
}
product物件下面的resellers是一個包含巢狀文件的陣列:
{
"query" : {"match" : {"name" : "led tv"}},
"agg" : {
"resellers" : {
"nested" : {"path" : "resellers"},
"agg" : {"min_price" : {"min" : {"field" : "resellers.price"}}}
}
}
}
這個示例會返回可以買到的產品的最低價。
嵌入聚合需要定義嵌入文件在頂級文件中的路徑(path)。我們可以在這些嵌入文件上定義任何型別的聚合。
返回值:
{
"aggregations" : {
"resellers" : {
“min_price” : {“value” : “350”}
}
}
}
Java API呼叫
下面是一個如何建立聚合請求的示例:
//時間範圍聚合
AggregationBuilder DateRange = AggregationBuilders
.dateRange("agg")
.field("tm")
.format("yyyy/MM/dd HH:mm:ss")
.addRange("2017/11/13 10:35:24", "2017/11/17");
//日期直方圖聚合
AggregationBuilder DateHistogram = AggregationBuilders
.dateHistogram("agg")
.field("tm")
.dateHistogramInterval(DateHistogramInterval.DAY);
//統計聚合
StatsAggregationBuilder Stats = AggregationBuilders
.stats("stats")
.field("value");
DateRange.subAggregation(DateHistogram.subAggregation(Stats));
SearchRequestBuilder srb = client.prepareSearch(indexName)
.setTypes(indexType);
//過濾條件(先過濾後聚合)
srb.setQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchPhraseQuery("uid", "0"))
.must(QueryBuilders.rangeQuery("value").gte(15)));
srb.addAggregation(DateRange);
SearchResponse response = srb.execute().actionGet();
Range agg = response.getAggregations().get("agg");
for (Range.Bucket entry : agg.getBuckets()) {
Histogram agg2 = (Histogram) entry.getAggregations().asMap().get("agg");
for(Histogram.Bucket entry2 : agg2.getBuckets()){
DateTime key = (DateTime) entry2.getKey(); // Key
String keyAsString = entry2.getKeyAsString(); // Key as String
double min = (double) entry2.getAggregations().asMap()
.get("stats").getProperty("min");
double max = (double) entry2.getAggregations().asMap()
.get("stats").getProperty("max");
double sum = (double) entry2.getAggregations().asMap()
.get("stats").getProperty("sum");
double avg = (double) entry2.getAggregations().asMap()
.get("stats").getProperty("avg");
double count = (double) entry2.getAggregations().asMap()
.get("stats").getProperty("count");
if(count>0){
System.out.println("key [{"+keyAsString+"}], date [{"+key.getDayOfMonth()+"}], doc_count [{"+count+"}, min_value[{"+min+"}], max_value[{"+max+"}], sum_value[{"+sum+"}], avg_value[{"+avg+"}]]");
}
}
}
相關推薦
Elasticsearch Java API 的使用(13)—分組聚合之一
分組聚和不像度量聚合那樣通過欄位進行計算,而是根據文件建立分組。每個聚合都關聯一個標準(取決於聚合的型別),決定了一個文件在當前的條件下是否會“劃入”分組中。 換句話說,分組實際上定義了一個文件集。除了這些分組之外,分組聚和也會計算和返回“劃入”每個分組中文件
Elasticsearch Java API 的使用(12)—度量聚合之一
度量聚合從文件中提取出來的值並進行計算。這些值通常從文件中的欄位(使用資料欄位)中提取出來,單也可以使用指令碼進行計算。 數字型度量聚合是一種特殊型別的度量聚合,輸出數字型的值。聚合輸出一個數字指標(例如平均值聚合)稱之為單值數字型度量聚合,產生多個指標值(例
zabbix的Java API(一)
strong 走了 .html image catch 是我 後來 resp 登錄密碼 上文說了,我是對zabbix做第二次開發的小白,既然要對zabbix做第二次開發又是小白,那麽就得來研究zabbix提供的相關API了。 於是我在zabbix網站各種找,終於在下面網
Java API (二)
height val 類型 highlight pic bool 包裝 大寫 clas 數組高級以及Arrays(掌握) 1、排序 2、查找 3、Arrays 工具類 Integer(掌握) 1、為了讓基本類型的數據進行更多的操作
JAVA基礎(13)---順序和選擇控制語句
流程控制 Java中的流程控制有三類:順序控制、選擇控制和迴圈控制 順序控制語句 在Java中的程式的執行流程: &nbs
zookeeper java api(2)
這裡介紹其他的API對zookeeper的操作。 同步方式獲取子節點資料 public static void getChildrenSync() throws KeeperException, InterruptedException {
zookeeper java api(1)
1 Zookeeper安裝以及啟動 這裡我已經進行了安裝,並且啟動了Zookeeper。埠是2182 2 Zookeeper config tickTime=2000 initLimit=10 syncLimit=5 dataDir=D://zook
Elasticsearch(二):使用JAVA API實現簡單查詢、聚合查詢
ES版本:2.3.1 JDK:1.8 所需要的jar包請在ES安裝路徑下的jars包中獲得,不要使用其他的jar否則容易出現版本問題! 注意:程式碼中TransportClient client=ESLink.getTransportClient()
elasticsearch-java api之文件(document)各種操作
使用java api和es互動時,可以是json字串、map物件;es中內建Jackson json序列化機制,可以將自定義物件轉成string或者byte,然後傳給es-java api使用。 1、新增document: public static bool
分散式搜尋elasticsearch java API 之 highlighting (對搜尋結果的高亮顯示)
搜尋請求的Body如下:: { "query" : {...}, "highlight" : { "fields" : { "title":{}, "intro" : {}
elasticsearch-java api之搜尋(一)
1、全文搜尋兩個最重要的方面是: 1)相關性(Relevance) 它是評價查詢與其結果間的相關程度,並根據這種相關程度對結果排名的一種能力,這種計算方式可以是 TF/IDF 方法(參見 相關性的介紹)、地理位置鄰近、模糊相似,或其他的某些演算法。 2)分析(Analysi
分散式搜尋elasticsearch java API 之(六)------批量新增刪除索引
elasticsearch支援批量新增或刪除索引文件,java api裡面就是通過構造BulkRequestBuilder,然後把批量的index/delete請求新增到BulkRequestBuilder裡面,執行BulkRequestBuilder。下面是個例子: im
ElasticSearch 5.3 java Api(增刪改)使用
話不多說,環境是ElasticSearch 安裝教程 可以看這個。我的環境是5.3 + 分詞 。 一、Index的建立 5.x的預設是不會在你插入資料的時候主動建立index的,所以網上其他地方的介紹程式碼,都有問題。你是沒法直接用的。 建
Elasticsearch Java API 的使用(11)—優化索引建立之mapping設定
優化索引建立 一、_all all欄位是把所有其它欄位中的值,以空格為分隔符組成一個大字串,然後被分析和索引,但是不儲存,也就是說它能被查詢,但不能被取回顯示。 _all欄位預設是關閉的,如果要開啟_all欄位,索引增大是不言而喻的。_all欄位開啟適
elasticsearch-java api之索引(index)的各種操作
1、建立索引: 1)簡單索引——沒有指定mapping public static boolean createIndex(String indexName) { IndicesAdminClient indicesAdminClient = transportClie
Elasticsearch Java API 的使用(2)—建立索引
Java建立索引 建立索引前需要建立elasticsearch客戶端,可檢視Elasticsearch Java API 的使用(1)—建立客戶端 public class EsIndex{
Elasticsearch Java API 的使用(17)—實現term查詢和terms查詢
term查詢 trem查詢只可指定查詢一個欄位對應單個詞條 public class EsTermQuery{ public void updateIndex(TransportClie
Elasticsearch Java API 的使用(7)—多條件查詢
多條件設定 //多條件設定 MatchPhraseQueryBuilder mpq1 = QueryBuilders .matchPhraseQuery("poin
Elasticsearch Java API 的使用(8)—Scroll (遊標)API詳解
滾動查詢 Elasticsearch中進行大資料量查詢時,往往因為裝置、網路傳輸問題影響查詢資料的效率;Elasticsearch中提供了Scroll(遊標)的方式對資料進行少量多批次的滾動查詢,來提高查詢效率。 public class Scroll
分散式搜尋elasticsearch java API 之(八)------使用More like this實現基於內容的推薦
基於內容的推薦通常是給定一篇文件資訊,然後給使用者推薦與該文件相識的文件。Lucene的api中有實現查詢文章相似度的介面,叫MoreLikeThis。Elasticsearch封裝了該介面,通過Elasticsearch的More like this查詢介面,我們可以非常