query

package
v0.12.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2026 License: GPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package query implements the structured Honeycomb-style query builder: a JSON AST that callers send, translated by Build into parameterized SQL over the spans or logs tables.

The wire shape is the same struct, so a POST /api/query handler can decode straight into it with encoding/json.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatDisplay added in v0.6.0

func FormatDisplay(q *Query) string

FormatDisplay produces a single-line, Honeycomb-style one-glance summary of a query for the history list view — "SELECT COUNT WHERE x = y GROUP BY z". It's intentionally lossy: SELECTs collapse multiple aggregations into a comma list, filter values render via %v, HAVING/ORDER BY aren't surfaced. If you want the full AST, rehydrate from query_json.

func HashQuery added in v0.6.0

func HashQuery(q *Query) ([]byte, error)

HashQuery returns a stable content hash of the query suitable for query_history deduplication. Time-range bounds are excluded — two runs of "same query, different wall-clock window" dedupe to one history row. BucketMS and granularity (if present) are included because they're meaningful to the result shape.

Types

type AggOp

type AggOp string

AggOp is the aggregation function. Names mirror Honeycomb's query builder.

const (
	OpCount         AggOp = "count"          // COUNT(*)
	OpCountField    AggOp = "count_field"    // COUNT(field) — rows where field IS NOT NULL
	OpCountDistinct AggOp = "count_distinct" // COUNT(DISTINCT field)
	OpSum           AggOp = "sum"
	OpAvg           AggOp = "avg"
	OpMin           AggOp = "min"
	OpMax           AggOp = "max"
	OpP001          AggOp = "p001"
	OpP01           AggOp = "p01"
	OpP05           AggOp = "p05"
	OpP10           AggOp = "p10"
	OpP25           AggOp = "p25"
	OpP50           AggOp = "p50"
	OpP75           AggOp = "p75"
	OpP90           AggOp = "p90"
	OpP95           AggOp = "p95"
	OpP99           AggOp = "p99"
	OpP999          AggOp = "p999"

	// Rate aggregations compute the per-second delta of the underlying
	// aggregation between consecutive time buckets. They require bucket_ms
	// to be set; the first bucket in each group emits NULL (no prior point
	// to diff against). The underlying aggregations are SUM/AVG/MAX
	// respectively — the rate layer is a post-processing step in the
	// executor, not a SQL expression.
	OpRateSum AggOp = "rate_sum"
	OpRateAvg AggOp = "rate_avg"
	OpRateMax AggOp = "rate_max"
)

type Aggregation

type Aggregation struct {
	Op    AggOp  `json:"op"`
	Field string `json:"field,omitempty"`
	Alias string `json:"alias,omitempty"`
}

Aggregation is one entry in SELECT.

type Column

type Column struct {
	Name string `json:"name"`
	Type string `json:"type"` // "string" | "int" | "float" | "bool" | "time"
}

Column describes one output column of a compiled query — used by both the executor (for scanning) and the HTTP response.

type Compiled

type Compiled struct {
	SQL     string
	Args    []any
	Columns []Column
	// HasBucket reports whether the query included a time bucket; when true,
	// the first column is the bucket time and callers should group rows into
	// time-series lines keyed by the GROUP BY tuple.
	HasBucket bool
	// GroupKeys holds the aliases of GROUP BY expressions (used to split
	// rows into series).
	GroupKeys []string
	// Rates describes post-processing transforms that turn underlying SUM/
	// AVG/MAX columns into per-second deltas. Executor applies them after
	// scanning the rows from SQLite.
	Rates []RateSpec
}

Compiled is the output of Build — a parameterized SQL statement plus the column metadata for decoding the result rows.

func Build

func Build(q *Query) (Compiled, error)

Build translates a validated Query into a parameterized SQL statement against the unified `events` table. Dataset selects a signal_type='…' preset filter prepended to the WHERE clause. Field references route to real columns when the key is whitelisted; otherwise they fall through to json_extract(attributes, '$."<key>"').

An empty Select means raw-rows mode: Build emits SELECT <fixed columns> and lets the caller iterate events instead of aggregates.

The caller must have invoked Query.Validate() first — Build assumes its input is well-formed.

type Dataset

type Dataset string

Dataset pins a query to exactly one signal type: spans/logs query the shared events table with a signal_type predicate; metrics query the metric_events table. Cross-signal queries are not supported — each table has its own column set, so a mixed-signal SELECT can't be expressed without a UNION that doesn't pay for its complexity.

const (
	DatasetSpans   Dataset = "spans"
	DatasetLogs    Dataset = "logs"
	DatasetMetrics Dataset = "metrics"
)

type Filter

type Filter struct {
	Field string   `json:"field"`
	Op    FilterOp `json:"op"`
	Value any      `json:"value,omitempty"`
}

Filter is one WHERE/HAVING predicate.

type FilterOp

type FilterOp string

FilterOp is the comparison operator. For SQL codegen, the equality-style operators are used literally as the operator between field and "?".

const (
	FilterEq           FilterOp = "="
	FilterNe           FilterOp = "!="
	FilterGt           FilterOp = ">"
	FilterGe           FilterOp = ">="
	FilterLt           FilterOp = "<"
	FilterLe           FilterOp = "<="
	FilterIn           FilterOp = "in"
	FilterNotIn        FilterOp = "!in"
	FilterExists       FilterOp = "exists"
	FilterNotExist     FilterOp = "!exists"
	FilterContains     FilterOp = "contains"
	FilterNotContain   FilterOp = "!contains"
	FilterStartsWith   FilterOp = "starts-with"
	FilterNotStartWith FilterOp = "!starts-with"
	FilterEndsWith     FilterOp = "ends-with"
	FilterNotEndWith   FilterOp = "!ends-with"
)

type JSONTime

type JSONTime struct {
	time.Time
}

JSONTime accepts RFC3339Nano strings or integer nanoseconds in the payload. The underlying storage is a time.Time; Unix() extracts nanoseconds for the SQL layer.

func (JSONTime) UnixNano

func (t JSONTime) UnixNano() int64

func (*JSONTime) UnmarshalJSON

func (t *JSONTime) UnmarshalJSON(b []byte) error

type Order

type Order struct {
	Field string `json:"field"`
	Dir   string `json:"dir,omitempty"` // "asc" | "desc" (default desc)
}

Order is one ORDER BY entry. The field may be a raw attribute name or one of the SELECT aliases.

type Query

type Query struct {
	Dataset   Dataset       `json:"dataset"`
	TimeRange TimeRange     `json:"time_range"`
	Select    []Aggregation `json:"select"`
	Where     []Filter      `json:"where,omitempty"`
	GroupBy   []string      `json:"group_by,omitempty"`
	OrderBy   []Order       `json:"order_by,omitempty"`
	Having    []Filter      `json:"having,omitempty"`
	Limit     int           `json:"limit,omitempty"`
	// BucketMS, when > 0, groups rows into time buckets of that size and
	// emits a `bucket_ns` column as the first SELECT entry. Use this for
	// time-series charts.
	BucketMS int64 `json:"bucket_ms,omitempty"`
}

Query is the wire-level structured query.

func (*Query) Validate

func (q *Query) Validate() error

Validate rejects obviously malformed queries before we start generating SQL. The builder performs its own field-name resolution against the per-dataset whitelist; this is the coarse first line of defense.

type RateSpec

type RateSpec struct {
	ColumnIndex int
	BucketSecs  float64
}

RateSpec identifies a column that should be transformed into a rate after execution. ColumnIndex is the 0-based index into the result's Columns/Rows.

type TimeRange

type TimeRange struct {
	From JSONTime `json:"from"`
	To   JSONTime `json:"to"`
}

TimeRange is absolute — relative ranges like "last 1h" must be resolved by the caller before the query is sent. That keeps the server stateless and the request reproducible.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL