Last active
July 5, 2025 14:36
-
-
Save TimVosch/0fb56e6aec77796d871c3444f7885d0e to your computer and use it in GitHub Desktop.
Composable DB models queries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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