mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-13 17:10:36 +00:00
Some checks are pending
Build , Vet Test, and Lint / Run Vet Tests (1.23.x) (push) Waiting to run
Build , Vet Test, and Lint / Run Vet Tests (1.24.x) (push) Waiting to run
Build , Vet Test, and Lint / Lint Code (push) Waiting to run
Build , Vet Test, and Lint / Build (push) Waiting to run
Tests / Integration Tests (push) Waiting to run
Tests / Unit Tests (push) Waiting to run
276 lines
6.2 KiB
Go
276 lines
6.2 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
|
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
|
)
|
|
|
|
// Example models for demonstrating preload functionality
|
|
|
|
// Author model - has many Posts
|
|
type Author struct {
|
|
ID int `db:"id"`
|
|
Name string `db:"name"`
|
|
Email string `db:"email"`
|
|
Posts []*Post `bun:"rel:has-many,join:id=author_id"`
|
|
}
|
|
|
|
func (a Author) TableName() string {
|
|
return "authors"
|
|
}
|
|
|
|
// Post model - belongs to Author, has many Comments
|
|
type Post struct {
|
|
ID int `db:"id"`
|
|
Title string `db:"title"`
|
|
Content string `db:"content"`
|
|
AuthorID int `db:"author_id"`
|
|
Author *Author `bun:"rel:belongs-to,join:author_id=id"`
|
|
Comments []*Comment `bun:"rel:has-many,join:id=post_id"`
|
|
}
|
|
|
|
func (p Post) TableName() string {
|
|
return "posts"
|
|
}
|
|
|
|
// Comment model - belongs to Post
|
|
type Comment struct {
|
|
ID int `db:"id"`
|
|
Content string `db:"content"`
|
|
PostID int `db:"post_id"`
|
|
Post *Post `bun:"rel:belongs-to,join:post_id=id"`
|
|
}
|
|
|
|
func (c Comment) TableName() string {
|
|
return "comments"
|
|
}
|
|
|
|
// ExamplePreload demonstrates the Preload functionality
|
|
func ExamplePreload() error {
|
|
dsn := "postgres://username:password@localhost:5432/dbname?sslmode=disable"
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
adapter := NewPgSQLAdapter(db)
|
|
ctx := context.Background()
|
|
|
|
// Example 1: Simple Preload (uses subquery for has-many)
|
|
var authors []*Author
|
|
err = adapter.NewSelect().
|
|
Model(&Author{}).
|
|
Table("authors").
|
|
Preload("Posts"). // Load all posts for each author
|
|
Scan(ctx, &authors)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Now authors[i].Posts will be populated with their posts
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExamplePreloadRelation demonstrates smart PreloadRelation with auto-detection
|
|
func ExamplePreloadRelation() error {
|
|
dsn := "postgres://username:password@localhost:5432/dbname?sslmode=disable"
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
adapter := NewPgSQLAdapter(db)
|
|
ctx := context.Background()
|
|
|
|
// Example 1: PreloadRelation auto-detects has-many (uses subquery)
|
|
var authors []*Author
|
|
err = adapter.NewSelect().
|
|
Model(&Author{}).
|
|
Table("authors").
|
|
PreloadRelation("Posts", func(q common.SelectQuery) common.SelectQuery {
|
|
return q.Where("published = ?", true).Order("created_at DESC")
|
|
}).
|
|
Where("active = ?", true).
|
|
Scan(ctx, &authors)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Example 2: PreloadRelation auto-detects belongs-to (uses JOIN)
|
|
var posts []*Post
|
|
err = adapter.NewSelect().
|
|
Model(&Post{}).
|
|
Table("posts").
|
|
PreloadRelation("Author"). // Will use JOIN because it's belongs-to
|
|
Scan(ctx, &posts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Example 3: Nested preloads
|
|
err = adapter.NewSelect().
|
|
Model(&Author{}).
|
|
Table("authors").
|
|
PreloadRelation("Posts", func(q common.SelectQuery) common.SelectQuery {
|
|
// First load posts, then preload comments for each post
|
|
return q.Limit(10)
|
|
}).
|
|
Scan(ctx, &authors)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Manually load nested relationships (two-level preloading)
|
|
for _, author := range authors {
|
|
if author.Posts != nil {
|
|
for _, post := range author.Posts {
|
|
var comments []*Comment
|
|
err := adapter.NewSelect().
|
|
Table("comments").
|
|
Where("post_id = ?", post.ID).
|
|
Scan(ctx, &comments)
|
|
if err == nil {
|
|
post.Comments = comments
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExampleJoinRelation demonstrates explicit JOIN loading
|
|
func ExampleJoinRelation() error {
|
|
dsn := "postgres://username:password@localhost:5432/dbname?sslmode=disable"
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
adapter := NewPgSQLAdapter(db)
|
|
ctx := context.Background()
|
|
|
|
// Example 1: Force JOIN for belongs-to relationship
|
|
var posts []*Post
|
|
err = adapter.NewSelect().
|
|
Model(&Post{}).
|
|
Table("posts").
|
|
JoinRelation("Author", func(q common.SelectQuery) common.SelectQuery {
|
|
return q.Where("active = ?", true)
|
|
}).
|
|
Scan(ctx, &posts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Example 2: Multiple JOINs
|
|
err = adapter.NewSelect().
|
|
Model(&Post{}).
|
|
Table("posts p").
|
|
Column("p.*", "a.name as author_name", "a.email as author_email").
|
|
LeftJoin("authors a ON a.id = p.author_id").
|
|
Where("p.published = ?", true).
|
|
Scan(ctx, &posts)
|
|
|
|
return err
|
|
}
|
|
|
|
// ExampleScanModel demonstrates ScanModel with struct destinations
|
|
func ExampleScanModel() error {
|
|
dsn := "postgres://username:password@localhost:5432/dbname?sslmode=disable"
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
adapter := NewPgSQLAdapter(db)
|
|
ctx := context.Background()
|
|
|
|
// Example 1: Scan single struct
|
|
author := Author{}
|
|
err = adapter.NewSelect().
|
|
Model(&author).
|
|
Table("authors").
|
|
Where("id = ?", 1).
|
|
ScanModel(ctx) // ScanModel automatically uses the model set with Model()
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Example 2: Scan slice of structs
|
|
authors := []*Author{}
|
|
err = adapter.NewSelect().
|
|
Model(&authors).
|
|
Table("authors").
|
|
Where("active = ?", true).
|
|
Limit(10).
|
|
ScanModel(ctx)
|
|
|
|
return err
|
|
}
|
|
|
|
// ExampleCompleteWorkflow demonstrates a complete workflow with preloading
|
|
func ExampleCompleteWorkflow() error {
|
|
dsn := "postgres://username:password@localhost:5432/dbname?sslmode=disable"
|
|
db, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
adapter := NewPgSQLAdapter(db)
|
|
adapter.EnableQueryDebug() // Enable query logging
|
|
ctx := context.Background()
|
|
|
|
// Step 1: Create an author
|
|
author := &Author{
|
|
Name: "John Doe",
|
|
Email: "john@example.com",
|
|
}
|
|
|
|
result, err := adapter.NewInsert().
|
|
Table("authors").
|
|
Value("name", author.Name).
|
|
Value("email", author.Email).
|
|
Returning("id").
|
|
Exec(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_ = result
|
|
|
|
// Step 2: Load author with all their posts
|
|
var loadedAuthor Author
|
|
err = adapter.NewSelect().
|
|
Model(&loadedAuthor).
|
|
Table("authors").
|
|
PreloadRelation("Posts", func(q common.SelectQuery) common.SelectQuery {
|
|
return q.Order("created_at DESC").Limit(5)
|
|
}).
|
|
Where("id = ?", 1).
|
|
ScanModel(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Step 3: Update author name
|
|
_, err = adapter.NewUpdate().
|
|
Table("authors").
|
|
Set("name", "Jane Doe").
|
|
Where("id = ?", 1).
|
|
Exec(ctx)
|
|
|
|
return err
|
|
}
|