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/6.7/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 timeZone string 27 format string 28 entries []DateRangeAggregationEntry 29} 30 31type DateRangeAggregationEntry struct { 32 Key string 33 From interface{} 34 To interface{} 35} 36 37func NewDateRangeAggregation() *DateRangeAggregation { 38 return &DateRangeAggregation{ 39 subAggregations: make(map[string]Aggregation), 40 entries: make([]DateRangeAggregationEntry, 0), 41 } 42} 43 44func (a *DateRangeAggregation) Field(field string) *DateRangeAggregation { 45 a.field = field 46 return a 47} 48 49func (a *DateRangeAggregation) Script(script *Script) *DateRangeAggregation { 50 a.script = script 51 return a 52} 53 54func (a *DateRangeAggregation) SubAggregation(name string, subAggregation Aggregation) *DateRangeAggregation { 55 a.subAggregations[name] = subAggregation 56 return a 57} 58 59// Meta sets the meta data to be included in the aggregation response. 60func (a *DateRangeAggregation) Meta(metaData map[string]interface{}) *DateRangeAggregation { 61 a.meta = metaData 62 return a 63} 64 65func (a *DateRangeAggregation) Keyed(keyed bool) *DateRangeAggregation { 66 a.keyed = &keyed 67 return a 68} 69 70func (a *DateRangeAggregation) Unmapped(unmapped bool) *DateRangeAggregation { 71 a.unmapped = &unmapped 72 return a 73} 74 75func (a *DateRangeAggregation) TimeZone(timeZone string) *DateRangeAggregation { 76 a.timeZone = timeZone 77 return a 78} 79 80func (a *DateRangeAggregation) Format(format string) *DateRangeAggregation { 81 a.format = format 82 return a 83} 84 85func (a *DateRangeAggregation) AddRange(from, to interface{}) *DateRangeAggregation { 86 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: to}) 87 return a 88} 89 90func (a *DateRangeAggregation) AddRangeWithKey(key string, from, to interface{}) *DateRangeAggregation { 91 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: to}) 92 return a 93} 94 95func (a *DateRangeAggregation) AddUnboundedTo(from interface{}) *DateRangeAggregation { 96 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: nil}) 97 return a 98} 99 100func (a *DateRangeAggregation) AddUnboundedToWithKey(key string, from interface{}) *DateRangeAggregation { 101 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: nil}) 102 return a 103} 104 105func (a *DateRangeAggregation) AddUnboundedFrom(to interface{}) *DateRangeAggregation { 106 a.entries = append(a.entries, DateRangeAggregationEntry{From: nil, To: to}) 107 return a 108} 109 110func (a *DateRangeAggregation) AddUnboundedFromWithKey(key string, to interface{}) *DateRangeAggregation { 111 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: nil, To: to}) 112 return a 113} 114 115func (a *DateRangeAggregation) Lt(to interface{}) *DateRangeAggregation { 116 a.entries = append(a.entries, DateRangeAggregationEntry{From: nil, To: to}) 117 return a 118} 119 120func (a *DateRangeAggregation) LtWithKey(key string, to interface{}) *DateRangeAggregation { 121 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: nil, To: to}) 122 return a 123} 124 125func (a *DateRangeAggregation) Between(from, to interface{}) *DateRangeAggregation { 126 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: to}) 127 return a 128} 129 130func (a *DateRangeAggregation) BetweenWithKey(key string, from, to interface{}) *DateRangeAggregation { 131 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: to}) 132 return a 133} 134 135func (a *DateRangeAggregation) Gt(from interface{}) *DateRangeAggregation { 136 a.entries = append(a.entries, DateRangeAggregationEntry{From: from, To: nil}) 137 return a 138} 139 140func (a *DateRangeAggregation) GtWithKey(key string, from interface{}) *DateRangeAggregation { 141 a.entries = append(a.entries, DateRangeAggregationEntry{Key: key, From: from, To: nil}) 142 return a 143} 144 145func (a *DateRangeAggregation) Source() (interface{}, error) { 146 // Example: 147 // { 148 // "aggs" : { 149 // "range" : { 150 // "date_range": { 151 // "field": "date", 152 // "format": "MM-yyy", 153 // "ranges": [ 154 // { "to": "now-10M/M" }, 155 // { "from": "now-10M/M" } 156 // ] 157 // } 158 // } 159 // } 160 // } 161 // } 162 // 163 // This method returns only the { "date_range" : { ... } } part. 164 165 source := make(map[string]interface{}) 166 opts := make(map[string]interface{}) 167 source["date_range"] = opts 168 169 // ValuesSourceAggregationBuilder 170 if a.field != "" { 171 opts["field"] = a.field 172 } 173 if a.script != nil { 174 src, err := a.script.Source() 175 if err != nil { 176 return nil, err 177 } 178 opts["script"] = src 179 } 180 181 if a.keyed != nil { 182 opts["keyed"] = *a.keyed 183 } 184 if a.unmapped != nil { 185 opts["unmapped"] = *a.unmapped 186 } 187 if a.timeZone != "" { 188 opts["time_zone"] = a.timeZone 189 } 190 if a.format != "" { 191 opts["format"] = a.format 192 } 193 194 var ranges []interface{} 195 for _, ent := range a.entries { 196 r := make(map[string]interface{}) 197 if ent.Key != "" { 198 r["key"] = ent.Key 199 } 200 if ent.From != nil { 201 switch from := ent.From.(type) { 202 case int, int16, int32, int64, float32, float64: 203 r["from"] = from 204 case *int, *int16, *int32, *int64, *float32, *float64: 205 r["from"] = from 206 case time.Time: 207 r["from"] = from.Format(time.RFC3339) 208 case *time.Time: 209 r["from"] = from.Format(time.RFC3339) 210 case string: 211 r["from"] = from 212 case *string: 213 r["from"] = from 214 } 215 } 216 if ent.To != nil { 217 switch to := ent.To.(type) { 218 case int, int16, int32, int64, float32, float64: 219 r["to"] = to 220 case *int, *int16, *int32, *int64, *float32, *float64: 221 r["to"] = to 222 case time.Time: 223 r["to"] = to.Format(time.RFC3339) 224 case *time.Time: 225 r["to"] = to.Format(time.RFC3339) 226 case string: 227 r["to"] = to 228 case *string: 229 r["to"] = to 230 } 231 } 232 ranges = append(ranges, r) 233 } 234 opts["ranges"] = ranges 235 236 // AggregationBuilder (SubAggregations) 237 if len(a.subAggregations) > 0 { 238 aggsMap := make(map[string]interface{}) 239 source["aggregations"] = aggsMap 240 for name, aggregate := range a.subAggregations { 241 src, err := aggregate.Source() 242 if err != nil { 243 return nil, err 244 } 245 aggsMap[name] = src 246 } 247 } 248 249 // Add Meta data if available 250 if len(a.meta) > 0 { 251 source["meta"] = a.meta 252 } 253 254 return source, nil 255} 256