Documentation
¶
Index ¶
- Constants
- Variables
- func ExplainStages(p Pipeline) string
- func SplitPrefix(p Pipeline) (Prefix, Pipeline)
- type AccumOp
- type AccumSpec
- type Accumulator
- type AddFieldsSpec
- type AddFieldsStage
- type ArrayExpr
- type CountSpec
- type CountStage
- type Ctx
- type Expr
- type FieldRefExpr
- type GroupSpec
- type GroupStage
- type LimitSpec
- type LimitStage
- type Limits
- type LiteralExpr
- type MatchSpec
- type MatchStage
- type MemAccount
- type ObjectExpr
- type Pipeline
- type Prefix
- type ProjectField
- type ProjectSpec
- type ProjectStage
- type SkipSpec
- type SkipStage
- type SortSpec
- type SortStage
- type Stage
- type StageSpec
- type UnwindSpec
- type UnwindStage
Constants ¶
const ( DefaultGroupLimit = 50_000 DefaultAccumArrayLimit = 10_000 DefaultMemoryLimit = 256 << 20 )
Variables ¶
var ( // ErrGroupLimitExceeded is returned when $group exceeds the configured // maximum number of unique group keys. ErrGroupLimitExceeded = errors.New("any-store/aggregate: group limit exceeded") // ErrAccumArrayLimitExceeded is returned when a $push or $addToSet // accumulator exceeds the configured maximum array length. ErrAccumArrayLimitExceeded = errors.New("any-store/aggregate: accumulator array limit exceeded") // ErrMemoryLimitExceeded is returned when the blocking stages of a // pipeline retain more than the configured memory budget. ErrMemoryLimitExceeded = errors.New("any-store/aggregate: memory limit exceeded") )
Functions ¶
func ExplainStages ¶
ExplainStages renders the in-pipeline stage list for Explain output, including the top-K annotation a following $skip/$limit folds into $sort.
func SplitPrefix ¶
SplitPrefix consumes the longest pushable pipeline prefix in canonical order — $match* → $sort? → $skip? → $limit? — and returns it together with the remaining stages (which run as in-pipeline operators). Pushdown stops at the first stage that synthesizes documents ($group/$project/$addFields/ $unwind/$count) and at any stage out of canonical order: a $match after $skip/$limit does not commute, and out-of-order combinations are rare enough that running them in-pipeline (correct, just unoptimized) beats rewrite complexity.
Types ¶
type Accumulator ¶
type Accumulator interface {
Step(val *anyenc.Value) error
Finalize(out *anyenc.Arena) (*anyenc.Value, error)
}
Accumulator folds the per-row values of one $group output field.
Step receives the already-evaluated argument expression (nil = missing); the value is transient — valid only during the call — so implementations retain data by marshaling into self-owned buffers. Finalize is called once, after the upstream drain; results may be allocated on out or on an accumulator-owned parser (the stage consumes the result before the next emit either way).
type AddFieldsSpec ¶
type AddFieldsSpec struct{ Fields []ProjectField }
func (AddFieldsSpec) String ¶
func (s AddFieldsSpec) String() string
type AddFieldsStage ¶
type AddFieldsStage struct {
Src Stage
Fields []ProjectField
// contains filtered or unexported fields
}
AddFieldsStage overlays computed fields onto each upstream document by mutating it in place (the same pattern FtsIter uses to inject _score): zero-copy, no output object rebuild. Mutation is safe because rows are transient — the underlying parse buffers are rewritten on the next row.
When an upstream $unwind may emit the same underlying document for several consecutive rows (undoOverlay, set by Build), the original value of every overlaid field is captured and rolled back before the next row is pulled: otherwise a later row of the same document would evaluate its expressions against the previous row's overlays instead of the stored fields.
func (*AddFieldsStage) Close ¶
func (s *AddFieldsStage) Close()
func (*AddFieldsStage) String ¶
func (s *AddFieldsStage) String() string
type ArrayExpr ¶
type ArrayExpr struct {
Exprs []Expr
}
ArrayExpr is an array expression: every element is an expression. Missing elements evaluate to null so the array length is preserved.
type CountStage ¶
CountStage drains the upstream stream and emits a single {"<field>": N} document.
func (*CountStage) Close ¶
func (s *CountStage) Close()
func (*CountStage) String ¶
func (s *CountStage) String() string
type Ctx ¶
type Ctx struct {
Context context.Context
RowArena *anyenc.Arena
Buf *syncpool.DocBuffer
Mem *MemAccount
}
Ctx carries per-pipeline shared state through the stage chain.
RowArena ownership: the arena is reset only by the current "row owner" — the source stage (once per source row), a blocking stage on its output side (once per emitted row), and $project/$addFields when Build proves nothing below them keeps arena-allocated data alive across pulls. Non-owning stages allocate on RowArena freely and never reset it, so synthesized values and zero-copy aliases of the source document stay valid for the whole life of the row.
type Expr ¶
type Expr interface {
// Eval returns the expression value for doc; nil means "missing".
// Results may be allocated on a; field refs alias doc (zero-copy) and are
// valid only while doc is.
Eval(a *anyenc.Arena, doc *anyenc.Value) (*anyenc.Value, error)
fmt.Stringer
}
Expr is an aggregation expression evaluated against one document.
v1 supports field references ("$a.b.c"), literals ({"$literal": x} or any plain value), and document/array expressions (objects and arrays whose members are themselves expressions, Mongo semantics). Compute operators ($add, $cond, ...) are additive future work: they become composite Expr implementations dispatched in ParseExpr.
func ParseExpr ¶
ParseExpr parses an aggregation expression value (Mongo expression-context rules): "$path" strings are field references, {"$literal": x} is an escaped literal, non-operator objects are document expressions, arrays are arrays of expressions, everything else is a literal. Unknown $-operators are rejected (compute operators are not supported in v1).
type FieldRefExpr ¶
type FieldRefExpr struct {
Field string // original spelling without "$", for String()
Path []string
}
FieldRefExpr resolves a pre-split document path ("$a.b.c").
func (*FieldRefExpr) String ¶
func (e *FieldRefExpr) String() string
type GroupStage ¶
type GroupStage struct {
Src Stage
Spec GroupSpec
Limits Limits
// contains filtered or unexported fields
}
GroupStage implements $group: a blocking hash aggregation.
Group keys are the marshaled anyenc bytes of the key expression (missing → null, Mongo semantics); the byte encoding is canonical, so byte equality is value equality. Lookups are alloc-free (map[string]X with a string(bytes) conversion the compiler elides); new keys are interned into a grow-only buffer with unsafe.String map keys (the query.In pattern). Group states live in one slab slice in first-seen order, which is also the emit order (unspecified for callers).
func (*GroupStage) Close ¶
func (s *GroupStage) Close()
func (*GroupStage) String ¶
func (s *GroupStage) String() string
type LimitStage ¶
LimitStage emits at most N rows and stops pulling upstream afterwards.
func (*LimitStage) Close ¶
func (s *LimitStage) Close()
func (*LimitStage) String ¶
func (s *LimitStage) String() string
type Limits ¶
type Limits struct {
// MaxGroups caps the number of unique $group keys.
MaxGroups int
// MaxAccumArrayLen caps $push / $addToSet result arrays.
MaxAccumArrayLen int
// MaxMemoryBytes caps retained bytes across all blocking stages.
MaxMemoryBytes int
}
Limits bounds the memory-unbounded parts of a pipeline. Zero values mean "use default"; negative values mean "unlimited".
func (Limits) WithDefaults ¶
type LiteralExpr ¶
type LiteralExpr struct {
// contains filtered or unexported fields
}
LiteralExpr yields the same constant value for every document. The value is detached from the input at parse time (owned bytes + dedicated parser), so it stays valid for the lifetime of the pipeline regardless of caller arenas.
func NewLiteralExpr ¶
func NewLiteralExpr(v *anyenc.Value) (*LiteralExpr, error)
func (*LiteralExpr) String ¶
func (e *LiteralExpr) String() string
type MatchStage ¶
MatchStage filters the upstream stream with a query filter. Matched rows are passed through untouched (1:1, no arena allocations); discarded rows are freed by the source's per-row arena reset on the following pull.
func (*MatchStage) Close ¶
func (s *MatchStage) Close()
func (*MatchStage) String ¶
func (s *MatchStage) String() string
type MemAccount ¶
type MemAccount struct {
// contains filtered or unexported fields
}
MemAccount is a coarse retained-bytes budget shared by the blocking stages of one pipeline ($group state, $sort arena). Streaming stages retain nothing and don't report.
func NewMemAccount ¶
func NewMemAccount(limit int) *MemAccount
func (*MemAccount) Add ¶
func (m *MemAccount) Add(n int) error
Add records n retained bytes and fails with ErrMemoryLimitExceeded once the budget is exhausted. n may be negative when a stage shrinks/releases.
func (*MemAccount) Used ¶
func (m *MemAccount) Used() int
Used returns the currently accounted retained bytes.
type ObjectExpr ¶
ObjectExpr is a document expression: an object whose values are expressions (Mongo semantics for non-operator objects in expression context, e.g. compound $group keys {"a":"$x","b":"$y"}). Missing member values are omitted from the result, like Mongo.
func (*ObjectExpr) String ¶
func (e *ObjectExpr) String() string
type Pipeline ¶
type Pipeline []StageSpec
Pipeline is a parsed aggregation pipeline: an ordered list of stage specs.
func MustParsePipeline ¶
MustParsePipeline is ParsePipeline that panics on error (tests).
func ParsePipeline ¶
ParsePipeline parses a JSON-ish aggregation pipeline (any input accepted by the same parser as Find conditions) into stage specs. The result is fully detached from the input value.
type Prefix ¶
type Prefix struct {
Filter query.Filter // folded leading $match chain; nil if none
Sort query.Sort // nil if no $sort was pushed
Skip int
Limit int // 0 = none
}
Prefix is the leading pipeline part that can be pushed down into the access plan (the regular Find machinery: index/CBO selection, FTS/vector sources, index-order sorts, cursor-level offset skips).
type ProjectField ¶
ProjectField is one output field of $project / $addFields.
type ProjectSpec ¶
type ProjectSpec struct{ Fields []ProjectField }
func (ProjectSpec) String ¶
func (s ProjectSpec) String() string
type ProjectStage ¶
type ProjectStage struct {
Src Stage
Fields []ProjectField
// contains filtered or unexported fields
}
ProjectStage replaces each upstream document with a new object containing only the specified fields. The output is built on RowArena; field values alias subtrees of the input document (zero-copy), which is safe because the input stays valid for the whole row lifetime.
func (*ProjectStage) Close ¶
func (s *ProjectStage) Close()
func (*ProjectStage) String ¶
func (s *ProjectStage) String() string
type SortSpec ¶
type SortStage ¶
type SortStage struct {
Src Stage
Spec SortSpec
TopK int
// contains filtered or unexported fields
}
SortStage is the blocking in-pipeline $sort over synthesized documents (stored-document sorts are pushed down to the access plan instead).
It mirrors qplanner.SortIter's packed-arena design, but each entry carries the full marshaled document after its sort-key tuple — rows here no longer correspond to stored docIds. With TopK > 0 (a following $skip/$limit was folded in by Build) a max-heap keeps only the smallest K entries: a losing row is built in the arena's spare tail and truncated away — zero retained bytes — and a fragmentation guard re-packs the arena when dead bytes from evictions exceed live data, keeping the high-water mark O(K).
Stability: the full sort uses slices.SortStableFunc over insertion-ordered entries; the top-K path appends a row sequence number to each key, which both totals the order (deterministic heap eviction: of equal keys the earliest rows survive) and makes the final sort stable by construction.
type Stage ¶
type Stage interface {
// Next returns the next document, or (nil, nil) at the end of the stream.
// The returned value is valid only until the next Next call on this stage.
Next(ctx *Ctx) (*anyenc.Value, error)
// Close releases stage resources. It must be safe to call multiple times
// and after a mid-stream abort.
Close()
// String describes the stage for Explain output. Never called on the hot
// path.
fmt.Stringer
}
Stage is a pull-driven aggregation operator over anyenc documents.
The chain is strictly synchronous: a stage produces a row only when its consumer asks for one, so row lifetimes are phase-locked across the chain.
func Build ¶
Build compiles parsed stage specs into an executable stage chain on top of source. The source must reset Ctx.RowArena once per row it yields (it is the default row owner).
Build also performs the arena-ownership analysis: $project / $addFields get permission to reset RowArena before pulling (freeing the previous row) when no stage below them keeps arena-allocated data alive across pulls. This keeps the arena footprint O(row) under $unwind row multiplication.
type StageSpec ¶
StageSpec is a parsed pipeline stage description (not yet an executable Stage; see Build).
type UnwindSpec ¶
type UnwindSpec struct {
Field string // original spelling without "$"
Path []string
PreserveNullAndEmptyArrays bool
}
func (UnwindSpec) String ¶
func (s UnwindSpec) String() string
type UnwindStage ¶
type UnwindStage struct {
Src Stage
Field string
Path []string
PreserveNullAndEmptyArrays bool
// contains filtered or unexported fields
}
UnwindStage emits one row per element of an array field, replacing the array with the current element by mutating the document in place: the element values stay alive because they belong to the original (detached) array value, while the document's field slot is repointed per emission. No arena allocations, no document rebuild.
Before pulling the next source row the original array is restored into the field slot. An upstream stage may hold and re-emit the same document (another $unwind multiplying rows does), so leaving the field pointing at the last element would corrupt every later row of that document.
Mongo semantics: missing / null / empty array drop the document (unless PreserveNullAndEmptyArrays, which emits it as-is — with the field removed for an empty array); a non-array value passes through unchanged.
func (*UnwindStage) Close ¶
func (s *UnwindStage) Close()
func (*UnwindStage) String ¶
func (s *UnwindStage) String() string