Skip to content

Instantly share code, notes, and snippets.

@TimVosch
Last active July 5, 2025 14:36
Show Gist options
  • Select an option

  • Save TimVosch/0fb56e6aec77796d871c3444f7885d0e to your computer and use it in GitHub Desktop.

Select an option

Save TimVosch/0fb56e6aec77796d871c3444f7885d0e to your computer and use it in GitHub Desktop.
Composable DB models queries
func TestBasicRelationalModel(t *testing.T) {
// Arrange test database
db, sq := setupDB(t)
sq.Insert("authors").
Values(1, "Jeff", "cool,awesome").
Values(2, "Madonna", "vocal").Exec()
sq.Insert("books").
Values(1, "Life of Jeff", 1).
Values(2, "Cooking like Jeff", 1).
Values(3, "Sing baby sing", 2).
Values(4, "the singeth hath endeth", 2).Exec()
sq.Insert("book_comments").
Values(1, "Great book!", 1).
Values(2, "Very insightful", 2).
Values(3, "A masterpiece", 3).
Values(4, "Could be better", 4).Exec()
// These models will be defined once for your persistence layer
comment := alacarte.NewModel[Comment]("book_comments").
AddField("id", alacarte.Col("id"), alacarte.Ptr(func(t *Comment) any { return &t.ID })).
AddField("name", alacarte.Col("name"), alacarte.Ptr(func(t *Comment) any { return &t.Name })).
AddField("book_id", alacarte.Col("book_id"), alacarte.Ptr(func(t *Comment) any { return &t.BookID }))
book := alacarte.NewModel[Book]("books").
AddField("id", alacarte.Col("id"), alacarte.Ptr(func(t *Book) any { return &t.ID })).
AddField("name", alacarte.Col("name"), alacarte.Ptr(func(t *Book) any { return &t.Name })).
AddField("author_id", alacarte.Col("author_id"), alacarte.Ptr(func(t *Book) any { return &t.AuthorID })).
AddRelation("comments",
alacarte.HasMany(comment,
alacarte.BindBy(
func(book Book, comment Comment) bool { return comment.BookID == book.ID },
func(book *Book, comments []Comment) { book.Comments = comments },
),
func(books []Book) alacarte.QueryMod {
return func(q alacarte.Q, table string) alacarte.Q {
ids := lo.Map(
books,
func(book Book, _ int) uint64 { return book.ID },
)
return q.Where(squirrel.Eq{"book_comments.book_id": ids})
}
},
),
)
author := alacarte.NewModel[Author]("authors").
AddField("id", alacarte.Col("id"), alacarte.Ptr(func(t *Author) any { return &t.ID })).
AddField("name", alacarte.Col("name"), alacarte.Ptr(func(t *Author) any { return &t.Name })).
// TODO: relations should specify mandatory fields required to resolve the relation.
AddRelation("books",
alacarte.HasMany(book,
alacarte.BindBy(
func(author Author, book Book) bool { return book.AuthorID == author.ID },
func(author *Author, books []Book) { author.Books = books },
),
func(authors []Author) alacarte.QueryMod {
return func(q alacarte.Q, table string) alacarte.Q {
ids := lo.Map(
authors,
func(author Author, _ int) uint64 { return author.ID },
)
return q.Where(squirrel.Eq{"books.author_id": ids})
}
},
),
)
// Perform a select query that will only fetch the selected fields and will resolve the relations.
authors, err := author.Select("id", "name", "books.id", "books.name", "books.author_id", "books.comments.id", "books.comments.name", "books.comments.book_id").
Resolve("books", "books.comments").
Collect(context.Background(), db)
require.NoError(t, err)
// Pretty print
data, _ := json.MarshalIndent(authors, "", " ")
t.Logf("%s", string(data))
}
// Queries ran:
SELECT authors.id, authors.name FROM authors
SELECT books.author_id, books.id, books.name FROM books WHERE books.author_id IN ('1','2')
SELECT book_comments.book_id, book_comments.id, book_comments.name FROM book_comments WHERE book_comments.book_id IN ('1','2','3','4')
// Test output
=== RUN TestBasicRelationalModel
relation_test.go:87: [
{
"ID": 1,
"Name": "Jeff",
"Tags": null,
"Books": [
{
"ID": 1,
"Name": "Life of Jeff",
"AuthorID": 1,
"Comments": [
{
"ID": 1,
"Name": "Great book!",
"BookID": 1
}
]
},
{
"ID": 2,
"Name": "Cooking like Jeff",
"AuthorID": 1,
"Comments": [
{
"ID": 2,
"Name": "Very insightful",
"BookID": 2
}
]
}
]
},
{
"ID": 2,
"Name": "Madonna",
"Tags": null,
"Books": [
{
"ID": 3,
"Name": "Sing baby sing",
"AuthorID": 2,
"Comments": [
{
"ID": 3,
"Name": "A masterpiece",
"BookID": 3
}
]
},
{
"ID": 4,
"Name": "the singeth hath endeth",
"AuthorID": 2,
"Comments": [
{
"ID": 4,
"Name": "Could be better",
"BookID": 4
}
]
}
]
}
]
--- PASS: TestBasicRelationalModel (0.00s)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment