{"id":13700921,"url":"https://github.com/jirfag/go-queryset","last_synced_at":"2025-05-16T18:10:00.889Z","repository":{"id":41293686,"uuid":"102281899","full_name":"jirfag/go-queryset","owner":"jirfag","description":"100% type-safe ORM for Go (Golang) with code generation and MySQL, PostgreSQL, Sqlite3, SQL Server support. GORM under the hood.","archived":false,"fork":false,"pushed_at":"2023-02-24T17:51:07.000Z","size":14218,"stargazers_count":727,"open_issues_count":20,"forks_count":74,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-04-12T17:00:31.128Z","etag":null,"topics":["codegenerator","go","golang","gorm","orm","queryset","typesafe"],"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/jirfag.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}},"created_at":"2017-09-03T17:29:30.000Z","updated_at":"2025-03-20T17:26:07.000Z","dependencies_parsed_at":"2024-01-11T13:20:18.587Z","dependency_job_id":null,"html_url":"https://github.com/jirfag/go-queryset","commit_stats":{"total_commits":113,"total_committers":18,"mean_commits":6.277777777777778,"dds":0.4601769911504425,"last_synced_commit":"36aee76bfd779da9e7f00b7591c255a20137f1a2"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jirfag%2Fgo-queryset","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jirfag%2Fgo-queryset/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jirfag%2Fgo-queryset/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jirfag%2Fgo-queryset/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jirfag","download_url":"https://codeload.github.com/jirfag/go-queryset/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582907,"owners_count":22095518,"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":["codegenerator","go","golang","gorm","orm","queryset","typesafe"],"created_at":"2024-08-02T20:01:08.619Z","updated_at":"2025-05-16T18:10:00.798Z","avatar_url":"https://github.com/jirfag.png","language":"Go","funding_links":[],"categories":["Go","ORM","\u003cspan id=\"orm\"\u003eORM\u003c/span\u003e","网络相关库","Relational Databases"],"sub_categories":["HTTP客户端","Advanced Console UIs","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","HTTP Clients","ORM","OpenGL","交流"],"readme":"# go-queryset [![GoDoc](https://godoc.org/github.com/jirfag/go-queryset?status.png)](http://godoc.org/github.com/jirfag/go-queryset) [![Go Report Card](https://goreportcard.com/badge/github.com/jirfag/go-queryset)](https://goreportcard.com/report/github.com/jirfag/go-queryset) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/04f432950cbc4e8cb0ec4ef36f4e90bb)](https://www.codacy.com/app/jirfag/go-queryset?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=jirfag/go-queryset\u0026amp;utm_campaign=Badge_Grade) [![Maintainability](https://api.codeclimate.com/v1/badges/8a726af72f338e8998bd/maintainability)](https://codeclimate.com/github/jirfag/go-queryset/maintainability) [![Build Status](https://travis-ci.org/jirfag/go-queryset.svg?branch=master)](https://travis-ci.org/jirfag/go-queryset) [![Coverage Status](https://coveralls.io/repos/github/jirfag/go-queryset/badge.svg?branch=master)](https://coveralls.io/github/jirfag/go-queryset?branch=master)\n\n100% type-safe ORM for Go (Golang) with code generation and MySQL, PostgreSQL, Sqlite3, SQL Server support. GORM under the hood.\n\n\n## Contents\n\n* [Installation](#installation)\n* [Usage](#usage)\n  * [Define models](#define-models)\n  * [Relation with GORM](#relation-with-gorm)\n  * [Create models](#create)\n  * [Select models](#select)\n  * [Update models](#update)\n  * [Delete models](#delete)\n  * [Full list of generated methods](#full-list-of-generated-methods)\n* [Golang version](#golang-version)\n* [Why](#why)\n  * [Why not just use GORM?](#why-not-just-use-gorm)\n  * [Why not another ORM?](#why-not-another-orm)\n  * [Why not any ORM?](#why-not-any-orm)\n  * [Why not raw SQL queries?](#why-not-raw-sql-queries)\n  * [Why not go-kallax?](#why-not-go-kallax)\n* [How it relates to another languages ORMs](#how-it-relates-to-another-languages-orms)\n* [Features](#features)\n* [Limitations](#limitations)\n* [Performance](#performance)\n\n\n# Installation\n```bash\ngo get -u github.com/jirfag/go-queryset/cmd/goqueryset\n```\n\n# Usage\n## Define models\nImagine you have model `User` in your [`models.go`](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm1/gorm1.go#L16) file:\n\n```go\ntype User struct {\n\tgorm.Model\n\tRating      int\n\tRatingMarks int\n}\n```\n\nNow transform it by [adding comments for query set generation](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/gorm4.go#L15):\n```go\n//go:generate goqueryset -in models.go\n\n// User struct represent user model. Next line (gen:qs) is needed to autogenerate UserQuerySet.\n// gen:qs\ntype User struct {\n\tgorm.Model\n\tRating      int\n\tRatingMarks int\n}\n```\n\nTake a look at line `// gen:qs`. It's a necessary line to enable querysets for this struct. You can put it at any line in struct's doc-comment.\n\nThen execute next shell command:\n```bash\ngo generate ./...\n```\n\nAnd you will get file [`autogenerated_models.go`](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/autogenerated_gorm4.go) in the same directory (and package) as `models.go`.\n\nIn this autogenerated file you will find a lot of autogenerated typesafe methods like these:\n```go\nfunc (qs UserQuerySet) CreatedAtGte(createdAt time.Time) UserQuerySet {\n\treturn qs.w(qs.db.Where(\"created_at \u003e= ?\", createdAt))\n}\n\nfunc (qs UserQuerySet) RatingGt(rating int) UserQuerySet {\n\treturn qs.w(qs.db.Where(\"rating \u003e ?\", rating))\n}\n\nfunc (qs UserQuerySet) IDEq(ID uint) UserQuerySet {\n\treturn qs.w(qs.db.Where(\"id = ?\", ID))\n}\n\nfunc (qs UserQuerySet) DeletedAtIsNull() UserQuerySet {\n\treturn qs.w(qs.db.Where(\"deleted_at IS NULL\"))\n}\n\nfunc (o *User) Delete(db *gorm.DB) error {\n\treturn db.Delete(o).Error\n}\n\nfunc (qs UserQuerySet) OrderAscByCreatedAt() UserQuerySet {\n\treturn qs.w(qs.db.Order(\"created_at ASC\"))\n}\n```\n\nSee full autogenerated file [here](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/autogenerated_gorm4.go).\n\nNow you can use this queryset for creating/reading/updating/deleting. Let's take a look at these operations.\n\n## Relation with GORM\nYou can embed and not embed `gorm.Model` into your model (e.g. if you don't need `DeletedAt` field), but you must use `*gorm.DB`\nto properly work. Don't worry if you don't use GORM yet, it's [easy to create `*gorm.DB`](http://jinzhu.me/gorm/database.html#connecting-to-a-database):\n```go\nimport (\n    \"github.com/jinzhu/gorm\"\n    _ \"github.com/jinzhu/gorm/dialects/mysql\"\n)\n\nfunc getGormDB() *gorm.DB {\n\tdb, err := gorm.Open(\"mysql\", \"user:password@/dbname?charset=utf8\u0026parseTime=True\u0026loc=Local\")\n\t// ...\n}\n```\n\nIf you already use another ORM or raw `sql.DB`, you can reuse your `sql.DB` object (to reuse connections pool):\n```go\n\tvar sqlDB *sql.DB = getSQLDBFromAnotherORM()\n\tvar gormDB *gorm.DB\n\tgormDB, err = gorm.Open(\"mysql\", sqlDB)\n```\n\n## Create\n```go\nu := User{\n\tRating: 5,\n\tRatingMarks: 0,\n}\nerr := u.Create(getGormDB())\n```\nUnder the hood `Create` method [just calls `db.Create(\u0026u)`](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/autogenerated_gorm4.go#L38).\n\n## Select\nIt's the most powerful feature of query set. Let's execute some queries:\n### Select all users\n```go\nvar users []User\nerr := NewUserQuerySet(getGormDB()).All(\u0026users)\nif err == gorm.ErrRecordNotFound {\n\t// no records were found\n}\n```\n\nIt generates this SQL request for MySQL:\n```sql\nSELECT * FROM `users` WHERE `users`.deleted_at IS NULL\n```\n\n`deleted_at` filtering is added by GORM (soft-delete), to disable it use [`Unscoped`](http://jinzhu.me/gorm/crud.html#delete).\n\n### Select one user\n```go\nvar user User\nerr := NewUserQuerySet(getGormDB()).One(\u0026user)\n```\n\n### Select N users with highest rating\n```go\nvar users []User\nerr := NewUserQuerySet(getGormDB()).\n\tRatingMarksGte(minMarks).\n\tOrderDescByRating().\n\tLimit(N).\n\tAll(\u0026users)\n```\n\n### Select users registered today\nIn this example we will define custom method on generated `UserQuerySet` for later reuse in multiple functions:\n```go\nfunc (qs UserQuerySet) RegisteredToday() UserQuerySet {\n\t// autogenerated typesafe method CreatedAtGte(time.Time)\n\treturn qs.CreatedAtGte(getTodayBegin())\n}\n\n...\nvar users []User\nerr := NewUserQuerySet(getGormDB()).\n\tRegisteredToday().\n\tOrderDescByCreatedAt().\n\tLimit(N).\n\tAll(\u0026users)\n```\n\n### Select specific fields\nBy default all fields are fetched using the `*` field selector.\nusing the `select` methd it is possible to limit the SQL statement to fetch specific fields:\n```go\nvar users []User\nerr := NewUserQuerySet(getGormDB()).Select(UserDBSchema.ID, UserDBSchema.Rating).All(\u0026users)\nif err == gorm.ErrRecordNotFound {\n\t// no records were found\n}\n```\n\nIt generates this SQL request for MySQL:\n```sql\nSELECT id, rating FROM `users` WHERE `users`.deleted_at IS NULL\n```\n\n\n## Update\n\n### Update one record by primary key\n```go\nu := User{\n\tModel: gorm.Model{\n\t\tID: uint(7),\n\t},\n\tRating: 1,\n}\nerr := u.Update(getGormDB(), UserDBSchema.Rating)\n```\n\nGoqueryset generates DB names for struct fields into [`UserDBSchema`](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/autogenerated_gorm4.go#L414) variable.\nIn this example we used [`UserDBSchema.Rating`](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/autogenerated_gorm4.go#L419).\n\nAnd this code generates next SQL:\n```sql\nUPDATE `users` SET `rating` = ? WHERE `users`.deleted_at IS NULL AND `users`.`id` = ?\n```\n\n### Update multiple record or without model object\nSometimes we don't have model object or we are updating multiple rows in DB.\nFor these cases there is another typesafe interface:\n\n```go\nerr := NewUserQuerySet(getGormDB()).\n\tRatingLt(1).\n\tGetUpdater().\n\tSetRatingMarks(0).\n\tUpdate()\n```\n```sql\nUPDATE `users` SET `rating_marks` = ? WHERE `users`.deleted_at IS NULL AND ((rating \u003c ?))\n```\n\n### UpdateNum\nThis method makes the same sql queries as Update() method, except return values: it returns\nnumber of affected rows and error\n\n```go\nnum, err := NewUserQuerySet(getGormDB()).\n\tRatingLt(1).\n\tGetUpdater().\n\tSetRatingMarks(0).\n\tUpdateNum()\n```\n```sql\nUPDATE `users` SET `rating_marks` = ? WHERE `users`.deleted_at IS NULL AND ((rating \u003c ?))\n```\n\n## Delete\n### Delete one record by primary key\n```go\nu := User{\n\tModel: gorm.Model{\n\t\tID: uint(7),\n\t},\n}\nerr := u.Delete(getGormDB())\n```\n\n### Delete multiple records\n```go\nerr := NewUserQuerySet(getGormDB()).\n\tRatingMarksEq(0).\n\tDelete()\n```\n\n## Full list of generated methods\n### QuerySet methods - `func (qs {StructName}QuerySet)`\n* create new queryset: `New{StructName}QuerySet(db *gorm.DB)`\n```go\nfunc NewUserQuerySet(db *gorm.DB) UserQuerySet\n```\n* filter by field (`where`)\n\t* all field types\n\t\t* Equals: `{FieldName}(Eq|Ne)(arg {FieldType})`\n\t\t```go\n\t\tfunc (qs UserQuerySet) RatingEq(rating int) UserQuerySet\n\t\t```\n\t\t* In: `{FieldName}(Not)In(arg {FieldType}, argsRest ...{FieldType})`\n\t\t```go\n\t\tfunc (qs UserQuerySet) NameIn(name string, nameRest ...string) UserQuerySet {}\n\t\tfunc (qs UserQuerySet) NameNotIn(name string, nameRest ...string) UserQuerySet {}\n\t\t```\n\t\t* `Order(Asc|Desc)By{FieldName}()`\n\t\t```go\n\t\tfunc (qs UserQuerySet) OrderDescByRating() UserQuerySet\n\t\t```\n\t* numeric types (`int`, `int64`, `uint` etc + `time.Time`):\n \t\t* `{FieldName}(Lt|Lte|Gt|Gte)(arg {FieldType)`\n\t\t```go\n\t\tfunc (qs UserQuerySet) RatingGt(rating int) UserQuerySet\n\t\t```\n\t* string types (`string`):\n \t\t* `{FieldName}(Like/Notlike)(arg {FieldType)`\n\t\t```go\n\t\tfunc (qs UserQuerySet) NameLike(name string) UserQuerySet\n\t\t```\n\t* pointer fields: `{FieldName}IsNull()`, `{FieldName}IsNotNull()`\n\t```go\n\tfunc (qs UserQuerySet) ProfileIsNull() UserQuerySet {}\n\tfunc (qs UserQuerySet) ProfileIsNotNull() UserQuerySet {}\n\t```\n* preload related object (for structs fields or pointers to structs fields): `Preload{FieldName}()`\n\tFor struct\n\t```go\n\t\ttype User struct {\n\t\t\tprofile *Profile\n\t\t}\n\t```\n\twill be generated:\n\t```go\n\tfunc (qs UserQuerySet) PreloadProfile() UserQuerySet\n\t```\n\t`Preload` functions call `gorm.Preload` to preload related object.\n\n* selectors\n\t* Select all objects, return `gorm.ErrRecordNotFound` if no records\n\t```go\n\tfunc (qs UserQuerySet) All(users *[]User) error\n\t```\n\t* Select one object, return `gorm.ErrRecordNotFound` if no records\n\t```go\n\tfunc (qs UserQuerySet) One(user *User) error\n\t```\n* Limit\n```go\nfunc (qs UserQuerySet) Limit(limit int) UserQuerySet\n```\n* [get updater](#update-multiple-record-or-without-model-object) (for update + where, based on current queryset):\n```go\nfunc (qs UserQuerySet) GetUpdater() UserUpdater\n```\n* delete with conditions from current queryset: `Delete()`\n```go\nfunc (qs UserQuerySet) Delete() error\n```\n* Aggregations\n\t* Count\n\t```go\n\tfunc (qs UserQuerySet) Count() (int, error)\n\t```\n\n### Object methods - `func (u *User)`\n* create object\n```go\nfunc (o *User) Create(db *gorm.DB) error\n```\n* delete object by PK\n```go\nfunc (o *User) Delete(db *gorm.DB) error\n```\n* update object by PK\n```go\nfunc (o *User) Update(db *gorm.DB, fields ...userDBSchemaField) error\n```\nPay attention that field names are automatically generated into variable\n```go\ntype userDBSchemaField string\n\n// UserDBSchema stores db field names of User\nvar UserDBSchema = struct {\n\tID          userDBSchemaField\n\tCreatedAt   userDBSchemaField\n\tUpdatedAt   userDBSchemaField\n\tDeletedAt   userDBSchemaField\n\tRating      userDBSchemaField\n\tRatingMarks userDBSchemaField\n}{\n\n\tID:          userDBSchemaField(\"id\"),\n\tCreatedAt:   userDBSchemaField(\"created_at\"),\n\tUpdatedAt:   userDBSchemaField(\"updated_at\"),\n\tDeletedAt:   userDBSchemaField(\"deleted_at\"),\n\tRating:      userDBSchemaField(\"rating\"),\n\tRatingMarks: userDBSchemaField(\"rating_marks\"),\n}\n```\n\nAnd they are typed, so you won't have string-misprint error.\n\n\n### Updater methods - `func (u UserUpdater)`\n* set field: `Set{FieldName}`\n```go\nfunc (u UserUpdater) SetCreatedAt(createdAt time.Time) UserUpdater\n```\n* execute update: `Update()`\n```go\nfunc (u UserUpdater) Update() error\n```\n\n# Golang version\nGolang \u003e= 1.8 is required. Tested on go 1.8, 1.9 versions by [Travis CI](https://travis-ci.org/jirfag/go-queryset)\n\n# Why?\n## Why not just use GORM?\nI like GORM: it's the best ORM for golang, it has fantastic documentation, but as a Golang developers team lead I can point out some troubles with it:\n1. GORM isn't typesafe: it's so easy to spend 1 hour trying to execute simple Update. GORM gets all arguments as `interface{}`\nand in the case of invalid GORM usage you won't get error: you will get invalid SQL, no SQL (!) and `error == nil` etc.\nIt's easy to get `SELECT * FROM t WHERE string_field == 1` SQL in production without type safety.\n2. GORM is difficult for beginners because of unclear `interface{}` interfaces: one can't easily find which arguments to pass to GORM methods.\n\n## Why not another ORM?\nType-safety, like with GORM.\n\n## Why not any ORM?\nI didn't see any ORM that properly handles code duplication. GORM is the best with `Scopes` support, but even it's far from ideal. E.g. we have GORM and [next typical code](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm1/gorm1.go#L16):\n```go\ntype User struct {\n\tgorm.Model\n\tRating      int\n\tRatingMarks int\n}\n\nfunc GetUsersWithMaxRating(limit int) ([]User, error) {\n\tvar users []User\n\tif err := getGormDB().Order(\"rating DESC\").Limit(limit).Find(\u0026users).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n\nfunc GetUsersRegisteredToday(limit int) ([]User, error) {\n\tvar users []User\n\ttoday := getTodayBegin()\n\terr := getGormDB().Where(\"created_at \u003e= ?\", today).Limit(limit).Find(\u0026users).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n```\n\nAt one moment PM asks us to implement new function, returning list of users registered today AND sorted by rating. Copy-paste way is to add `Order(\"rating DESC\")` to `GetUsersRegisteredToday`. But it leads to typical copy-paste troubles: when we change rating calculation logics (e.g. to `.Where(\"rating_marks \u003e= ?\", 10).Order(\"rating DESC\")`) we must change it in two places.\n\nHow to solve it? First idea is to [make reusable functions](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm2/gorm2.go#L27):\n```go\nfunc queryUsersWithMaxRating(db *gorm.DB, limit int) *gorm.DB {\n\treturn db.Order(\"rating DESC\").Limit(limit)\n}\n\nfunc queryUsersRegisteredToday(db *gorm.DB, limit int) *gorm.DB {\n\ttoday := getTodayBegin()\n\treturn db.Where(\"created_at \u003e= ?\", today).Limit(limit)\n}\n\nfunc GetUsersWithMaxRating(limit int) ([]User, error) {\n\tvar users []User\n\tif err := queryUsersWithMaxRating(getGormDB(), limit).Find(\u0026users).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n\nfunc GetUsersRegisteredToday(limit int) ([]User, error) {\n\tvar users []User\n\tif err := queryUsersRegisteredToday(getGormDB(), limit).Find(\u0026users).Error; err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n\nfunc GetUsersRegisteredTodayWithMaxRating(limit int) ([]User, error) {\n\tvar users []User\n\terr := queryUsersWithMaxRating(queryUsersRegisteredToday(getGormDB(), limit), limit).\n\t\tFind(\u0026users).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n```\n\nWe can use GORM [Scopes](http://jinzhu.me/gorm/crud.html#scopes) to improve [how it looks](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm3/gorm3.go#L64):\n```go\nfunc queryUsersWithMaxRating(db *gorm.DB) *gorm.DB {\n\treturn db.Order(\"rating DESC\")\n}\n\nfunc queryUsersRegisteredToday(db *gorm.DB) *gorm.DB {\n\treturn db.Where(\"created_at \u003e= ?\", getTodayBegin())\n}\n\nfunc GetUsersRegisteredTodayWithMaxRating(limit int) ([]User, error) {\n\tvar users []User\n\terr := getGormDB().\n\t\tScopes(queryUsersWithMaxRating, queryUsersRegisteredToday).\n\t\tLimit(limit).\n\t\tFind(\u0026users).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n```\n\nLooks nice, but we loosed ability to parametrize our reusable GORM queries (scopes): they must have only one argument of type `*gorm.DB`. It means that we must move out `Limit` from them (let's say we get it from user). If we need to implement query `QueryUsersRegisteredAfter(db *gorm.DB, t time.Time)` - we can't do it.\n\nNow compare it with [go-queryset solution](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/gorm4.go#L30):\n```go\n// UserQuerySet is an autogenerated struct with a lot of typesafe methods.\n// We can define any methods on it because it's in the same package\nfunc (qs UserQuerySet) WithMaxRating(minMarks int) UserQuerySet {\n\treturn qs.RatingMarksGte(minMarks).OrderDescByRating()\n}\n\nfunc (qs UserQuerySet) RegisteredToday() UserQuerySet {\n\t// autogenerated typesafe method CreatedAtGte(time.Time)\n\treturn qs.CreatedAtGte(getTodayBegin())\n}\n\n// now we can parametrize it\nconst minRatingMarks = 10\n\nfunc GetUsersWithMaxRating(limit int) ([]User, error) {\n\tvar users []User\n\terr := NewUserQuerySet(getGormDB()).\n\t\tWithMaxRating(minRatingMarks). // reuse our method\n\t\tLimit(limit).                  // autogenerated typesafe method Limit(int)\n\t\tAll(\u0026users)                    // autogenerated typesafe method All(*[]User)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n\nfunc GetUsersRegisteredToday(limit int) ([]User, error) {\n\tvar users []User\n\terr := NewUserQuerySet(getGormDB()).\n\t\tRegisteredToday(). // reuse our method\n\t\tLimit(limit).      // autogenerated typesafe method Limit(int)\n\t\tAll(\u0026users)        // autogenerated typesafe method All(*[]User)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n\nfunc GetUsersRegisteredTodayWithMaxRating(limit int) ([]User, error) {\n\tvar users []User\n\terr := NewUserQuerySet(getGormDB()).\n\t\tRegisteredToday().             // reuse our method\n\t\tWithMaxRating(minRatingMarks). // reuse our method\n\t\tLimit(limit).\n\t\tAll(\u0026users) // autogenerated typesafe method All(*[]User)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn users, nil\n}\n```\n## Why not raw SQL queries?\nNo type-safety, a lot of boilerplate code.\n\n## Why not [go-kallax](https://github.com/src-d/go-kallax)?\n1. It works only with PostgreSQL. Go-queryset supports mysql, postgresql, sqlite, mssql etc (all that gorm supports).\n2. Lacks simplier model updating interface\n\n## How it relates to another languages ORMs\nQuerySet pattern is similar to:\n* [Django QuerySet](https://docs.djangoproject.com/en/dev/ref/models/querysets/), but better than it because of type-safety (Python)\n* [Rails Active Record and it's scopes](http://guides.rubyonrails.org/active_record_querying.html#scopes), but better than it because of type-safety (Ruby)\n\n# Features\n* 100% typesafe: there is no one method with `interface{}` arguments.\n* QuerySet pattern allows to reuse queries by defining [custom methods](https://github.com/jirfag/go-queryset/blob/master/examples/comparison/gorm4/gorm4.go#L30) on it.\n* Supports all DBMS that GORM supports: MySQL, PostgreSQL, Sqlite3, SQL Server.\n* Supports creating, selecting, updating, deleting of objects.\n\n# Limitations\n* Joins aren't supported\n* Struct tags aren't supported\n\n# Performance\n## Runtime\nPerformance is similar to GORM performance. GORM uses reflection and it may be slow, so why don't we generate raw SQL code?\n1. Despite the fact GORM uses reflection, it's the most popular ORM for golang. There are really few tasks where you are CPU-bound while working with DB, usually you are CPU-bound in machine with DB and network/disk bound on machine with golang server.\n2. Premature optimization is the root of all evil.\n3. Go-queryset is fully compatible with GORM.\n4. Code generation is used here not to speedup things, but to create nice interfaces.\n5. The main purpose of go-queryset isn't speed, but usage convenience.\n\n## Code generation\nCode generation is fast:\n1. We parse AST of needed file and find needed structs.\n2. We load package and parse it by `go/types`\n3. We don't use `reflect` module for parsing, because it's slow\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjirfag%2Fgo-queryset","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjirfag%2Fgo-queryset","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjirfag%2Fgo-queryset/lists"}