{"id":19095682,"url":"https://github.com/insei/gerpo","last_synced_at":"2025-04-30T14:09:30.610Z","repository":{"id":257821435,"uuid":"864147820","full_name":"Insei/gerpo","owner":"Insei","description":"GERPO is a generic repository pattern implementation with language integrated query support. Works with any sql sources.","archived":false,"fork":false,"pushed_at":"2025-04-18T18:56:56.000Z","size":268,"stargazers_count":1,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-19T03:34:36.691Z","etag":null,"topics":["cache","generic","go","golang","query-builder","repository","sql","struct-mapping"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Insei.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-27T15:24:59.000Z","updated_at":"2025-04-18T18:56:57.000Z","dependencies_parsed_at":"2024-10-11T07:54:54.588Z","dependency_job_id":"5d51e1a8-21a5-40e2-bf5a-3d7005c3e671","html_url":"https://github.com/Insei/gerpo","commit_stats":null,"previous_names":["insei/gerpo"],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insei%2Fgerpo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insei%2Fgerpo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insei%2Fgerpo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Insei%2Fgerpo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Insei","download_url":"https://codeload.github.com/Insei/gerpo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251717063,"owners_count":21632232,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","generic","go","golang","query-builder","repository","sql","struct-mapping"],"created_at":"2024-11-09T03:34:46.225Z","updated_at":"2025-04-30T14:09:30.584Z","avatar_url":"https://github.com/Insei.png","language":"Go","readme":"# GERPO\n\n[![codecov](https://codecov.io/gh/Insei/gerpo/graph/badge.svg?token=LGY9O9OJF5)](https://codecov.io/gh/Insei/gerpo)\n[![build](https://github.com/Insei/gerpo/actions/workflows/go.yml/badge.svg)](https://github.com/Insei/gerpo/actions/workflows/go.yml)\n[![Goreport](https://goreportcard.com/badge/github.com/insei/gerpo)](https://goreportcard.com/report/github.com/insei/gerpo)\n[![GoDoc](https://godoc.org/github.com/insei/gerpo?status.svg)](https://godoc.org/github.com/insei/gerpo)\n\nWelcome to the **GERPO** repository! This document provides a brief overview of the project, build and run instructions, and other helpful information.\n\n## About GERPO\n**GERPO** (Golang + Repository) is a generic repository implementation with advanced configuration capabilities and easy to use builders.\n\nThis project under active development.\n\nRelease 1.0.0 Road Map:\n * Repository builder changes:\n   * Add Caching engine configuration in repository builder.\n   * Column builder changes:\n     * New API for configuring virtual (calculated fields). Current Virtual fields configuration API marked as deprecated.\n * **All other API is stable and not planned to change in 1.0.0 release.**\n\n### Why GERPO?\n1. Support any database drivers via simple [db adapters wrappers](https://github.com/Insei/gerpo/tree/main/executor/adapters#executor-db-adapters).\n2. Fast repository level implementation, oriented on microservices.\n3. Easily handle CRUD operations (Create, Read, Update, Delete) with powerful filtering and sorting.\n4. Already implemented pagination for list queries.\n5. Straightforward configuration user-friendly builders and there needed you can use SQL commands.\n6. All SQL code in one place — inside the configuration.\n7. Virtual (calculated, joined) columns with mapping to struct fields.\n8. No dependent to other libraries. Only [fmap](https://github.com/Insei/fmap) was used for working with fields pointers.\n9. [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).\n\n### Ideology\n\nThe GERPO ideology consists of several rules:\n1) If SQL code is used, then only in the repository configuration.\n2) All columns are attached to entity fields via pointers to them in the repository settings.\n3) There are no references in the entities that they are stored in the database (i.e. there are no tags)\n4) We do not implement entity relationships.\n5) We do not do migrations or other actions on database structure.\n\n## Features\nEssentially, **GERPO** is generic repository pattern implementation,\nyes GERPO looks like ORM in some cases, but it's not an ORM.\n\n- **Database adapters support**:\n  - Any database can be used.\n  - You can use tracing wrappers and any other wrappers.\n- **Caching Engine**\n  - Cache results in context for do not thinking about duplicated queries to database.\n  - Cache in external store, like redis.\n- **Repository configuration**\n  - Map struct fields to SQL columns via pointers. \n    - Easy rename and refactor them.\n    - Easy delete fields. Errors can be found at build time.\n    - You always know where you can found columns mapping settings.\n    - Protect fields to insert/update in database.\n    - Define virtual(calculated)/joined fields.\n  - Add callbacks and hooks.\n    - Before insert.\n    - Before update.\n    - After select.\n  - Define persistent filters, groupings, and joins.\n  - Configure soft deletion.\n    - Use special builder that replace delete function with update with needed fields update.\n    - Configure Persistent filters for excluding soft deleted entities.\n  - Configure Errors transformer to transform GERPO errors to you business Errors.\n\n- **Per-query configuration**:\n  - Query builder allows you to set up some query rules:\n    - Execution fields selector allows you manage fields at update/insert operations:\n        - Exclude certain fields by fields pointers.\n        - Select only needed columns by fields pointers.\n    - Where builder allows you:\n      - Configure grouped filters.\n      - Support OR/AND cases.\n      - All of this via fields pointers.\n    - Order builder:\n      - Configure order via fields pointers.\n    - Work with transactions.\n    - Use already implemented pagination in your List queries.\n\n## Performance\nGERPO uses a minimal amount of reflection and is designed to have minimal allocations when used,\nmost allocations are initialized during configuration.\nWe work with unsafe pointers and offsets to determine the necessary fields in\nthe repository configuration and when querying the database.\n\nI made 2 tests with absolutely identical conditions. Pure PGX V4 Pool vs GERPO over PGX V4 Pool.\nYes, we do 2x more allocations in a heap.\nBut I think our functionality is worth it.\nIn terms of time per operation, we are behind pure PGX v4 Pool by 8%.\n\nWell, I didn't know what the results would be at the beginning of the design. But I have ideas for optimizations.\n#### Pure PGX v4 pool:\n```\nBenchmarkGetOneFromDb-32           18049             65033 ns/op            1554 B/op         21 allocs/op\nBenchmarkGetOneFromDb-32           18288             66617 ns/op            1555 B/op         21 allocs/op\nBenchmarkGetOneFromDb-32           17860             66640 ns/op            1555 B/op         21 allocs/op\nBenchmarkGetOneFromDb-32           18193             64665 ns/op            1555 B/op         21 allocs/op\nBenchmarkGetOneFromDb-32           18198             65171 ns/op            1559 B/op         21 allocs/op\n```\n#### GERPO:\n```\nBenchmarkGetFirst/GetFirst-32              16808             69738 ns/op            2961 B/op         51 allocs/op\nBenchmarkGetFirst/GetFirst-32              16614             70462 ns/op            2961 B/op         51 allocs/op\nBenchmarkGetFirst/GetFirst-32              16905             70796 ns/op            2961 B/op         51 allocs/op\nBenchmarkGetFirst/GetFirst-32              17059             70737 ns/op            2961 B/op         51 allocs/op\nBenchmarkGetFirst/GetFirst-32              17184             69587 ns/op            2961 B/op         51 allocs/op\n```\n\n```\n+------------------------------------------------------------+\n|                  |              Get One/First              |\n+------------------+---------------------+-------------------+\n|                  |  PURE GPX Pool v4   | GERPO PGX Pool v4 |\n|                  |                     | v4 Adapter        |\n+------------------+---------------------+-------------------+\n| B/op             | 0%                  | +100%             |\n| allocs/op        | 0%                  | +120%             |\n| ns/op            | 0%                  | +7.75%            |\n+------------------+---------------------+-------------------+\n```\n\n## Installation\nGo minimal version is `1.21`.\n```bash\ngo get github.com/insei/gerpo@latest\n```\n\n## Examples\nBelow you’ll find various configurations and usage examples.\n\n### Repository Configuration\n\n#### Columns\n```go\npackage main\n\nimport (\n    \"time\"\n    \"github.com/insei/gerpo\"\n)\n\ntype test struct {\n    ID        int\n    CreatedAt time.Time\n    UpdatedAt *time.Time\n    Name      string\n    Age       int\n}\n\nfunc main() {\n    repo, err := gerpo.NewBuilder[test]().\n        DB(dbWrap).\n        Table(\"tests\").\n        Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {\n            columns.Field(\u0026m.ID).AsColumn().WithUpdateProtection()\n            columns.Field(\u0026m.CreatedAt).AsColumn().WithUpdateProtection()\n            columns.Field(\u0026m.UpdatedAt).AsColumn().WithInsertProtection()\n            columns.Field(\u0026m.Name).AsColumn()\n            columns.Field(\u0026m.Age).AsColumn()\n        }).\n        Build()\n\n    // Handle err and proceed with repo usage\n}\n```\n\n#### Joins\n```go\npackage main\n\nimport (\n    \"context\"\n    \"time\"\n    \"github.com/insei/gerpo\"\n    \"github.com/insei/gerpo/query\"\n)\n\ntype test struct {\n    ID        int\n    CreatedAt time.Time\n    UpdatedAt *time.Time\n    Name      string\n    Age       int\n    Joined    string\n}\n\nfunc main() {\n    repo, err := gerpo.NewBuilder[test]().\n        DB(dbWrap).\n        Table(\"tests\").\n        Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {\n            columns.Field(\u0026m.ID).AsColumn().WithUpdateProtection()\n            columns.Field(\u0026m.CreatedAt).AsColumn().WithUpdateProtection()\n            columns.Field(\u0026m.UpdatedAt).AsColumn().WithInsertProtection()\n            columns.Field(\u0026m.Name).AsColumn()\n            columns.Field(\u0026m.Age).AsColumn()\n            columns.Field(\u0026m.Joined).AsColumn().WithTable(\"joined_table\")\n        }).\n        WithQuery(func(m *test, h query.PersistentHelper[test]) {\n            h.LeftJoin(func(ctx context.Context) string {\n                return \"\u003cSQL JOIN COMMAND\u003e\"\n            })\n        }).\n        Build()\n\n    // Handle err and proceed with repo usage\n}\n```\n\n#### Soft Deletion\n```go\npackage main\n\nimport (\n    \"context\"\n    \"time\"\n    \"github.com/insei/gerpo\"\n    \"github.com/insei/gerpo/query\"\n)\n\ntype test struct {\n    ID        int\n    CreatedAt time.Time\n    UpdatedAt *time.Time\n    Name      string\n    Age       int\n\tDeletedAt *time.Time\n}\n\nfunc main() {\n    repo, err := gerpo.NewBuilder[test]().\n        DB(dbWrap).\n        Table(\"tests\").\n        Columns(func(m *test, columns *gerpo.ColumnBuilder[test]) {\n            columns.Field(\u0026m.ID).AsColumn().WithUpdateProtection()\n            columns.Field(\u0026m.CreatedAt).AsColumn().WithUpdateProtection()\n            columns.Field(\u0026m.UpdatedAt).AsColumn().WithInsertProtection()\n            columns.Field(\u0026m.Name).AsColumn()\n            columns.Field(\u0026m.Age).AsColumn()\n            columns.Field(\u0026m.DeletedAt).AsColumn().WithInsertProtection() // configure soft deletion field/column\n        }).\n        WithSoftDeletion(func(m *User, softDeletion *gerpo.SoftDeletionBuilder[User]) {\n            //Configure set value for soft deletion fields/columns\n            softDeletion.Field(\u0026m.DeletedAt).SetValueFn(func(ctx context.Context) any {\n                deletedAt := time.Now().UTC()\n                return \u0026deletedAt\n            })\n        }).\n        WithQuery(func(m *test, h query.PersistentHelper[test]) {\n            // Permanently exclude deleted elements from all queries\n            h.Where().Field(\u0026m.DeletedAt).EQ(nil)\n        }).\n        Build()\n\n    // Handle err and proceed with repo usage\n}\n```\n\n### Per-request Configuration\n\n#### Exclude\nExclude certain fields from commands like SELECT/UPDATE/INSERT (Update/GetFirst/Insert/GetList):\n```go\npackage main\n\nimport (\n    \"context\"\n    \"github.com/insei/gerpo\"\n    \"github.com/insei/gerpo/query\"\n)\n\ntype test struct {\n  ID        int\n  CreatedAt time.Time\n  UpdatedAt *time.Time\n  Name      string\n  Age       int\n  Joined    string\n}\n\nfunc main() {\n  var repo gerpo.Repository[test] // Already initialized\n    list, err := repo.GetList(ctx, func(m *test, h query.GetListHelper[test]) {\n        h.Page(1).Size(2) // Pagination\n        h.Exclude(\u0026m.UpdatedAt, \u0026m.ID)\n    })\n    // Handle err and work with the list\n}\n```\n\n#### Where\nAvailable for Count/GetFirst/GetList/Delete/Update, supporting where grouping (AND/OR):\n```go\npackage main\n\nimport (\n    \"context\"\n    \"github.com/insei/gerpo\"\n    \"github.com/insei/gerpo/query\"\n)\n\ntype test struct {\n  ID        int\n  CreatedAt time.Time\n  UpdatedAt *time.Time\n  Name      string\n  Age       int\n  Joined    string\n}\n\nfunc main() {\n    var repo gerpo.Repository[test] // Already initialized\n    list, err := repo.GetList(ctx, func(m *test, h query.GetListHelper[test]) {\n        h.Where().Field(\u0026m.ID).LT(7) // Items with ID \u003c 7\n    })\n    // Handle err and use the list\n}\n```\n\n#### Order\nAvailable for GetFirst/GetList:\n```go\npackage main\n\nimport (\n    \"context\"\n    \"github.com/insei/gerpo\"\n    \"github.com/insei/gerpo/query\"\n)\n\ntype test struct {\n  ID        int\n  CreatedAt time.Time\n  UpdatedAt *time.Time\n  Name      string\n  Age       int\n  Joined    string\n}\n\nfunc main() {\n  var repo gerpo.Repository[test] // Already initialized\n    item, err := repo.GetFirst(ctx, func(m *test, h query.GetFirstHelper[test]) {\n        h.OrderBy().Field(\u0026m.CreatedAt).DESC()\n    })\n    // Handle err and use 'item'\n}\n```\n\n---\n\nWe 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.\n## Documentation:\n* repository (main repository code - uses sqlstmt, query and executor for work, execute hooks, callbacks and error transformer)\n* query (User API Query interface - works with sqlstmt via linq API)\n  * linq (Internal Query API interface - works with sqlstmt)\n* sqlstmt (Internal SQL queries generator interface - generates SQL queries and stores arguments)\n* [executor](https://github.com/Insei/gerpo/tree/main/executor) (Internal SQL Queries executor API - execute SQL queries and map values to entities)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finsei%2Fgerpo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finsei%2Fgerpo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finsei%2Fgerpo/lists"}