Documentation
¶
Overview ¶
Package sql provides a SQL adapter for the sift filter library. It translates sift filter expressions into SQL WHERE clause syntax.
Example ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Create a simple filter: status = "active"
filter := sift.Eq("status", "active")
// Create SQL adapter
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: status = $1 Args: [active]
Index ¶
- type Adapter
- func (a *Adapter) Args() []interface{}
- func (a *Adapter) EvaluateAnd(ctx context.Context, node *sift.AndOperation) error
- func (a *Adapter) EvaluateCondition(ctx context.Context, node *sift.Condition) error
- func (a *Adapter) EvaluateNot(ctx context.Context, node *sift.NotOperation) error
- func (a *Adapter) EvaluateOffsetPagination(ctx context.Context, page *sift.OffsetPagination) error
- func (a *Adapter) EvaluateOr(ctx context.Context, node *sift.OrOperation) error
- func (a *Adapter) EvaluateSortList(ctx context.Context, list *sift.SortList) error
- func (a *Adapter) Evaluator(ctx context.Context) *sift.Evaluator
- func (a *Adapter) Limit() int
- func (a *Adapter) Offset() int
- func (a *Adapter) OrderBy() string
- func (a *Adapter) Query() string
- type Config
- type Dialect
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter translates sift filter expressions into SQL WHERE clause syntax. It accumulates the SQL query string and parameter values during traversal.
Example (BetweenOperation) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// BETWEEN operation
filter := sift.Between("age", 18, 65)
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: age BETWEEN $1 AND $2 Args: [18 65]
Example (CaseInsensitive) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Case-insensitive string matching
config := &siftsql.Config{
Dialect: siftsql.DialectPostgreSQL,
CaseSensitive: false,
}
filter := sift.Contains("email", "@EXAMPLE.COM")
adapter := siftsql.NewAdapterWithConfig(config)
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
}
Output: Query: LOWER(email) LIKE LOWER($1)
Example (ComplexFilter) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Create a complex filter: (status = "active" AND age > 18) OR role = "admin"
filter := sift.Eq("status", "active").
And(sift.Gt("age", 18)).
Or(sift.Eq("role", "admin"))
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: ((status = $1) AND (age > $2)) OR (role = $3) Args: [active 18 admin]
Example (ExistenceChecks) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Existence checks: IS NULL and IS NOT NULL
filter := sift.Exists("email").And(sift.NotExists("deleted_at"))
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
}
Output: Query: (email IS NOT NULL) AND (deleted_at IS NULL)
Example (InOperation) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// IN operation with multiple values
filter := &sift.Condition{
Name: "status",
Operation: sift.OperationIn,
Value: "active,pending,approved",
}
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: status IN ($1, $2, $3) Args: [active pending approved]
Example (NotOperation) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// NOT operation
filter := sift.Eq("deleted", "true").Not()
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: NOT (deleted = $1) Args: [true]
Example (StringOperations) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// String operations: contains and begins_with
filter := sift.Contains("email", "@example.com").
And(&sift.Condition{
Name: "name",
Operation: sift.OperationBeginsWith,
Value: "John",
})
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: (email LIKE $1) AND (name LIKE $2) Args: [%@example.com% John%]
Example (WithDatabase) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Example showing usage with database/sql
filter := sift.Eq("status", "active").And(sift.Gt("age", 18))
adapter := siftsql.NewAdapter()
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
// Use with database/sql (pseudo-code)
query := fmt.Sprintf("SELECT * FROM users WHERE %s", adapter.Query())
fmt.Printf("SQL: %s\n", query)
fmt.Printf("Args: %v\n", adapter.Args())
// In real code:
// rows, err := db.Query(query, adapter.Args()...)
}
Output: SQL: SELECT * FROM users WHERE (status = $1) AND (age > $2) Args: [active 18]
func NewAdapter ¶
func NewAdapter() *Adapter
NewAdapter creates a new SQL adapter with default configuration (PostgreSQL dialect).
func NewAdapterWithConfig ¶
NewAdapterWithConfig creates a new SQL adapter with custom configuration.
Example (Mysql) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Configure for MySQL dialect
config := &siftsql.Config{
Dialect: siftsql.DialectMySQL,
QuoteIdentifiers: true,
}
filter := sift.Eq("user_name", "john").And(sift.Gt("age", 18))
adapter := siftsql.NewAdapterWithConfig(config)
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: (`user_name` = ?) AND (`age` > ?) Args: [john 18]
Example (Sqlserver) ¶
package main
import (
"context"
"fmt"
"github.com/nisimpson/sift"
siftsql "github.com/nisimpson/sift/thru/sql"
)
func main() {
// Configure for SQL Server dialect
config := &siftsql.Config{
Dialect: siftsql.DialectSQLServer,
}
filter := sift.Eq("status", "active").And(sift.Gt("age", 18))
adapter := siftsql.NewAdapterWithConfig(config)
sift.Thru(context.Background(), adapter, sift.WithFilter(filter))
fmt.Printf("Query: %s\n", adapter.Query())
fmt.Printf("Args: %v\n", adapter.Args())
}
Output: Query: (status = @p1) AND (age > @p2) Args: [active 18]
func (*Adapter) Args ¶
func (a *Adapter) Args() []interface{}
Args returns the parameter values for the query.
func (*Adapter) EvaluateAnd ¶
EvaluateAnd combines two conditions with logical AND.
func (*Adapter) EvaluateCondition ¶
EvaluateCondition translates a sift condition into a SQL WHERE clause fragment.
func (*Adapter) EvaluateNot ¶
EvaluateNot negates a condition.
func (*Adapter) EvaluateOffsetPagination ¶
EvaluateOffsetPagination sets the LIMIT and OFFSET values.
func (*Adapter) EvaluateOr ¶
EvaluateOr combines two conditions with logical OR.
func (*Adapter) EvaluateSortList ¶
EvaluateSortList translates a sort list into SQL ORDER BY clause.
func (*Adapter) Limit ¶
Limit returns the LIMIT value. Returns the configured DefaultLimit if no pagination was specified. Returns 0 if no limit should be applied.
type Config ¶
type Config struct {
// Dialect specifies the SQL dialect to use for query generation.
// Defaults to DialectPostgreSQL.
Dialect Dialect
// QuoteIdentifiers determines whether to quote column names.
// PostgreSQL/SQLite use double quotes, MySQL uses backticks.
QuoteIdentifiers bool
// CaseSensitive determines whether string comparisons are case-sensitive.
// When false, uses LOWER() for case-insensitive comparisons.
CaseSensitive bool
// DefaultLimit is applied when no pagination is specified.
// Set to 0 to disable default limit (not recommended for production).
DefaultLimit int
}
Config holds configuration options for the SQL adapter.
type Dialect ¶
type Dialect string
Dialect represents a SQL dialect for query generation.
const ( // DialectPostgreSQL generates PostgreSQL-compatible queries with $1, $2, etc. placeholders DialectPostgreSQL Dialect = "postgresql" // DialectMySQL generates MySQL-compatible queries with ? placeholders DialectMySQL Dialect = "mysql" // DialectSQLite generates SQLite-compatible queries with ? placeholders DialectSQLite Dialect = "sqlite" // DialectSQLServer generates SQL Server-compatible queries with @p1, @p2, etc. placeholders DialectSQLServer Dialect = "sqlserver" )