Documentation
¶
Overview ¶
Package sift provides a universal filter expression language using an Abstract Syntax Tree (AST). Write filter logic once, then implement backend-specific evaluators to translate it into native queries.
Index ¶
- Variables
- func ErrorNodeNotSupported[T Expression](expr T) error
- func ErrorOperationNotSupported(operation string) error
- func Format(registry *Registry, opts ...FormatOption) (string, error)
- func FormatFilter(expr Expression, registry *Registry) (string, error)
- func Thru(ctx context.Context, adapter Adapter, options ...Option) error
- type Adapter
- type AndEvaluator
- type AndOperation
- type Condition
- type ConditionEvaluator
- type CursorPagination
- type CursorPaginationEvaluator
- type CustomEvaluator
- type CustomExpression
- type CustomFormatter
- type Evaluator
- type Expression
- type ExpressionBuilder
- func Between[T comparable](field string, minVal, maxVal T) ExpressionBuilder
- func Contains[T comparable](field string, val T) ExpressionBuilder
- func Eq[T comparable](field string, val T) ExpressionBuilder
- func Exists(field string) ExpressionBuilder
- func Gt[T comparable](field string, val T) ExpressionBuilder
- func Gte[T comparable](field string, val T) ExpressionBuilder
- func In[T comparable](field string, val T) ExpressionBuilder
- func Lt[T comparable](field string, val T) ExpressionBuilder
- func Lte[T comparable](field string, val T) ExpressionBuilder
- func Neq[T comparable](field string, val T) ExpressionBuilder
- func NewExpressionBuilder(expr Expression) ExpressionBuilder
- func NotExists(field string) ExpressionBuilder
- type FormatOption
- type NotEvaluator
- type NotOperation
- type OffsetPagination
- type OffsetPaginationEvaluator
- type Operation
- type Option
- type OrEvaluator
- type OrOperation
- type PaginationBuilder
- type PaginationExpression
- type Parser
- type Query
- type Registry
- type SortBuilder
- type SortDirection
- type SortExpression
- type SortField
- type SortFieldEvaluator
- type SortList
- type SortListEvaluator
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUnsupported indicates an operation or node type is not supported by the evaluator. ErrUnsupported = errors.ErrUnsupported )
Functions ¶
func ErrorNodeNotSupported ¶
func ErrorNodeNotSupported[T Expression](expr T) error
ErrorNodeNotSupported returns an error indicating the expression type is not supported.
func ErrorOperationNotSupported ¶
ErrorOperationNotSupported returns an error indicating the operation is not supported.
func Format ¶
func Format(registry *Registry, opts ...FormatOption) (string, error)
Format serializes filter, sort, and pagination expressions to a string using prefix notation. The format is URL-safe and unambiguous, suitable for query strings.
If registry is nil, no custom expressions are supported.
Supported filter operations:
eq(field,value) // equal ne(field,value) // not equal lt(field,value) // less than le(field,value) // less than or equal gt(field,value) // greater than ge(field,value) // greater than or equal contains(field,value) // substring match begins_with(field,value) // prefix match in(field,value) // value in list exists(field) // field exists not_exists(field) // field doesn't exist between(field,value) // value between bounds and(expr1,expr2) // logical AND or(expr1,expr2) // logical OR not(expr) // logical NOT
Sort format:
field:asc // single field ascending field:desc // single field descending field:asc:nullslast // with nulls last field1:desc,field2:asc // multiple fields
Pagination format:
size:20,number:2 // offset-based (page number) size:20,cursor:token123 // cursor-based
Custom expressions registered in the registry are also supported.
Special characters (commas, parentheses, backslashes, colons) in values are escaped with backslash.
Example:
str, _ := sift.Format(registry,
sift.WithFilter(filterExpr),
sift.WithSort(sortExpr),
sift.WithPagination(pageExpr))
// Output: filter(and(eq(status,active),gt(age,18))),sort(created_at:desc,name:asc),page(size:20,number:2)
func FormatFilter ¶
func FormatFilter(expr Expression, registry *Registry) (string, error)
FormatFilter serializes a filter expression to a string using prefix notation. This is a convenience function for formatting only a filter expression.
Example:
str, _ := sift.FormatFilter(filterExpr, registry) // Output: and(eq(status,active),gt(age,18))
Types ¶
type Adapter ¶
type Adapter interface {
// Evaluator returns an evaluator configured for the specific backend.
// The evaluator should have the appropriate interface implementations set
// based on what operations the backend supports.
Evaluator(ctx context.Context) *Evaluator
}
Adapter provides the bridge between the sift filter AST and backend-specific implementations. Backends implement this interface to provide their own evaluator that can translate filter expressions into native queries or operations.
type AndEvaluator ¶
type AndEvaluator interface {
EvaluateAnd(ctx context.Context, node *AndOperation) error
}
AndEvaluator evaluates AND operations. Implement this interface to support logical AND in your backend.
type AndOperation ¶
type AndOperation struct {
Left Expression
Right Expression
}
AndOperation represents a logical AND between two filter expressions.
func (AndOperation) String ¶
func (a AndOperation) String() string
String returns the string representation of the AND operation.
type Condition ¶
type Condition struct {
Name string // attribute name
Operation Operation // operation type (use Operation constants)
Value string // comparison value
}
Condition represents a single filter condition (e.g., "status = active").
func (*Condition) And ¶
func (c *Condition) And(expr Expression) ExpressionBuilder
And creates a logical AND operation between this condition and another expression. Returns a Builder that can be used to chain additional operations.
func (*Condition) Not ¶
func (c *Condition) Not() ExpressionBuilder
Not creates a logical NOT operation, negating this condition. Returns an ExpressionBuilder that can be used to chain additional operations.
func (*Condition) Or ¶
func (c *Condition) Or(expr Expression) ExpressionBuilder
Or creates a logical OR operation between this condition and another expression. Returns a Builder that can be used to chain additional operations.
type ConditionEvaluator ¶
ConditionEvaluator evaluates single filter conditions. Implement this interface to handle basic comparisons in your backend.
type CursorPagination ¶
type CursorPagination struct {
Size int // Number of items per page
Cursor string // Opaque cursor token for the next page
}
CursorPagination represents cursor-based pagination (cursor + size). This is commonly used with DynamoDB, GraphQL, and other cursor-based APIs.
type CursorPaginationEvaluator ¶
type CursorPaginationEvaluator interface {
EvaluateCursorPagination(ctx context.Context, page *CursorPagination) error
}
CursorPaginationEvaluator evaluates cursor-based pagination.
type CustomEvaluator ¶
type CustomEvaluator interface {
EvaluateCustom(ctx context.Context, node CustomExpression) error
}
CustomEvaluator evaluates custom node types. Implement this interface to support backend-specific operations.
type CustomExpression ¶
CustomExpression allows backends to define custom node types beyond the standard operations.
type CustomFormatter ¶
type CustomFormatter interface {
// FormatCustomExpression serializes a custom expression to a string.
FormatCustomExpression(expression CustomExpression) (string, error)
// ParseCustomExpression deserializes a string to a [CustomExpression].
// The parser has already consumed the function name and opening parenthesis.
// The parser should consume up to and including the closing parenthesis.
ParseCustomExpression(p *Parser) (CustomExpression, error)
}
CustomFormatter handles serialization and deserialization of CustomExpression types.
type Evaluator ¶
type Evaluator struct {
ConditionEvaluator
AndEvaluator
OrEvaluator
NotEvaluator
CustomEvaluator
SortFieldEvaluator
SortListEvaluator
OffsetPaginationEvaluator
CursorPaginationEvaluator
}
Evaluator holds the interface implementations for evaluating filter nodes. Backend implementations should embed this in their evaluator types and set only the interfaces they support.
type Expression ¶
Expression represents an expression in the filter AST.
func NewCustomExpression ¶
func NewCustomExpression(custom CustomExpression) Expression
NewCustomExpression wraps a custom node implementation to make it compatible with the Expression interface. This allows backends to define their own node types while integrating with the standard AST.
type ExpressionBuilder ¶
type ExpressionBuilder struct {
// contains filtered or unexported fields
}
ExpressionBuilder provides a fluent interface for constructing complex filter expressions. It wraps an Expression and allows chaining of logical operations like And, Or, and Not.
func Between ¶
func Between[T comparable](field string, minVal, maxVal T) ExpressionBuilder
Between creates a new ExpressionBuilder for range checking. The resulting expression will be true if the field value is between minVal and maxVal (inclusive).
func Contains ¶
func Contains[T comparable](field string, val T) ExpressionBuilder
Contains creates a new ExpressionBuilder for a containment check. The resulting expression will be true if the field value contains the given value.
func Eq ¶
func Eq[T comparable](field string, val T) ExpressionBuilder
Eq creates a new ExpressionBuilder for an equality comparison. The resulting expression will be true if the field value equals the given value.
func Exists ¶
func Exists(field string) ExpressionBuilder
Exists creates a new ExpressionBuilder that checks if a field exists. The resulting expression will be true if the field is present.
func Gt ¶
func Gt[T comparable](field string, val T) ExpressionBuilder
Gt creates a new ExpressionBuilder for a "greater than" comparison. The resulting expression will be true if the field value is greater than the given value.
func Gte ¶
func Gte[T comparable](field string, val T) ExpressionBuilder
Gte creates a new ExpressionBuilder for a "greater than or equal" comparison. The resulting expression will be true if the field value is greater than or equal to the given value.
func In ¶
func In[T comparable](field string, val T) ExpressionBuilder
In creates a new ExpressionBuilder for membership testing. The resulting expression will be true if the field value is in the given collection.
func Lt ¶
func Lt[T comparable](field string, val T) ExpressionBuilder
Lt creates a new ExpressionBuilder for a "less than" comparison. The resulting expression will be true if the field value is less than the given value.
func Lte ¶
func Lte[T comparable](field string, val T) ExpressionBuilder
Lte creates a new ExpressionBuilder for a "less than or equal" comparison. The resulting expression will be true if the field value is less than or equal to the given value.
func Neq ¶
func Neq[T comparable](field string, val T) ExpressionBuilder
Neq creates a new ExpressionBuilder for a "not equal" comparison. The resulting expression will be true if the field value does not equal the given value.
func NewExpressionBuilder ¶
func NewExpressionBuilder(expr Expression) ExpressionBuilder
NewExpressionBuilder creates a new ExpressionBuilder that wraps the given expression. This allows the expression to be used with the fluent interface for chaining logical operations like And, Or, and Not.
func NotExists ¶
func NotExists(field string) ExpressionBuilder
NotExists creates a new ExpressionBuilder that checks if a field does not exist. The resulting expression will be true if the field is not present.
func (ExpressionBuilder) And ¶
func (b ExpressionBuilder) And(expr Expression) ExpressionBuilder
And creates a new Builder that represents the logical AND of this Builder and the given expression. The resulting expression will be true only if both operands evaluate to true.
func (ExpressionBuilder) Not ¶
func (b ExpressionBuilder) Not() ExpressionBuilder
Not creates a new Builder that represents the logical NOT of this Builder. The resulting expression will be true if this Builder evaluates to false, and vice versa.
func (ExpressionBuilder) Or ¶
func (b ExpressionBuilder) Or(expr Expression) ExpressionBuilder
Or creates a new Builder that represents the logical OR of this Builder and the given expression. The resulting expression will be true if either operand evaluates to true.
func (ExpressionBuilder) String ¶
func (b ExpressionBuilder) String() string
String implements the fmt.Stringer interface by delegating to the wrapped expression.
type FormatOption ¶
type FormatOption interface {
Option // Embed Option interface
// contains filtered or unexported methods
}
FormatOption configures what to include in the formatted output. It also implements Option so it can be used with Thru().
func WithFilter ¶
func WithFilter(expr Expression) FormatOption
WithFilter adds a filter expression. It can be used with both Format() and Thru().
Example:
// With Format str, _ := sift.Format(registry, sift.WithFilter(filterExpr)) // With Thru sift.Thru(ctx, adapter, sift.WithFilter(filterExpr))
func WithPagination ¶
func WithPagination(expr PaginationExpression) FormatOption
WithPagination adds a pagination expression. It can be used with both Format() and Thru().
Example:
// With Format str, _ := sift.Format(registry, sift.WithPagination(pageExpr)) // With Thru sift.Thru(ctx, adapter, sift.WithPagination(pageExpr))
func WithSort ¶
func WithSort(expr SortExpression) FormatOption
WithSort adds a sort expression. It can be used with both Format() and Thru().
Example:
// With Format str, _ := sift.Format(registry, sift.WithSort(sortExpr)) // With Thru sift.Thru(ctx, adapter, sift.WithSort(sortExpr))
type NotEvaluator ¶
type NotEvaluator interface {
EvaluateNot(ctx context.Context, node *NotOperation) error
}
NotEvaluator evaluates NOT operations. Implement this interface to support logical negation in your backend.
type NotOperation ¶
type NotOperation struct {
Child Expression
}
NotOperation represents a logical NOT (negation) of a filter expression.
func (NotOperation) String ¶
func (n NotOperation) String() string
String returns the string representation of the NOT operation.
type OffsetPagination ¶
type OffsetPagination struct {
Size int // Number of items per page
Number int // Page number (1-based)
}
OffsetPagination represents offset-based pagination (page number + size). This is commonly used with SQL databases using LIMIT/OFFSET.
type OffsetPaginationEvaluator ¶
type OffsetPaginationEvaluator interface {
EvaluateOffsetPagination(ctx context.Context, page *OffsetPagination) error
}
OffsetPaginationEvaluator evaluates offset-based pagination.
type Operation ¶
type Operation string
Operation defines the type of comparison or logical operation.
const ( OperationEQ Operation = "eq" // equal OperationNEQ Operation = "ne" // not equal OperationLT Operation = "lt" // less than OperationLTE Operation = "le" // less than or equal OperationGT Operation = "gt" // greater than OperationGTE Operation = "ge" // greater than or equal OperationContains Operation = "contains" // substring match OperationBeginsWith Operation = "begins_with" // prefix match OperationIn Operation = "in" // value in list OperationExists Operation = "exists" // attribute exists OperationNotExists Operation = "not_exists" // attribute doesn't exist OperationBetween Operation = "between" // value between two bounds )
Operation constants define the supported comparison and existence operations.
func (Operation) IsCondition ¶
IsCondition returns true if the operation is a condition operation that requires a value. Condition operations include equality, inequality, relational, and pattern matching operations.
func (Operation) IsExistence ¶
IsExistence returns true if the operation is an existence check that only tests for the presence or absence of an attribute without comparing values.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option represents a query option that can be applied to an adapter. Options include filtering, sorting, and pagination. FormatOption is a superset that also supports formatting.
type OrEvaluator ¶
type OrEvaluator interface {
EvaluateOr(ctx context.Context, node *OrOperation) error
}
OrEvaluator evaluates OR operations. Implement this interface to support logical OR in your backend.
type OrOperation ¶
type OrOperation struct {
Left Expression
Right Expression
}
OrOperation represents a logical OR between two filter expressions.
func (OrOperation) String ¶
func (o OrOperation) String() string
String returns the string representation of the OR operation.
type PaginationBuilder ¶
type PaginationBuilder struct {
// contains filtered or unexported fields
}
PaginationBuilder provides a fluent interface for constructing pagination expressions.
func Paginate ¶
func Paginate() PaginationBuilder
Paginate creates a new PaginationBuilder. Use Size() to set the page size, then either Number() for offset-based or Cursor() for cursor-based pagination.
Example (offset-based):
page := sift.Paginate().Size(20).Number(2)
Example (cursor-based):
page := sift.Paginate().Size(20).Cursor("token123")
func (PaginationBuilder) Cursor ¶
func (p PaginationBuilder) Cursor(cursor string) PaginationExpression
Cursor sets the cursor token for cursor-based pagination. This creates a CursorPagination expression. If Number() was previously called, this overrides it.
func (PaginationBuilder) Number ¶
func (p PaginationBuilder) Number(number int) PaginationExpression
Number sets the page number for offset-based pagination. This creates an OffsetPagination expression. If Cursor() was previously called, this overrides it.
func (PaginationBuilder) Size ¶
func (p PaginationBuilder) Size(size int) PaginationBuilder
Size sets the page size (number of items per page).
type PaginationExpression ¶
type PaginationExpression interface {
// contains filtered or unexported methods
}
PaginationExpression represents a pagination expression in the AST.
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
Parser is a recursive descent parser for prefix notation filter expressions. It is exported to allow CustomFormatter parsers to access parsing utilities.
func (*Parser) Expect ¶
Expect checks if the current character matches the expected character. If it matches, advances the position and returns true. This method is exported for use by CustomFormatter parsers.
func (*Parser) ReadValue ¶
ReadValue reads a field name or value, handling escape sequences. Stops at unescaped commas or closing parentheses. This method is exported for use by custom expression parsers.
func (*Parser) SkipWhitespace ¶
func (p *Parser) SkipWhitespace()
SkipWhitespace advances the position past any whitespace characters. This method is exported for use by CustomFormatter parsers.
type Query ¶
type Query struct {
Filter Expression
Sort SortExpression
Pagination PaginationExpression
}
Query represents a complete query with filter, sort, and pagination.
func ParseQuery ¶
ParseQuery deserializes a complete query string into filter, sort, and pagination expressions. The input should be in the format produced by Format() with multiple options.
Format: filter(...),sort(...),page(...) Any component can be omitted.
Example:
query, _ := sift.ParseQuery("filter(eq(status,active)),sort(created_at:desc),page(size:20,number:2)", registry)
// query.Filter, query.Sort, query.Pagination are all populated
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds custom expression formatters. Use NewRegistry to create a registry and Register to add formatters.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a new custom expression registry.
func (*Registry) Register ¶
func (r *Registry) Register(name string, formatter CustomFormatter)
Register registers a custom formatter by name. The name is what appears in the serialized format (e.g., "size", "geo_within"). Panics if a formatter with the same name is already registered.
Example:
registry := sift.NewRegistry()
registry.Register("size", SizeFormatter{})
type SortBuilder ¶
type SortBuilder struct {
// contains filtered or unexported fields
}
SortBuilder provides a fluent API for building sort expressions.
func Sort ¶
func Sort(name string, direction SortDirection) SortBuilder
Sort creates a new sort expression with a single field.
Example:
sort := sift.Sort("created_at", sift.SortDesc)
func (SortBuilder) NullsLast ¶
func (b SortBuilder) NullsLast() SortBuilder
NullsLast sets the NullsLast flag on the most recently added field. This controls whether NULL values appear last in the sort order. Note: Not all backends support this feature.
Example:
sort := sift.Sort("email", sift.SortAsc).NullsLast()
func (SortBuilder) ThenBy ¶
func (b SortBuilder) ThenBy(name string, direction SortDirection) SortBuilder
ThenBy adds another sort field to the expression. Fields are applied in the order they are added.
Example:
sort := sift.Sort("created_at", sift.SortDesc).ThenBy("name", sift.SortAsc)
type SortDirection ¶
type SortDirection string
SortDirection represents the direction of sorting.
const ( // SortAsc sorts in ascending order (A-Z, 0-9, oldest-newest). SortAsc SortDirection = "asc" // SortDesc sorts in descending order (Z-A, 9-0, newest-oldest). SortDesc SortDirection = "desc" )
type SortExpression ¶
type SortExpression interface {
// contains filtered or unexported methods
}
SortExpression represents a sorting expression in the AST.
type SortField ¶
type SortField struct {
Name string
Direction SortDirection
NullsLast bool // Control NULL ordering (backend-specific support)
}
SortField represents a single field to sort by.
func (SortField) ThenBy ¶
func (s SortField) ThenBy(name string, direction SortDirection) SortBuilder
ThenBy creates a new SortBuilder starting with this field and adding another field. This allows chaining sort operations starting from a SortField.
Example:
field := &sift.SortField{Name: "created_at", Direction: sift.SortDesc}
sort := field.ThenBy("name", sift.SortAsc)
type SortFieldEvaluator ¶
type SortFieldEvaluator interface {
EvaluateSortField(ctx context.Context, field *SortField) error
}
SortFieldEvaluator evaluates a single sort field.