influxdb-client-go icon indicating copy to clipboard operation
influxdb-client-go copied to clipboard

Query into Point structs

Open jpmeijers opened this issue 2 years ago • 2 comments

Proposal: I'd like to have symmetry between writes and reads. When writing data into Infludb one would use a Point struct, with strict Tags, Fields, Measurement and Time values. When querying the data I want to see the returned results in this same format.

Current behavior: The Point object when writing data is clear, and follows the basic concepts of Tags and Fields. When querying data the result is a pointer that contains a messy combination of tables and columns. Filtering these columns into Tags and Fields are tricky.

Desired behavior: A query should ideally return the same format struct as what is written to the database. In other words I should be able to take a result and write it directly back to the database, without any data being lost. Updating a field would then be a write, update of the field value, and then a write.

Alternatives considered: Describe other solutions or features you considered.

Use case: Why is this important (helps with prioritizing requests)?

jpmeijers avatar May 26 '23 11:05 jpmeijers

type ResultPoint struct {
	Tags  map[string]string
	Field string
	Time  time.Time
	Value interface{}
}

func QueryIntoPoints(query string) ([]ResultPoint, error) {
	var resultPoints []ResultPoint

	result, err := queryApi.Query(context.Background(), query)
	if err != nil {
		return nil, err
	}
	// check for an error
	if result.Err() != nil {
		return nil, result.Err()
	}

	var tagKeys []string
	ignoredColumns := []string{"result", "table"}

	// Iterate over query response
	for result.Next() {
		// Notice when group key has changed
		if result.TableChanged() {
			tagKeys = tagKeys[:0]
			for _, col := range result.TableMetadata().Columns() {
				if strings.HasPrefix(col.Name(), "_") {
					// don't use
				} else if slices.Contains(ignoredColumns, col.Name()) {
					// don't use
				} else {
					tagKeys = append(tagKeys, col.Name())
				}
			}
		}

		// Create a point and store time and value
		resultPoint := ResultPoint{
			Tags:  make(map[string]string, 0),
			Time:  result.Record().Time(),
			Value: result.Record().Value(),
		}

		// Set tags
		for _, tagKey := range tagKeys {
			tagValue, ok := result.Record().ValueByKey(tagKey).(string)
			if ok {
				resultPoint.Tags[tagKey] = tagValue
			}
		}

		// Set field
		field, ok := result.Record().ValueByKey("_field").(string)
		if ok {
			resultPoint.Field = field
		}

		resultPoints = append(resultPoints, resultPoint)
	}

	return resultPoints, err
}

This gives me something that resembles a Point struct.

jpmeijers avatar May 26 '23 11:05 jpmeijers

Hi,

When sending data to InfluxDB, it needs to be in line protocol format. The point helper class essentially is a wrapper around that format. When querying data in InfluxDB v2, flux is used to query and return data in a format that is more easily used for additional querying, filtering, and parsing.

Having a function that would take the flux tables that are returned and converting that data into line protocol might be possible, assuming that all the field and tag data is present. We would accept a PR that provides this functionality, but it is not something we would prioritize as part of our own future development.

Thanks

powersj avatar May 30 '23 14:05 powersj