github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/query/time_bucketizer_test.go (about) 1 // Copyright (c) 2017-2018 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package query 16 17 import ( 18 "encoding/json" 19 "time" 20 21 "github.com/onsi/ginkgo" 22 . "github.com/onsi/gomega" 23 "github.com/uber-go/tally" 24 "github.com/uber/aresdb/common" 25 "github.com/uber/aresdb/memstore" 26 "github.com/uber/aresdb/query/expr" 27 "github.com/uber/aresdb/utils" 28 ) 29 30 var _ = ginkgo.Describe("Time Bucketizer", func() { 31 var qc *AQLQueryContext 32 ginkgo.BeforeEach(func() { 33 q := &AQLQuery{ 34 Table: "trips", 35 Measures: []Measure{ 36 {Expr: "count()"}, 37 }, 38 TimeFilter: TimeFilter{ 39 From: "-1d", 40 To: "0d", 41 }, 42 Dimensions: []Dimension{Dimension{Expr: "requested_at", TimeBucketizer: "hour"}}, 43 } 44 qc = &AQLQueryContext{ 45 Query: q, 46 } 47 48 qc.parseExprs() 49 }) 50 51 ginkgo.It("qc.buildTimeDimensionExpr for regular interval should work", func() { 52 timeColumn := &expr.VarRef{ 53 Val: "request_at", 54 } 55 exp, err := qc.buildTimeDimensionExpr("Day", timeColumn) 56 Ω(exp).ShouldNot(BeNil()) 57 Ω(exp.String()).Should(Equal("request_at FLOOR 86400")) 58 Ω(err).Should(BeNil()) 59 60 exp, err = qc.buildTimeDimensionExpr("m", timeColumn) 61 Ω(exp).ShouldNot(BeNil()) 62 Ω(exp.String()).Should(Equal("request_at FLOOR 60")) 63 Ω(err).Should(BeNil()) 64 65 exp, err = qc.buildTimeDimensionExpr("3m", timeColumn) 66 Ω(exp).ShouldNot(BeNil()) 67 Ω(exp.String()).Should(Equal("request_at FLOOR 180")) 68 Ω(err).Should(BeNil()) 69 70 exp, err = qc.buildTimeDimensionExpr("3 minutes", timeColumn) 71 Ω(exp).ShouldNot(BeNil()) 72 Ω(exp.String()).Should(Equal("request_at FLOOR 180")) 73 Ω(err).Should(BeNil()) 74 75 exp, err = qc.buildTimeDimensionExpr("h", timeColumn) 76 Ω(exp).ShouldNot(BeNil()) 77 Ω(exp.String()).Should(Equal("request_at FLOOR 3600")) 78 Ω(err).Should(BeNil()) 79 80 exp, err = qc.buildTimeDimensionExpr("4h", timeColumn) 81 Ω(exp).ShouldNot(BeNil()) 82 Ω(exp.String()).Should(Equal("request_at FLOOR 14400")) 83 Ω(err).Should(BeNil()) 84 85 exp, err = qc.buildTimeDimensionExpr("4 hours", timeColumn) 86 Ω(exp).ShouldNot(BeNil()) 87 Ω(exp.String()).Should(Equal("request_at FLOOR 14400")) 88 Ω(err).Should(BeNil()) 89 90 exp, err = qc.buildTimeDimensionExpr("d", timeColumn) 91 Ω(exp).ShouldNot(BeNil()) 92 Ω(exp.String()).Should(Equal("request_at FLOOR 86400")) 93 Ω(err).Should(BeNil()) 94 95 exp, err = qc.buildTimeDimensionExpr("week", timeColumn) 96 Ω(exp).ShouldNot(BeNil()) 97 Ω(exp.String()).Should(Equal("GET_WEEK_START(request_at)")) 98 Ω(err).Should(BeNil()) 99 100 utils.Init(common.AresServerConfig{Query: common.QueryConfig{TimezoneTable: common.TimezoneConfig{ 101 TableName: "", 102 }}}, common.NewLoggerFactory().GetDefaultLogger(), common.NewLoggerFactory().GetDefaultLogger(), tally.NewTestScope("test", nil)) 103 qc.timezoneTable.tableColumn = "timezone" 104 qc.timezoneTable.tableAlias = defaultTimezoneTableAlias 105 qc.TableIDByAlias = map[string]int{defaultTimezoneTableAlias: 0} 106 qc.TableScanners = []*TableScanner{{Schema: &memstore.TableSchema{ColumnIDs: map[string]int{"timezone": 1}}}} 107 exp, err = qc.buildTimeDimensionExpr("week", timeColumn) 108 Ω(exp).ShouldNot(BeNil()) 109 Ω(exp.String()).Should(Equal("GET_WEEK_START(request_at CONVERT_TZ __timezone_lookup.timezone)")) 110 Ω(err).Should(BeNil()) 111 112 qc.timezoneTable.tableColumn = "" 113 qc.fixedTimezone = time.FixedZone("Foo", 2018) 114 qc.fromTime = &alignedTime{Time: utils.Now().In(qc.fixedTimezone)} 115 qc.toTime = &alignedTime{Time: utils.Now().In(qc.fixedTimezone)} 116 exp, err = qc.buildTimeDimensionExpr("week", timeColumn) 117 Ω(exp).ShouldNot(BeNil()) 118 Ω(exp.String()).Should(Equal("GET_WEEK_START(request_at CONVERT_TZ 2018)")) 119 Ω(err).Should(BeNil()) 120 121 // bucket "Day-X" is illegal 122 exp, err = qc.buildTimeDimensionExpr("Day-X", timeColumn) 123 Ω(exp).Should(BeNil()) 124 Ω(err).ShouldNot(BeNil()) 125 126 // 7m can not align to start of hour 127 exp, err = qc.buildTimeDimensionExpr("7m", timeColumn) 128 Ω(exp).Should(BeNil()) 129 Ω(err).ShouldNot(BeNil()) 130 131 // 7h can not align to start of day 132 exp, err = qc.buildTimeDimensionExpr("7h", timeColumn) 133 Ω(exp).Should(BeNil()) 134 Ω(err).ShouldNot(BeNil()) 135 136 // 0m is not valid 137 exp, err = qc.buildTimeDimensionExpr("7m", timeColumn) 138 Ω(exp).Should(BeNil()) 139 Ω(err).ShouldNot(BeNil()) 140 141 // non minute/hour Unit with Size is not valid 142 exp, err = qc.buildTimeDimensionExpr("1 centry", timeColumn) 143 Ω(exp).Should(BeNil()) 144 Ω(err).ShouldNot(BeNil()) 145 utils.ResetDefaults() 146 }) 147 148 ginkgo.It("qc.buildTimeDimensionExpr for irregular interval should work", func() { 149 timeColumn := &expr.VarRef{ 150 Val: "request_at", 151 } 152 exp, err := qc.buildTimeDimensionExpr("month", timeColumn) 153 Ω(exp).ShouldNot(BeNil()) 154 Ω(exp.String()).Should(Equal("GET_MONTH_START(request_at)")) 155 Ω(err).Should(BeNil()) 156 157 exp, err = qc.buildTimeDimensionExpr("quarter", timeColumn) 158 Ω(exp).ShouldNot(BeNil()) 159 Ω(exp.String()).Should(Equal("GET_QUARTER_START(request_at)")) 160 Ω(err).Should(BeNil()) 161 162 exp, err = qc.buildTimeDimensionExpr("year", timeColumn) 163 Ω(exp).ShouldNot(BeNil()) 164 Ω(exp.String()).Should(Equal("GET_YEAR_START(request_at)")) 165 Ω(err).Should(BeNil()) 166 }) 167 168 ginkgo.It("qc.buildTimeDimensionExpr for regular recurring time bucketizer should work", func() { 169 timeColumn := &expr.VarRef{ 170 Val: "request_at", 171 } 172 exp, err := qc.buildTimeDimensionExpr("time of day", timeColumn) 173 Ω(exp).ShouldNot(BeNil()) 174 Ω(exp.String()).Should(Equal("request_at % 86400")) 175 Ω(err).Should(BeNil()) 176 177 exp, err = qc.buildTimeDimensionExpr("2 minutes of day", timeColumn) 178 Ω(err).Should(BeNil()) 179 Ω(exp).ShouldNot(BeNil()) 180 Ω(exp.String()).Should(Equal("request_at % 86400 FLOOR 120")) 181 182 exp, err = qc.buildTimeDimensionExpr("7 minutes of day", timeColumn) 183 Ω(err).ShouldNot(BeNil()) 184 Ω(err.Error()).Should(ContainSubstring("Only {2,3,4,5,6,10,15,20,30} minutes of day are allowed : got 7 minutes of day")) 185 186 exp, err = qc.buildTimeDimensionExpr("hour of day", timeColumn) 187 Ω(exp).ShouldNot(BeNil()) 188 Ω(exp.String()).Should(Equal("request_at % 86400 FLOOR 3600")) 189 Ω(err).Should(BeNil()) 190 191 exp, err = qc.buildTimeDimensionExpr("hour of week", timeColumn) 192 Ω(exp).ShouldNot(BeNil()) 193 Ω(exp.String()).Should(Equal("request_at - 345600 % 604800 FLOOR 3600")) 194 Ω(err).Should(BeNil()) 195 196 exp, err = qc.buildTimeDimensionExpr("day of week", timeColumn) 197 Ω(exp).ShouldNot(BeNil()) 198 Ω(exp.String()).Should(Equal("request_at - 345600 % 604800 FLOOR 86400 / 86400.00")) 199 Ω(err).Should(BeNil()) 200 }) 201 202 ginkgo.It("qc.buildTimeDimensionExpr for irregular recurring time bucketizer should work", func() { 203 timeColumn := &expr.VarRef{ 204 Val: "request_at", 205 } 206 exp, err := qc.buildTimeDimensionExpr("day of month", timeColumn) 207 Ω(exp).ShouldNot(BeNil()) 208 Ω(exp.String()).Should(Equal("GET_DAY_OF_MONTH(request_at)")) 209 Ω(err).Should(BeNil()) 210 211 exp, err = qc.buildTimeDimensionExpr("day of year", timeColumn) 212 Ω(exp).ShouldNot(BeNil()) 213 Ω(exp.String()).Should(Equal("GET_DAY_OF_YEAR(request_at)")) 214 Ω(err).Should(BeNil()) 215 216 exp, err = qc.buildTimeDimensionExpr("month of year", timeColumn) 217 Ω(exp).ShouldNot(BeNil()) 218 Ω(exp.String()).Should(Equal("GET_MONTH_OF_YEAR(request_at)")) 219 Ω(err).Should(BeNil()) 220 221 exp, err = qc.buildTimeDimensionExpr("quarter of year", timeColumn) 222 Ω(exp).ShouldNot(BeNil()) 223 Ω(exp.String()).Should(Equal("GET_QUARTER_OF_YEAR(request_at)")) 224 Ω(err).Should(BeNil()) 225 }) 226 227 ginkgo.It("parses query with TimeSeriesBucketizer", func() { 228 goodQuery := &AQLQuery{ 229 Table: "trips", 230 Dimensions: []Dimension{ 231 { 232 Expr: "request_at", 233 TimeBucketizer: "quarter-hour", 234 }, 235 }, 236 } 237 qc.Query = goodQuery 238 qc.parseExprs() 239 Ω(qc.Error).Should(BeNil()) 240 241 // missing time column 242 badQuery := &AQLQuery{ 243 Table: "trips", 244 Dimensions: []Dimension{ 245 { 246 TimeBucketizer: "quarter-hour", 247 }, 248 }, 249 } 250 qc.Query = badQuery 251 qc.parseExprs() 252 Ω(qc.Error).ShouldNot(BeNil()) 253 }) 254 255 ginkgo.It("fixed timezone across DST switch timestamp", func() { 256 qc = &AQLQueryContext{ 257 Query: &AQLQuery{ 258 Table: "trips", 259 Measures: []Measure{ 260 {Expr: "count()"}, 261 }, 262 TimeFilter: TimeFilter{ 263 From: "1509772380", 264 To: "1509882360", 265 }, 266 Dimensions: []Dimension{Dimension{Expr: "requested_at", TimeBucketizer: "hour"}}, 267 Timezone: "America/Los_Angeles", 268 }, 269 } 270 qc.processTimezone() 271 Ω(qc.Error).Should(BeNil()) 272 qc.parseExprs() 273 Ω(qc.Error).Should(BeNil()) 274 Ω(qc.fromTime).ShouldNot(BeNil()) 275 Ω(qc.toTime).ShouldNot(BeNil()) 276 Ω(qc.fixedTimezone.String()).Should(Equal("America/Los_Angeles")) 277 bb, _ := json.Marshal(qc.Query.Dimensions[0].expr) 278 Ω(string(bb)).Should(MatchJSON( 279 ` 280 { 281 "Op": "FLOOR", 282 "LHS": { 283 "Op": "+", 284 "LHS": { 285 "Val": "requested_at", 286 "ExprType": "Unknown", 287 "TableID": 0, 288 "ColumnID": 0, 289 "DataType": 0, 290 "IsHLLColumn": false 291 }, 292 "RHS": { 293 "Op": "+", 294 "LHS": { 295 "Val": 0, 296 "Int": -25200, 297 "Expr": "-25200", 298 "ExprType": "Signed" 299 }, 300 "RHS": { 301 "Op": "*", 302 "LHS": { 303 "Val": 0, 304 "Int": 3600, 305 "Expr": "3600", 306 "ExprType": "Signed" 307 }, 308 "RHS": { 309 "Op": ">=", 310 "LHS": { 311 "Val": "requested_at", 312 "ExprType": "Unknown", 313 "TableID": 0, 314 "ColumnID": 0, 315 "DataType": 0, 316 "IsHLLColumn": false 317 }, 318 "RHS": { 319 "Val": 0, 320 "Int": 1509872400, 321 "Expr": "1509872400", 322 "ExprType": "Unknown" 323 }, 324 "ExprType": "Boolean" 325 }, 326 "ExprType": "Signed" 327 }, 328 "ExprType": "Unknown" 329 }, 330 "ExprType": "Unknown" 331 }, 332 "RHS": { 333 "Val": 0, 334 "Int": 3600, 335 "Expr": "3600", 336 "ExprType": "Unsigned" 337 }, 338 "ExprType": "Unknown" 339 }`)) 340 }) 341 })