{"id":42166463,"url":"https://github.com/julian7/dmpr","last_synced_at":"2026-01-26T21:06:19.298Z","repository":{"id":57601672,"uuid":"203774489","full_name":"julian7/dmpr","owner":"julian7","description":"data mapper for go","archived":false,"fork":false,"pushed_at":"2019-08-30T09:52:26.000Z","size":70,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T11:59:11.416Z","etag":null,"topics":["data-mapper","golang-library","sql","sql-query-builder"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/julian7.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-22T10:50:53.000Z","updated_at":"2024-06-20T11:59:11.417Z","dependencies_parsed_at":"2022-09-26T20:00:41.754Z","dependency_job_id":null,"html_url":"https://github.com/julian7/dmpr","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/julian7/dmpr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fdmpr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fdmpr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fdmpr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fdmpr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/julian7","download_url":"https://codeload.github.com/julian7/dmpr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julian7%2Fdmpr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28788140,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T21:02:48.137Z","status":"ssl_error","status_checked_at":"2026-01-26T21:01:13.039Z","response_time":59,"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":["data-mapper","golang-library","sql","sql-query-builder"],"created_at":"2026-01-26T21:06:18.519Z","updated_at":"2026-01-26T21:06:19.282Z","avatar_url":"https://github.com/julian7.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DMPR: Database mapper from scratch\n\nThis database mapper is written for sqlx database library, mainly supporting postgres. It aims to be as light weight as possible.\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/julian7/dmpr)](https://goreportcard.com/report/github.com/julian7/dmpr)\n[![GoDoc](https://godoc.org/github.com/julian7/dmpr?status.svg)](https://godoc.org/github.com/julian7/dmpr)\n[![Releases](https://img.shields.io/github/release/julian7/dmpr/all.svg)](https://github.com/julian7/dmpr/releases)\n\n## In Scope\n\n* maintains a database connection\n* provides logrus logging\n* provides health report on the connection\n* provides basic query functionality on top of sqlx for logging purposes\n* provides basic model query functionality (Find, FindBy, All, Create, Update, Delete)\n* provides basic \"belongs to\", \"has one\", \"has many\", and \"many to many\" relationships (NewSelect)\n\n## Out of Scope\n\n* Transactions (for now)\n* Cascading joins in select: all joins are referencing the original model only.\n\n## Map models\n\nModels are structs, and mapper reads their \"db\" tags for meta-information, just like sqlx. There are a couple of rule of thumbs, which might make your life easier:\n\n* Database table names are generated by struct names by converting to snake_cased, pluralized form.\n* Empty `db:\"...\"` tag names are not handled well. If there is a tag, it must be named.\n* if the tag is \"-\" (just like in `db:\"-\"`), then that field will not be represented in the database.\n* if the tag is missing, sqlx uses a standard mapping: field name converted to lower case, and never `snake_case` (wrt. table names).\n* mapper accepts the following tag options (optional fields after a comma):\n  * omitempty: if the field is empty in the model, it won't be added to Create / Update query\n  * relation: it represents \"has one\" or \"has many\" relationships (depending on the field type)\n  * belongs: represents \"belongs_to\" relationship. It assumes another field with the same name, but with `_id` suffix.\n  * related maps can and should be added to structs. To avoid circular references, use pointers for related structs.\n* References may accept both values or pointers. However, go doesn't accept circular value references. As a simple rule, I'd suggest you to use values at \"belongs to\", but use pointers at \"has one\" or \"has many\" relationships.\n* Known issue: slice of values don't fill well. Use slice of pointers for \"has many\" and \"many to many\" relations.\n\n## Relations\n\n### Belongs\n\nWhen a struct \"belongs to\" another struct, it stores the other struct's ID like this:\n\n```golang\ntype Message struct {\n    ID    int\n    Title string\n    Body  string\n}\n\ntype Comment struct {\n    ID     int\n    Title  string\n    Body   string\n    PostID int     `db:\"post_id\"`\n    Post   Message `db:\"post,belongs\"`\n}\n```\n\nIn this case, Comment belongs to Message, and it's referenced internally as \"post\". It also requires a `post_id` field, as it will be stored in the table.\n\nSelecting a Comment looks like this:\n\n```golang\nimport \"gitlab.com/julian7/dmpr\"\n\ncomments := \u0026[]Comment{}\nquery, err := dmpr.NewSelect(comments)\nif err != nil {\n    panic(err)\n}\nquery.Where(dmpr.Eq(\"id\", 1)).Join(\"post\").All()\n```\n\nThis query loads `comment` with an appropriate comment, with the data of `Post`, which is a `Message` object.\n\n## Has one / has many\n\nWhen a struct \"has one\" another struct, it stores the struct ID at the other struct:\n\n```golang\ntype User struct {\n    ID       int\n    Name     string\n    Password string\n    Profile  *Profile `db:\"profile,relation=user\"`\n}\n\ntype Profile struct {\n    ID      int\n    UserID  int  `db:\"user_id\"`\n    Email   string\n}\n```\n\nIn this case, User \"has a\" Profile, but Profile doesn't \"belong to\" User. User requires a reference to a profile, and Profile requieres a `user_id` field. User's Profile field requires an option \"relation\" with a value how Profile is referencing it.\n\nSelecting a User with profile looks like this:\n\n```golang\nimport \"gitlab.com/julian7/dmpr\"\n\nusers := \u0026[]User{}\nquery, err := dmpr.NewSelect(users)\nif err != nil {\n    panic(err)\n}\nquery.Where(dmpr.Eq(\"id\", 1)).Join(\"profile\").All()\n```\n\nA \"has_many\" relationship is similar to \"has_one\", but the referencing struct is in a slice:\n\n```golang\ntype ToDoList struct {\n    ID         int\n    Name       string\n    ToDoItems []*ToDoItem `db:\"to_do_items,relation=list\"`\n}\n\ntype UserGroup struct {\n    ID      int\n    ListID  int     `db:\"list_id\"`\n    Name    string\n}\n\ntoDoLists := \u0026[]ToDoList{}\nquery, err := dmpr.NewSelect(users)\nif err != nil {\n    panic(err)\n}\nquery.Join(\"to_do_items\").All()\n```\n\n## Many to many\n\nA \"many to many\" relation represents an _n:m_ relationship, with an anonymous linking table:\n\n```sql\nCREATE TABLE users (\n    id SERIAL,\n    name VARCHAR(32)\n);\n\nCREATE TABLE groups (\n    id SERIAL,\n    name VARCHAR(32)\n);\n\nCREATE TABLE user_groups (\n    user_id INT,\n    group_id INT\n);\n\nSELECT t1.id, t1.name, t2.id AS group_id, t2.name AS group_name\nFROM users t1\nLEFT JOIN user_groups tt2 ON (t1.id=tt2.user_id)\nLEFT JOIN groups t2 ON (t2.id=tt2.group_id);\n```\n\nIt is more compact in go:\n\n```go\ntype User struct {\n    ID   int\n    Name string\n    Groups []Group `db:groups,relation=user,reverse=group,through=user_groups\"`\n}\n\ntype Group struct {\n    ID int\n    Name string\n    Users []*User `db:users,relation=group,reverse=user,through=user_groups\"`\n}\n\nusers := \u0026[]User{}\nquery, err := dmpr.NewSelect(users)\nif err != nil {\n    panic(err)\n}\nquery.Join(\"groups\").All()\n\n```\n\n## Operators\n\nThere are just a couple of operators implemented, but it's very easy to add more. They work in a way query builder can fetch their columns and their relations too.\n\n* Null operator: `dmpr.Null(\"column\", true)` provides a \"column IS NULL\" operator. If the second parameter is `false`, then it will provide \"column IS NOT NULL\" instead.\n* Eq operator: `dmpr.Eq(\"column\", value)` provides an equivalence operator, in the form of `column = VALUE` or `column IN (value...)`.\n* Lt / Gt / Le / Ge operators: they are simple binary operators, implementing \"less than,\" \"greater than,\" \"less than or equal,\" and \"greater than or equal\" operators.\n  They are similar to `dmpr.Eq(column, value)`, but they cannot handle slices.\n* Not operator: `dmpr.Not(operator)` negates an operator. For example, `dmpr.Not(dmpr.Null(\"column\", true))` returns `colum IS NOT NULL`.\n* And operator: `dmpr.And(operator...)` groups other operators together, to provide a single operator with an AND relationship between them.\n* Or operator: `dmpr.Or(operator...)` groups other operators together, to provide a single operator with an OR relationship between them.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian7%2Fdmpr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulian7%2Fdmpr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulian7%2Fdmpr/lists"}