1// Copyright 2012-present Oliver Eilhard. All rights reserved. 2// Use of this source code is governed by a MIT-license. 3// See http://olivere.mit-license.org/license.txt for details. 4 5package elastic 6 7import ( 8 "time" 9) 10 11// DateRangeAggregation is a range aggregation that is dedicated for 12// date values. The main difference between this aggregation and the 13// normal range aggregation is that the from and to values can be expressed 14// in Date Math expressions, and it is also possible to specify a 15// date format by which the from and to response fields will be returned. 16// Note that this aggregration includes the from value and excludes the to 17// value for each range. 18// See: https://www.elastic.co/guide/en/elasticsearch/reference/5.2/search-aggregations-bucket-daterange-aggregation.html 19type DateRangeAggregation struct { 20 field string 21 script *Script 22 subAggregations map[string]Aggregation 23 meta map[string]interface{} 24 keyed *bool 25 unmapped *bool 26 format string 27 entries []DateRangeAggregationEntry 28} 29 30type DateRangeAggregationEntry struct { 31 Key string 32 From interface{} 33 To interface{} 34} 35 36func NewDateRangeAggregation() *DateRangeAggregation { 37 return &DateRangeAggregation{ 38 subAggregations: make(map[string]Aggregation), 39 entries: make([]DateRangeAggregationEntry, 0), 40 } 41} 42 43func (a *DateRangeAggregation) Field(field string) *DateRangeAggregation { 44 a.field = field 45 return a 46} 47 48func (a *DateRangeAggregation) Script(script *Script) *DateRangeAggregation { 49 a.script = script 50 return a 51} 52 53func (a *DateRangeAggregation) SubAggregation(name string, subAggregation Aggregation) *DateRangeAggregation { 54 a.subAggregations[name] = subAggregation 55 return a 56} 57 58// Meta sets the meta data to be included in the aggregation response. 59func (a *DateRangeAggregation) Meta(metaData map[string]interface{}) *DateRangeAggregation { 60 a.meta = metaData 61 return a 62} 63 64func (a *DateRangeAggregation) Keyed(keyed bool) *DateRangeAggregation { 65 a.keyed = &keyed 66 return a 67} 68 69func (a *DateRangeAggregation) Unmapped(unmapped bool) *DateRangeAggregation { 70 a.unmapped = &unmapped 71 return a 72} 73 74func (a *DateRangeAggregation) Format(format string) *DateRangeAggregation { 75 a.format = format 76 return a 77} 78 79func (a *DateRangeAggregation) AddRange(from, to interface{}) *DateRangeAggregation { 80 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: to}) 81 return a 82} 83 84func (a *DateRangeAggregation) AddRangeWithKey(key string, from, to interface{}) *DateRangeAggregation { 85 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: to}) 86 return a 87} 88 89func (a *DateRangeAggregation) AddUnboundedTo(from interface{}) *DateRangeAggregation { 90 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: nil}) 91 return a 92} 93 94func (a *DateRangeAggregation) AddUnboundedToWithKey(key string, from interface{}) *DateRangeAggregation { 95 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: nil}) 96 return a 97} 98 99func (a *DateRangeAggregation) AddUnboundedFrom(to interface{}) *DateRangeAggregation { 100 a.entries = append(a.entries, DateRangeAggregationEntry{From: nil, To: to}) 101 return a 102} 103 104func (a *DateRangeAggregation) AddUnboundedFromWithKey(key string, to interface{}) *DateRangeAggregation { 105 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: nil, To: to}) 106 return a 107} 108 109func (a *DateRangeAggregation) Lt(to interface{}) *DateRangeAggregation { 110 a.entries = append(a.entries, DateRangeAggregationEntry{From: nil, To: to}) 111 return a 112} 113 114func (a *DateRangeAggregation) LtWithKey(key string, to interface{}) *DateRangeAggregation { 115 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: nil, To: to}) 116 return a 117} 118 119func (a *DateRangeAggregation) Between(from, to interface{}) *DateRangeAggregation { 120 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: to}) 121 return a 122} 123 124func (a *DateRangeAggregation) BetweenWithKey(key string, from, to interface{}) *DateRangeAggregation { 125 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: to}) 126 return a 127} 128 129func (a *DateRangeAggregation) Gt(from interface{}) *DateRangeAggregation { 130 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: nil}) 131 return a 132} 133 134func (a *DateRangeAggregation) GtWithKey(key string, from interface{}) *DateRangeAggregation { 135 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: nil}) 136 return a 137} 138 139func (a *DateRangeAggregation) Source() (interface{}, error) { 140 // Example: 141 // { 142 // "aggs" : { 143 // "range" : { 144 // "date_range": { 145 // "field": "date", 146 // "format": "MM-yyy", 147 // "ranges": [ 148 // { "to": "now-10M/M" }, 149 // { "from": "now-10M/M" } 150 // ] 151 // } 152 // } 153 // } 154 // } 155 // } 156 // 157 // This method returns only the { "date_range" : { ... } } part. 158 159 source := make(map[string]interface{}) 160 opts := make(map[string]interface{}) 161 source["date_range"] = opts 162 163 // ValuesSourceAggregationBuilder 164 if a.field != "" { 165 opts["field"] = a.field 166 } 167 if a.script != nil { 168 src, err := a.script.Source() 169 if err != nil { 170 return nil, err 171 } 172 opts["script"] = src 173 } 174 175 if a.keyed != nil { 176 opts["keyed"] = *a.keyed 177 } 178 if a.unmapped != nil { 179 opts["unmapped"] = *a.unmapped 180 } 181 if a.format != "" { 182 opts["format"] = a.format 183 } 184 185 var ranges []interface{} 186 for _, ent := range a.entries { 187 r := make(map[string]interface{}) 188 if ent.Key != "" { 189 r["key"] = ent.Key 190 } 191 if ent.From != nil { 192 switch from := ent.From.(type) { 193 case int, int16, int32, int64, float32, float64: 194 r["from"] = from 195 case *int, *int16, *int32, *int64, *float32, *float64: 196 r["from"] = from 197 case time.Time: 198 r["from"] = from.Format(time.RFC3339) 199 case *time.Time: 200 r["from"] = from.Format(time.RFC3339) 201 case string: 202 r["from"] = from 203 case *string: 204 r["from"] = from 205 } 206 } 207 if ent.To != nil { 208 switch to := ent.To.(type) { 209 case int, int16, int32, int64, float32, float64: 210 r["to"] = to 211 case *int, *int16, *int32, *int64, *float32, *float64: 212 r["to"] = to 213 case time.Time: 214 r["to"] = to.Format(time.RFC3339) 215 case *time.Time: 216 r["to"] = to.Format(time.RFC3339) 217 case string: 218 r["to"] = to 219 case *string: 220 r["to"] = to 221 } 222 } 223 ranges = append(ranges, r) 224 } 225 opts["ranges"] = ranges 226 227 // AggregationBuilder (SubAggregations) 228 if len(a.subAggregations) > 0 { 229 aggsMap := make(map[string]interface{}) 230 source["aggregations"] = aggsMap 231 for name, aggregate := range a.subAggregations { 232 src, err := aggregate.Source() 233 if err != nil { 234 return nil, err 235 } 236 aggsMap[name] = src 237 } 238 } 239 240 // Add Meta data if available 241 if len(a.meta) > 0 { 242 source["meta"] = a.meta 243 } 244 245 return source, nil 246} 247