Queries
go-lightning provides two functions for reading data: Select for multiple rows and SelectSingle for a single row.
Select
Returns all matching rows as a slice of pointers:
func Select[T any](ex Executor, query string, args ...any) ([]*T, error)Example
// Get all users
users, err := lit.Select[User](db, "SELECT id, first_name, last_name, email FROM users")
if err != nil {
return err
}
for _, user := range users {
fmt.Printf("%s %s\n", user.FirstName, user.LastName)
}With Parameters
// PostgreSQL
users, err := lit.Select[User](db,
"SELECT id, first_name, last_name, email FROM users WHERE last_name = $1",
"Doe")
// MySQL
users, err := lit.Select[User](db,
"SELECT id, first_name, last_name, email FROM users WHERE last_name = ?",
"Doe")SelectSingle
Returns a single row or nil if not found:
func SelectSingle[T any](ex Executor, query string, args ...any) (*T, error)Example
user, err := lit.SelectSingle[User](db,
"SELECT id, first_name, last_name, email FROM users WHERE id = $1", id)
if err != nil {
return err
}
if user == nil {
fmt.Println("User not found")
return nil
}
fmt.Printf("Found: %s\n", user.Email)Column Validation
go-lightning validates that all columns in your SELECT match fields in your struct. This catches errors early:
// This will return an error if 'nonexistent' is not a field in User
users, err := lit.Select[User](db,
"SELECT id, first_name, nonexistent FROM users")
// Error: invalid column that is not found in the struct: nonexistentProjections (DTOs)
You can project query results into any struct, not just your registered models. This is useful for:
- Selecting only specific columns
- JOINs that combine multiple tables
- Aggregations
Partial Select
type UserSummary struct {
Id int
Email string
}
lit.RegisterModel[UserSummary](lit.PostgreSQL)
summaries, err := lit.Select[UserSummary](db,
"SELECT id, email FROM users")JOIN Results
type UserWithOrder struct {
UserId int
UserEmail string
OrderId int
Total float64
}
lit.RegisterModel[UserWithOrder](lit.PostgreSQL)
results, err := lit.Select[UserWithOrder](db, `
SELECT
u.id as user_id,
u.email as user_email,
o.id as order_id,
o.total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.total > $1`, 100.00)See Projections & DTOs for more examples.
Native Queries
For complex scenarios where automatic mapping doesn't work, use SelectMultipleNative:
func SelectMultipleNative[T any](
ex Executor,
mapLine func(*interface{ Scan(...any) error }, *T) error,
query string,
args ...any,
) ([]*T, error)This gives you full control over the scanning process:
type CustomResult struct {
Name string
Count int
}
results, err := lit.SelectMultipleNative[CustomResult](db,
func(scanner *interface{ Scan(...any) error }, r *CustomResult) error {
return (*scanner).Scan(&r.Name, &r.Count)
},
"SELECT name, COUNT(*) FROM items GROUP BY name")