Documentation
¶
Overview ¶
Package dataset turns a single DataSetRequest (search + partition + ordering + pagination) into a fully-shaped metaquery builder, so any metaquery select becomes a searchable/sortable/paginated "DataSet" in one call.
It is adapter-agnostic: Shape mutates a *metaquery.Builder (no DB), and Run takes a scan function so callers wire in their adapter (mqpgx.Scan / mqsqlite.Scan) without this package depending on pgx or database/sql.
b := db.WrapListUsers(arg) // generated builder over a sqlc query
res, err := dataset.Run(ctx, b, req, cfg,
func(ctx context.Context, b *metaquery.Builder) (*metaquery.TypedResult[UserView], error) {
return mqpgx.Scan[UserView](ctx, pool, b)
})
Search/partition are compiled by metaquery/search against the builder's output columns; cfg embeds search.Config, so the zero value already searches text columns and exact-matches typed columns by target.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ColumnInfo ¶
type ColumnInfo struct {
Name string `json:"name"` // output column name (snake_case)
Title string `json:"title"` // humanized name ("org_id" → "Org Id")
Type string `json:"type"` // coarse kind: text/int/float/bool/time/...
Searchable bool `json:"searchable"` // reachable by search (global or field:value)
Global bool `json:"global"` // matched by unqualified free-text terms
Orderable bool `json:"orderable"` // client may sort by it
Sort string `json:"sort,omitempty"` // "ASC"/"DESC" if currently sorted by this column
}
ColumnInfo describes one output column and its capabilities under the Config, for clients that render columns/headers dynamically (redline DataSetResponse.columns). Derived entirely from metadata + Config; no DB.
type Config ¶
type Config struct {
search.Config
Searchable []string // global free-text + targetable; empty+Targetable empty = type default
Targetable []string // targeted-only (field:value), not matched by bare terms
Orderable []string // allowed sort fields; empty = any output column
DefaultPageSize int // 0 -> 25
MaxPageSize int // 0 -> 200
}
Config drives shaping. The embedded search.Config customizes search/partition (Fields, Named); the zero value is sensible. Orderable, when non-empty, restricts which fields clients may sort by (OrderBy already whitelists against output columns, so this is an additional policy layer).
Searchable / Targetable mirror redline's per-field search config. When either is non-empty they form a HARD allowlist: columns in Searchable are global free-text + targetable; columns in Targetable are targeted-only (field:value, like redline's search(global=false)); every other column is NOT searchable by any path. This matches redline, where a field with no search config is not searchable. When both are empty, the type-driven default applies (all text columns global). Explicit per-column entries in search.Config.Fields always win over the allowlist. Names are matched case-insensitively.
type CountFunc ¶
CountFunc returns count(*) over a builder (its BuildCount query). Satisfied by a closure over the adapter's count path.
type Order ¶
type Order struct {
Field string `json:"field"`
Order string `json:"order" enum:"ASC,DESC" doc:"Sort direction."`
}
Order is one sort directive (mirrors redline's DataSetOrder).
type Rendered ¶
type Rendered struct {
Search string `json:"search,omitempty"`
Partition string `json:"partition,omitempty"`
}
Rendered carries the normalized (auto-escaped) search/partition strings, for DataSetResponse.searchRendered so the UI can show what actually ran.
type Request ¶
type Request struct {
Page int `json:"page,omitempty"`
PageSize int `json:"pageSize,omitempty"`
Ordering []Order `json:"ordering,omitempty"`
Search string `json:"search,omitempty"`
Partition string `json:"partition,omitempty"`
ShowCounts bool `json:"showCounts,omitempty"`
ShowColumns bool `json:"showColumns,omitempty"`
}
Request mirrors redline's DataSetRequest. Page is 0-indexed.
type Response ¶
type Response[T any] struct { Data []T `json:"data"` Count *Count `json:"count,omitempty"` Rendered Rendered `json:"rendered"` Columns []ColumnInfo `json:"columns,omitempty"` }
Response mirrors redline's DataSetResponse[T]. It is intentionally lean so it can be serialized straight to clients: it does NOT embed metaquery.Meta, which carries compiled filter exprs and bound argument values (see Builder .Meta() / the scan TypedResult if you need column/filter introspection). Columns is populated only when Request.ShowColumns.
func Run ¶
func Run[T any](ctx context.Context, b *metaquery.Builder, req Request, cfg Config, scan ScanFunc[T]) (Response[T], error)
Run shapes b from req and scans it, returning the DataSet response envelope. When req.ShowCounts, InQuery is the total after partition+search.
InPartition (rows after partition only) currently mirrors InQuery; a partition-only count is a second pass over a fresh builder — see RunWithPartitionCount and the plan's phase 4.
func RunWithPartitionCount ¶
func RunWithPartitionCount[T any]( ctx context.Context, newBuilder func() *metaquery.Builder, req Request, cfg Config, scan ScanFunc[T], count CountFunc, ) (Response[T], error)
RunWithPartitionCount is Run plus a true InPartition: it builds a second, partition-only builder via newBuilder (a fresh WrapXxx()), applies only the partition, and counts it. Use when count.inPartition must exclude the search.