From 09f9f0b557400c28084906612d068f1c36f3c58f Mon Sep 17 00:00:00 2001 From: Manuel Doncel Martos Date: Tue, 24 Dec 2024 14:32:34 +0100 Subject: [PATCH] Fixing bug with Preloads --- CHANGELOG.md | 6 +++ README.md | 4 ++ examples/many-pages-preload/main.go | 66 +++++++++++++++++++++++++++++ pagorminator.go | 28 ++++++------ pagorminator_test.go | 62 ++++++++++++++++++++++++++- 5 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 examples/many-pages-preload/main.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a27dbce..1a1db9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.0.1-rc4] 2024-12-24 + +## BugFix + +- Fixing bug of using Pagination with preload + ## [v0.0.1-rc3] 2024-12-17 ## BugFix diff --git a/README.md b/README.md index d8bc6cb..68f91f8 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ Using where to filter Unpaged query +- [Many Pages With Preload](./examples/many-pages-preload/main.go) + +Example using Preload + ## 🔗 Contact - 📧 manueldoncelmartos@gmail.com diff --git a/examples/many-pages-preload/main.go b/examples/many-pages-preload/main.go new file mode 100644 index 0000000..23925c9 --- /dev/null +++ b/examples/many-pages-preload/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "github.com/manuelarte/pagorminator" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "strconv" +) + +type Product struct { + gorm.Model + Code string + Price Price +} + +type Price struct { + gorm.Model + Unit uint + Currency string + ProductID uint +} + +func (p Product) String() string { + return fmt.Sprintf("Product{Code: %s, Price: %d}", p.Code, p.Price) +} + +func main() { + db, err := gorm.Open(sqlite.Open("file:mem?mode=memory&cache=shared"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + + _ = db.Use(pagorminator.PaGormMinator{}) + _ = db.AutoMigrate(&Product{}, &Price{}) + length := 10 + for i := 0; i < length; i++ { + errCreatingProduct := db.Create(&Product{Code: strconv.Itoa(i), Price: Price{Unit: uint(i), Currency: "EUR"}}) + if errCreatingProduct.Error != nil { + panic(errCreatingProduct.Error) + } + } + + fmt.Printf("%d product created\n", length) + + var products []*Product + pageRequest, _ := pagorminator.PageRequest(0, 5) + txErr := db.Debug().Clauses(pageRequest).Preload("Price").Find(&products).Error + if txErr != nil { + panic(txErr) + } + + fmt.Printf("PageRequest result:(Page: %d, Size: %d, TotalElements: %d, TotalPages: %d)\n", + pageRequest.GetPage(), pageRequest.GetSize(), pageRequest.GetTotalElements(), pageRequest.GetTotalPages()) + for _, product := range products { + fmt.Printf("\t Product: %s\n", product) + } + + pageRequest, _ = pagorminator.PageRequest(1, 5) + db.Clauses(pageRequest).Find(&products) + fmt.Printf("PageRequest result:(Page: %d, Size: %d, TotalElements: %d, TotalPages: %d)\n", + pageRequest.GetPage(), pageRequest.GetSize(), pageRequest.GetTotalElements(), pageRequest.GetTotalPages()) + for _, product := range products { + fmt.Printf("\t Product: %s\n", product) + } +} diff --git a/pagorminator.go b/pagorminator.go index e4903db..6ef172e 100644 --- a/pagorminator.go +++ b/pagorminator.go @@ -30,19 +30,21 @@ func (p PaGormMinator) count(db *gorm.DB) { if db.Statement.Schema != nil { if pageable, ok := p.getPageRequest(db); ok { if value, ok := db.Get(countKey); !ok || !value.(bool) { - newDb := db.Session(&gorm.Session{NewDB: true}) - newDb.Statement = db.Statement.Statement - - var totalElements int64 - tx := newDb.Set(countKey, true).Model(newDb.Statement.Model) - if whereClause, existWhere := db.Statement.Clauses["WHERE"]; existWhere { - tx.Where(whereClause.Expression) - } - tx.Count(&totalElements) - if tx.Error != nil { - _ = db.AddError(tx.Error) - } else { - pageable.totalElements = totalElements + if pageable.totalElements == 0 { + newDb := db.Session(&gorm.Session{NewDB: true}) + newDb.Statement = db.Statement.Statement + + var totalElements int64 + tx := newDb.Set(countKey, true).Model(newDb.Statement.Model) + if whereClause, existWhere := db.Statement.Clauses["WHERE"]; existWhere { + tx.Where(whereClause.Expression) + } + tx.Count(&totalElements) + if tx.Error != nil { + _ = db.AddError(tx.Error) + } else { + pageable.totalElements = totalElements + } } } } diff --git a/pagorminator_test.go b/pagorminator_test.go index 95febc6..898778e 100644 --- a/pagorminator_test.go +++ b/pagorminator_test.go @@ -13,6 +13,18 @@ type TestStruct struct { Price uint } +type TestProduct struct { + gorm.Model + Code string + Price TestPrice +} +type TestPrice struct { + gorm.Model + Amount uint + Currency string + TestProductID uint +} + func TestPaginationScopeMetadata_NoWhere(t *testing.T) { t.Parallel() tests := map[string]struct { @@ -146,6 +158,54 @@ func TestPaginationScopeMetadata_Where(t *testing.T) { } } +func TestPaginationWithPreload(t *testing.T) { + t.Parallel() + tests := map[string]struct { + toMigrate []*TestProduct + pageRequest *Pagination + expectedPage *Pagination + }{ + "UnPaged one item, not filtered": { + toMigrate: []*TestProduct{ + {Code: "1", Price: TestPrice{Amount: 1, Currency: "EUR"}}, + }, + pageRequest: UnPaged(), + expectedPage: &Pagination{ + page: 0, + size: 0, + totalElements: 1, + }, + }, + "Paged 1/2 items": { + toMigrate: []*TestProduct{ + {Code: "1", Price: TestPrice{Amount: 1, Currency: "EUR"}}, + {Code: "2", Price: TestPrice{Amount: 2, Currency: "EUR"}}, + }, + pageRequest: &Pagination{page: 0, size: 1}, + expectedPage: &Pagination{ + page: 0, + size: 1, + totalElements: 2, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + db := setupDb(t, name) + db.CreateInBatches(&test.toMigrate, len(test.toMigrate)) + + // Read + var products []*TestProduct + + db.Clauses(test.pageRequest).Preload("Price").Find(&products) + if !equalPageRequests(test.pageRequest, test.expectedPage) { + t.Fatalf("expected page to be %d, got %d", test.expectedPage, test.pageRequest) + } + }) + } +} + func setupDb(t *testing.T, name string) *gorm.DB { db, err := gorm.Open(sqlite.Open(fmt.Sprintf("file:%s?mode=memory&cache=shared", name)), &gorm.Config{}) if err != nil { @@ -153,7 +213,7 @@ func setupDb(t *testing.T, name string) *gorm.DB { } // Migrate the schema - err = db.AutoMigrate(&TestStruct{}) + err = db.AutoMigrate(&TestStruct{}, &TestProduct{}, &TestPrice{}) if err != nil { t.Fatal(err) }