dataset

package
v1.2.0-rc2 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2026 License: MIT Imports: 6 Imported by: 0

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 Count

type Count struct {
	InQuery     int64 `json:"inQuery"`
	InPartition int64 `json:"inPartition"`
}

Count is the response count envelope (redline DataSetResponse.count).

type CountFunc

type CountFunc func(context.Context, *metaquery.Builder) (int64, error)

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.

func Shape

func Shape(b *metaquery.Builder, req Request, cfg Config) (Rendered, error)

Shape applies req to b: partition first, then search (both AND into WHERE), then whitelisted ordering, then clamped pagination (with total when ShowCounts). It returns the normalized search/partition strings. No DB.

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.

type ScanFunc

type ScanFunc[T any] func(context.Context, *metaquery.Builder) (*metaquery.TypedResult[T], error)

ScanFunc executes a shaped builder and returns typed rows + Meta. Satisfied by a closure over mqpgx.Scan[T] / mqsqlite.Scan[T].

Jump to

Keyboard shortcuts

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