{"id":28476920,"url":"https://github.com/cjp2600/protoc-gen-structify","last_synced_at":"2026-02-27T06:57:08.068Z","repository":{"id":181026398,"uuid":"665919170","full_name":"cjp2600/protoc-gen-structify","owner":"cjp2600","description":"protobuf base db-store wrapper","archived":false,"fork":false,"pushed_at":"2025-10-22T04:26:14.000Z","size":16979,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-22T06:16:34.764Z","etag":null,"topics":["database","generator","golang","orm","protobuf"],"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/cjp2600.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-07-13T09:40:49.000Z","updated_at":"2025-10-22T04:26:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"fd54df21-352c-47f4-a253-adf8e3806502","html_url":"https://github.com/cjp2600/protoc-gen-structify","commit_stats":null,"previous_names":["cjp2600/structify"],"tags_count":65,"template":false,"template_full_name":null,"purl":"pkg:github/cjp2600/protoc-gen-structify","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjp2600%2Fprotoc-gen-structify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjp2600%2Fprotoc-gen-structify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjp2600%2Fprotoc-gen-structify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjp2600%2Fprotoc-gen-structify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cjp2600","download_url":"https://codeload.github.com/cjp2600/protoc-gen-structify/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjp2600%2Fprotoc-gen-structify/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29887120,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T05:38:26.446Z","status":"ssl_error","status_checked_at":"2026-02-27T05:38:25.235Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["database","generator","golang","orm","protobuf"],"created_at":"2025-06-07T15:38:04.382Z","updated_at":"2026-02-27T06:57:08.062Z","avatar_url":"https://github.com/cjp2600.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# protoc-gen-structify\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/cjp2600/protoc-gen-structify)](https://goreportcard.com/report/github.com/cjp2600/protoc-gen-structify)\n\n`protoc-gen-structify` is a powerful Protocol Buffers (protobuf) plugin that generates Go code for database operations. It provides a seamless way to create type-safe database access layers from your protobuf definitions.\n\n## Features\n\n- **Multiple Database Support**: Currently supports PostgreSQL, SQLite, and ClickHouse\n- **Type-Safe Operations**: Generates strongly-typed Go code for database operations\n- **CRUD Operations**: Automatically generates Create, Read, Update, Delete operations\n- **Relations Support**: Handles one-to-one, one-to-many, and many-to-many relationships\n- **Customizable**: Supports custom options for table names, field names, and more\n- **Transaction Support**: Built-in transaction management\n- **Query Builder**: Integrated with Squirrel query builder for complex queries\n- **Filtering System**: Built-in filtering and querying capabilities\n- **Relation Handling**: Handles various types of relations\n\n## Installation\n\n```bash\ngo install github.com/cjp2600/protoc-gen-structify@latest\n```\n\nMake sure your `GOPATH/bin` is in your `PATH` environment variable.\n\n## Quick Start\n\n1. Define your protobuf messages:\n\n```protobuf\nsyntax = \"proto3\";\n\npackage example;\n\nimport \"google/protobuf/timestamp.proto\";\nimport \"structify/options.proto\";\n\nmessage User {\n  option (structify.table) = \"users\";\n  \n  int64 id = 1 [(structify.field).primary_key = true];\n  string name = 2;\n  string email = 3 [(structify.field).unique = true];\n  google.protobuf.Timestamp created_at = 4;\n  \n  repeated Post posts = 5 [(structify.relation) = {\n    field: \"user_id\",\n    reference: \"id\"\n  }];\n}\n\nmessage Post {\n  option (structify.table) = \"posts\";\n  \n  int64 id = 1 [(structify.field).primary_key = true];\n  string title = 2;\n  string content = 3;\n  int64 user_id = 4 [(structify.field).foreign_key = \"users.id\"];\n}\n```\n\n2. Generate the code:\n\n```bash\nprotoc --go_out=. --structify_out=. user.proto\n```\n\n3. Use the generated code:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"database/sql\"\n    \"log\"\n    \n    _ \"github.com/lib/pq\"\n    \"your/package/generated\"\n)\n\nfunc main() {\n    db, err := sql.Open(\"postgres\", \"postgres://user:pass@localhost/dbname?sslmode=disable\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer db.Close()\n    \n    // Initialize the client\n    client := generated.NewUserDatabaseClient(db)\n    \n    // Create a new user\n    user := \u0026generated.User{\n        Name:  \"John Doe\",\n        Email: \"john@example.com\",\n    }\n    \n    err = client.Create(context.Background(), user)\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    // Find user by ID\n    found, err := client.GetByID(context.Background(), user.ID)\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    // Update user\n    found.Name = \"John Updated\"\n    err = client.Update(context.Background(), found)\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    // Find users with conditions\n    users, err := client.FindMany(context.Background(), generated.NewUserCondition().\n        WhereName(\"John Updated\").\n        WhereEmail(\"john@example.com\"))\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\n## Supported Databases\n\n### PostgreSQL\n```protobuf\noption (structify.provider) = \"postgres\";\n```\n\n### SQLite\n```protobuf\noption (structify.provider) = \"sqlite\";\n```\n\n### ClickHouse\n```protobuf\noption (structify.provider) = \"clickhouse\";\n```\n\n#### ClickHouse Query Settings and PREWHERE\n\nClickHouse provider supports:\n1. **Query Settings** - fine-tune query performance and behavior\n2. **PREWHERE** - optimize filtering by reading selective columns first\n\n**PREWHERE usage:**\n```go\n// PREWHERE filters data before reading all columns\nresults, err := storage.FindMany(ctx,\n    PrewhereBuilder(\n        CustomerIdEq(customerId),\n        OperationDateGTE(startDate),\n        OperationDateLT(endDate),\n    ),\n    SortBuilder(OperationDateOrderBy(true)),\n)\n```\n\n**Settings usage:**\n```go\n// Single setting\nresults, err := storage.FindMany(ctx,\n    SettingBuilder(SettingMaxThreads, 4),\n)\n\n// Multiple settings\nsettings := map[string]interface{}{\n    SettingOptimizeReadInOrder: 1,\n    SettingMaxThreads:          4,\n    SettingMaxMemoryUsage:      8000000000,\n}\nresults, err := storage.FindMany(ctx,\n    SettingsBuilder(settings),\n)\n```\n\n**Combining PREWHERE, WHERE, and SETTINGS:**\n```go\nresults, err := storage.FindMany(ctx,\n    PrewhereBuilder(CustomerIdEq(customerId)),    // Highly selective\n    FilterBuilder(StatusEq(\"completed\")),         // Additional filtering\n    SettingsBuilder(map[string]interface{}{\n        SettingOptimizeReadInOrder: 1,\n        SettingMaxThreads:          4,\n    }),\n)\n```\n\n**Available setting constants:**\n- `SettingMaxThreads` - maximum number of threads for query execution\n- `SettingMaxMemoryUsage` - maximum memory usage\n- `SettingMaxExecutionTime` - maximum execution time in seconds\n- `SettingOptimizeReadInOrder` - optimize reading in sort order\n- `SettingForcePrimaryKey` - force use of primary key\n- And many more...\n\nFor complete documentation and examples, see [CLICKHOUSE_SETTINGS.md](CLICKHOUSE_SETTINGS.md).\n\n## Field Options\n\n### Primary Key\n```protobuf\nint64 id = 1 [(structify.field).primary_key = true];\n```\n\n### Unique Constraint\n```protobuf\nstring email = 1 [(structify.field).unique = true];\n```\n\n### Foreign Key\n```protobuf\nint64 user_id = 1 [(structify.field).foreign_key = \"users.id\"];\n```\n\n### Nullable Field\n```protobuf\nstring description = 1 [(structify.field).nullable = true];\n```\n\n### Google Struct (JSONB)\n```protobuf\nimport \"google/protobuf/struct.proto\";\n\ngoogle.protobuf.Struct metadata = 1;\n```\n\nFor PostgreSQL provider this field is generated as:\n\n```go\nMetadata structpb.Struct\n```\n\n## Relation Options\n\n### One-to-Many\n```protobuf\nrepeated Post posts = 1 [(structify.relation) = {\n    field: \"user_id\",\n    reference: \"id\"\n}];\n```\n\n### Many-to-One\n```protobuf\nUser user = 1 [(structify.relation) = {\n    field: \"id\",\n    reference: \"user_id\"\n}];\n```\n\n## Generated Code Structure\n\nThe plugin generates the following components:\n\n1. **Database Client**: Main interface for database operations\n2. **Storage**: Type-safe storage implementation\n3. **Conditions**: Query builder for complex queries\n4. **Types**: Go structs matching your protobuf messages\n5. **Constants**: Generated constants for field names and table names\n\n## Filtering System\n\nThe generated code provides both convenient, type-safe filter helpers for each field and generic helpers for dynamic cases.\n\n**How to use:**\n- Use the generated wrappers for each field and filter type (e.g., `db.UserAgeEq`, `db.UserEmailLike`, etc.).\n- For custom or dynamic cases, use generic helpers like `db.Eq(\"field\", value)`.\n- Always wrap your filter (or filter composition) with `db.FilterBuilder(...)` when passing to query methods.\n- Combine filters with `db.And(...)`, `db.Or(...)`, etc.\n\n**Example:**\n```go\n// Get users with pagination and complex filter\nusers, paginator, err := userStorage.FindManyWithPagination(\n    ctx,\n    10, // limit\n    1,  // page\n    db.FilterBuilder(\n        db.And(\n            db.Eq(\"status\", \"active\"),\n            db.UserAgeEq(12),\n        ),\n    ),\n)\n```\n\n**Available filter helpers for each field:**\n- `db.UserAgeEq(value int32)`\n- `db.UserAgeGT(value int32)`\n- `db.UserAgeBetween(min, max int32)`\n- ...and so on for every field in your struct\n\n**Generic filter helpers:**\n- `db.Eq(\"field\", value)`\n- `db.NotEq(\"field\", value)`\n- `db.In(\"field\", values...)`\n- `db.Like(\"field\", pattern)`\n- etc.\n\n**Logical composition:**\n- `db.And(filter1, filter2, ...)`\n- `db.Or(filter1, filter2, ...)`\n\n**Note:**\n- Always use `db.FilterBuilder(...)` to pass filters to search/query methods.\n- You can mix generic and generated filters inside logical compositions.\n\n## Filtering System — Practical Examples\n\n### 1. Using Generated Field Filters\n\nFor each struct field, filter functions are generated, for example:\n- `UserAgeEq(value int32)`\n- `UserEmailLike(value string)`\n- `BotUserIdEq(value string)`\n- etc.\n\n**Example:**\n```go\nusers, err := userStorage.FindMany(ctx,\n    db.FilterBuilder(db.UserAgeEq(25)),\n    db.FilterBuilder(db.UserEmailLike(\"%@gmail.com\")),\n)\n```\n\n### 2. Using Generic Filters\n\nYou can use generic filters for dynamic scenarios:\n```go\nusers, err := userStorage.FindMany(ctx,\n    db.FilterBuilder(db.Eq(\"status\", \"active\")),\n    db.FilterBuilder(db.Like(\"email\", \"%@gmail.com\")),\n)\n```\n\n### 3. Composing Filters\n\nFor complex conditions, use composition:\n```go\nusers, paginator, err := userStorage.FindManyWithPagination(\n    ctx,\n    10, // limit\n    1,  // page\n    db.FilterBuilder(\n        db.And(\n            db.Eq(\"status\", \"active\"),\n            db.UserAgeEq(12),\n            db.Or(\n                db.UserEmailLike(\"%@gmail.com\"),\n                db.UserEmailLike(\"%@yahoo.com\"),\n            ),\n        ),\n    ),\n)\n```\n\n### 4. Filters for Related Entities\n\nTo filter by relations, use the corresponding filters:\n```go\nbots, err := botStorage.FindMany(ctx,\n    db.FilterBuilder(db.BotUserIdEq(\"user-123\")),\n)\n```\nOr for date range:\n```go\nbots, err := botStorage.FindMany(ctx,\n    db.FilterBuilder(db.BotCreatedAtBetween(time1, time2)),\n)\n```\n\n### 5. Batch Filters\n\n```go\nbots, err := botStorage.FindMany(ctx,\n    db.FilterBuilder(db.BotUserIdIn(\"user-1\", \"user-2\", \"user-3\")),\n)\n```\n\n### 6. Sorting\n\n```go\nusers, err := userStorage.FindMany(ctx,\n    db.FilterBuilder(db.UserAgeOrderBy(true)), // true = ASC, false = DESC\n)\n```\n\n---\n\n## Summary\n\n- **Always use `db.FilterBuilder(...)`** to pass filters to query methods.\n- You can combine generic filters (`db.Eq`, `db.Like`, ...) and generated filters (`db.UserAgeEq`, `db.BotUserIdEq`, ...).\n- For complex conditions, use composition with `db.And`, `db.Or`.\n- For batch operations, use filters like `FieldIn`, `FieldNotIn`.\n- For sorting, use `FieldOrderBy`.\n\n## Join Operations\n\nThe system supports various types of joins:\n\n```go\ntype JoinType string\n\nconst (\n    LeftJoin  JoinType = \"LEFT\"\n    InnerJoin JoinType = \"INNER\"\n    RightJoin JoinType = \"RIGHT\"\n)\n\n// Example join\njoin := Join(\n    InnerJoin,\n    userTable,\n    Eq(\"users.id\", \"posts.user_id\")\n)\n```\n\n## Transactions\n\nThe generated code includes transaction support:\n\n```go\n// Start transaction\ntx, err := db.Begin()\nif err != nil {\n    return err\n}\ndefer tx.Rollback()\n\n// Use transaction in context\nctx := WithTx(ctx, tx)\n\n// Perform operations\nerr = userStorage.Create(ctx, user)\nif err != nil {\n    return err\n}\n\n// Commit transaction\nerr = tx.Commit()\n```\n\n## Error Handling\n\nThe generated code uses `fmt.Errorf` for error wrapping:\n\n```go\nif err != nil {\n    return fmt.Errorf(\"failed to create user: %w\", err)\n}\n```\n\n## Relations\n\nDefine relations in your protobuf messages:\n\n```protobuf\nmessage Post {\n  string id = 1 [(structify.field) = {primary_key: true}];\n  string title = 2;\n  string content = 3;\n  string user_id = 4 [(structify.field) = {index: true}];\n  User author = 5 [(structify.field) = {relation: { field: \"user_id\", reference: \"id\" }}];\n}\n```\n\n## Examples\n\n### Basic CRUD Operations\n\n```go\n// Create\nuser := \u0026User{\n    Name: \"John Doe\",\n    Age: 30,\n    Email: \"john@example.com\",\n}\nid, err := userStorage.Create(ctx, user)\n\n// Read\nuser, err := userStorage.FindByID(ctx, id)\n\n// Update\nupdate := \u0026UserUpdate{\n    Name: \"John Smith\",\n}\nerr = userStorage.Update(ctx, id, update)\n\n// Delete\nerr = userStorage.DeleteByID(ctx, id)\n```\n\n### Querying with Filters\n\n```go\n// Find users with age \u003e 18 and active status\nusers, err := userStorage.FindMany(ctx,\n    And(\n        Gt(\"age\", 18),\n        Eq(\"status\", \"active\"),\n    ),\n)\n\n// Find one user by email\nuser, err := userStorage.FindOne(ctx,\n    Eq(\"email\", \"john@example.com\"),\n)\n```\n\n### Pagination\n\n```go\n// Get users with pagination\nusers, paginator, err := userStorage.FindManyWithPagination(\n    ctx,\n    10,  // limit\n    1,   // page\n    Eq(\"status\", \"active\"),\n)\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjp2600%2Fprotoc-gen-structify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcjp2600%2Fprotoc-gen-structify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjp2600%2Fprotoc-gen-structify/lists"}