https://github.com/insei/gerpo
GERPO is a generic repository pattern implementation with language integrated query support. Works with any sql sources.
https://github.com/insei/gerpo
cache generic go golang query-builder repository sql struct-mapping
Last synced: 13 days ago
JSON representation
GERPO is a generic repository pattern implementation with language integrated query support. Works with any sql sources.
- Host: GitHub
- URL: https://github.com/insei/gerpo
- Owner: Insei
- License: mit
- Created: 2024-09-27T15:24:59.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2025-04-18T18:56:56.000Z (25 days ago)
- Last Synced: 2025-04-19T03:34:36.691Z (25 days ago)
- Topics: cache, generic, go, golang, query-builder, repository, sql, struct-mapping
- Language: Go
- Homepage:
- Size: 262 KB
- Stars: 1
- Watchers: 1
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# GERPO
[](https://codecov.io/gh/Insei/gerpo)
[](https://github.com/Insei/gerpo/actions/workflows/go.yml)
[](https://goreportcard.com/report/github.com/insei/gerpo)
[](https://godoc.org/github.com/insei/gerpo)Welcome to the **GERPO** repository! This document provides a brief overview of the project, build and run instructions, and other helpful information.
## About GERPO
**GERPO** (Golang + Repository) is a generic repository implementation with advanced configuration capabilities and easy to use builders.This project under active development.
Release 1.0.0 Road Map:
* Repository builder changes:
* Add Caching engine configuration in repository builder.
* Column builder changes:
* New API for configuring virtual (calculated fields). Current Virtual fields configuration API marked as deprecated.
* **All other API is stable and not planned to change in 1.0.0 release.**### Why GERPO?
1. Support any database drivers via simple [db adapters wrappers](https://github.com/Insei/gerpo/tree/main/executor/adapters#executor-db-adapters).
2. Fast repository level implementation, oriented on microservices.
3. Easily handle CRUD operations (Create, Read, Update, Delete) with powerful filtering and sorting.
4. Already implemented pagination for list queries.
5. Straightforward configuration user-friendly builders and there needed you can use SQL commands.
6. All SQL code in one place — inside the configuration.
7. Virtual (calculated, joined) columns with mapping to struct fields.
8. No dependent to other libraries. Only [fmap](https://github.com/Insei/fmap) was used for working with fields pointers.
9. [Caching support](https://github.com/Insei/gerpo/tree/main/executor/cache) (currently only context-oriented cache is supported, but it’s easy to implement other caching mechanisms).### Ideology
The GERPO ideology consists of several rules:
1) If SQL code is used, then only in the repository configuration.
2) All columns are attached to entity fields via pointers to them in the repository settings.
3) There are no references in the entities that they are stored in the database (i.e. there are no tags)
4) We do not implement entity relationships.
5) We do not do migrations or other actions on database structure.## Features
Essentially, **GERPO** is generic repository pattern implementation,
yes GERPO looks like ORM in some cases, but it's not an ORM.- **Database adapters support**:
- Any database can be used.
- You can use tracing wrappers and any other wrappers.
- **Caching Engine**
- Cache results in context for do not thinking about duplicated queries to database.
- Cache in external store, like redis.
- **Repository configuration**
- Map struct fields to SQL columns via pointers.
- Easy rename and refactor them.
- Easy delete fields. Errors can be found at build time.
- You always know where you can found columns mapping settings.
- Protect fields to insert/update in database.
- Define virtual(calculated)/joined fields.
- Add callbacks and hooks.
- Before insert.
- Before update.
- After select.
- Define persistent filters, groupings, and joins.
- Configure soft deletion.
- Use special builder that replace delete function with update with needed fields update.
- Configure Persistent filters for excluding soft deleted entities.
- Configure Errors transformer to transform GERPO errors to you business Errors.- **Per-query configuration**:
- Query builder allows you to set up some query rules:
- Execution fields selector allows you manage fields at update/insert operations:
- Exclude certain fields by fields pointers.
- Select only needed columns by fields pointers.
- Where builder allows you:
- Configure grouped filters.
- Support OR/AND cases.
- All of this via fields pointers.
- Order builder:
- Configure order via fields pointers.
- Work with transactions.
- Use already implemented pagination in your List queries.## Performance
GERPO uses a minimal amount of reflection and is designed to have minimal allocations when used,
most allocations are initialized during configuration.
We work with unsafe pointers and offsets to determine the necessary fields in
the repository configuration and when querying the database.I made 2 tests with absolutely identical conditions. Pure PGX V4 Pool vs GERPO over PGX V4 Pool.
Yes, we do 2x more allocations in a heap.
But I think our functionality is worth it.
In terms of time per operation, we are behind pure PGX v4 Pool by 8%.Well, I didn't know what the results would be at the beginning of the design. But I have ideas for optimizations.
#### Pure PGX v4 pool:
```
BenchmarkGetOneFromDb-32 18049 65033 ns/op 1554 B/op 21 allocs/op
BenchmarkGetOneFromDb-32 18288 66617 ns/op 1555 B/op 21 allocs/op
BenchmarkGetOneFromDb-32 17860 66640 ns/op 1555 B/op 21 allocs/op
BenchmarkGetOneFromDb-32 18193 64665 ns/op 1555 B/op 21 allocs/op
BenchmarkGetOneFromDb-32 18198 65171 ns/op 1559 B/op 21 allocs/op
```
#### GERPO:
```
BenchmarkGetFirst/GetFirst-32 16808 69738 ns/op 2961 B/op 51 allocs/op
BenchmarkGetFirst/GetFirst-32 16614 70462 ns/op 2961 B/op 51 allocs/op
BenchmarkGetFirst/GetFirst-32 16905 70796 ns/op 2961 B/op 51 allocs/op
BenchmarkGetFirst/GetFirst-32 17059 70737 ns/op 2961 B/op 51 allocs/op
BenchmarkGetFirst/GetFirst-32 17184 69587 ns/op 2961 B/op 51 allocs/op
``````
+------------------------------------------------------------+
| | Get One/First |
+------------------+---------------------+-------------------+
| | PURE GPX Pool v4 | GERPO PGX Pool v4 |
| | | v4 Adapter |
+------------------+---------------------+-------------------+
| B/op | 0% | +100% |
| allocs/op | 0% | +120% |
| ns/op | 0% | +7.75% |
+------------------+---------------------+-------------------+
```## Installation
Go minimal version is `1.21`.
```bash
go get github.com/insei/gerpo@latest
```## Examples
Below you’ll find various configurations and usage examples.### Repository Configuration
#### Columns
```go
package mainimport (
"time"
"github.com/insei/gerpo"
)type test struct {
ID int
CreatedAt time.Time
UpdatedAt *time.Time
Name string
Age int
}func main() {
repo, err := gerpo.NewBuilder[test]().
DB(dbWrap).
Table("tests").
Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {
columns.Field(&m.ID).AsColumn().WithUpdateProtection()
columns.Field(&m.CreatedAt).AsColumn().WithUpdateProtection()
columns.Field(&m.UpdatedAt).AsColumn().WithInsertProtection()
columns.Field(&m.Name).AsColumn()
columns.Field(&m.Age).AsColumn()
}).
Build()// Handle err and proceed with repo usage
}
```#### Joins
```go
package mainimport (
"context"
"time"
"github.com/insei/gerpo"
"github.com/insei/gerpo/query"
)type test struct {
ID int
CreatedAt time.Time
UpdatedAt *time.Time
Name string
Age int
Joined string
}func main() {
repo, err := gerpo.NewBuilder[test]().
DB(dbWrap).
Table("tests").
Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {
columns.Field(&m.ID).AsColumn().WithUpdateProtection()
columns.Field(&m.CreatedAt).AsColumn().WithUpdateProtection()
columns.Field(&m.UpdatedAt).AsColumn().WithInsertProtection()
columns.Field(&m.Name).AsColumn()
columns.Field(&m.Age).AsColumn()
columns.Field(&m.Joined).AsColumn().WithTable("joined_table")
}).
WithQuery(func(m *test, h query.PersistentHelper[test]) {
h.LeftJoin(func(ctx context.Context) string {
return ""
})
}).
Build()// Handle err and proceed with repo usage
}
```#### Soft Deletion
```go
package mainimport (
"context"
"time"
"github.com/insei/gerpo"
"github.com/insei/gerpo/query"
)type test struct {
ID int
CreatedAt time.Time
UpdatedAt *time.Time
Name string
Age int
DeletedAt *time.Time
}func main() {
repo, err := gerpo.NewBuilder[test]().
DB(dbWrap).
Table("tests").
Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {
columns.Field(&m.ID).AsColumn().WithUpdateProtection()
columns.Field(&m.CreatedAt).AsColumn().WithUpdateProtection()
columns.Field(&m.UpdatedAt).AsColumn().WithInsertProtection()
columns.Field(&m.Name).AsColumn()
columns.Field(&m.Age).AsColumn()
columns.Field(&m.DeletedAt).AsColumn().WithInsertProtection() // configure soft deletion field/column
}).
WithSoftDeletion(func(m *User, softDeletion *gerpo.SoftDeletionBuilder[User]) {
//Configure set value for soft deletion fields/columns
softDeletion.Field(&m.DeletedAt).SetValueFn(func(ctx context.Context) any {
deletedAt := time.Now().UTC()
return &deletedAt
})
}).
WithQuery(func(m *test, h query.PersistentHelper[test]) {
// Permanently exclude deleted elements from all queries
h.Where().Field(&m.DeletedAt).EQ(nil)
}).
Build()// Handle err and proceed with repo usage
}
```### Per-request Configuration
#### Exclude
Exclude certain fields from commands like SELECT/UPDATE/INSERT (Update/GetFirst/Insert/GetList):
```go
package mainimport (
"context"
"github.com/insei/gerpo"
"github.com/insei/gerpo/query"
)type test struct {
ID int
CreatedAt time.Time
UpdatedAt *time.Time
Name string
Age int
Joined string
}func main() {
var repo gerpo.Repository[test] // Already initialized
list, err := repo.GetList(ctx, func(m *test, h query.GetListHelper[test]) {
h.Page(1).Size(2) // Pagination
h.Exclude(&m.UpdatedAt, &m.ID)
})
// Handle err and work with the list
}
```#### Where
Available for Count/GetFirst/GetList/Delete/Update, supporting where grouping (AND/OR):
```go
package mainimport (
"context"
"github.com/insei/gerpo"
"github.com/insei/gerpo/query"
)type test struct {
ID int
CreatedAt time.Time
UpdatedAt *time.Time
Name string
Age int
Joined string
}func main() {
var repo gerpo.Repository[test] // Already initialized
list, err := repo.GetList(ctx, func(m *test, h query.GetListHelper[test]) {
h.Where().Field(&m.ID).LT(7) // Items with ID < 7
})
// Handle err and use the list
}
```#### Order
Available for GetFirst/GetList:
```go
package mainimport (
"context"
"github.com/insei/gerpo"
"github.com/insei/gerpo/query"
)type test struct {
ID int
CreatedAt time.Time
UpdatedAt *time.Time
Name string
Age int
Joined string
}func main() {
var repo gerpo.Repository[test] // Already initialized
item, err := repo.GetFirst(ctx, func(m *test, h query.GetFirstHelper[test]) {
h.OrderBy().Field(&m.CreatedAt).DESC()
})
// Handle err and use 'item'
}
```---
We hope this information helps you quickly get started with **GERPO** and integrate it into your own projects. If you have any questions or suggestions, feel free to open an issue or contribute to the repository.
## Documentation:
* repository (main repository code - uses sqlstmt, query and executor for work, execute hooks, callbacks and error transformer)
* query (User API Query interface - works with sqlstmt via linq API)
* linq (Internal Query API interface - works with sqlstmt)
* sqlstmt (Internal SQL queries generator interface - generates SQL queries and stores arguments)
* [executor](https://github.com/Insei/gerpo/tree/main/executor) (Internal SQL Queries executor API - execute SQL queries and map values to entities)