{"id":13836858,"url":"https://github.com/src-d/go-kallax","last_synced_at":"2025-05-16T19:00:21.591Z","repository":{"id":15724401,"uuid":"78661980","full_name":"src-d/go-kallax","owner":"src-d","description":"Kallax is a PostgreSQL typesafe ORM for the Go language.","archived":false,"fork":false,"pushed_at":"2022-12-28T15:54:30.000Z","size":820,"stargazers_count":856,"open_issues_count":28,"forks_count":72,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-05-05T05:05:42.965Z","etag":null,"topics":["database","golang","orm","postgres","postgresql"],"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/src-d.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}},"created_at":"2017-01-11T17:23:57.000Z","updated_at":"2025-04-29T20:17:24.000Z","dependencies_parsed_at":"2023-01-13T18:33:30.306Z","dependency_job_id":null,"html_url":"https://github.com/src-d/go-kallax","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/src-d%2Fgo-kallax","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/src-d%2Fgo-kallax/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/src-d%2Fgo-kallax/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/src-d%2Fgo-kallax/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/src-d","download_url":"https://codeload.github.com/src-d/go-kallax/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254592367,"owners_count":22097010,"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":["database","golang","orm","postgres","postgresql"],"created_at":"2024-08-04T15:00:55.804Z","updated_at":"2025-05-16T19:00:21.525Z","avatar_url":"https://github.com/src-d.png","language":"Go","readme":"\u003cimg src=\"https://cdn.rawgit.com/src-d/go-kallax/master/kallax.svg\" width=\"400\" /\u003e\n\n[![GoDoc](https://godoc.org/gopkg.in/src-d/go-kallax.v1?status.svg)](https://godoc.org/gopkg.in/src-d/go-kallax.v1) [![Build Status](https://travis-ci.org/src-d/go-kallax.svg?branch=master)](https://travis-ci.org/src-d/go-kallax) [![codecov](https://codecov.io/gh/src-d/go-kallax/branch/master/graph/badge.svg)](https://codecov.io/gh/src-d/go-kallax) [![Go Report Card](https://goreportcard.com/badge/github.com/src-d/go-kallax)](https://goreportcard.com/report/github.com/src-d/go-kallax) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n\nKallax is a PostgreSQL typesafe ORM for the Go language.\n\nIt aims to provide a way of programmatically write queries and interact with a PostgreSQL database without having to write a single line of SQL, use strings to refer to columns and use values of any type in queries.\n\nFor that reason, the first priority of kallax is to provide type safety to the data access layer.\nAnother of the goals of kallax is make sure all models are, first and foremost, Go structs without having to use database-specific types such as, for example, `sql.NullInt64`.\nSupport for arrays of all basic Go types and all JSON and arrays operators is provided as well.\n\n## Contents\n\n* [Installation](#installation)\n* [Usage](#usage)\n* [Define models](#define-models)\n  * [Struct tags](#struct-tags)\n  * [Primary keys](#primary-keys)\n  * [Model constructors](#model-constructors)\n  * [Model events](#model-events)\n* [Model schema](#model-schema)\n  * [Use schema](#use-schema)\n* [Manipulate models](#manipulate-models)\n  * [Insert models](#insert-models)\n  * [Update models](#update-models)\n  * [Save models](#save-models)\n  * [Delete models](#delete-models)\n* [Query models](#query-models)\n  * [Simple queries](#simple-queries)\n  * [Generated findbys](#generated-findbys)\n  * [Query with relationships](#query-with-relationships)\n  * [Querying JSON](#querying-json)\n* [Transactions](#transactions)\n* [Caveats](#caveats)\n* [Migrations](#migrations)\n* [Custom operators](#custom-operators)\n* [Debug SQL queries](#debug-sql-queries)\n* [Benchmarks](#benchmarks)\n* [Acknowledgements](#acknowledgements)\n* [Contributing](#contributing)\n\n## Installation\n\nThe recommended way to install `kallax` is:\n\n```\ngo get -u gopkg.in/src-d/go-kallax.v1/...\n```\n\n\u003e *kallax* includes a binary tool used by [go generate](http://blog.golang.org/generate),\nplease be sure that `$GOPATH/bin` is on your `$PATH`\n\n## Usage\n\nImagine you have the following file in the package where your models are.\n```go\npackage models\n\ntype User struct {\n        kallax.Model         `table:\"users\" pk:\"id\"`\n        ID       kallax.ULID\n        Username string\n        Email    string\n        Password string\n}\n```\n\nThen put the following on any file of that package:\n\n```go\n//go:generate kallax gen\n```\n\nNow all you have to do is run `go generate ./...` and a `kallax.go` file will be generated with all the generated code for your model.\n\nIf you don't want to use `go generate`, even though is the preferred use, you can just go to your package and run `kallax gen` yourself.\n\n### Excluding files from generation\n\nSometimes you might want to use the generated code in the same package it is defined and cause problems during the generation when you regenerate your models. You can exclude files in the package by changing the `go:generate` comment to the following:\n\n```go\n//go:generate kallax gen -e file1.go -e file2.go\n```\n\n## Define models\n\nA model is just a Go struct that embeds the `kallax.Model` type. All the fields of this struct will be columns in the database table.\n\nA model also needs to have one (and just one) primary key. The primary key is defined using the `pk` struct tag on the `kallax.Model` embedding. You can also set the primary key in a field of the struct with the struct tag `pk`, which can be `pk:\"\"` for a non auto-incrementable primary key or `pk:\"autoincr\"` for one that is auto-incrementable.\nMore about primary keys is discussed at the [primary keys](#primary-keys) section.\n\nFirst, let's review the rules and conventions for model fields:\n* All the fields with basic types or types that implement [sql.Scanner](https://golang.org/pkg/database/sql/#Scanner) and [driver.Valuer](https://golang.org/pkg/database/sql/driver/#Valuer) will be considered a column in the table of their matching type.\n* Arrays or slices of types mentioned above will be treated as PostgreSQL arrays of their matching type.\n* Fields that are structs (or pointers to structs) or interfaces not implementing [sql.Scanner](https://golang.org/pkg/database/sql/#Scanner) and [driver.Valuer](https://golang.org/pkg/database/sql/driver/#Valuer) will be considered as JSON. Same with arrays or slices of types that follow these rules.\n* Fields that are structs (or pointers to structs) with the struct tag `kallax:\",inline\"` or are embedded will be considered inline, and their fields would be considered as if they were at the root of the model.\n* All pointer fields are nullable by default. That means you do not need to use `sql.NullInt64`, `sql.NullBool` and the likes because kallax automatically takes care of that for you. **WARNING:** all JSON and `sql.Scanner` implementors will be initialized with `new(T)` in case they are `nil` before they are scanned.\n* By default, the name of a column will be the name of the struct field converted to lower snake case (e.g. `UserName` =\u003e `user_name`, `UserID` =\u003e `user_id`). You can override it with the struct tag `kallax:\"my_custom_name\"`.\n* Slices of structs (or pointers to structs) that are models themselves will be considered a 1:N relationship. Arrays of models are **not supported** by design.\n* A struct or pointer to struct field that is a model itself will be considered a 1:1 relationship.\n* For relationships, the foreign key is assumed to be the name of the model converted to lower snake case plus `_id` (e.g. `User` =\u003e `user_id`). You can override this with the struct tag `fk:\"my_custom_fk\"`.\n* For inverse relationship, you need to use the struct tag `fk:\",inverse\"`. You can combine the `inverse` with overriding the foreign key with `fk:\"my_custom_fk,inverse\"`. In the case of inverses, the foreign key name does not specify the name of the column in the relationship table, but the name of the column in the own table. The name of the column in the other table is always the primary key of the other model and cannot be changed for the time being.\n* Foreign keys *do not have to be in the model*, they are automagically managed underneath by kallax.\n\nKallax also provides a `kallax.Timestamps` struct that contains `CreatedAt` and `UpdatedAt` that will be managed automatically.\n\nLet's see an example of models with all these cases:\n\n```go\ntype User struct {\n        kallax.Model       `table:\"users\" pk:\"id,autoincr\"`\n        kallax.Timestamps\n        ID        int64\n        Username  string\n        Password  string\n        Emails    []string\n        // This is for demo purposes, please don't do this\n        // 1:N relationships load all N rows by default, so\n        // only do it when N is small.\n        // If N is big, you should probably be querying the posts\n        // table instead.\n        Posts []*Post `fk:\"poster_id\"`\n}\n\ntype Post struct {\n        kallax.Model      `table:\"posts\"`\n        kallax.Timestamps\n        ID       int64    `pk:\"autoincr\"`\n        Content  string   `kallax:\"post_content\"`\n        Poster   *User    `fk:\"poster_id,inverse\"`\n        Metadata Metadata `kallax:\",inline\"`\n}\n\ntype Metadata struct {\n        MetadataType MetadataType\n        Metadata map[string]interface{} // this will be json\n}\n```\n\n### Struct tags\n\n| Tag | Description | Can be used in |\n| --- | --- | --- |\n| `table:\"table_name\"` | Specifies the name of the table for a model. If not provided, the name of the table will be the name of the struct in lower snake case (e.g. `UserPreference` =\u003e `user_preference`) | embedded `kallax.Model` |\n| `pk:\"primary_key_column_name\"` | Specifies the column name of the primary key. | embedded `kallax.Model` |\n| `pk:\"primary_key_column_name,autoincr\"` | Specifies the column name of the autoincrementable primary key. | embedded `kallax.Model` |\n| `pk:\"\"` | Specifies the field is a primary key | any field with a valid identifier type |\n| `pk:\"autoincr\"` | Specifies the field is an auto-incrementable primary key | any field with a valid identifier type |\n| `kallax:\"column_name\"` | Specifies the name of the column | Any model field that is not a relationship |\n| `kallax:\"-\"` | Ignores the field and does not store it | Any model field |\n| `kallax:\",inline\"` | Adds the fields of the struct field to the model. Column name can also be given before the comma, but it is ignored, since the field is not a column anymore | Any struct field |\n| `fk:\"foreign_key_name\"` | Name of the foreign key column | Any relationship field |\n| `fk:\",inverse\"` | Specifies the relationship is an inverse relationship. Foreign key name can also be given before the comma | Any relationship field |\n| `unique:\"true\"` | Specifies the column has an unique constraint. | Any non-primary key field |\n\n### Primary keys\n\nPrimary key types need to satisfy the [Identifier](https://godoc.org/github.com/src-d/go-kallax/#Identifier) interface. Even though they have to do that, the generator is smart enough to know when to wrap some types to make it easier on the user.\n\nThe following types can be used as primary key:\n\n* `int64`\n* [`uuid.UUID`](https://godoc.org/github.com/gofrs/uuid#UUID)\n* [`kallax.ULID`](https://godoc.org/github.com/src-d/go-kallax/#ULID): this is a type kallax provides that implements a lexically sortable UUID. You can store it as `uuid` like any other UUID, but internally it's an ULID and you will be able to sort lexically by it.\n\nDue to how sql mapping works, pointers to `uuid.UUID` and `kallax.ULID` are not set to `nil` if they appear as `NULL` in the database, but to [`uuid.Nil`](https://godoc.org/github.com/satori/go.uuid#pkg-variables). Using pointers to UUIDs is discouraged for this reason.\n\nIf you need another type as primary key, feel free to open a pull request implementing that.\n\n**Known limitations**\n\n* Only one primary key can be specified and it can't be a composite key.\n\n### Model constructors\n\nKallax generates a constructor for your type named `New{TypeName}`. But you can customize it by implementing a private constructor named `new{TypeName}`. The constructor generated by kallax will use the same signature your private constructor has. You can use this to provide default values or construct the model with some values.\n\nIf you implement this constructor:\n\n```go\nfunc newUser(username, password string, emails ...string) (*User, error) {\n        if username == \"\" || len(emails) == 0 || password == \"\" {\n                return nil, errors.New(\"all fields are required\")\n        }\n\n        return \u0026User{Username: username, Password: password, Emails: emails}, nil\n}\n```\n\nKallax will generate one with the following signature:\n\n```go\nfunc NewUser(username string, password string, emails ...string) (*User, error)\n```\n\n**IMPORTANT:** if your primary key is not auto-incrementable, you should set an ID for every model you create in your constructor. Or, at least, set it before saving it. Inserting, updating, deleting or reloading an object with no primary key set will return an error.\n\nIf you don't implement your own constructor it's ok, kallax will generate one for you just instantiating your object like this:\n\n```go\nfunc NewT() *T {\n        return new(T)\n}\n```\n\n### Model events\n\nEvents can be defined for models and they will be invoked at certain times of the model lifecycle.\n\n* `BeforeInsert`: will be called before inserting the model.\n* `BeforeUpdate`: will be called before updating the model.\n* `BeforeSave`: will be called before updating or inserting the model. It's always called before `BeforeInsert` and `BeforeUpdate`.\n* `BeforeDelete`: will be called before deleting the model.\n* `AfterInsert`: will be called after inserting the model. The presence of this event will cause the insertion of the model to run in a transaction. If the event returns an error, it will be rolled back.\n* `AfterUpdate`: will be called after updating the model. The presence of this event will cause the update of the model to run in a transaction. If the event returns an error, it will be rolled back.\n* `AfterSave`: will be called after updating or inserting the model. It's always called after `AfterInsert` and `AfterUpdate`. The presence of this event will cause the operation with the model to run in a transaction. If the event returns an error, it will be rolled back.\n* `AfterDelete`: will be called after deleting the model. The presence of this event will cause the deletion to run in a transaction. If the event returns an error, it will be rolled back.\n\nTo implement these events, just implement the following interfaces. You can implement as many as you want:\n\n* [BeforeInserter](https://godoc.org/github.com/src-d/go-kallax#BeforeInserter)\n* [BeforeUpdater](https://godoc.org/github.com/src-d/go-kallax#BeforeUpdater)\n* [BeforeSaver](https://godoc.org/github.com/src-d/go-kallax#BeforeSaver)\n* [BeforeDeleter](https://godoc.org/github.com/src-d/go-kallax#BeforeDeleter)\n* [AfterInserter](https://godoc.org/github.com/src-d/go-kallax#AfterInserter)\n* [AfterUpdater](https://godoc.org/github.com/src-d/go-kallax#AfterUpdater)\n* [AfterSaver](https://godoc.org/github.com/src-d/go-kallax#AfterSaver)\n* [AfterDeleter](https://godoc.org/github.com/src-d/go-kallax#AfterDeleter)\n\nExample:\n\n```go\nfunc (u *User) BeforeSave() error {\n        if u.Password == \"\" {\n                return errors.New(\"cannot save user without password\")\n        }\n\n        if !isCrypted(u.Password) {\n                u.Password = crypt(u.Password)\n        }\n        return nil\n}\n```\n\n## Kallax generated code\n\nKallax generates a bunch of code for every single model you have and saves it to a file named `kallax.go` in the same package.\n\nFor every model you have, kallax will generate the following for you:\n\n* Internal methods for your model to make it work with kallax and satisfy the [Record](https://godoc.org/github.com/src-d/go-kallax#Record) interface.\n* A store named `{TypeName}Store`: the store is the way to access the data. A store of a given type is the way to access and manipulate data of that type. You can get an instance of the type store with `New{TypeName}Store(*sql.DB)`.\n* A query named `{TypeName}Query`: the query is the way you will be able to build programmatically the queries to perform on the store. A store only will accept queries of its own type. You can create a new query with `New{TypeName}Query()`.\nThe query will contain methods for adding criteria to your query for every field of your struct, called `FindBy`s. The query object is not immutable, that is, every condition added to it, changes the query. If you want to reuse part of a query, you can call the `Copy()` method of a query, which will return a query identical to the one used to call the method.\n* A resultset named `{TypeName}ResultSet`: a resultset is the way to iterate over and obtain all elements in a resultset returned by the store. A store of a given type will always return a result set of the matching type, which will only return records of that type.\n* Schema of all the models containing all the fields. That way, you can access the name of a specific field without having to use a string, that is, a typesafe way.\n\n## Model schema\n\n### Use schema\n\nA global variable `Schema` will be created in your `kallax.go`, that contains a field with the name of every of your models. Those are the schemas of your models. Each model schema contains all the fields of that model.\n\nSo, to access the username field of the user model, it can be accessed as:\n\n```go\nSchema.User.Username\n```\n\n## Manipulate models\n\nFor all of the following sections, we will assume we have a store `store` for our model's type.\n\n### Insert models\n\nTo insert a model we just need to use the `Insert` method of the store and pass it a model. If the primary key is not auto-incrementable and the object does not have one set, the insertion will fail.\n\n```go\nuser := NewUser(\"fancy_username\", \"super_secret_password\", \"foo@email.me\")\nerr := store.Insert(user)\nif err != nil {\n        // handle error\n}\n```\n\nIf our model has relationships, they will be saved, and so will the relationships of the relationships and so on. TL;DR: inserts are recursive.\n**Note:** the relationships will be saved using `Save`, not `Insert`.\n\n```go\nuser := NewUser(\"foo\")\nuser.Posts = append(user.Posts, NewPost(user, \"new post\"))\n\nerr := store.Insert(user)\nif err != nil {\n        // handle error\n}\n```\n\nIf there are any relationships in the model, both the model and the relationships will be saved in a transaction and only succeed if all of them are saved correctly.\n\n### Update models\n\nTo insert a model we just need to use the `Update` method of the store and pass it a model. It will return an error if the model was not already persisted or has not an ID.\n\n```go\nuser := FindLast()\nrowsUpdated, err := store.Update(user)\nif err != nil {\n        // handle error\n}\n```\n\nBy default, when a model is updated, all its fields are updated. You can also specify which fields to update passing them to update.\n\n```go\nrowsUpdated, err := store.Update(user, Schema.User.Username, Schema.User.Password)\nif err != nil {\n        // handle error\n}\n```\n\nIf our model has relationships, they will be saved, and so will the relationships of the relationships and so on. TL;DR: updates are recursive.\n**Note:** the relationships will be saved using `Save`, not `Update`.\n\n```go\nuser := FindLastPoster()\nrowsUpdated, err := store.Update(user)\nif err != nil {\n        // handle error\n}\n```\n\nIf there are any relationships in the model, both the model and the relationships will be saved in a transaction and only succeed if all of them are saved correctly.\n\n### Save models\n\nTo save a model we just need to use the `Save` method of the store and pass it a model. `Save` is just a shorthand that will call `Insert` if the model is not yet persisted and `Update` if it is.\n\n```go\nupdated, err := store.Save(user)\nif err != nil {\n        // handle error\n}\n\nif updated {\n        // it was updated, not inserted\n}\n```\n\nIf our model has relationships, they will be saved, and so will the relationships of the relationships and so on. TL;DR: saves are recursive.\n\n```go\nuser := NewUser(\"foo\")\nuser.Posts = append(user.Posts, NewPost(user, \"new post\"))\n\nupdated, err := store.Save(user)\nif err != nil {\n        // handle error\n}\n```\n\nIf there are any relationships in the model, both the model and the relationships will be saved in a transaction and only succeed if all of them are saved correctly.\n\n### Delete models\n\nTo delete a model we just have to use the `Delete` method of the store. It will return an error if the model was not already persisted.\n\n```go\nerr := store.Delete(user)\nif err != nil {\n        // handle error\n}\n```\n\nRelationships of the model are **not** automatically removed using `Delete`.\n\nFor that, specific methods are generated in the store of the model.\n\nFor one to many relationships:\n\n```go\n// remove specific posts\nerr := store.RemovePosts(user, post1, post2, post3)\nif err != nil {\n        // handle error\n}\n\n// remove all posts\nerr := store.RemovePosts(user)\n```\n\nFor one to one relationships:\n\n```go\n// remove the thing\nerr := store.RemoveThing(user)\n```\n\nNote that for that to work, the thing you're deleting must **not** be empty. That is, you need to eagerly load (or set afterwards) the relationships.\n\n```go\nuser, err := store.FindOne(NewUserQuery())\ncheckErr(err)\n\n// THIS WON'T WORK! We've not loaded \"Things\"\nerr := store.RemoveThings(user)\n\nuser, err := store.FindOne(NewUserQuery().WithThings())\ncheckErr(err)\n\n// THIS WILL WORK!\nerr := store.RemoveThings(user)\n```\n\n## Query models\n\n### Simple queries\n\nTo perform a query you have to do the following things:\n* Create a query\n* Pass the query to `Find`, `FindOne`, `MustFind` or `MustFindOne` of the store\n* Gather the results from the result set, if the used method was `Find` or `MustFind`\n\n```go\n// Create the query\nq := NewUserQuery().\n        Where(kallax.Like(Schema.User.Username, \"joe%\")).\n        Order(kallax.Asc(Schema.User.Username)).\n        Limit(20).\n        Offset(2)\n\nrs, err := store.Find(q)\nif err != nil {\n        // handle error\n}\n\nfor rs.Next() {\n        user, err := rs.Get()\n        if err != nil {\n                // handle error\n        }\n}\n```\n\nNext will automatically close the result set when it hits the end. If you have to prematurely exit the iteration you can close it manually with `rs.Close()`.\n\nYou can query just a single row with `FindOne`.\n\n```go\nq := NewUserQuery().\n        Where(kallax.Eq(Schema.User.Username, \"Joe\"))\n\nuser, err := store.FindOne(q)\n```\n\nYou can also get all of the rows in a result without having to manually iterate the result set with `FindAll`.\n\n```go\nq := NewUserQuery().\n        Where(kallax.Like(Schema.User.Username, \"joe%\")).\n        Order(kallax.Asc(Schema.User.Username)).\n        Limit(20).\n        Offset(2)\n\nusers, err := store.FindAll(q)\nif err != nil {\n        // handle error\n}\n```\n\nBy default, all columns in a row are retrieved. To not retrieve all of them, you can specify the columns to include/exclude. Take into account that partial records retrieved from the database will not be writable. To make them writable you will need to [`Reload`](#reloading-a-model) the object.\n\n```go\n// Select only Username and password\nNewUserQuery().Select(Schema.User.Username, Schema.User.Password)\n\n// Select all but password\nNewUserQuery().SelectNot(Schema.User.Password)\n```\n\n### Generated findbys\n\nKallax generates a `FindBy` for every field of your model for which it makes sense to do so. What is a `FindBy`? It is a shorthand to add a condition to the query for a specific field.\n\nConsider the following model:\n\n```go\ntype Person struct {\n        kallax.Model\n        ID        int64     `pk:\"autoincr\"`\n        Name      string\n        BirthDate time.Time\n        Age       int\n}\n```\n\nFour `FindBy`s will be generated for this model:\n\n```go\nfunc (*PersonQuery) FindByID(...int64) *PersonQuery\nfunc (*PersonQuery) FindByName(string) *PersonQuery\nfunc (*PersonQuery) FindByBirthDate(kallax.ScalarCond, time.Time) *PersonQuery\nfunc (*PersonQuery) FindByAge(kallax.ScalarCond, int) *PersonQuery\n```\n\nThat way, you can just do the following:\n\n```go\nNewPersonQuery().\n        FindByAge(kallax.GtOrEq, 18).\n        FindByName(\"Bobby\")\n```\n\ninstead of:\n\n```go\nNewPersonQuery().\n        Where(kallax.GtOrEq(Schema.Person.Age, 18)).\n        Where(kallax.Eq(Schema.Person.Name, \"Bobby\"))\n```\n\nWhy are there three different types of methods generated?\n\n- The primary key field is treated in a special way and allows multiple IDs to be passed, since searching by multiple IDs is a common operation.\n- Types that are not often searched by equality (integers, floats, times, ...) allow an operator to be passed to them to determine the operator to use.\n- Types that can only be searched by value (strings, bools, ...) only allow a value to be passed.\n\n### Count results\n\nInstead of passing the query to `Find` or `FindOne`, you can pass it to `Count` to get the number of rows in the resultset.\n\n```go\nn, err := store.Count(q)\n```\n\n### Query with relationships\n\nBy default, no relationships are retrieved unless the query specifies so.\n\nFor each of your relationships, a method in your query is created to be able to include these relationships in your query.\n\nOne to one relationships:\n\n```go\n// Select all posts including the user that posted them\nq := NewPostQuery().WithPoster()\nrs, err := store.Find(q)\n```\n\nOne to one relationships are always included in the same query. So, if you have 4 one to one relationships and you want them all, only 1 query will be done, but everything will be retrieved.\n\nOne to many relationships:\n\n```go\n// Select all users including their posts\n// NOTE: this is a really bad idea, because all posts will be loaded\n// if the N side of your 1:N relationship is big, consider querying the N store\n// instead of doing this\n// A condition can be passed to the `With{Name}` method to filter the results.\nq := NewUserQuery().WithPosts(nil)\nrs, err := store.Find(q)\n```\n\nTo avoid the N+1 problem with 1:N relationships, kallax performs batching in this case.\nSo, a batch of users are retrieved from the database in a single query, then all the posts for those users and finally, they are merged.\nThis process is repeated until there are no more rows in the result.\nBecause of this, retrieving 1:N relationships is really fast.\n\nThe default batch size is 50, you can change this using the `BatchSize` method all queries have.\n\n**NOTE:** if a filter is passed to a `With{Name}` method we can no longer guarantee that all related objects are there and, therefore, the retrieved records will **not** be writable.\n\n### Reloading a model\n\nIf, for example, you have a model that is not writable because you only selected one field you can always reload it and have the full object. When the object is reloaded, all the changes made to the object that have not been saved will be discarded and overwritten with the values in the database.\n\n```go\nerr := store.Reload(user)\n```\n\nReload will not reload any relationships, just the model itself. After a `Reload` the model will **always** be writable.\n\n### Querying JSON\n\nYou can query arbitrary JSON using the JSON operators defined in the [kallax](https://godoc.org/github.com/src-d/go-kallax) package. The schema of the JSON (if it's a struct, obviously for maps it is not) is also generated.\n\n```go\nq := NewPostQuery().Where(kallax.JSONContainsAnyKey(\n        Schema.Post.Metadata,\n        \"foo\", \"bar\",\n))\n```\n\n## Transactions\n\nTo execute things in a transaction the `Transaction` method of the model store can be used. All the operations done using the store provided to the callback will be run in a transaction.\nIf the callback returns an error, the transaction will be rolled back.\n\n```go\nstore.Transaction(func(s *UserStore) error {\n        if err := s.Insert(user1); err != nil {\n                return err\n        }\n\n        return s.Insert(user2)\n})\n```\n\nThe fact that a transaction receives a store with the type of the model can be a problem if you want to store several models of different types. Kallax has a method named `StoreFrom` that initializes a store of the type you want to have the same underlying store as some other.\n\n```go\nstore.Transaction(func(s *UserStore) error {\n        var postStore PostStore\n        kallax.StoreFrom(\u0026postStore, s)\n\n        for _, p := range posts {\n                if err := postStore.Insert(p); err != nil {\n                        return err\n                }\n        }\n\n        return s.Insert(user)\n})\n```\n\n`Transaction` can be used inside a transaction, but it does not open a new one, reuses the existing one.\n\n## Caveats\n\n* It is not possible to use slices or arrays of types that are not one of these types:\n  * Basic types (e.g. `[]string`, `[]int64`) (except for `rune`, `complex64` and `complex128`)\n  * Types that implement `sql.Scanner` and `driver.Valuer`\n  The reason why this is not possible is because kallax implements support for arrays of all basic Go types by hand and also for types implementing `sql.Scanner` and `driver.Valuer` (using reflection in this case), but without having a common interface to operate on them, arbitrary types can not be supported.\n  For example, consider the following type `type Foo string`, using `[]Foo` would not be supported. Know that this will fail during the scanning of rows and not in code-generation time for now. In the future, might be moved to a warning or an error during code generation.\n  Aliases of slice types are supported, though. If we have `type Strings []string`, using `Strings` would be supported, as a cast like this `([]string)(\u0026slice)` it's supported and `[]string` is supported.\n* `time.Time` and `url.URL` need to be used as is. That is, you can not use a type `Foo` being `type Foo time.Time`. `time.Time` and `url.URL` are types that are treated in a special way, if you do that, it would be the same as saying `type Foo struct { ... }` and kallax would no longer be able to identify the correct type.\n* `time.Time` fields will be truncated to remove its nanoseconds on `Save`, `Insert` or `Update`, since PostgreSQL will not be able to store them. PostgreSQL stores times with timezones as UTC internally. So, times will come back as UTC (you can use `Local` method to convert them back to the local timezone). You can change the timezone that will be used to bring times back from the database in [the PostgreSQL configuration](https://www.postgresql.org/docs/9.6/static/datatype-datetime.html).\n* Multidimensional arrays or slices are **not supported** except inside a JSON field.\n\n## Migrations\n\nKallax can generate migrations for your schema automatically, if you want to. It is a process completely separated from the model generation, so it does not force you to generate your migrations using kallax.\n\nSometimes, kallax won't be able to infer a type or you will want a specific column type for a field. You can specify so with the `sqltype` struct tag on a field.\n\n```go\ntype Model struct {\n        kallax.Model `table:\"foo\"`\n        Stuff SuperCustomType `sqltype:\"bytea\"`\n}\n```\n\nYou can see the [**full list of default type mappings**](#type-mappings) between Go and SQL.\n\n### Generate migrations\n\nTo generate a migration, you have to run the command `kallax migrate`.\n\n```\nkallax migrate --input ./users/ --input ./posts/ --out ./migrations --name initial_schema\n```\n\nThe `migrate` command accepts the following flags:\n\n| Name | Repeated | Description | Default |\n| --- | --- | --- | --- |\n| `--name` or `-n` | no | name of the migration file (will be converted to `a_snakecase_name`) | `migration` |\n| `--input` or `-i` | yes | every occurrence of this flag will specify a directory in which kallax models can be found. You can specify multiple times this flag if you have your models scattered across several packages | required |\n| `--out` or `-o` | no | destination folder where the migrations will be generated | `./migrations` |\n\nEvery single migration consists of 2 files:\n\n- `TIMESTAMP_NAME.up.sql`: script that will upgrade your database to this version.\n- `TIMESTAMP_NAME.down.sql`: script that will downgrade your database to this version.\n\nAdditionally, there is a `lock.json` file where schema of the last migration is store to diff against the current models.\n\n### Run migrations\n\nTo run a migration you can either use `kallax migrate up` or `kallax migrate down`. `up` will upgrade your database and `down` will downgrade it.\n\nThese are the flags available for `up` and `down`:\n\n| Name | Description | Default |\n| --- | --- | --- |\n| `--dir` or `-d` | directory where your migrations are stored | `./migrations` |\n| `--dsn` | database connection string | required |\n| `--steps` or `-s` | maximum number of migrations to run | `0` |\n| `--all` | migrate all the way up (only available for `up` |\n| `--version` or `-v` | final version of the database we want after running the migration. The version is the timestamp value at the beginning of migration files | `0` |\n\n* If no `--steps` or `--version` are provided to `down`, they will do nothing. If `--all` is provided to `up`, it will upgrade the database all the way up.\n* If `--steps` and `--version` are provided to either `up` or `down` it will use only `--version`, as it is more specific.\n\n**Example:**\n\n```\nkallax migrate up --dir ./my-migrations --dsn 'user:pass@localhost:5432/dbname?sslmode=disable' --version 1493991142\n```\n\n### Type mappings\n\n| Go type | SQL type |\n| --- | --- |\n| `kallax.ULID` | `uuid` |\n| `kallax.UUID` | `uuid` |\n| `kallax.NumericID` | `serial` on primary keys, `bigint` on foreign keys |\n| `int64` on primary keys | `serial` |\n| `int64` on foreign keys and other fields| `bigint` |\n| `string` | `text` |\n| `rune` | `char(1)` |\n| `uint8` | `smallint` |\n| `int8` | `smallint` |\n| `byte` | `smallint` |\n| `uint16` | `integer` |\n| `int16` | `smallint` |\n| `uint32` | `bigint` |\n| `int32` | `integer` |\n| `uint` | `numeric(20)` |\n| `int` | `bigint` |\n| `int64` | `bigint` |\n| `uint64` | `numeric(20)` |\n| `float32` | `real` |\n| `float64` | `double` |\n| `bool` | `boolean` |\n| `url.URL` | `text` |\n| `time.Time` | `timestamptz` |\n| `time.Duration` | `bigint` |\n| `[]byte` | `bytea` |\n| `[]T` | `T'[]` * where `T'` is the SQL type of type `T`, except for `T` = `byte` |\n| `map[K]V` | `jsonb` |\n| `struct` | `jsonb` |\n| `*struct` | `jsonb` |\n\nAny other type must be explicitly specified.\n\nAll types that are not pointers will be `NOT NULL`.\n\n## Custom operators\n\nYou can create custom operators with kallax using the `NewOperator` and `NewMultiOperator` functions.\n\n`NewOperator` creates an operator with the specified format. It returns a function that given a schema field and a value returns a condition.\n\nThe format is a string in which `:col:` will get replaced with the schema field and `:arg:` will be replaced with the value.\n\n```go\nvar Gt = kallax.NewOperator(\":col: \u003e :arg:\")\n\n// can be used like this:\nquery.Where(Gt(SomeSchemaField, 9000))\n```\n\n`NewMultiOperator` does exactly the same as the previous one, but it accepts a variable number of values.\n\n```go\nvar In = kallax.NewMultiOperator(\":col: IN :arg:\")\n\n// can be used like this:\nquery.Where(In(SomeSchemaField, 4, 5, 6))\n```\n\nThis function already takes care of wrapping `:arg:` with parenthesis.\n\n### Further customization\n\nIf you need further customization, you can create your own custom operator.\n\nYou need these things:\n\n* A condition constructor (the operator itself) that takes the field and the values to create the proper SQL expression.\n* A `ToSqler` that yields your SQL expression.\n\nImagine we want a greater than operator that only works with integers.\n\n```go\nfunc GtInt(col kallax.SchemaField, n int) kallax.Condition {\n        return func(schema kallax.Schema) kallax.ToSqler {\n                // it is VERY important that all SchemaFields\n                // are qualified using the schema\n                return \u0026gtInt{col.QualifiedName(schema), n}\n        }\n}\n\ntype gtInt struct {\n        col string\n        val int\n}\n\nfunc (g *gtInt) ToSql() (sql string, params []interface{}, err error) {\n        return fmt.Sprintf(\"%s \u003e ?\", g.col), []interface{}{g.val}, nil\n}\n\n// can be used like this:\nquery.Where(GtInt(SomeSchemaField, 9000))\n```\n\nFor most of the operators, `NewOperator` and `NewMultiOperator` are enough, so the usage of these functions is preferred over the completely custom approach. Use it only if there is no other way to build your custom operator.\n\n## Debug SQL queries\n\nIt is possible to debug the SQL queries being executed with kallax. To do that, you just need to call the `Debug` method of a store. This returns a new store with debugging enabled.\n\n```go\nstore.Debug().Find(myQuery)\n```\n\nThis will log to stdout using `log.Printf` `kallax: Query: THE QUERY SQL STATEMENT, args: [arg1 arg2]`.\n\nYou can use a custom logger (any function with a type `func(string, ...interface{})` using the `DebugWith` method instead.\n\n```go\nfunc myLogger(message string, args ...interface{}) {\n        myloglib.Debugf(\"%s, args: %v\", message, args)\n}\n\nstore.DebugWith(myLogger).Find(myQuery)\n```\n\n## Benchmarks\n\nHere are some benchmarks against [GORM](https://github.com/jinzhu/gorm), [SQLBoiler](https://github.com/vattle/sqlboiler) and `database/sql`. In the future we might add benchmarks for some more complex cases and other available ORMs.\n\n```\nBenchmarkKallaxUpdate-4                       \t     300\t   4179176 ns/op\t     656 B/op\t      25 allocs/op\nBenchmarkKallaxUpdateWithRelationships-4      \t     200\t   5662703 ns/op\t    6642 B/op\t     175 allocs/op\n\nBenchmarkKallaxInsertWithRelationships-4      \t     200\t   5648433 ns/op\t   10221 B/op\t     218 allocs/op\nBenchmarkSQLBoilerInsertWithRelationships-4   \t     XXX\t   XXXXXXX ns/op\t    XXXX B/op\t     XXX allocs/op\nBenchmarkRawSQLInsertWithRelationships-4      \t     200\t   5427503 ns/op\t    4516 B/op\t     127 allocs/op\nBenchmarkGORMInsertWithRelationships-4        \t     200\t   6196277 ns/op\t   35080 B/op\t     610 allocs/op\n\nBenchmarkKallaxInsert-4                       \t     300\t   3916239 ns/op\t    1218 B/op\t      29 allocs/op\nBenchmarkSQLBoilerInsert-4                    \t     300\t   4356432 ns/op\t    1151 B/op\t      35 allocs/op\nBenchmarkRawSQLInsert-4                       \t     300\t   4065924 ns/op\t    1052 B/op\t      27 allocs/op\nBenchmarkGORMInsert-4                         \t     300\t   4398799 ns/op\t    4678 B/op\t     107 allocs/op\n\nBenchmarkKallaxQueryRelationships/query-4     \t     500\t   2900095 ns/op\t  269157 B/op\t    6200 allocs/op\nBenchmarkSQLBoilerQueryRelationships/query-4  \t    1000\t   2082963 ns/op\t  125587 B/op\t    5098 allocs/op\nBenchmarkRawSQLQueryRelationships/query-4     \t      20\t  59400759 ns/op\t  294176 B/op\t   11424 allocs/op\nBenchmarkGORMQueryRelationships/query-4       \t     300\t   4758555 ns/op\t 1069118 B/op\t   20833 allocs/op\n\nBenchmarkKallaxQuery/query-4                  \t    3000\t    546742 ns/op\t   50673 B/op\t    1590 allocs/op\nBenchmarkSQLBoilerQuery/query-4               \t    2000\t    677839 ns/op\t   54082 B/op\t    2436 allocs/op\nBenchmarkRawSQLQuery/query-4                  \t    3000\t    464498 ns/op\t   37480 B/op\t    1525 allocs/op\nBenchmarkGORMQuery/query-4                    \t    1000\t   1388406 ns/op\t  427401 B/op\t    7068 allocs/op\n\nPASS\nok  \tgopkg.in/src-d/go-kallax.v1/benchmarks\t44.899s\n```\n\nAs we can see on the benchmark, the performance loss is not very much compared to raw `database/sql`, while GORMs performance loss is very big and the memory consumption is way higher. SQLBoiler, on the other hand, has a lower memory footprint than kallax (in some cases), but a bigger performance loss (though not very significant), except for queries with relationships (that is a regression, though, and should be improved in the future).\n\nSource code of the benchmarks can be found on the [benchmarks](https://github.com/src-d/go-kallax/tree/master/benchmarks) folder.\n\n**Notes:**\n\n* Benchmark runs are out of date as of 2018-05-28 (result of PR #269), some results are pending a re-run and will be updated soon.\n* Benchmarks were run on a 2015 MacBook Pro with i5 and 8GB of RAM and 128GB SSD hard drive running fedora 25.\n* Benchmark of `database/sql` for querying with relationships is implemented with a very naive 1+n solution. That's why the result is that bad.\n\n## Acknowledgements\n\n* Big thank you to the [Masterminds/squirrel](https://github.com/Masterminds/squirrel) library, which is an awesome query builder used internally in this ORM.\n* [lib/pq](https://github.com/lib/pq), the Golang PostgreSQL driver that ships with a ton of support for builtin Go types.\n* [mattes/migrate](https://github.com/mattes/migrate), a Golang library to manage database migrations.\n\n## Contributing\n\n### Reporting bugs\n\nKallax is a code generation tool, so it obviously has not been tested with all possible types and cases. If you find a case where the code generation is broken, please report an issue providing a minimal snippet for us to be able to reproduce the issue and fix it.\n\n### Suggesting features\n\nKallax is a very opinionated ORM that works for us, so changes that make things not work for us or add complexity via configuration will not be considered for adding.\nIf we decide not to implement the feature you're suggesting, just keep in mind that it might not be because it is not a good idea, but because it does not work for us or is not aligned with the direction we want kallax to be moving forward.\n\n### Running tests\n\nFor obvious reasons, an instance of PostgreSQL is required to run the tests of this package.\n\nBy default, it assumes that an instance exists at `0.0.0.0:5432` with an user, password and database name all equal to `testing`.\n\nIf that is not the case you can set the following environment variables:\n\n- `DBNAME`: name of the database\n- `DBUSER`: database user\n- `DBPASS`: database user password\n\n#### Docker PostgreSQL\n\nIf you have docker, you may run an instance of postgres in a container:\n\n```\ndocker run -it --rm --name kallax \\\n -e POSTGRES_PASSWORD=testing \\\n -e POSTGRES_USER=testing \\\n -e POSTGRES_DB=testing \\\n -v `pwd`/.pgdata:/var/lib/postgresql/data \\\n -p 127.0.0.1:5432:5432 \\\n postgres:11\n```\n\nRemove `.pgdata` after you are done.\n\nLicense\n-------\n\nMIT, see [LICENSE](LICENSE)\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrc-d%2Fgo-kallax","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsrc-d%2Fgo-kallax","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrc-d%2Fgo-kallax/lists"}