1// Copyright 2020-2021 InfluxData, Inc. All rights reserved.
2// Use of this source code is governed by MIT
3// license that can be found in the LICENSE file.
4
5// Package query defined types for representing flux query result
6package query
7
8import (
9	"fmt"
10	"strings"
11	"time"
12)
13
14// FluxTableMetadata holds flux query result table information represented by collection of columns.
15// Each new table is introduced by annotations
16type FluxTableMetadata struct {
17	position int
18	columns  []*FluxColumn
19}
20
21// FluxColumn holds flux query table column properties
22type FluxColumn struct {
23	index        int
24	name         string
25	dataType     string
26	group        bool
27	defaultValue string
28}
29
30// FluxRecord represents row in the flux query result table
31type FluxRecord struct {
32	table  int
33	values map[string]interface{}
34}
35
36// NewFluxTableMetadata creates FluxTableMetadata for the table on position
37func NewFluxTableMetadata(position int) *FluxTableMetadata {
38	return NewFluxTableMetadataFull(position, make([]*FluxColumn, 0, 10))
39}
40
41// NewFluxTableMetadataFull creates FluxTableMetadata
42func NewFluxTableMetadataFull(position int, columns []*FluxColumn) *FluxTableMetadata {
43	return &FluxTableMetadata{position: position, columns: columns}
44}
45
46// Position returns position of the table in the flux query result
47func (f *FluxTableMetadata) Position() int {
48	return f.position
49}
50
51// Columns returns slice of flux query result table
52func (f *FluxTableMetadata) Columns() []*FluxColumn {
53	return f.columns
54}
55
56// AddColumn adds column definition to table metadata
57func (f *FluxTableMetadata) AddColumn(column *FluxColumn) *FluxTableMetadata {
58	f.columns = append(f.columns, column)
59	return f
60}
61
62// Column returns flux table column by index.
63// Returns nil if index is out of the bounds.
64func (f *FluxTableMetadata) Column(index int) *FluxColumn {
65	if len(f.columns) == 0 || index < 0 || index >= len(f.columns) {
66		return nil
67	}
68	return f.columns[index]
69}
70
71// String returns FluxTableMetadata string dump
72func (f *FluxTableMetadata) String() string {
73	var buffer strings.Builder
74	for i, c := range f.columns {
75		if i > 0 {
76			buffer.WriteString(",")
77		}
78		buffer.WriteString("col")
79		buffer.WriteString(c.String())
80	}
81	return buffer.String()
82}
83
84// NewFluxColumn creates FluxColumn for position
85func NewFluxColumn(index int) *FluxColumn {
86	return &FluxColumn{index: index}
87}
88
89// NewFluxColumnFull creates FluxColumn
90func NewFluxColumnFull(dataType string, defaultValue string, name string, group bool, index int) *FluxColumn {
91	return &FluxColumn{index: index, name: name, dataType: dataType, group: group, defaultValue: defaultValue}
92}
93
94// SetDefaultValue sets default value for the column
95func (f *FluxColumn) SetDefaultValue(defaultValue string) {
96	f.defaultValue = defaultValue
97}
98
99// SetGroup set group flag for the column
100func (f *FluxColumn) SetGroup(group bool) {
101	f.group = group
102}
103
104// SetDataType sets data type for the column
105func (f *FluxColumn) SetDataType(dataType string) {
106	f.dataType = dataType
107}
108
109// SetName sets name of the column
110func (f *FluxColumn) SetName(name string) {
111	f.name = name
112}
113
114// DefaultValue returns default value of the column
115func (f *FluxColumn) DefaultValue() string {
116	return f.defaultValue
117}
118
119// IsGroup return true if the column is grouping column
120func (f *FluxColumn) IsGroup() bool {
121	return f.group
122}
123
124// DataType returns data type of the column
125func (f *FluxColumn) DataType() string {
126	return f.dataType
127}
128
129// Name returns name of the column
130func (f *FluxColumn) Name() string {
131	return f.name
132}
133
134// Index returns index of the column
135func (f *FluxColumn) Index() int {
136	return f.index
137}
138
139// String returns FluxColumn string dump
140func (f *FluxColumn) String() string {
141	return fmt.Sprintf("{%d: name: %s, datatype: %s, defaultValue: %s, group: %v}", f.index, f.name, f.dataType, f.defaultValue, f.group)
142}
143
144// NewFluxRecord returns new record for the table with values
145func NewFluxRecord(table int, values map[string]interface{}) *FluxRecord {
146	return &FluxRecord{table: table, values: values}
147}
148
149// Table returns index of the table record belongs to
150func (r *FluxRecord) Table() int {
151	return r.table
152}
153
154// Start returns the inclusive lower time bound of all records in the current table.
155// Returns empty time.Time if there is no column "_start".
156func (r *FluxRecord) Start() time.Time {
157	return timeValue(r.values, "_start")
158}
159
160// Stop returns the exclusive upper time bound of all records in the current table.
161// Returns empty time.Time if there is no column "_stop".
162func (r *FluxRecord) Stop() time.Time {
163	return timeValue(r.values, "_stop")
164}
165
166// Time returns the time of the record.
167// Returns empty time.Time if there is no column "_time".
168func (r *FluxRecord) Time() time.Time {
169	return timeValue(r.values, "_time")
170}
171
172// Value returns the default _value column value or nil if not present
173func (r *FluxRecord) Value() interface{} {
174	return r.ValueByKey("_value")
175}
176
177// Field returns the field name.
178// Returns empty string if there is no column "_field".
179func (r *FluxRecord) Field() string {
180	return stringValue(r.values, "_field")
181}
182
183// Result returns the value of the _result column, which represents result name.
184// Returns empty string if there is no column "result".
185func (r *FluxRecord) Result() string {
186	return stringValue(r.values, "result")
187}
188
189// Measurement returns the measurement name of the record
190// Returns empty string if there is no column "_measurement".
191func (r *FluxRecord) Measurement() string {
192	return stringValue(r.values, "_measurement")
193}
194
195// Values returns map of the values where key is the column name
196func (r *FluxRecord) Values() map[string]interface{} {
197	return r.values
198}
199
200// ValueByKey returns value for given column key for the record or nil of result has no value the column key
201func (r *FluxRecord) ValueByKey(key string) interface{} {
202	return r.values[key]
203}
204
205// String returns FluxRecord string dump
206func (r *FluxRecord) String() string {
207	var buffer strings.Builder
208	i := 0
209	for k, v := range r.values {
210		if i > 0 {
211			buffer.WriteString(",")
212		}
213		buffer.WriteString(fmt.Sprintf("%s:%v", k, v))
214		i++
215	}
216	return buffer.String()
217}
218
219// timeValue returns time.Time value from values map according to the key
220// Empty time.Time value is returned if key is not found
221func timeValue(values map[string]interface{}, key string) time.Time {
222	if val, ok := values[key]; ok {
223		if t, ok := val.(time.Time); ok {
224			return t
225		}
226	}
227	return time.Time{}
228}
229
230// timeValue returns string value from values map according to the key
231// Empty string is returned if key is not found
232func stringValue(values map[string]interface{}, key string) string {
233	if val, ok := values[key]; ok {
234		if s, ok := val.(string); ok {
235			return s
236		}
237	}
238	return ""
239}
240