func NewTimestampAggregator(query *parser.Query, _ *parser.Value) (Aggregator, error) { duration, err := query.GetGroupByClause().GetGroupByTime() if err != nil { return nil, err } var durationPtr *int64 if duration != nil { newDuration := int64(*duration / time.Microsecond) durationPtr = &newDuration } return &TimestampAggregator{ timestamps: make(map[string]map[interface{}]int64), duration: durationPtr, }, nil }
func (self *QueryEngine) executeCountQueryWithGroupBy(user common.User, database string, query *parser.Query, yield func(*protocol.Series) error) error { duration, err := query.GetGroupByClause().GetGroupByTime() if err != nil { return err } aggregators := []Aggregator{} for _, value := range query.GetColumnNames() { if value.IsFunctionCall() { lowerCaseName := strings.ToLower(value.Name) initializer := registeredAggregators[lowerCaseName] if initializer == nil { return common.NewQueryError(common.InvalidArgument, fmt.Sprintf("Unknown function %s", value.Name)) } aggregator, err := initializer(query, value) if err != nil { return err } aggregators = append(aggregators, aggregator) } } timestampAggregator, err := NewTimestampAggregator(query, nil) if err != nil { return err } groups := make(map[string]map[interface{}]bool) groupBy := query.GetGroupByClause() var inverse InverseMapper err = self.distributeQuery(user, database, query, func(series *protocol.Series) error { var mapper Mapper mapper, inverse, err = createValuesToInterface(groupBy, series.Fields) if err != nil { return err } for _, aggregator := range aggregators { if err := aggregator.InitializeFieldsMetadata(series); err != nil { return err } } for _, point := range series.Points { value := mapper(point) for _, aggregator := range aggregators { aggregator.AggregatePoint(*series.Name, value, point) } timestampAggregator.AggregatePoint(*series.Name, value, point) seriesGroups := groups[*series.Name] if seriesGroups == nil { seriesGroups = make(map[interface{}]bool) groups[*series.Name] = seriesGroups } seriesGroups[value] = true } return nil }) if err != nil { return err } var sequenceNumber uint32 = 1 fields := []string{} for _, aggregator := range aggregators { columnName := aggregator.ColumnName() fields = append(fields, columnName) } for _, value := range groupBy { if value.IsFunctionCall() { continue } tempName := value.Name fields = append(fields, tempName) } for table, tableGroups := range groups { tempTable := table points := []*protocol.Point{} for groupId, _ := range tableGroups { timestamp := *timestampAggregator.GetValue(table, groupId)[0].Int64Value values := [][]*protocol.FieldValue{} for _, aggregator := range aggregators { values = append(values, aggregator.GetValue(table, groupId)) } // do cross product of all the values values = crossProduct(values) for _, v := range values { /* groupPoints := []*protocol.Point{} */ point := &protocol.Point{ SequenceNumber: &sequenceNumber, Values: v, } point.SetTimestampInMicroseconds(timestamp) // FIXME: this should be looking at the fields slice not the group by clause // FIXME: we should check whether the selected columns are in the group by clause for idx, _ := range groupBy { if duration != nil && idx == 0 { continue } value := inverse(groupId, idx) switch x := value.(type) { case string: point.Values = append(point.Values, &protocol.FieldValue{StringValue: &x}) case bool: point.Values = append(point.Values, &protocol.FieldValue{BoolValue: &x}) case float64: point.Values = append(point.Values, &protocol.FieldValue{DoubleValue: &x}) case int64: point.Values = append(point.Values, &protocol.FieldValue{Int64Value: &x}) } } points = append(points, point) } } expectedData := &protocol.Series{ Name: &tempTable, Fields: fields, Points: points, } yield(expectedData) } return nil }