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  })