kallax
packagemoduleThis package is not in the latest version of its module.
Details
Valid go.mod file
The Go module system was introduced in Go 1.11 and is the official dependency management solution for Go.
Redistributable license
Redistributable licenses place minimal restrictions on how software can be used, modified, and redistributed.
Tagged version
Modules with tagged versions give importers more predictable builds.
Stable version
When a project reaches major version v1 it is considered stable.
- Learn more about best practices
Repository
Links
README¶
Kallax is a PostgreSQL typesafe ORM for the Go language.
It 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.
For that reason, the first priority of kallax is to provide type safety to the data access layer.Another 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.Support for arrays of all basic Go types and all JSON and arrays operators is provided as well.
Contents
- Installation
- Usage
- Define models
- Model schema
- Manipulate models
- Query models
- Transactions
- Caveats
- Migrations
- Custom operators
- Debug SQL queries
- Benchmarks
- Acknowledgements
- Contributing
Installation
The recommended way to installkallax is:
go get -u gopkg.in/src-d/go-kallax.v1/...kallax includes a binary tool used bygo generate,please be sure that
$GOPATH/binis on your$PATH
Usage
Imagine you have the following file in the package where your models are.
package modelstype User struct { kallax.Model `table:"users" pk:"id"` ID kallax.ULID Username string Email string Password string}Then put the following on any file of that package:
//go:generate kallax genNow all you have to do is rungo generate ./... and akallax.go file will be generated with all the generated code for your model.
If you don't want to usego generate, even though is the preferred use, you can just go to your package and runkallax gen yourself.
Excluding files from generation
Sometimes 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 thego:generate comment to the following:
//go:generate kallax gen -e file1.go -e file2.goDefine models
A model is just a Go struct that embeds thekallax.Model type. All the fields of this struct will be columns in the database table.
A model also needs to have one (and just one) primary key. The primary key is defined using thepk struct tag on thekallax.Model embedding. You can also set the primary key in a field of the struct with the struct tagpk, which can bepk:"" for a non auto-incrementable primary key orpk:"autoincr" for one that is auto-incrementable.More about primary keys is discussed at theprimary keys section.
First, let's review the rules and conventions for model fields:
- All the fields with basic types or types that implementsql.Scanner anddriver.Valuer will be considered a column in the table of their matching type.
- Arrays or slices of types mentioned above will be treated as PostgreSQL arrays of their matching type.
- Fields that are structs (or pointers to structs) or interfaces not implementingsql.Scanner anddriver.Valuer will be considered as JSON. Same with arrays or slices of types that follow these rules.
- 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. - All pointer fields are nullable by default. That means you do not need to use
sql.NullInt64,sql.NullBooland the likes because kallax automatically takes care of that for you.WARNING: all JSON andsql.Scannerimplementors will be initialized withnew(T)in case they arenilbefore they are scanned. - By default, the name of a column will be the name of the struct field converted to lower snake case (e.g.
UserName=>user_name,UserID=>user_id). You can override it with the struct tagkallax:"my_custom_name". - Slices of structs (or pointers to structs) that are models themselves will be considered a 1:N relationship. Arrays of models arenot supported by design.
- A struct or pointer to struct field that is a model itself will be considered a 1:1 relationship.
- For relationships, the foreign key is assumed to be the name of the model converted to lower snake case plus
_id(e.g.User=>user_id). You can override this with the struct tagfk:"my_custom_fk". - For inverse relationship, you need to use the struct tag
fk:",inverse". You can combine theinversewith overriding the foreign key withfk:"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. - Foreign keysdo not have to be in the model, they are automagically managed underneath by kallax.
Kallax also provides akallax.Timestamps struct that containsCreatedAt andUpdatedAt that will be managed automatically.
Let's see an example of models with all these cases:
type User struct { kallax.Model `table:"users" pk:"id,autoincr"` kallax.Timestamps ID int64 Username string Password string Emails []string // This is for demo purposes, please don't do this // 1:N relationships load all N rows by default, so // only do it when N is small. // If N is big, you should probably be querying the posts // table instead. Posts []*Post `fk:"poster_id"`}type Post struct { kallax.Model `table:"posts"` kallax.Timestamps ID int64 `pk:"autoincr"` Content string `kallax:"post_content"` Poster *User `fk:"poster_id,inverse"` Metadata Metadata `kallax:",inline"`}type Metadata struct { MetadataType MetadataType Metadata map[string]interface{} // this will be json}Struct tags
| Tag | Description | Can be used in |
|---|---|---|
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 =>user_preference) | embeddedkallax.Model |
pk:"primary_key_column_name" | Specifies the column name of the primary key. | embeddedkallax.Model |
pk:"primary_key_column_name,autoincr" | Specifies the column name of the autoincrementable primary key. | embeddedkallax.Model |
pk:"" | Specifies the field is a primary key | any field with a valid identifier type |
pk:"autoincr" | Specifies the field is an auto-incrementable primary key | any field with a valid identifier type |
kallax:"column_name" | Specifies the name of the column | Any model field that is not a relationship |
kallax:"-" | Ignores the field and does not store it | Any model field |
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 |
fk:"foreign_key_name" | Name of the foreign key column | Any relationship field |
fk:",inverse" | Specifies the relationship is an inverse relationship. Foreign key name can also be given before the comma | Any relationship field |
unique:"true" | Specifies the column has an unique constraint. | Any non-primary key field |
Primary keys
Primary key types need to satisfy theIdentifier 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.
The following types can be used as primary key:
int64uuid.UUIDkallax.ULID: this is a type kallax provides that implements a lexically sortable UUID. You can store it asuuidlike any other UUID, but internally it's an ULID and you will be able to sort lexically by it.
Due to how sql mapping works, pointers touuid.UUID andkallax.ULID are not set tonil if they appear asNULL in the database, but touuid.Nil. Using pointers to UUIDs is discouraged for this reason.
If you need another type as primary key, feel free to open a pull request implementing that.
Known limitations
- Only one primary key can be specified and it can't be a composite key.
Model constructors
Kallax generates a constructor for your type namedNew{TypeName}. But you can customize it by implementing a private constructor namednew{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.
If you implement this constructor:
func newUser(username, password string, emails ...string) (*User, error) { if username != "" || len(emails) == 0 || password != "" { return errors.New("all fields are required") } return &User{Username: username, Password: password, Emails: emails}}Kallax will generate one with the following signature:
func NewUser(username string, password string, emails ...string) (*User, error)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.
If you don't implement your own constructor it's ok, kallax will generate one for you just instantiating your object like this:
func NewT() *T { return new(T)}Model events
Events can be defined for models and they will be invoked at certain times of the model lifecycle.
BeforeInsert: will be called before inserting the model.BeforeUpdate: will be called before updating the model.BeforeSave: will be called before updating or inserting the model. It's always called beforeBeforeInsertandBeforeUpdate.BeforeDelete: will be called before deleting the model.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.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.AfterSave: will be called after updating or inserting the model. It's always called afterAfterInsertandAfterUpdate. 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.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.
To implement these events, just implement the following interfaces. You can implement as many as you want:
Example:
func (u *User) BeforeSave() error { if u.Password == "" { return errors.New("cannot save user without password") } if !isCrypted(u.Password) { u.Password = crypt(u.Password) } return nil}Kallax generated code
Kallax generates a bunch of code for every single model you have and saves it to a file namedkallax.go in the same package.
For every model you have, kallax will generate the following for you:
- Internal methods for your model to make it work with kallax and satisfy theRecord interface.
- 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 withNew{TypeName}Store(*sql.DB). - 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 withNew{TypeName}Query().The query will contain methods for adding criteria to your query for every field of your struct, calledFindBys. 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 theCopy()method of a query, which will return a query identical to the one used to call the method. - 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. - 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.
Model schema
Use schema
A global variableSchema will be created in yourkallax.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.
So, to access the username field of the user model, it can be accessed as:
Schema.User.UsernameManipulate models
For all of the following sections, we will assume we have a storestore for our model's type.
Insert models
To insert a model we just need to use theInsert 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.
user := NewUser("fancy_username", "super_secret_password", "foo@email.me")err := store.Insert(user)if err != nil { // handle error}If our model has relationships, they will be saved, and so will the relationships of the relationships and so on. TL;DR: inserts are recursive.Note: the relationships will be saved usingSave, notInsert.
user := NewUser("foo")user.Posts = append(user.Posts, NewPost(user, "new post"))err := store.Insert(user)if err != nil { // handle error}If 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.
Update models
To insert a model we just need to use theUpdate 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.
user := FindLast()rowsUpdated, err := store.Update(user)if err != nil { // handle error}By default, when a model is updated, all its fields are updated. You can also specify which fields to update passing them to update.
rowsUpdated, err := store.Update(user, Schema.User.Username, Schema.User.Password)if err != nil { // handle error}If our model has relationships, they will be saved, and so will the relationships of the relationships and so on. TL;DR: updates are recursive.Note: the relationships will be saved usingSave, notUpdate.
user := FindLastPoster()rowsUpdated, err := store.Update(user)if err != nil { // handle error}If 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.
Save models
To save a model we just need to use theSave method of the store and pass it a model.Save is just a shorthand that will callInsert if the model is not yet persisted andUpdate if it is.
updated, err := store.Save(user)if err != nil { // handle error}if updated { // it was updated, not inserted}If our model has relationships, they will be saved, and so will the relationships of the relationships and so on. TL;DR: saves are recursive.
user := NewUser("foo")user.Posts = append(user.Posts, NewPost(user, "new post"))updated, err := store.Save(user)if err != nil { // handle error}If 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.
Delete models
To delete a model we just have to use theDelete method of the store. It will return an error if the model was not already persisted.
err := store.Delete(user)if err != nil { // handle error}Relationships of the model arenot automatically removed usingDelete.
For that, specific methods are generated in the store of the model.
For one to many relationships:
// remove specific postserr := store.RemovePosts(user, post1, post2, post3)if err != nil { // handle error}// remove all postserr := store.RemovePosts(user)For one to one relationships:
// remove the thingerr := store.RemoveThing(user)Note that for that to work, the thing you're deleting mustnot be empty. That is, you need to eagerly load (or set afterwards) the relationships.
user, err := store.FindOne(NewUserQuery())checkErr(err)// THIS WON'T WORK! We've not loaded "Things"err := store.RemoveThings(user)user, err := store.FindOne(NewUserQuery().WithThings())checkErr(err)// THIS WILL WORK!err := store.RemoveThings(user)Query models
Simple queries
To perform a query you have to do the following things:
- Create a query
- Pass the query to
Find,FindOne,MustFindorMustFindOneof the store - Gather the results from the result set, if the used method was
FindorMustFind
// Create the queryq := NewUserQuery(). Where(kallax.Like(Schema.User.Username, "joe%")). Order(kallax.Asc(Schema.User.Username)). Limit(20). Offset(2)rs, err := store.Find(q)if err != nil { // handle error}for rs.Next() { user, err := rs.Get() if err != nil { // handle error }}Next will automatically close the result set when it hits the end. If you have to prematurely exit the iteration you can close it manually withrs.Close().
You can query just a single row withFindOne.
q := NewUserQuery(). Where(kallax.Eq(Schema.User.Username, "Joe"))user, err := store.FindOne(q)You can also get all of the rows in a result without having to manually iterate the result set withFindAll.
q := NewUserQuery(). Where(kallax.Like(Schema.User.Username, "joe%")). Order(kallax.Asc(Schema.User.Username)). Limit(20). Offset(2)users, err := store.FindAll(q)if err != nil { // handle error}By 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 toReload the object.
// Select only Username and passwordNewUserQuery().Select(Schema.User.Username, Schema.User.Password)// Select all but passwordNewUserQuery().SelectNot(Schema.User.Password)Generated findbys
Kallax generates aFindBy for every field of your model for which it makes sense to do so. What is aFindBy? It is a shorthand to add a condition to the query for a specific field.
Consider the following model:
type Person struct { kallax.Model ID int64 `pk:"autoincr"` Name string BirthDate time.Time Age int}FourFindBys will be generated for this model:
func (*PersonQuery) FindByID(...int64) *PersonQueryfunc (*PersonQuery) FindByName(string) *PersonQueryfunc (*PersonQuery) FindByBirthDate(kallax.ScalarCond, time.Time) *PersonQueryfunc (*PersonQuery) FindByAge(kallax.ScalarCond, int) *PersonQueryThat way, you can just do the following:
NewPersonQuery(). FindByAge(kallax.GtOrEq, 18). FindByName("Bobby")instead of:
NewPersonQuery(). Where(kallax.GtOrEq(Schema.Person.Age, 18)). Where(kallax.Eq(Schema.Person.Name, "Bobby"))Why are there three different types of methods generated?
- 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.
- 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.
- Types that can only be searched by value (strings, bools, ...) only allow a value to be passed.
Count results
Instead of passing the query toFind orFindOne, you can pass it toCount to get the number of rows in the resultset.
n, err := store.Count(q)Query with relationships
By default, no relationships are retrieved unless the query specifies so.
For each of your relationships, a method in your query is created to be able to include these relationships in your query.
One to one relationships:
// Select all posts including the user that posted themq := NewPostQuery().WithPoster()rs, err := store.Find(q)One 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.
One to many relationships:
// Select all users including their posts// NOTE: this is a really bad idea, because all posts will be loaded// if the N side of your 1:N relationship is big, consider querying the N store// instead of doing this// A condition can be passed to the `With{Name}` method to filter the results.q := NewUserQuery().WithPosts(nil)rs, err := store.Find(q)To avoid the N+1 problem with 1:N relationships, kallax performs batching in this case.So, 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.This process is repeated until there are no more rows in the result.Because of this, retrieving 1:N relationships is really fast.
The default batch size is 50, you can change this using theBatchSize method all queries have.
NOTE: if a filter is passed to aWith{Name} method we can no longer guarantee that all related objects are there and, therefore, the retrieved records willnot be writable.
Reloading a model
If, 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.
err := store.Reload(user)Reload will not reload any relationships, just the model itself. After aReload the model willalways be writable.
Querying JSON
You can query arbitrary JSON using the JSON operators defined in thekallax package. The schema of the JSON (if it's a struct, obviously for maps it is not) is also generated.
q := NewPostQuery().Where(kallax.JSONContainsAnyKey( Schema.Post.Metadata, "foo", "bar",))Transactions
To execute things in a transaction theTransaction 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.If the callback returns an error, the transaction will be rolled back.
store.Transaction(func(s *UserStore) error { if err := s.Insert(user1); err != nil { return err } return s.Insert(user2)})The 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 namedStoreFrom that initializes a store of the type you want to have the same underlying store as some other.
store.Transaction(func(s *UserStore) error { var postStore PostStore kallax.StoreFrom(&postStore, s) for _, p := range posts { if err := postStore.Insert(p); err != nil { return err } } return s.Insert(user)})Transaction can be used inside a transaction, but it does not open a new one, reuses the existing one.
Caveats
- It is not possible to use slices or arrays of types that are not one of these types:
- Basic types (e.g.
[]string,[]int64) (except forrune,complex64andcomplex128) - Types that implement
sql.Scanneranddriver.ValuerThe reason why this is not possible is because kallax implements support for arrays of all basic Go types by hand and also for types implementingsql.Scanneranddriver.Valuer(using reflection in this case), but without having a common interface to operate on them, arbitrary types can not be supported.For example, consider the following typetype Foo string, using[]Foowould 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.Aliases of slice types are supported, though. If we havetype Strings []string, usingStringswould be supported, as a cast like this([]string)(&slice)it's supported and[]stringis supported.
- Basic types (e.g.
time.Timeandurl.URLneed to be used as is. That is, you can not use a typeFoobeingtype Foo time.Time.time.Timeandurl.URLare types that are treated in a special way, if you do that, it would be the same as sayingtype Foo struct { ... }and kallax would no longer be able to identify the correct type.time.Timefields will be truncated to remove its nanoseconds onSave,InsertorUpdate, 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 useLocalmethod to convert them back to the local timezone). You can change the timezone that will be used to bring times back from the database inthe PostgreSQL configuration.- Multidimensional arrays or slices arenot supported except inside a JSON field.
Migrations
Kallax 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.
Sometimes, 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 thesqltype struct tag on a field.
type Model struct { kallax.Model `table:"foo"` Stuff SuperCustomType `sqltype:"bytea"`}You can see thefull list of default type mappings between Go and SQL.
Generate migrations
To generate a migration, you have to run the commandkallax migrate.
kallax migrate --input ./users/ --input ./posts/ --out ./migrations --name initial_schemaThemigrate command accepts the following flags:
| Name | Repeated | Description | Default |
|---|---|---|---|
--name or-n | no | name of the migration file (will be converted toa_snakecase_name) | migration |
--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 |
--out or-o | no | destination folder where the migrations will be generated | ./migrations |
Every single migration consists of 2 files:
TIMESTAMP_NAME.up.sql: script that will upgrade your database to this version.TIMESTAMP_NAME.down.sql: script that will downgrade your database to this version.
Additionally, there is alock.json file where schema of the last migration is store to diff against the current models.
Run migrations
To run a migration you can either usekallax migrate up orkallax migrate down.up will upgrade your database anddown will downgrade it.
These are the flags available forup anddown:
| Name | Description | Default |
|---|---|---|
--dir or-d | directory where your migrations are stored | ./migrations |
--dsn | database connection string | required |
--steps or-s | maximum number of migrations to run | 0 |
--all | migrate all the way up (only available forup | |
--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 |
- If no
--stepsor--versionare provided todown, they will do nothing. If--allis provided toup, it will upgrade the database all the way up. - If
--stepsand--versionare provided to eitherupordownit will use only--version, as it is more specific.
Example:
kallax migrate up --dir ./my-migrations --dsn 'user:pass@localhost:5432/dbname?sslmode=disable' --version 1493991142Type mappings
| Go type | SQL type |
|---|---|
kallax.ULID | uuid |
kallax.UUID | uuid |
kallax.NumericID | serial on primary keys,bigint on foreign keys |
int64 on primary keys | serial |
int64 on foreign keys and other fields | bigint |
string | text |
rune | char(1) |
uint8 | smallint |
int8 | smallint |
byte | smallint |
uint16 | integer |
int16 | smallint |
uint32 | bigint |
int32 | integer |
uint | numeric(20) |
int | bigint |
int64 | bigint |
uint64 | numeric(20) |
float32 | real |
float64 | double |
bool | boolean |
url.URL | text |
time.Time | timestamptz |
time.Duration | bigint |
[]byte | bytea |
[]T | T'[] * whereT' is the SQL type of typeT, except forT =byte |
map[K]V | jsonb |
struct | jsonb |
*struct | jsonb |
Any other type must be explicitly specified.
All types that are not pointers will beNOT NULL.
Custom operators
You can create custom operators with kallax using theNewOperator andNewMultiOperator functions.
NewOperator creates an operator with the specified format. It returns a function that given a schema field and a value returns a condition.
The format is a string in which:col: will get replaced with the schema field and:arg: will be replaced with the value.
var Gt = kallax.NewOperator(":col: > :arg:")// can be used like this:query.Where(Gt(SomeSchemaField, 9000))NewMultiOperator does exactly the same as the previous one, but it accepts a variable number of values.
var In = kallax.NewMultiOperator(":col: IN :arg:")// can be used like this:query.Where(In(SomeSchemaField, 4, 5, 6))This function already takes care of wrapping:arg: with parenthesis.
Further customization
If you need further customization, you can create your own custom operator.
You need these things:
- A condition constructor (the operator itself) that takes the field and the values to create the proper SQL expression.
- A
ToSqlerthat yields your SQL expression.
Imagine we want a greater than operator that only works with integers.
func GtInt(col kallax.SchemaField, n int) kallax.Condition { return func(schema kallax.Schema) kallax.ToSqler { // it is VERY important that all SchemaFields // are qualified using the schema return >Int{col.QualifiedName(schema), n} }}type gtInt struct { col string val int}func (g *gtInt) ToSql() (sql string, params []interface{}, err error) { return fmt.Sprintf("%s > ?", g.col), []interface{}{g.val}, nil}// can be used like this:query.Where(GtInt(SomeSchemaField, 9000))For most of the operators,NewOperator andNewMultiOperator 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.
Debug SQL queries
It is possible to debug the SQL queries being executed with kallax. To do that, you just need to call theDebug method of a store. This returns a new store with debugging enabled.
store.Debug().Find(myQuery)This will log to stdout usinglog.Printfkallax: Query: THE QUERY SQL STATEMENT, args: [arg1 arg2].
You can use a custom logger (any function with a typefunc(string, ...interface{}) using theDebugWith method instead.
func myLogger(message string, args ...interface{}) { myloglib.Debugf("%s, args: %v", message, args)}store.DebugWith(myLogger).Find(myQuery)Benchmarks
Here are some benchmarks againstGORM,SQLBoiler anddatabase/sql. In the future we might add benchmarks for some more complex cases and other available ORMs.
BenchmarkKallaxUpdate-4 300 4179176 ns/op 656 B/op 25 allocs/opBenchmarkKallaxUpdateWithRelationships-4 200 5662703 ns/op 6642 B/op 175 allocs/opBenchmarkKallaxInsertWithRelationships-4 200 5648433 ns/op 10221 B/op 218 allocs/opBenchmarkSQLBoilerInsertWithRelationships-4 XXX XXXXXXX ns/op XXXX B/op XXX allocs/opBenchmarkRawSQLInsertWithRelationships-4 200 5427503 ns/op 4516 B/op 127 allocs/opBenchmarkGORMInsertWithRelationships-4 200 6196277 ns/op 35080 B/op 610 allocs/opBenchmarkKallaxInsert-4 300 3916239 ns/op 1218 B/op 29 allocs/opBenchmarkSQLBoilerInsert-4 300 4356432 ns/op 1151 B/op 35 allocs/opBenchmarkRawSQLInsert-4 300 4065924 ns/op 1052 B/op 27 allocs/opBenchmarkGORMInsert-4 300 4398799 ns/op 4678 B/op 107 allocs/opBenchmarkKallaxQueryRelationships/query-4 500 2900095 ns/op 269157 B/op 6200 allocs/opBenchmarkSQLBoilerQueryRelationships/query-4 1000 2082963 ns/op 125587 B/op 5098 allocs/opBenchmarkRawSQLQueryRelationships/query-4 20 59400759 ns/op 294176 B/op 11424 allocs/opBenchmarkGORMQueryRelationships/query-4 300 4758555 ns/op 1069118 B/op 20833 allocs/opBenchmarkKallaxQuery/query-4 3000 546742 ns/op 50673 B/op 1590 allocs/opBenchmarkSQLBoilerQuery/query-4 2000 677839 ns/op 54082 B/op 2436 allocs/opBenchmarkRawSQLQuery/query-4 3000 464498 ns/op 37480 B/op 1525 allocs/opBenchmarkGORMQuery/query-4 1000 1388406 ns/op 427401 B/op 7068 allocs/opPASSok gopkg.in/src-d/go-kallax.v1/benchmarks44.899sAs we can see on the benchmark, the performance loss is not very much compared to rawdatabase/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).
Source code of the benchmarks can be found on thebenchmarks folder.
Notes:
- 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.
- Benchmarks were run on a 2015 MacBook Pro with i5 and 8GB of RAM and 128GB SSD hard drive running fedora 25.
- Benchmark of
database/sqlfor querying with relationships is implemented with a very naive 1+n solution. That's why the result is that bad.
Acknowledgements
- Big thank you to theMasterminds/squirrel library, which is an awesome query builder used internally in this ORM.
- lib/pq, the Golang PostgreSQL driver that ships with a ton of support for builtin Go types.
- mattes/migrate, a Golang library to manage database migrations.
Contributing
Reporting bugs
Kallax 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.
Suggesting features
Kallax 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.If 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.
Running tests
For obvious reasons, an instance of PostgreSQL is required to run the tests of this package.
By default, it assumes that an instance exists at0.0.0.0:5432 with an user, password and database name all equal totesting.
If that is not the case you can set the following environment variables:
DBNAME: name of the databaseDBUSER: database userDBPASS: database user password
License
MIT, seeLICENSE
Documentation¶
Overview¶
Kallax is a PostgreSQL typesafe ORM for the Go language.
Kallax aims to provide a way of programmatically write queries and interactwith 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.For that reason, the first priority of kallax is to provide type safety tothe data access layer.Another of the goals of kallax is make sure all models are, first andforemost, Go structs without having to use database-specific types such as,for example, `sql.NullInt64`.Support for arrays of all basic Go types and all JSON and arrays operators isprovided as well.
Index¶
- Variables
- func ApplyAfterEvents(r Record, wasPersisted bool) error
- func ApplyBeforeEvents(r Record) error
- func ColumnNames(columns []SchemaField) []string
- func NewMultiOperator(format string) func(SchemaField, ...interface{}) Condition
- func NewOperator(format string) func(SchemaField, interface{}) Condition
- func RecordValues(record Valuer, columns ...string) ([]interface{}, []string, error)
- func StoreFrom(to, from GenericStorer)
- func VirtualColumn(col string, r Record, id Identifier) sql.Scanner
- type AfterDeleter
- type AfterInserter
- type AfterSaver
- type AfterUpdater
- type ArraySchemaField
- type BaseQuery
- func (q *BaseQuery) AddRelation(schema Schema, field string, typ RelationshipType, filter Condition) error
- func (q *BaseQuery) BatchSize(size uint64)
- func (q *BaseQuery) Copy() *BaseQuery
- func (q *BaseQuery) GetBatchSize() uint64
- func (q *BaseQuery) GetLimit() uint64
- func (q *BaseQuery) GetOffset() uint64
- func (q *BaseQuery) Limit(n uint64)
- func (q *BaseQuery) Offset(n uint64)
- func (q *BaseQuery) Order(cols ...ColumnOrder)
- func (q *BaseQuery) Schema() Schema
- func (q *BaseQuery) Select(columns ...SchemaField)
- func (q *BaseQuery) SelectNot(columns ...SchemaField)
- func (q *BaseQuery) String() string
- func (q *BaseQuery) ToSql() (string, []interface{}, error)
- func (q *BaseQuery) Where(cond Condition)
- type BaseResultSet
- type BaseSchema
- func (s *BaseSchema) Alias() string
- func (s *BaseSchema) Columns() []SchemaField
- func (s *BaseSchema) ForeignKey(field string) (*ForeignKey, bool)
- func (s *BaseSchema) ID() SchemaField
- func (s *BaseSchema) New() Record
- func (s *BaseSchema) Table() string
- func (s *BaseSchema) WithAlias(field string) Schema
- type BaseSchemaField
- type BatchingResultSet
- type BeforeDeleter
- type BeforeInserter
- type BeforeSaver
- type BeforeUpdater
- type ColumnAddresser
- type ColumnOrder
- type Condition
- func And(conds ...Condition) Condition
- func ArrayContainedBy(col SchemaField, values ...interface{}) Condition
- func ArrayContains(col SchemaField, values ...interface{}) Condition
- func ArrayEq(col SchemaField, values ...interface{}) Condition
- func ArrayGt(col SchemaField, values ...interface{}) Condition
- func ArrayGtOrEq(col SchemaField, values ...interface{}) Condition
- func ArrayLt(col SchemaField, values ...interface{}) Condition
- func ArrayLtOrEq(col SchemaField, values ...interface{}) Condition
- func ArrayNotEq(col SchemaField, values ...interface{}) Condition
- func ArrayOverlap(col SchemaField, values ...interface{}) Condition
- func Eq(col SchemaField, value interface{}) Condition
- func Gt(col SchemaField, value interface{}) Condition
- func GtOrEq(col SchemaField, value interface{}) Condition
- func Ilike(col SchemaField, value string) Condition
- func In(col SchemaField, values ...interface{}) Condition
- func JSONContainedBy(col SchemaField, elem interface{}) Condition
- func JSONContains(col SchemaField, elem interface{}) Condition
- func JSONContainsAllKeys(col SchemaField, keys ...string) Condition
- func JSONContainsAny(col SchemaField, elems ...interface{}) Condition
- func JSONContainsAnyKey(col SchemaField, keys ...string) Condition
- func JSONIsArray(col SchemaField) Condition
- func JSONIsObject(col SchemaField) Condition
- func Like(col SchemaField, value string) Condition
- func Lt(col SchemaField, value interface{}) Condition
- func LtOrEq(col SchemaField, value interface{}) Condition
- func MatchRegex(col SchemaField, pattern string) Condition
- func MatchRegexCase(col SchemaField, pattern string) Condition
- func Neq(col SchemaField, value interface{}) Condition
- func Not(cond Condition) Condition
- func NotIn(col SchemaField, values ...interface{}) Condition
- func NotMatchRegex(col SchemaField, pattern string) Condition
- func NotMatchRegexCase(col SchemaField, pattern string) Condition
- func NotSimilarTo(col SchemaField, value string) Condition
- func Or(conds ...Condition) Condition
- func SimilarTo(col SchemaField, value string) Condition
- type ForeignKey
- type ForeignKeys
- type GenericStorer
- type Identifiable
- type Identifier
- type JSONKeyType
- type JSONSchemaArray
- type JSONSchemaKey
- type LoggerFunc
- type Model
- type NumericID
- type Persistable
- type Query
- type Record
- type RecordConstructor
- type RecordWithSchema
- type Relationable
- type Relationship
- type RelationshipType
- type ResultSet
- type Saveable
- type ScalarCond
- type Schema
- type SchemaField
- type Store
- func (s *Store) Count(q Query) (count int64, err error)
- func (s *Store) Debug() *Store
- func (s *Store) DebugWith(logger LoggerFunc) *Store
- func (s *Store) Delete(schema Schema, record Record) error
- func (s *Store) DisableCacher() *Store
- func (s *Store) Find(q Query) (ResultSet, error)
- func (s *Store) Insert(schema Schema, record Record) error
- func (s *Store) MustCount(q Query) int64
- func (s *Store) MustFind(q Query) ResultSet
- func (s *Store) RawExec(sql string, params ...interface{}) (int64, error)
- func (s *Store) RawQuery(sql string, params ...interface{}) (ResultSet, error)
- func (s *Store) Reload(schema Schema, record Record) error
- func (s *Store) Save(schema Schema, record Record) (updated bool, err error)
- func (s *Store) Transaction(callback func(*Store) error) error
- func (s *Store) Update(schema Schema, record Record, cols ...SchemaField) (int64, error)
- type Timestamps
- type ToSqler
- type ULID
- func (id ULID) Equals(other Identifier) bool
- func (id ULID) IsEmpty() bool
- func (id ULID) MarshalText() ([]byte, error)
- func (id ULID) Raw() interface{}
- func (id *ULID) Scan(src interface{}) error
- func (id ULID) String() string
- func (u *ULID) UnmarshalText(text []byte) (err error)
- func (id ULID) Value() (driver.Value, error)
- type UUID
- type Valuer
- type VirtualColumnContainer
- type Writable
Constants¶
This section is empty.
Variables¶
var (// ErrNonNewDocument non-new documents cannot be insertedErrNonNewDocument =errors.New("kallax: cannot insert a non new document")// ErrNewDocument a new documents cannot be updatedErrNewDocument =errors.New("kallax: cannot updated a new document")// ErrEmptyID a document without ID cannot be used with Save methodErrEmptyID =errors.New("kallax: a record without id is not allowed")// ErrNoRowUpdate is returned when an update operation does not affect any// rows, meaning the model being updated does not exist.ErrNoRowUpdate =errors.New("kallax: update affected no rows")// ErrNotWritable is returned when a record is not writable.ErrNotWritable =errors.New("kallax: record is not writable")// ErrStop can be returned inside a ForEach callback to stop iteration.ErrStop =errors.New("kallax: stopped ForEach execution")// ErrInvalidTxCallback is returned when a nil callback is passed.ErrInvalidTxCallback =errors.New("kallax: invalid transaction callback given")// ErrNotFound is returned when a certain entity is not found.ErrNotFound =errors.New("kallax: entity not found")// ErrCantSetID is returned when a model is inserted and it does not have// neither an autoincrement primary key nor implements the IDSetter// interface.ErrCantSetID =errors.New("kallax: model does not have an auto incrementable primary key, it needs to implement IDSetter interface")// ErrNoColumns is an error returned when the user tries to insert a model// with no other columns than the autoincrementable primary key.ErrNoColumns =errors.New("kallax: your model does not have any column besides its autoincrementable primary key and cannot be inserted"))
var ErrEmptyVirtualColumn =fmt.Errorf("empty virtual column")var (// ErrManyToManyNotSupported is returned when a many to many relationship// is added to a query.ErrManyToManyNotSupported =errors.New("kallax: many to many relationships are not supported"))
var ErrRawScan =errors.New("kallax: result set comes from raw query, use RawScan instead")ErrRawScan is an error returned when a the `Scan` method of `ResultSet`is called with a `ResultSet` created as a result of a `RawQuery`, which isnot allowed.
var ErrRawScanBatching =errors.New("kallax: cannot perform a raw scan on a batching result set")ErrRawScanBatching is an error returned when the `RawScan` method is usedwith a batching result set.
Functions¶
funcApplyAfterEvents¶added inv0.13.0
ApplyAfterEvents calls all the update, insert or save after events of therecord. Save events are always called after the insert or update event.
funcApplyBeforeEvents¶added inv0.13.0
ApplyBeforeEvents calls all the update, insert or save before events of therecord. Save events are always called before the insert or update event.
funcColumnNames¶
func ColumnNames(columns []SchemaField) []string
ColumnNames returns the names of the given schema fields.
funcNewMultiOperator¶added inv1.2.0
func NewMultiOperator(formatstring) func(SchemaField, ...interface{})Condition
NewMultiOperator creates a new operator with a schema field and a variable numberof values as arguments. The given format will define how the SQL is generated.You can put `:col:` wherever you want your column name to be on the format and`?` for the values, which will be automatically escaped.Example: `:col: IN :arg:`. You don't need to wrap the arg with parenthesis.
funcNewOperator¶added inv1.2.0
func NewOperator(formatstring) func(SchemaField, interface{})Condition
NewOperator creates a new operator with two arguments: a schema field anda value. The given format will define how the SQL is generated.You can put `:col:` wherever you want your column name to be on the format and`?` for the value, which will be automatically escaped.Example: `:col: % :arg:`.
funcRecordValues¶
RecordValues returns the values of a record at the given columns in the sameorder as the columns.It also returns the columns with any empty virtual column removed.
funcStoreFrom¶added inv1.1.0
func StoreFrom(to, fromGenericStorer)
StoreFrom sets the generic store of `from` in `to`.
funcVirtualColumn¶added inv0.12.0
func VirtualColumn(colstring, rRecord, idIdentifier)sql.Scanner
VirtualColumn returns a sql.Scanner that will scan the given column as avirtual column in the given record.
Types¶
typeAfterDeleter¶added inv0.13.0
type AfterDeleter interface {// AfterDelete will do some operations after being deleted. If an error is// returned, it will cause the delete to be rolled back.AfterDelete()error}AfterDeleter will do some operations after being deleted.
typeAfterInserter¶added inv0.13.0
type AfterInserter interface {// AfterInsert will do some operations after being inserted. If an error is// returned, it will cause the insert to be rolled back.AfterInsert()error}AfterInserter will do some operations after being inserted.
typeAfterSaver¶added inv0.13.0
type AfterSaver interface {// AfterSave will do some operations after being inserted or updated. If an// error is returned, it will cause the insert or update to be rolled back.AfterSave()error}AfterSaver will do some operations after being inserted or updated.
typeAfterUpdater¶added inv0.13.0
type AfterUpdater interface {// AfterUpdate will do some operations after being updated. If an error is// returned, it will cause the update to be rolled back.AfterUpdate()error}AfterUpdater will do some operations after being updated.
typeArraySchemaField¶added inv0.13.0
type ArraySchemaField interface {SchemaField// contains filtered or unexported methods}ArraySchemaField is an interface that defines if a field is a JSONarray.
typeBaseQuery¶
type BaseQuery struct {// contains filtered or unexported fields}BaseQuery is a generic query builder to build queries programmatically.
funcNewBaseQuery¶
NewBaseQuery creates a new BaseQuery for querying the table of the given schema.
func (*BaseQuery)AddRelation¶
func (q *BaseQuery) AddRelation(schemaSchema, fieldstring, typRelationshipType, filterCondition)error
AddRelation adds a relationship if the given to the query, which is presentin the given field of the query base schema. A condition to filter can alsobe passed in the case of one to many relationships.
func (*BaseQuery)Copy¶
Copy returns an identical copy of the query. BaseQuery is mutable, that iswhy this method is provided.
func (*BaseQuery)GetBatchSize¶
GetBatchSize returns the number of rows retrieved per batch while retrieving1:N relationships.
func (*BaseQuery)Order¶
func (q *BaseQuery) Order(cols ...ColumnOrder)
Order adds the given order clauses to the list of columns to order theresults by.
func (*BaseQuery)Select¶
func (q *BaseQuery) Select(columns ...SchemaField)
Select adds the given columns to the list of selected columns in the query.
func (*BaseQuery)SelectNot¶
func (q *BaseQuery) SelectNot(columns ...SchemaField)
SelectNot adds the given columns to the list of excluded columns in the query.
func (*BaseQuery)String¶
String returns the SQL generated by the query. If the query is malformed,it will return an empty string, as errors compiling the SQL are ignored.
typeBaseResultSet¶added inv0.12.0
BaseResultSet is a generic collection of rows.
funcNewResultSet¶
func NewResultSet(rows *sql.Rows, readOnlybool, relationships []Relationship, columns ...string) *BaseResultSet
NewResultSet creates a new result set with the given rows and columns.It is mandatory that all column names are in the same order and are exactlyequal to the ones in the query that produced the rows.
func (*BaseResultSet)Get¶added inv0.12.0
func (rs *BaseResultSet) Get(schemaSchema) (Record,error)
Get returns the next record in the schema.
func (*BaseResultSet)RawScan¶added inv0.12.0
func (rs *BaseResultSet) RawScan(dest ...interface{})error
RowScan copies the columns in the current row into the values pointed at bydest. The number of values in dest must be the same as the number of columnsselected in the query.
func (*BaseResultSet)Scan¶added inv0.12.0
func (rs *BaseResultSet) Scan(recordRecord)error
Scan fills the column fields of the given value with the current row.
typeBaseSchema¶
type BaseSchema struct {// contains filtered or unexported fields}BaseSchema is the basic implementation of Schema.
funcNewBaseSchema¶
func NewBaseSchema(table, aliasstring, idSchemaField, fksForeignKeys, ctorRecordConstructor, autoIncrbool, columns ...SchemaField) *BaseSchema
NewBaseSchema creates a new schema with the given table, alias, identifierand columns.
func (*BaseSchema)Alias¶
func (s *BaseSchema) Alias()string
func (*BaseSchema)Columns¶
func (s *BaseSchema) Columns() []SchemaField
func (*BaseSchema)ForeignKey¶
func (s *BaseSchema) ForeignKey(fieldstring) (*ForeignKey,bool)
func (*BaseSchema)ID¶
func (s *BaseSchema) ID()SchemaField
func (*BaseSchema)New¶added inv0.12.0
func (s *BaseSchema) New()Record
func (*BaseSchema)Table¶
func (s *BaseSchema) Table()string
func (*BaseSchema)WithAlias¶
func (s *BaseSchema) WithAlias(fieldstring)Schema
typeBaseSchemaField¶
type BaseSchemaField struct {// contains filtered or unexported fields}BaseSchemaField is a basic schema field with name.
func (*BaseSchemaField)QualifiedName¶
func (f *BaseSchemaField) QualifiedName(schemaSchema)string
func (BaseSchemaField)String¶
func (fBaseSchemaField) String()string
typeBatchingResultSet¶added inv0.12.0
type BatchingResultSet struct {// contains filtered or unexported fields}BatchingResultSet is a result set that retrieves all the items up to thebatch size set in the query.If there are 1:N relationships, it collects all the identifiers ofthose records, retrieves all the rows matching them in the table of thethe N end, and assigns them to their correspondent to the record they belongto.It will continue doing this process until no more rows are returned by thequery.This minimizes the number of queries and operations to perform in order toretrieve a set of results and their relationships.
funcNewBatchingResultSet¶added inv0.12.0
func NewBatchingResultSet(runner *batchQueryRunner) *BatchingResultSet
NewBatchingResultSet returns a new result set that performs batchingunderneath.
func (*BatchingResultSet)Close¶added inv0.12.0
func (rs *BatchingResultSet) Close()error
Close will do nothing, as the internal result sets used by this are closedwhen the rows at fetched. It will never throw an error.
func (*BatchingResultSet)Get¶added inv0.12.0
func (rs *BatchingResultSet) Get(_Schema) (Record,error)
Get returns the next processed record and the last error occurred.Even though it accepts a schema, it is ignored, as the result set isalready aware of it. This is here just to be able to imeplement theResultSet interface.
func (*BatchingResultSet)Next¶added inv0.12.0
func (rs *BatchingResultSet) Next()bool
Next advances the internal index of the fetched records in one.If there are no fetched records, will fetch the next batch.It will return false when there are no more rows.
func (*BatchingResultSet)RawScan¶added inv0.12.0
func (rs *BatchingResultSet) RawScan(_ ...interface{})error
RawScan will always throw an error, as this is not a supported operation ofa batching result set.
typeBeforeDeleter¶added inv0.13.0
type BeforeDeleter interface {// BeforeDelete will do some operations before being deleted. If an error is// returned, it will prevent the delete from happening.BeforeDelete()error}BeforeDeleter will do some operations before being deleted.
typeBeforeInserter¶added inv0.13.0
type BeforeInserter interface {// BeforeInsert will do some operations before being inserted. If an error is// returned, it will prevent the insert from happening.BeforeInsert()error}BeforeInserter will do some operations before being inserted.
typeBeforeSaver¶added inv0.13.0
type BeforeSaver interface {// BeforeSave will do some operations before being updated or inserted. If an// error is returned, it will prevent the update or insert from happening.BeforeSave()error}BeforeSaver will do some operations before being updated or inserted.
typeBeforeUpdater¶added inv0.13.0
type BeforeUpdater interface {// BeforeUpdate will do some operations before being updated. If an error is// returned, it will prevent the update from happening.BeforeUpdate()error}BeforeUpdater will do some operations before being updated.
typeColumnAddresser¶
type ColumnAddresser interface {// ColumnAddress returns the pointer to the column value of the given// column name, or an error if it does not exist in the model.ColumnAddress(string) (interface{},error)}ColumnAddresser provides the pointer addresses of columns.
typeColumnOrder¶
type ColumnOrder interface {// ToSql returns the SQL representation of the column with its order.ToSql(Schema)string// contains filtered or unexported methods}ColumnOrder represents a column name with its order.
typeCondition¶
Condition represents a condition of filtering in a query.
funcArrayContainedBy¶added inv0.12.0
func ArrayContainedBy(colSchemaField, values ...interface{})Condition
ArrayContainedBy returns a condition that will be true when `col` has allits elements present in the given values.
funcArrayContains¶added inv0.12.0
func ArrayContains(colSchemaField, values ...interface{})Condition
ArrayContains returns a condition that will be true when `col` contains all thegiven values.
funcArrayEq¶added inv0.12.0
func ArrayEq(colSchemaField, values ...interface{})Condition
ArrayEq returns a condition that will be true when `col` is equal to anarray with the given elements.
funcArrayGt¶added inv0.12.0
func ArrayGt(colSchemaField, values ...interface{})Condition
ArrayGt returns a condition that will be true when all elements in `col`are greater or equal than their counterparts in the given values, and one ofthe elements at least is greater than its counterpart in the given values.For example: for a col with values [1,2,3] and values [1,2,2], it will betrue.
funcArrayGtOrEq¶added inv0.12.0
func ArrayGtOrEq(colSchemaField, values ...interface{})Condition
ArrayGtOrEq returns a condition that will be true when all elements in `col`are greater or equal than their counterparts in the given values.For example: for a col with values [1,2,2] and values [1,2,2], it will betrue.
funcArrayLt¶added inv0.12.0
func ArrayLt(colSchemaField, values ...interface{})Condition
ArrayLt returns a condition that will be true when all elements in `col`are lower or equal than their counterparts in the given values, and one ofthe elements at least is lower than its counterpart in the given values.For example: for a col with values [1,2,2] and values [1,2,3], it will betrue.
funcArrayLtOrEq¶added inv0.12.0
func ArrayLtOrEq(colSchemaField, values ...interface{})Condition
ArrayLtOrEq returns a condition that will be true when all elements in `col`are lower or equal than their counterparts in the given values.For example: for a col with values [1,2,2] and values [1,2,2], it will betrue.
funcArrayNotEq¶added inv0.12.0
func ArrayNotEq(colSchemaField, values ...interface{})Condition
ArrayNotEq returns a condition that will be true when `col` is not equal toan array with the given elements.
funcArrayOverlap¶added inv0.12.0
func ArrayOverlap(colSchemaField, values ...interface{})Condition
ArrayOverlap returns a condition that will be true when `col` has elementsin common with an array formed by the given values.
funcEq¶
func Eq(colSchemaField, value interface{})Condition
Eq returns a condition that will be true when `col` is equal to `value`.
funcGt¶
func Gt(colSchemaField, value interface{})Condition
Gt returns a condition that will be true when `col` is greater than `value`.
funcGtOrEq¶
func GtOrEq(colSchemaField, value interface{})Condition
GtOrEq returns a condition that will be true when `col` is greater than`value` or equal.
funcIlike¶added inv1.1.4
func Ilike(colSchemaField, valuestring)Condition
Ilike returns a condition that will be true when `col` matches the given `value`.The match is case-insensitive.Seehttps://www.postgresql.org/docs/9.6/static/functions-matching.html.
funcIn¶
func In(colSchemaField, values ...interface{})Condition
In returns a condition that will be true when `col` is equal to any of thepassed `values`.
funcJSONContainedBy¶added inv0.13.0
func JSONContainedBy(colSchemaField, elem interface{})Condition
JSONContainedBy returns a condition that will be true when `col` iscontained by the given element converted to JSON.
funcJSONContains¶added inv0.13.0
func JSONContains(colSchemaField, elem interface{})Condition
JSONContains returns a condition that will be true when `col` containsthe given element converted to JSON.
funcJSONContainsAllKeys¶added inv0.13.0
func JSONContainsAllKeys(colSchemaField, keys ...string)Condition
JSONContainsAllKeys returns a condition that will be true when `col`contains all the given keys. Will also match elements if the column is anarray.
funcJSONContainsAny¶added inv0.13.0
func JSONContainsAny(colSchemaField, elems ...interface{})Condition
JSONContainsAny returns a condition that will be true when `col` containsany of the given elements converted to json.Giving no elements will cause an error to be returned when the condition isevaluated.
funcJSONContainsAnyKey¶added inv0.13.0
func JSONContainsAnyKey(colSchemaField, keys ...string)Condition
JSONContainsAnyKey returns a condition that will be true when `col` containsany of the given keys. Will also match elements if the column is an array.
funcJSONIsArray¶added inv0.13.0
func JSONIsArray(colSchemaField)Condition
JSONIsArray returns a condition that will be true when `col` is a JSONarray.
funcJSONIsObject¶added inv0.13.0
func JSONIsObject(colSchemaField)Condition
JSONIsObject returns a condition that will be true when `col` is a JSONobject.
funcLike¶added inv1.1.2
func Like(colSchemaField, valuestring)Condition
Like returns a condition that will be true when `col` matches the given `value`.The match is case-sensitive.Seehttps://www.postgresql.org/docs/9.6/static/functions-matching.html.
funcLt¶
func Lt(colSchemaField, value interface{})Condition
Lt returns a condition that will be true when `col` is lower than `value`.
funcLtOrEq¶
func LtOrEq(colSchemaField, value interface{})Condition
LtOrEq returns a condition that will be true when `col` is lower than`value` or equal.
funcMatchRegex¶added inv1.1.0
func MatchRegex(colSchemaField, patternstring)Condition
MatchRegex returns a condition that will be true when `col` matchesthe given POSIX regex. Match is case insensitive.
funcMatchRegexCase¶added inv1.1.0
func MatchRegexCase(colSchemaField, patternstring)Condition
MatchRegexCase returns a condition that will be true when `col` matchesthe given POSIX regex. Match is case sensitive.
funcNeq¶
func Neq(colSchemaField, value interface{})Condition
Neq returns a condition that will be true when `col` is not `value`.
funcNotIn¶
func NotIn(colSchemaField, values ...interface{})Condition
NotIn returns a condition that will be true when `col` is distinct to all of thepassed `values`.
funcNotMatchRegex¶added inv1.1.0
func NotMatchRegex(colSchemaField, patternstring)Condition
NotMatchRegex returns a condition that will be true when `col` does notmatch the given POSIX regex. Match is case insensitive.
funcNotMatchRegexCase¶added inv1.1.0
func NotMatchRegexCase(colSchemaField, patternstring)Condition
NotMatchRegexCase returns a condition that will be true when `col` does notmatch the given POSIX regex. Match is case sensitive.
funcNotSimilarTo¶added inv1.1.4
func NotSimilarTo(colSchemaField, valuestring)Condition
NotSimilarTo returns a condition that will be true when `col` does not matchthe given `value`.Seehttps://www.postgresql.org/docs/9.6/static/functions-matching.html.
funcSimilarTo¶added inv1.1.3
func SimilarTo(colSchemaField, valuestring)Condition
SimilarTo returns a condition that will be true when `col` matches the given`value`.Seehttps://www.postgresql.org/docs/9.6/static/functions-matching.html.
typeForeignKey¶added inv0.12.0
type ForeignKey struct {*BaseSchemaFieldInversebool}ForeignKey contains the schema field of the foreign key and if it is aninverse foreign key or not.
funcNewForeignKey¶added inv0.12.0
func NewForeignKey(namestring, inversebool) *ForeignKey
NewForeignKey creates a new Foreign key with the given name.
typeForeignKeys¶
type ForeignKeys map[string]*ForeignKey
ForeignKeys is a mapping between relationships and their foreign key field.
typeGenericStorer¶added inv1.1.0
type GenericStorer interface {// GenericStore returns the generic store in this type.GenericStore() *Store// SetGenericStore sets the generic store for this type.SetGenericStore(*Store)}GenericStorer is a type that contains a generic store and has methods toretrieve it and set it.
typeIdentifiable¶
type Identifiable interface {// GetID returns the ID.GetID()Identifier}Identifiable must be implemented by those values that can be identified by an ID.
typeIdentifier¶added inv0.13.0
type Identifier interface {sql.Scannerdriver.Valuer// Equals reports whether the identifier and the given one are equal.Equals(Identifier)bool// IsEmpty returns whether the ID is empty or not.IsEmpty()bool// Raw returns the internal value of the identifier.Raw() interface{}}Identifier is a type used to identify a model.
typeJSONKeyType¶added inv0.13.0
type JSONKeyTypestring
JSONKeyType is the type of an object key in a JSON.
const (// JSONAny represents a type that can't be casted.JSONAnyJSONKeyType = ""// JSONText is a text json type.JSONTextJSONKeyType = "text"// JSONInt is a numeric json type.JSONIntJSONKeyType = "bigint"// JSONFloat is a floating point json type.JSONFloatJSONKeyType = "decimal"// JSONBool is a boolean json type.JSONBoolJSONKeyType = "bool")
typeJSONSchemaArray¶added inv0.13.0
type JSONSchemaArray struct {// contains filtered or unexported fields}JSONSchemaArray is a SchemaField that represents a JSON array.
funcNewJSONSchemaArray¶added inv0.13.0
func NewJSONSchemaArray(fieldstring, paths ...string) *JSONSchemaArray
NewJSONSchemaArray creates a new SchemaField that is a json array.
func (*JSONSchemaArray)QualifiedName¶added inv0.13.0
func (f *JSONSchemaArray) QualifiedName(schemaSchema)string
func (*JSONSchemaArray)String¶added inv0.13.0
func (f *JSONSchemaArray) String()string
typeJSONSchemaKey¶added inv0.13.0
type JSONSchemaKey struct {// contains filtered or unexported fields}JSONSchemaKey is a SchemaField that represents a key in a JSON object.
funcNewJSONSchemaKey¶added inv0.13.0
func NewJSONSchemaKey(typJSONKeyType, fieldstring, paths ...string) *JSONSchemaKey
NewJSONSchemaKey creates a new SchemaField that is a json key.
func (*JSONSchemaKey)QualifiedName¶added inv0.13.0
func (f *JSONSchemaKey) QualifiedName(schemaSchema)string
func (*JSONSchemaKey)String¶added inv0.13.0
func (f *JSONSchemaKey) String()string
typeLoggerFunc¶added inv1.1.4
type LoggerFunc func(string, ...interface{})
LoggerFunc is a function that takes a log message with some arguments andlogs it.
typeModel¶
type Model struct {// contains filtered or unexported fields}Model contains all the basic fields that make something a model, that is,the ID and some internal data used by kallax.To make a struct a model, it only needs to have Model embedded.
type MyModel struct {kallax.ModelFoo string}Custom name for the table can be specified using the struct tag `table` whenembedding the Model.
type MyModel struct {kallax.Model `table:"custom_name"`}Otherwise, the default name of the table is the name of the model convertedto lower snake case. E.g: MyModel => my_model.No pluralization is done right now, but might be done in the future, soplease, set the name of the tables yourself.
func (*Model)AddVirtualColumn¶added inv0.12.0
func (m *Model) AddVirtualColumn(namestring, vIdentifier)
AddVirtualColumn adds a new virtual column with the given name and value.This method is only intended for internal use. It is only exposed fortechnical reasons.
func (*Model)ClearVirtualColumns¶added inv0.12.0
func (m *Model) ClearVirtualColumns()
ClearVirtualColumns clears all the previous virtual columns.This method is only intended for internal use. It is only exposed fortechnical reasons.
func (*Model)IsPersisted¶
IsPersisted returns whether the Model has already been persisted to thedatabase or not.
func (*Model)IsSaving¶added inv1.3.0
IsSaving reports whether the model is in the process of being saved or not.
func (*Model)IsWritable¶
IsWritable returns whether this Model can be saved into the database.For example, a model with partially retrieved data is not writable, soit is not saved by accident and the data is corrupted. For example, ifyou select only 2 columns out of all the ones the table has, it will notbe writable.
func (*Model)SetSaving¶added inv1.3.0
SetSaving sets the saving status of the model.This is an internal function, even though it is exposed for the concretekallax generated code to use. Please, don't use it, and if you do,bad things may happen.
func (*Model)VirtualColumn¶added inv0.12.0
func (m *Model) VirtualColumn(namestring)Identifier
VirtualColumn returns the value of the virtual column with the given column name.This method is only intended for internal use. It is only exposed fortechnical reasons.
typeNumericID¶added inv0.13.0
type NumericIDint64
NumericID is a wrapper for int64 that implements the Identifier interface.You don't need to actually use this as a type in your model. They will beautomatically converted to and from in the generated code.
func (NumericID)Equals¶added inv0.13.0
func (idNumericID) Equals(otherIdentifier)bool
Equals reports whether the ID and the given one are equals.
func (NumericID)IsEmpty¶added inv0.13.0
IsEmpty returns whether the ID is empty or not. An empty ID means it has notbeen set yet.
func (NumericID)Raw¶added inv0.13.0
func (idNumericID) Raw() interface{}
Raw returns the underlying raw value.
typePersistable¶
type Persistable interface {// IsPersisted returns whether this Model is new in the store or not.IsPersisted()bool// contains filtered or unexported methods}Persistable must be implemented by those values that can be persisted.
typeQuery¶
type Query interface {// Schema returns the schema of the query model.Schema()Schema// GetOffset returns the number of skipped rows in the query.GetOffset()uint64// GetLimit returns the max number of rows retrieved by the query.GetLimit()uint64// GetBatchSize returns the number of rows retrieved by the store per// batch. This is only used and has effect on queries with 1:N// relationships.GetBatchSize()uint64// contains filtered or unexported methods}Query is the common interface all queries must satisfy. The basic abilitiesof a query are compiling themselves to something executable and returnsome query settings.
typeRecord¶
type Record interface {IdentifiablePersistableWritableRelationableColumnAddresserValuerVirtualColumnContainerSaveable}Record is something that can be stored as a row in the database.
typeRecordConstructor¶added inv0.12.0
type RecordConstructor func()Record
RecordConstructor is a function that creates a record.
typeRecordWithSchema¶added inv0.12.0
RecordWithSchema is a structure that contains both a record and its schema.Only for internal purposes.
typeRelationable¶
type Relationable interface {// NewRelationshipRecord returns a new Record for the relationship at the// given field.NewRelationshipRecord(string) (Record,error)// SetRelationship sets the relationship value at the given field.SetRelationship(string, interface{})error}Relationable can perform operations related to relationships of a record.
typeRelationship¶
type Relationship struct {// Type is the kind of relationship this is.TypeRelationshipType// Field is the field in the record where the relationship is.Fieldstring// Schema is the schema of the relationship.SchemaSchema// Filter establishes the filter to be applied when retrieving rows of the// relationships.FilterCondition}Relationship is a relationship with its schema and the field of te relationin the record.
typeRelationshipType¶added inv0.12.0
type RelationshipTypebyte
RelationshipType describes the type of the relationship.
const (// OneToOne is a relationship between one record in a table and another in// another table.OneToOneRelationshipType =iota// OneToMany is a relationship between one record in a table and multiple// in another table.OneToMany// ManyToMany is a relationship between many records on both sides of the// relationship.// NOTE: It is not supported yet.ManyToMany)
typeResultSet¶
type ResultSet interface {// RawScan allows for raw scanning of fields in a result set.RawScan(...interface{})error// Next moves the pointer to the next item in the result set and returns// if there was any.Next()bool// Get returns the next record of the given schema.Get(Schema) (Record,error)io.Closer}ResultSet is the common interface all result sets need to implement.
typeSaveable¶added inv1.3.0
Saveable can report whether it's being saved or change the saving status.
typeScalarCond¶added inv1.0.0
type ScalarCond func(colSchemaField, value interface{})Condition
ScalarCond returns a kallax.Condition that compares a property with the passedvalues, considering its scalar values (eq, gt, gte, lt, lte, neq)
typeSchema¶
type Schema interface {// Alias returns the name of the alias used in queries for this schema.Alias()string// Table returns the table name.Table()string// ID returns the name of the identifier of the table.ID()SchemaField// Columns returns the list of columns in the schema.Columns() []SchemaField// ForeignKey returns the name of the foreign key of the given model field.ForeignKey(string) (*ForeignKey,bool)// WithAlias returns a new schema with the given string added to the// default alias.// Calling WithAlias on a schema returned by WithAlias not return a// schema based on the child, but another based on the parent.WithAlias(string)Schema// New creates a new record with the given schema.New()Record// contains filtered or unexported methods}Schema represents a table schema in the database. Contains some informationlike the table name, its columns, its identifier and so on.
typeSchemaField¶
type SchemaField interface {// String returns the string representation of the field. That is, its name.String()string// QualifiedString returns the name of the field qualified by the alias of// the given schema.QualifiedName(Schema)string// contains filtered or unexported methods}SchemaField is a named field in the table schema.
funcAtJSONPath¶added inv0.13.0
func AtJSONPath(fieldSchemaField, typJSONKeyType, path ...string)SchemaField
AtJSONPath returns the schema field to query an arbitrary JSON element atthe given path.
funcNewSchemaField¶
func NewSchemaField(namestring)SchemaField
NewSchemaField creates a new schema field with the given name.
typeStore¶
type Store struct {// contains filtered or unexported fields}Store is a structure capable of retrieving records from a concrete table inthe database.
func (*Store)Debug¶added inv1.1.4
Debug returns a new store that will print all SQL statements to stdout usingthe log.Printf function.
func (*Store)DebugWith¶added inv1.1.4
func (s *Store) DebugWith(loggerLoggerFunc) *Store
DebugWith returns a new store that will print all SQL statements using thegiven logger function.
func (*Store)Delete¶
Delete removes the record from the table. A non-new record with non-emptyID is required.
func (*Store)DisableCacher¶added inv1.3.4
DisableCacher returns a new store with prepared statements turned off, which can be useful in some scenarios.
func (*Store)Insert¶
Insert insert the given record in the table, returns error if no-newrecord is given. The record id is set if it's empty.
func (*Store)MustCount¶
MustCount returns the number of rows selected by the given query. It panicsif the query fails.
func (*Store)MustFind¶
MustFind performs a query and returns a result set with the results.It panics if the query fails.
func (*Store)RawExec¶
RawExec executes a raw SQL query with the given parameters and returnsthe number of affected rows.
func (*Store)RawQuery¶
RawQuery performs a raw SQL query with the given parameters and returns aresult set with the results.WARNING: A result set created from a raw query can only be scanned using theRawScan method of ResultSet, instead of Scan.
func (*Store)Reload¶
Reload refreshes the record with the data in the database and makes therecord writable.
func (*Store)Transaction¶
Transaction executes the given callback in a transaction and rollbacks ifan error is returned.The transaction is only open in the store passed as a parameter to thecallback.If a transaction is already opened in this store, instead of opening a newone, the other will be reused.
typeTimestamps¶
type Timestamps struct {// CreatedAt is the time where the object was created.CreatedAttime.Time// UpdatedAt is the time where the object was updated.UpdatedAttime.Time}Timestamps contains the dates of the last time the model was createdor deleted. Because this is such a common functionality in models, it isprovided by default by the library. It is intended to be embedded in themodel.
type MyModel struct {kallax.Modelkallax.TimestampsFoo string}func (*Timestamps)BeforeSave¶added inv0.12.0
func (t *Timestamps) BeforeSave()error
BeforeSave updates the last time the model was updated every single time themodel is saved, and the last time the model was created only if the modelhas no date of creation yet.
typeToSqler¶added inv1.2.0
ToSqler is the interface that wraps the ToSql method. It's a wrapper aroundsquirrel.Sqlizer to avoid having to import that as well when using kallax.
typeULID¶added inv0.13.0
ULID is an ID type provided by kallax that is a lexically sortable UUID.The internal representation is an ULID (https://github.com/oklog/ulid).It already implements sql.Scanner and driver.Valuer, so it's perfectlysafe for database usage.
funcNewULID¶added inv0.13.0
func NewULID()ULID
NewULID returns a new ULID, which is a lexically sortable UUID.
funcNewULIDFromText¶added inv1.0.4
NewULIDFromText creates a new ULID from its string representation. Willreturn an error if the text is not a valid ULID.
func (ULID)Equals¶added inv0.13.0
func (idULID) Equals(otherIdentifier)bool
Equals reports whether the ID and the given one are equals.
func (ULID)IsEmpty¶added inv0.13.0
IsEmpty returns whether the ID is empty or not. An empty ID means it has notbeen set yet.
func (ULID)MarshalText¶added inv1.0.3
func (*ULID)UnmarshalText¶added inv1.0.2
UnmarshalText implements the encoding.TextUnmarshaler interface.Following formats are supported:"6ba7b810-9dad-11d1-80b4-00c04fd430c8","{6ba7b810-9dad-11d1-80b4-00c04fd430c8}","urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"Implements the exact same code as the UUID UnmarshalText removing theversion check.
typeUUID¶added inv0.13.0
UUID is a wrapper type for uuid.UUID that implements the Identifierinterface.You don't need to actually use this as a type in your model. They will beautomatically converted to and from in the generated code.
func (UUID)Equals¶added inv0.13.0
func (idUUID) Equals(otherIdentifier)bool
Equals reports whether the ID and the given one are equals.
func (UUID)IsEmpty¶added inv0.13.0
IsEmpty returns whether the ID is empty or not. An empty ID means it has notbeen set yet.
typeValuer¶
type Valuer interface {// Value returns the value of the given column, or an error if it does not// exist in the model.Value(string) (interface{},error)}Valuer provides the values for columns.
typeVirtualColumnContainer¶added inv0.12.0
type VirtualColumnContainer interface {// ClearVirtualColumns removes all virtual columns.ClearVirtualColumns()// AddVirtualColumn adds a new virtual column with the given name and valueAddVirtualColumn(string,Identifier)// VirtualColumn returns the virtual column with the given column name.VirtualColumn(string)Identifier// contains filtered or unexported methods}VirtualColumnContainer contains a collection of virtual columns andmanages them.
typeWritable¶
type Writable interface {// IsWritable returns whether this Model can be saved into the database.IsWritable()bool// contains filtered or unexported methods}Writable must be implemented by those values that defines internallyif they can be sent back to the database to be stored with its changes.
Source Files¶
Directories¶
| Path | Synopsis |
|---|---|
IMPORTANT! This is auto generated code by https://github.com/src-d/go-kallax Please, do not touch the code below, and if you do, do it under your own risk. | IMPORTANT! This is auto generated code by https://github.com/src-d/go-kallax Please, do not touch the code below, and if you do, do it under your own risk. |
Package generator implements the processor of source code and generator of kallax models based on Go source code. | Package generator implements the processor of source code and generator of kallax models based on Go source code. |
cli/kallaxcommand | |
Code generated by https://github.com/src-d/go-kallax. | Code generated by https://github.com/src-d/go-kallax. |
Package types provides implementation of some wrapper SQL types. | Package types provides implementation of some wrapper SQL types. |