Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

KORM, an elegant and lightning-fast ORM for all your concurrent and async needs. Inspired by the highly popular Django Framework, KORM offers similar functionality with the added bonus of performance

License

NotificationsYou must be signed in to change notification settings

kamalshkeir/korm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation



Buy Me A Coffee

Introducing Korm - the elegant, lightning-fast ORM/Framework for all your needs, seebenchmarks. Inspired by the highly popular Django Framework, Korm offers similar functionality with the added bonus of performance

It is also composable, allowing for integration with a network websocket PubSub usingWithBus when you want to synchronise your data between multiple Korm orWithDashboard to have a complete setup of server bus and Admin Dashboard.

Why settle for less when you can have the best ?

  • Django become very hard to work with when you need concurrency and async, you will need django channels and a server like daphne or uvicorn, Go have the perfect implementation
  • Django can handle at most 300 request per second, Go handle 44,000 requests per second (benchmarks done on my machine)
  • The API is also more user-friendly and less verbose than Django's
  • Deploying an executable binary file using Korm , with automatic TLS Let's encrypt, a built-in Admin Dashboard, Interactive Shell, Eventbus to communicate between multiple Korm applications is pretty neat
  • Additionally, its caching system uses goroutines and channels to efficiently to clean the cache when rows or tables are created, updated, deleted, or dropped

It Has :

  • New: When using korm.WithDashboard, now you have access to all logs in realtime (websockets) from admin dashboard when you log using lg pkg. By default only 10 last logs are keeped in memory, you can increase it using lg.SaveLogs(50) for keeping last 50 logs

  • New: Automatic check your structs (schema) against database tables, prompt you with changes, and so it can add or remove columns by adding or removing fields to the struct, it is Disabled by default, usekorm.EnableCheck() to enable it

  • New:Handle Nested or Embeded structs and slice of structs through joins, like sqlx, but sqlx doesn't handle slice of structs

  • New: korm.QueryNamed, QueryNamedS, korm.ExecNamed, korm.ExecContextNamed and WhereNamed(query string, args map[string]any) like :Where("email = :email",map[string]any{"email":"abc@mail.com"})

  • New: korm.LogsQueries() that log statements and time tooked by sql queries

  • Auto Docs with Model API and video tutoriel

  • PPROF Go profiling tool andMetrics Prometheus

  • Logs Middleware

  • Admin dashboard with ready offline and installable PWA (using /static/sw.js and /static/manifest.webmanifest). All statics mentionned insw.js will be cached and served by the service worker, you can inspect the Network Tab in the browser to check it

  • Shared Network Bus allowing you to send and recv data in realtime using pubsub websockets between your ORMs, so you can decide how you data will be distributed between different databases, seeExample

  • Built-in Authentication usingkorm.Auth ,korm.Admin orkorm.BasicAuth middlewares, whenever Auth and Admin middlewares are used, you get access to the.User model and variable.IsAuthenticated from any template html like this exampleadmin_nav.html

  • Interactive Shell, to CRUD in your databases from command line, usekorm.WithShell()

  • AutoMigrate directly from struct

  • Compatible with official database/sql, so you can do your queries yourself using sql.DB `korm.GetConnection()``, and overall a painless integration of your existing codebases using database/sql

  • Router/Mux accessible from the serverBus after callingkorm.WithBus(...opts) orkorm.WithDashboard(addr, ...opts)

  • Hooks : OnInsert OnSet OnDelete and OnDrop

  • many to many relationships

  • GENERATED ALWAYS AS tag added (all dialects)

  • Concatination and Length support forWhere and for tags:check andgenerated (all dialects)

  • Support for foreign keys, indexes , checks,...See all

  • Kenv load env vars to struct

  • Python Bus Clientpip install ksbus

All drivers concurrent safe read and write

Supported databases:

  • Sqlite
  • Mysql
  • Maria
  • Postgres
  • Cockroach

Installation

go get -u github.com/kamalshkeir/korm@latest

Drivers moved outside this package to not get them all in your go.mod file

go get -u github.com/kamalshkeir/sqlitedriver@latestgo get -u github.com/kamalshkeir/pgdriver@latestgo get -u github.com/kamalshkeir/mysqldriver@latest

Global Vars

// Debug when true show extra useful logs for queries executed for migrations and queries statementsDebug=false// FlushCacheEvery execute korm.FlushCache() every 10 min by default, you should not worry about it, but useful that you can change itFlushCacheEvery=10*time.Minute// SetCacheMaxMemory set max size of each cache cacheAllS AllM ...korm.SetCacheMaxMemory(megaByteint)// default maximum of 50 Mb , cannot be lower// Connection poolMaxOpenConns=20MaxIdleConns=20MaxLifetime=30*time.MinuteMaxIdleTime=15*time.Minute

Connect to a database

// sqlite// go get github.com/kamalshkeir/sqlitedrivererr:=korm.New(korm.SQLITE,"dbName",sqlitedriver.Use())// Connect// postgres, cockroach// go get github.com/kamalshkeir/pgdrivererr:=korm.New(korm.POSTGRES,"dbName",pgdriver.Use(),"user:password@localhost:5432")// Connect// mysql, maria// go get github.com/kamalshkeir/mysqldrivererr:=korm.New(korm.MYSQL,"dbName",mysqldriver.Use(),"user:password@localhost:3306")// Connectkorm.Shutdown(databasesName...string)error

Hello world example

package mainimport ("fmt""time""github.com/kamalshkeir/lg""github.com/kamalshkeir/korm""github.com/kamalshkeir/sqlitedriver")typeClassstruct {Iduint`korm:"pk"`NamestringStudents []Student}typeStudentstruct {Iduint`korm:"pk"`NamestringClassuint`korm:"fk:classes.id:cascade:cascade"`ClassesClass}funcmain() {err:=korm.New(korm.SQLITE,"db",sqlitedriver.Use())iflg.CheckError(err) {return}deferkorm.Shutdown()server:=korm.WithDashboard(":9313")korm.WithShell()err= korm.AutoMigrate[Class]("classes")lg.CheckError(err)err= korm.AutoMigrate[Student]("students")lg.CheckError(err)// go run main.go shell to createsuperuser// connect to admin and create some data to query// nested structs with joins, scan the result to the channel directly after each row// so instead of receiving a slice, you will receive data on the channel[0] of the passed slicestudentsChan:= []chanStudent{make(chanStudent)}gofunc() {fors:=rangestudentsChan[0] {fmt.Println("chan students:",s)}}()err=korm.To(&studentsChan).Query("select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class")lg.CheckError(err)fmt.Println()// Named with nested (second argument of 'To') filled automatically from join, support nested slices and structsclasses:= []Class{}query:="select classes.*, students.id as 'students.id',students.name as 'students.name' from classes join students on students.class = classes.id order by :order_here"err=korm.To(&classes,true).Named(query,map[string]any{"order_here":"classes.id",})lg.CheckError(err)for_,s:=rangeclasses {fmt.Println("class:",s)}fmt.Println()// // not nested, only remove second arg true from 'To' methodstudents:= []Student{}err=korm.To(&students,true).Query("select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class")lg.CheckError(err)for_,s:=rangestudents {fmt.Println("student:",s)}fmt.Println()maps:= []map[string]any{}err=korm.To(&maps).Query("select * from students")lg.CheckError(err)fmt.Println("maps =",maps)fmt.Println()names:= []*string{}err=korm.To(&names).Query("select name from students")lg.CheckError(err)fmt.Println("names =",names)fmt.Println()ids:= []int{}err=korm.To(&ids).Query("select id from students")lg.CheckError(err)fmt.Println("ids =",ids)fmt.Println()bools:= []bool{}err=korm.To(&bools).Query("select is_admin from users")lg.CheckError(err)fmt.Println("bools =",bools)fmt.Println()times:= []time.Time{}err=korm.To(&times).Query("select created_at from users")lg.CheckError(err)fmt.Println("times =",times)server.Run()}// OUTPUT// chan students: {1 student-1 1 {1 Math []}}// chan students: {2 student-2 2 {2 French []}}// chan students: {3 student-3 1 {1 Math []}}// chan students: {4 student-4 2 {2 French []}}// class: {1 Math [{1 student-1 0 {0  []}} {3 student-3 0 {0  []}}]}// class: {2 French [{2 student-2 0 {0  []}} {4 student-4 0 {0  []}}]}// student: &{1 student-1 1 {1 Math []}}// student: &{2 student-2 2 {2 French []}}// student: &{3 student-3 1 {1 Math []}}// student: &{4 student-4 2 {2 French []}}// maps = [map[class:1 id:1 name:student-1] map[class:2 id:2 name:student-2] map[class:1 id:3 name:student-3] map[class:2 id:4 name:student-4]]// names = [student-1 student-2 student-3 student-4]// ids = [1 2 3 4]// bools = [true]// times = [2023-04-30 19:19:32 +0200 CEST]

AutoMigrate

Available Tags (SQL)

SQL:

korm.AutoMigrate[Tcomparable](tableNamestring,dbName...string)errorerr:= korm.AutoMigrate[User]("users")err:= korm.AutoMigrate[Bookmark ]("bookmarks")typeUserstruct {Idint`korm:"pk"`// AUTO Increment ID primary keyUuidstring`korm:"size:40"`// VARCHAR(50)Emailstring`korm:"size:50;iunique"`// insensitive uniquePasswordstring`korm:"size:150"`// VARCHAR(150)IsAdminbool`korm:"default:false"`// DEFAULT 0Imagestring`korm:"size:100;default:''"`CreatedAt time.Time`korm:"now"`// auto nowIgnoredstring`korm:"-"`}typeBookmarkstruct {Iduint`korm:"pk"`UserIdint`korm:"fk:users.id:cascade:setnull"`// options cascade,donothing/noaction, setnull/null, setdefault/defaultIsDoneboolToCheckstring`korm:"size:50; notnull; check: len(to_check) > 2 AND len(to_check) < 10; check: is_done=true"`// column type will be VARCHAR(50)Contentstring`korm:"text"`// column type will be TEXT not VARCHARUpdatedAt time.Time`korm:"update"`// will update when model updated, handled by triggers for sqlite, cockroach and postgres, and on migration for mysqlCreatedAt time.Time`korm:"now"`// now is default to current timestamp and of type TEXT for sqlite}all,_:=korm.Model[User]()                   .Where("id = ?",id)                    .Select("item1","item2")                   .OrderBy("created")   .Limit(8)   .Page(2)                   .All()

API

korm.New(dbTypeDialect,dbNamestring,dbDriverdriver.Driver,dbDSN...string)errorkorm.LogQueries()korm.GetConnection(dbName...string)*sql.DBkorm.To[Tany](dest*[]T,nestedSlice...bool)*Selector[T]// scan query to any type slice, even channels and slices with nested structs and joins(sl*Selector[T])Ctx(ctcontext.Context)*Selector[T](sl*Selector[T])Query(statementstring,args...any)error(sl*Selector[T])Named(statementstring,argsmap[string]any,unsafe...bool)errorkorm.WithBus(...opts)*ksbus.Server// Usage: WithBus(...opts) or share an existing onekorm.WithDashboard(address,...opts)*ksbus.Serverkorm.WithShell()korm.WithDocs(generateJsonDocsbool,outJsonDocsstring,handlerMiddlewares...func(handlerkmux.Handler)kmux.Handler)*ksbus.Serverkorm.WithEmbededDocs(embededembed.FS,embededDirPathstring,handlerMiddlewares...func(handler kmux.Handler) kmux.Handler)*ksbus.Serverkorm.WithMetrics(httpHandlerhttp.Handler)*ksbus.Serverkorm.WithPprof(path...string)*ksbus.Serverkorm.Transaction(dbName...string) (*sql.Tx,error)korm.Exec(dbName,querystring,args...any)errorkorm.ExecContext(ctxcontext.Context,dbName,querystring,args...any)errorkorm.ExecNamed(querystring,argsmap[string]any,dbName...string)errorkorm.ExecContextNamed(ctxcontext.Context,querystring,argsmap[string]any,dbName...string)errorkorm.BeforeServersData(fnfunc(dataany,conn*ws.Conn))korm.BeforeDataWS(fnfunc(datamap[string]any,conn*ws.Conn,originalRequest*http.Request)bool)korm.GetAllTables(dbName...string) []stringkorm.GetAllColumnsTypes(tablestring,dbName...string)map[string]stringkorm.GetMemoryTable(tbNamestring,dbName...string) (TableEntity,error)korm.GetMemoryTables(dbName...string) ([]TableEntity,error)korm.GetMemoryDatabases() []DatabaseEntitykorm.GetMemoryDatabase(dbNamestring) (*DatabaseEntity,error)korm.Shutdown(databasesName...string)errorkorm.FlushCache()korm.DisableCache()korm.ManyToMany(table1,table2string,dbName...string)error// add table relation m2m

BuilderStruct:

korm.Exec(dbName,querystring,args...any)errorkorm.Transaction(dbName...string) (*sql.Tx,error)// Model is a starter for BuiderfuncModel[Tcomparable](tableName...string)*BuilderS[T]// Database allow to choose database to execute query onfunc (b*BuilderS[T])Database(dbNamestring)*BuilderS[T]// Insert insert a row into a table and return inserted PKfunc (b*BuilderS[T])Insert(model*T) (int,error)// InsertR add row to a table using input struct, and return the inserted rowfunc (b*BuilderS[T])InsertR(model*T) (T,error)// BulkInsert insert many row at the same time in one queryfunc (b*BuilderS[T])BulkInsert(models...*T) ([]int,error)// AddRelated used for many to many, and after korm.ManyToMany, to add a class to a student or a student to a class, class or student should exist in the database before adding themfunc (b*BuilderS[T])AddRelated(relatedTablestring,whereRelatedTablestring,whereRelatedArgs...any) (int,error)// DeleteRelated delete a relations many to manyfunc (b*BuilderS[T])DeleteRelated(relatedTablestring,whereRelatedTablestring,whereRelatedArgs...any) (int,error)// GetRelated used for many to many to get related classes to a student or related students to a classfunc (b*BuilderS[T])GetRelated(relatedTablestring,destany)error// JoinRelated same as get, but it join datafunc (b*BuilderS[T])JoinRelated(relatedTablestring,destany)error// Set used to update, Set("email,is_admin","example@mail.com",true) or Set("email = ? AND is_admin = ?","example@mail.com",true)func (b*BuilderS[T])Set(querystring,args...any) (int,error)// Delete data from database, can be multiple, depending on the where, return affected rows(Not every database or database driver may support affected rows)func (b*BuilderS[T])Delete() (int,error)// Drop drop table from dbfunc (b*BuilderS[T])Drop() (int,error)// Select usage: Select("email","password")func (b*BuilderS[T])Select(columns...string)*BuilderS[T]// Where can be like : Where("id > ?",1) or Where("id",1) = Where("id = ?",1)func (b*BuilderS[T])Where(querystring,args...any)*BuilderS[T]// Limit set limitfunc (b*BuilderS[T])Limit(limitint)*BuilderS[T]// Context allow to query or execute using ctxfunc (b*BuilderS[T])Context(ctx context.Context)*BuilderS[T]// Page return paginated elements using Limit for specific pagefunc (b*BuilderS[T])Page(pageNumberint)*BuilderS[T]// OrderBy can be used like: OrderBy("-id","-email") OrderBy("id","-email") OrderBy("+id","email")func (b*BuilderS[T])OrderBy(fields...string)*BuilderS[T]// Debug print prepared statement and values for this operationfunc (b*BuilderS[T])Debug()*BuilderS[T]// All get all datafunc (b*BuilderS[T])All() ([]T,error)// One get single rowfunc (b*BuilderS[T])One() (T,error)Examples:korm.Model[models.User]().Select("email","uuid").OrderBy("-id").Limit(PAGINATION_PER).Page(1).All()// INSERTuuid,_:=korm.GenerateUUID()hashedPass,_:=argon.Hash(password)korm.Model[models.User]().Insert(&models.User{Uuid:uuid,Email:"test@example.com",Password:hashedPass,IsAdmin:false,Image:"",CreatedAt:time.Now(),})//if using more than one dbkorm.Database[models.User]("dbNameHere").Where("id = ? AND email = ?",1,"test@example.com").All()// wherekorm.Model[models.User]().Where("id = ? AND email = ?",1,"test@example.com").One()// deletekorm.Model[models.User]().Where("id = ? AND email = ?",1,"test@example.com").Delete()// drop tablekorm.Model[models.User]().Drop()// updatekorm.Model[models.User]().Where("id = ?",1).Set("email = ?","new@example.com")

Buildermap[string]any:

// BuilderM is query builder map string anytypeBuilderMstruct// Table is a starter for BuiderMfuncTable(tableNamestring)*BuilderM// Database allow to choose database to execute query onfunc (b*BuilderM)Database(dbNamestring)*BuilderM// Select select table columns to returnfunc (b*BuilderM)Select(columns...string)*BuilderM// Where can be like: Where("id > ?",1) or Where("id",1) = Where("id = ?",1)func (b*BuilderM)Where(querystring,args...any)*BuilderM// Limit set limitfunc (b*BuilderM)Limit(limitint)*BuilderM// Page return paginated elements using Limit for specific pagefunc (b*BuilderM)Page(pageNumberint)*BuilderM// OrderBy can be used like: OrderBy("-id","-email") OrderBy("id","-email") OrderBy("+id","email")func (b*BuilderM)OrderBy(fields...string)*BuilderM// Context allow to query or execute using ctxfunc (b*BuilderM)Context(ctx context.Context)*BuilderM// Debug print prepared statement and values for this operationfunc (b*BuilderM)Debug()*BuilderM// All get all datafunc (b*BuilderM)All() ([]map[string]any,error)// One get single rowfunc (b*BuilderM)One() (map[string]any,error)// Insert add row to a table using input map, and return PK of the inserted rowfunc (b*BuilderM)Insert(rowDatamap[string]any) (int,error)// InsertR add row to a table using input map, and return the inserted rowfunc (b*BuilderM)InsertR(rowDatamap[string]any) (map[string]any,error)// BulkInsert insert many row at the same time in one queryfunc (b*BuilderM)BulkInsert(rowsData...map[string]any) ([]int,error)// Set used to update, Set("email,is_admin","example@mail.com",true) or Set("email = ? AND is_admin = ?","example@mail.com",true)func (b*BuilderM)Set(querystring,args...any) (int,error)// Delete data from database, can be multiple, depending on the where, return affected rows(Not every database or database driver may support affected rows)func (b*BuilderM)Delete() (int,error)// Drop drop table from dbfunc (b*BuilderM)Drop() (int,error)// AddRelated used for many to many, and after korm.ManyToMany, to add a class to a student or a student to a class, class or student should exist in the database before adding themfunc (b*BuilderM)AddRelated(relatedTablestring,whereRelatedTablestring,whereRelatedArgs...any) (int,error)// GetRelated used for many to many to get related classes to a student or related students to a classfunc (b*BuilderM)GetRelated(relatedTablestring,dest*[]map[string]any)error// JoinRelated same as get, but it join datafunc (b*BuilderM)JoinRelated(relatedTablestring,dest*[]map[string]any)error// DeleteRelated delete a relations many to manyfunc (b*BuilderM)DeleteRelated(relatedTablestring,whereRelatedTablestring,whereRelatedArgs...any) (int,error)Examples:sliceMapStringAny,err:=korm.Table("users").Select("email","uuid").OrderBy("-id").Limit(PAGINATION_PER).Page(1).All()// INSERTuuid,_:=korm.GenerateUUID()hashedPass,_:=argon.Hash("password")// github.com/kamalshkeir/argonkorm.Table("users").Insert(map[string]any{"uuid":uuid,"email":"test@example.com",...})//if using more than one dbkorm.Database("dbNameHere").Table("tableName").Where("id = ? AND email = ?",1,"test@example.com").All()// whereWhere("id = ? AND email = ?",1,"test@example.com")// this workWhere("id,email",1,"test@example.com")// and this workkorm.Table("tableName").Where("id = ? AND email = ?",1,"test@example.com").One()// deletekorm.Table("tableName").Where("id = ? AND email = ?",1,"test@example.com").Delete()// drop tablekorm.Table("tableName").Drop()// updatekorm.Table("tableName").Where("id = ?",1).Set("email = ?","new@example.com")korm.Table("tableName").Where("id",1).Set("email","new@example.com")

Dashboard defaults you can set

korm.PaginationPer=10korm.DocsUrl="docs"korm.EmbededDashboard=falsekorm.MediaDir="media"korm.AssetsDir="assets"korm.StaticDir=path.Join(AssetsDir,"/","static")korm.TemplatesDir=path.Join(AssetsDir,"/","templates")korm.RepoUser="kamalshkeir"korm.RepoName="korm-dash"korm.adminPathNameGroup="/admin"// korm.SetAdminPath("/another")// so you can create a custom dashboard, upload it to your repos and change like like above korm.RepoUser and korm.RepoName

Example With Dashboard (you don't need korm.WithBus with it, because WithDashboard already call it and return the server bus for you)

package mainimport ("github.com/kamalshkeir/lg""github.com/kamalshkeir/ksmux""github.com/kamalshkeir/korm""github.com/kamalshkeir/sqlitedriver")funcmain() {err:=korm.New(korm.SQLITE,"db",sqlitedriver.Use())lg.CheckError(err)serverBus:=korm.WithDashboard("localhost:9313")korm.WithShell()// you can overwrite Admin and Auth middleware used for dashboard (dash_middlewares.go)//korm.Auth = func(handler ksmux.Handler) ksmux.Handler {}//korm.Admin = func(handler ksmux.Handler) ksmux.Handler {}// and also all handlers (dash_views.go)//korm.LoginView = func(c *ksmux.Context) {//c.Html("admin/new_admin_login.html", nil)//}// add extra static directory if you want//serverBus.App.LocalStatics("assets/mystatic","myassets") // will be available at /myassets/*//serverBus.App.LocalTemplates("assets/templates") // will make them available to use with c.Html// serve HTML// serverBus.App.Get("/",func(c *ksmux.Context) {// c.Html("index.html", map[string]any{// "data": data,// })// })serverBus.Run()// OR run https if you have certificatesserverBus.RunTLS(certstring,certKeystring)// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more infosserverBus.RunAutoTLS(subDomains...string)}

Then create admin user to connect to the dashboard

go run main.go shellcreatesuperuser

Then you can visit/admin

Auth middleware example

funcmain() {err:=korm.New(korm.SQLITE,"db",sqlitedriver.Use())iflg.CheckError(err) {return}deferkorm.Shutdown()srv:=korm.WithDashboard("localhost:9313")korm.WithShell()lg.Printfs("mgrunning on http://localhost:9313\n")app:=srv.Appapp.Get("/",korm.Auth(func(c*ksmux.Context) {// work with korm.Admin also// c.IsAuthenticated also return boolifv,ok:=c.User();ok {c.Json(map[string]any{"msg":"Authenticated","v":v.(korm.User).Email,})}else {c.Json(map[string]any{"error":"not auth",})}}))srv.Run()}

Example Admin Auth User and IsAuthenticated

{{define "admin_nav"}}<headerid="admin-header"><nav><ahref="/"><h1>KORM</h1></a><ul><li><a{{ifeq.Request.URL.Path "/" }}class="active"{{end}}href="/">Home</a></li><li><a{{ifcontains.Request.URL.Path.admin_path}}class="active"{{end}}href="{{.admin_path}}">Admin</a></li>        {{if .IsAuthenticated}}<li><ahref="{{.admin_path}}/logout">Logout</a></li>                        {{if .User.Email}}<li><span>Hello {{.User.Email}}</span></li>            {{end}}        {{end}}</ul></nav></header>{{end}}

Admin middlewares

// dash_middlewares.gopackage kormimport ("context""net/http""github.com/kamalshkeir/aes""github.com/kamalshkeir/ksmux")varAuth=func(handler ksmux.Handler) ksmux.Handler {returnfunc(c*ksmux.Context) {session,err:=c.GetCookie("session")iferr!=nil||session=="" {// NOT AUTHENTICATEDc.DeleteCookie("session")handler(c)return}session,err=aes.Decrypt(session)iferr!=nil {handler(c)return}// Check sessionuser,err:=Model[User]().Where("uuid = ?",session).One()iferr!=nil {// session failhandler(c)return}// AUTHENTICATED AND FOUND IN DBc.SetKey("korm-user",user)handler(c)}}varAdmin=func(handler ksmux.Handler) ksmux.Handler {returnfunc(c*ksmux.Context) {session,err:=c.GetCookie("session")iferr!=nil||session=="" {// NOT AUTHENTICATEDc.DeleteCookie("session")c.Status(http.StatusTemporaryRedirect).Redirect(adminPathNameGroup+"/login")return}session,err=aes.Decrypt(session)iferr!=nil {c.Status(http.StatusTemporaryRedirect).Redirect(adminPathNameGroup+"/login")return}user,err:=Model[User]().Where("uuid = ?",session).One()iferr!=nil {// AUTHENTICATED BUT NOT FOUND IN DBc.Status(http.StatusTemporaryRedirect).Redirect(adminPathNameGroup+"/login")return}// Not adminif!user.IsAdmin {c.Status(403).Text("Middleware : Not allowed to access this page")return}c.SetKey("korm-user",user)handler(c)}}varBasicAuth=func(handler ksmux.Handler) ksmux.Handler {returnksmux.BasicAuth(handler,BASIC_AUTH_USER,BASIC_AUTH_PASS)}

Example With Bus between 2 KORM

KORM 1:

package mainimport ("net/http""github.com/kamalshkeir/lg""github.com/kamalshkeir/ksmux""github.com/kamalshkeir/ksmux/ws""github.com/kamalshkeir/korm""github.com/kamalshkeir/ksbus""github.com/kamalshkeir/sqlitedriver")funcmain() {err:=korm.New(korm.SQLITE,"db1",sqlitedriver.Use())iflg.CheckError(err) {return}korm.WithShell()serverBus:=korm.WithBus(ksbus.ServerOpts{IDstringAddressstringPathstringOnWsClosefunc(connIDstring)OnDataWSfunc(datamap[string]any,conn*ws.Conn,originalRequest*http.Request)errorOnServerDatafunc(dataany,conn*ws.Conn)OnIdfunc(datamap[string]any)OnUpgradeWsfunc(r*http.Request)boolWithOtherRouter*ksmux.RouterWithOtherBus*Bus})// handler authenticationkorm.BeforeDataWS(func(datamap[string]any,conn*ws.Conn,originalRequest*http.Request)bool {lg.Info("handle authentication here")returntrue})// handler data from other KORMkorm.BeforeServersData(func(dataany,conn*ws.Conn) {lg.Info("recv orm:","data",data)})// built in router to the bus, check it at https://github.com/kamalshkeir/ksbusserverBus.App.Get("/",func(c*ksmux.Context) {serverBus.SendToServer("localhost:9314",map[string]any{"msg":"hello from server 1",})c.Text("ok")})serverBus.Run("localhost:9313")// OR run https if you have certificatesserverBus.RunTLS(addrstring,certstring,certKeystring)// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more detailsserverBus.RunAutoTLS(domainNamestring,subDomains...string)}

KORM 2:

package mainimport ("net/http""github.com/kamalshkeir/lg""github.com/kamalshkeir/ksmux""github.com/kamalshkeir/ksmux/ws""github.com/kamalshkeir/korm""github.com/kamalshkeir/sqlitedriver")funcmain() {err:=korm.New(korm.SQLITE,"db2",sqlitedriver.Use())iflg.CheckError(err) {return}korm.WithShell()// if dashboard used, this line should be after itserverBus:=korm.WithBus(ksbus.ServerOpts{IDstringAddressstringPathstringOnWsClosefunc(connIDstring)OnDataWSfunc(datamap[string]any,conn*ws.Conn,originalRequest*http.Request)errorOnServerDatafunc(dataany,conn*ws.Conn)OnIdfunc(datamap[string]any)OnUpgradeWsfunc(r*http.Request)boolWithOtherRouter*ksmux.RouterWithOtherBus*Bus})korm.BeforeServersData(func(dataany,conn*ws.Conn) {lg.Info("recv","data",data)})// built in router to the bus, check it at https://github.com/kamalshkeir/ksbusserverBus.App.GET("/",func(c*ksmux.Context) {serverBus.SendToServer("localhost:9314",map[string]any{"msg":"hello from server 2",})c.Status(200).Text("ok")})// Run Server BusserverBus.Run("localhost:9314")// OR run https if you have certificatesserverBus.RunTLS(addrstring,certstring,certKeystring)// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more infosserverBus.RunAutoTLS(domainNamestring,subDomains...string)}

Example generated tag

// generated example using concatination and lengthtypeTestUserstruct {Id*uint`korm:"pk"`Uuidstring`korm:"size:40;iunique"`Email*string`korm:"size:100;iunique"`Genstring`korm:"size:250;generated: concat(uuid,'working',len(password))"`PasswordstringIsAdmin*boolCreatedAt time.Time`korm:"now"`UpdatedAt time.Time`korm:"update"`}funcTestGeneratedAs(t*testing.T) {u,err:=Model[TestUser]().Limit(3).All()iferr!=nil {t.Error(err)}iflen(u)!=3 {t.Error("len not 20")}ifu[0].Gen!=u[0].Uuid+"working"+fmt.Sprintf("%d",len(u[0].Password)) {t.Error("generated not working:",u[0].Gen)}}

Example concat and len from korm_test.go

// Where examplefuncTestConcatANDLen(t*testing.T) {groupes,err:=Model[Group]().Where("name = concat(?,'min') AND len(name) = ?","ad",5).Debug().All()// translated to select * from groups WHERE name = 'ad' || 'min'  AND  length(name) = 5 (sqlite)// translated to select * from groups WHERE name = concat('ad','min')  AND  char_length(name) = 5 (postgres, mysql)iferr!=nil {t.Error(err)}iflen(groupes)!=1||groupes[0].Name!="admin" {t.Error("len(groupes) != 1 , got: ",groupes)}}

Router/Mux

Learn more aboutKsmux

funcmain() {err:=korm.New(korm.SQLITE,"db",sqlitedriver.Use())iferr!=nil {log.Fatal(err)}serverBus:=korm.WithDashboard("localhost:9313")korm.WithShell()mux:=serverBus.App// add global middlewaresmux.Use((midws...func(http.Handler) http.Handler))...}

Pprof

serverBus:=korm.WithDashboard("localhost:9313")// or srv := korm.WithBus()serverBus.WithPprof(path...string)// path is 'debug' by defaultwillenable:-/debug/pprof-/debug/profile-/debug/heap-/debug/trace

To execute profile cpu:go tool pprof -http=":8000" pprofbin http://localhost:9313/debug/profile?seconds=18To execute profile memory:go tool pprof -http=":8000" pprofbin http://localhost:9313/debug/heap?seconds=18To execute generate trace: go to endpointhttp://localhost:9313/debug/trace?seconds=18 from browser , this will download the trace of 18 secondsThen to see the trace :go tool trace path/to/trace

Metrics Prometheus

// or srv := korm.WithBus()//srv.WithMetrics(httpHandler http.Handler, path ...string) path default to 'metrics'srv.WithMetrics(promhttp.Handler())willenable:-/metrics

Logs middleware

// or srv := korm.WithBus()//srv.WithMetrics(httpHandler http.Handler, path ...string) path default to 'metrics'srv.App.Use(ksmux.Logs())// it take an optional callback executed on each request if you want to add log to a file or sendsrv.App.Use(ksmux.Logs(func(method,path,remotestring,statusint,took time.Duration) {// save somewhere}))willenable:-/metrics

Hooks

korm.OnInsert(func(database,tablestring,datamap[string]any)error {fmt.Println("inserting into",database,table,data)// if error returned, it will not insertreturnnil})korm.OnSet(func(database,tablestring,datamap[string]any)error {fmt.Println("set into",database,table,data)returnnil})korm.OnDelete(func(database,table,querystring,args...any)error {})korm.OnDrop(func(database,tablestring)error {})

Python bus client example

pip install ksbus==1.1.0# if it doesn't work , execute it again
fromksbusimportBus# onOpen callback that let you know when connection is ready, it take the bus as paramdefOnOpen(bus):print("connected")# bus.autorestart=True# Publish publish to topicbus.Publish("top", {"data":"hello from python"    })# Subscribe, it also return the subscriptionbus.Subscribe("python",pythonTopicHandler)# SendToNamed publish to named topicbus.SendToNamed("top:srv", {"data":"hello again from python"    })# bus.Unsubscribe("python")print("finish everything")# pythonTopicHandler handle topic 'python'defpythonTopicHandler(data,subs):print("recv on topic python:",data)# Unsubscribe#subs.Unsubscribe()if__name__=="__main__":Bus("localhost:9313",onOpen=OnOpen)# blockingprint("prorgram exited")

ManyToMany Relationships Example

typeClassstruct {Iduint`korm:"pk"`Namestring`korm:"size:100"`IsAvailableboolCreatedAt   time.Time`korm:"now"`}typeStudentstruct {Iduint`korm:"pk"`Namestring`korm:"size:100"`CreatedAt time.Time`korm:"now"`}// migratefuncmigrate() {err:= korm.AutoMigrate[Class]("classes")iflg.CheckError(err) {return}err= korm.AutoMigrate[Student]("students")iflg.CheckError(err) {return}err=korm.ManyToMany("classes","students")iflg.CheckError(err) {return}}// korm.ManyToMany create relation table named m2m_classes_students// then you can use it like so to get related data// get related to map to structstd:= []Student{}err=korm.Model[Class]().Where("name = ?","Math").Select("name").OrderBy("-name").Limit(1).GetRelated("students",&std)// get related to mapstd:= []map[string]any{}err=korm.Table("classes").Where("name = ?","Math").Select("name").OrderBy("-name").Limit(1).GetRelated("students",&std)// join related to mapstd:= []map[string]any{}err=korm.Table("classes").Where("name = ?","Math").JoinRelated("students",&std)// join related to strcucu:= []JoinClassUser{}err=korm.Model[Class]().Where("name = ?","Math").JoinRelated("students",&cu)// to add relation_,err=korm.Model[Class]().AddRelated("students","name = ?","hisName")_,err=korm.Model[Student]().AddRelated("classes","name = ?","French")_,err=korm.Table("students").AddRelated("classes","name = ?","French")// delete relation_,err=korm.Model[Class]().Where("name = ?","Math").DeleteRelated("students","name = ?","hisName")_,err=korm.Table("classes").Where("name = ?","Math").DeleteRelated("students","name = ?","hisName")

Swagger documentation

korm.DocsUrl="docs"// default endpoint '/docs'korm.BASIC_AUTH_USER="test"korm.BASIC_AUTH_PASS="pass"korm.WithDocs(generate,dirPath,korm.BasicAuth)korm.WithDocs(true,"",korm.BasicAuth)// dirPath default to 'assets/static/docs'korm.WithEmbededDocs(embededembed.FS,dirPath,korm.BasicAuth)// dirPath default to 'assets/static/docs' if empty

Interactive shell

Commands:  [databases, use, tables, columns, migrate, createsuperuser, createuser, query, getall, get, drop, delete, clear/cls, q/quit/exit, help/commands]'databases':  list all connected databases'use':  use a specific database'tables':  list all tablesin database'columns':  list all columns of a table  (accept but not required extra param like:'columns' or'columns users')'migrate':  migrate or execute sql file'createsuperuser': (only with dashboard)  create a admin user'createuser': (only with dashboard)  create a regular user'query':   query data from database   (accept but not required extra param like:'query' or'query select * from users where ...')'getall':   get all rows given a table name  (accept but not required extra param like:'getall' or'getall users')'get':  get single row   (accept but not required extra param like:'get' or'get users email like "%anything%"')'delete':  delete rows where field equal_to  (accept but not required extra param like:'delete' or'delete users email="email@example.com"')'drop':  drop a table given table name  (accept but not required extra param like:'drop' or'drop users')'clear / cls':  clear shell console'q / quit / exit / q!':exit shell'help':  show thishelp message

Example, not required, Load config from env directly to struct using Kenv

import"github.com/kamalshkeir/kenv"typeEmbedSstruct {Staticbool`kenv:"EMBED_STATIC|false"`Templatesbool`kenv:"EMBED_TEMPLATES|false"`}typeGlobalConfigstruct {Hoststring`kenv:"HOST|localhost"`// DEFAULT to 'localhost': if HOST not found in envPortstring`kenv:"PORT|9313"`EmbedEmbedSDbstruct {Namestring`kenv:"DB_NAME|db"`// NOT REQUIRED: if DB_NAME not found, defaulted to 'db'Typestring`kenv:"DB_TYPE"`// REEQUIRED: this env var is required, you will have error if emptyDSNstring`kenv:"DB_DSN|"`// NOT REQUIRED: if DB_DSN not found it's not required, it's ok to stay empty}Smtpstruct {Emailstring`kenv:"SMTP_EMAIL|"`Passstring`kenv:"SMTP_PASS|"`Hoststring`kenv:"SMTP_HOST|"`Portstring`kenv:"SMTP_PORT|"`}Profilerbool`kenv:"PROFILER|false"`Docsbool`kenv:"DOCS|false"`Logsbool`kenv:"LOGS|false"`Monitoringbool`kenv:"MONITORING|false"`}kenv.Load(".env")// load env file// Fill struct from env loaded before:Config:=&GlobalConfig{}err:=kenv.Fill(Config)// fill struct with env vars loaded before

Example nested or embeded structs

package mainimport ("fmt""time""github.com/kamalshkeir/lg""github.com/kamalshkeir/korm""github.com/kamalshkeir/sqlitedriver")typeClassstruct {Iduint`korm:"pk"`NamestringStudents []Student}typeStudentstruct {Iduint`korm:"pk"`NamestringClassuint`korm:"fk:classes.id:cascade:cascade"`ClassesClass}funcmain() {err:=korm.New(korm.SQLITE,"db",sqlitedriver.Use())iflg.CheckError(err) {return}deferkorm.Shutdown()server:=korm.WithDashboard("localhost:9313")korm.WithShell()err= korm.AutoMigrate[Class]("classes")lg.CheckError(err)err= korm.AutoMigrate[Student]("students")lg.CheckError(err)// go run main.go shell to createsuperuser// connect to admin and create some data to query// nested structs with joins, scan the result to the channel directly after each row// so instead of receiving a slice, you will receive data on the channel[0] of the passed slicestudentsChan:= []chanStudent{make(chanStudent)}gofunc() {fors:=rangestudentsChan[0] {fmt.Println("chan students:",s)}}()err=korm.To(&studentsChan).Query("select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class")lg.CheckError(err)fmt.Println()// nested (second argument of 'Scan') filled automatically from join, support nested slices and structsclasses:= []Class{}err=korm.To(&classes,true).Query("select classes.*, students.id as 'students.id',students.name as 'students.name' from classes join students on students.class = classes.id order by classes.id")lg.CheckError(err)for_,s:=rangeclasses {fmt.Println("class:",s)}fmt.Println()// // not nested, only remove second arg true from Scan methodstudents:= []Student{}err=korm.To(&students,true).Query("select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class")lg.CheckError(err)for_,s:=rangestudents {fmt.Println("student:",s)}fmt.Println()maps:= []map[string]any{}err=korm.To(&maps).Query("select * from students")lg.CheckError(err)fmt.Println("maps =",maps)fmt.Println()names:= []*string{}err=korm.To(&names).Query("select name from students")lg.CheckError(err)fmt.Println("names =",names)fmt.Println()ids:= []int{}err=korm.To(&ids).Query("select id from students")lg.CheckError(err)fmt.Println("ids =",ids)fmt.Println()bools:= []bool{}err=korm.To(&bools).Query("select is_admin from users")lg.CheckError(err)fmt.Println("bools =",bools)fmt.Println()times:= []time.Time{}err=korm.To(&times).Query("select created_at from users")lg.CheckError(err)fmt.Println("times =",times)server.Run()}// OUTPUT// chan students: {1 student-1 1 {1 Math []}}// chan students: {2 student-2 2 {2 French []}}// chan students: {3 student-3 1 {1 Math []}}// chan students: {4 student-4 2 {2 French []}}// class: {1 Math [{1 student-1 0 {0  []}} {3 student-3 0 {0  []}}]}// class: {2 French [{2 student-2 0 {0  []}} {4 student-4 0 {0  []}}]}// student: &{1 student-1 1 {1 Math []}}// student: &{2 student-2 2 {2 French []}}// student: &{3 student-3 1 {1 Math []}}// student: &{4 student-4 2 {2 French []}}// maps = [map[class:1 id:1 name:student-1] map[class:2 id:2 name:student-2] map[class:1 id:3 name:student-3] map[class:2 id:4 name:student-4]]// names = [student-1 student-2 student-3 student-4]// ids = [1 2 3 4]// bools = [true]// times = [2023-04-30 19:19:32 +0200 CEST]

Benchmark vs Tarantool, Pgx, Gorm

https://github.com/kamalshkeir/korm-vs-gorm-vs-tarantool-vs-pgx

Benchmarks vs Gorm

goos: windowsgoarch: amd64pkg: github.com/kamalshkeir/korm/benchmarkscpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz

To execute these benchmarks on your machine, very easy :

typeTestTablestruct {Iduint`korm:"pk"`EmailstringContentstringPasswordstringIsAdminboolCreatedAt time.Time`korm:"now"`UpdatedAt time.Time`korm:"update"`}typeTestTableGormstruct {Iduint`gorm:"primarykey"`EmailstringContentstringPasswordstringIsAdminboolCreatedAt time.TimeUpdatedAt time.Time}////////////////////////////////////////////  query 7000 rows  //////////////////////////////////////////////BenchmarkGetAllS_GORM-41956049832ns/op12163316B/op328790allocs/opBenchmarkGetAllS-42708934395.3ns/op224B/op1allocs/opBenchmarkGetAllM_GORM-41862989567ns/op13212278B/op468632allocs/opBenchmarkGetAllM-44219461273.5ns/op224B/op1allocs/opBenchmarkGetRowS_GORM-41218896988ns/op5930B/op142allocs/opBenchmarkGetRowS-41473164805.1ns/op336B/op7allocs/opBenchmarkGetRowM_GORM-411402101638ns/op7408B/op203allocs/opBenchmarkGetRowM-41752652671.9ns/op336B/op7allocs/opBenchmarkPagination10_GORM-47714153304ns/op19357B/op549allocs/opBenchmarkPagination10-41285722934.5ns/op400B/op7allocs/opBenchmarkPagination100_GORM-41364738934ns/op165423B/op4704allocs/opBenchmarkPagination100-41278724956.5ns/op400B/op7allocs/opBenchmarkQueryS-45781499207.7ns/op4B/op1allocs/opBenchmarkQueryM-44643155227.2ns/op4B/op1allocs/opBenchmarkGetAllTables-44746586525.48ns/op0B/op0allocs/opBenchmarkGetAllColumns-42365701942.82ns/op0B/op0allocs/op////////////////////////////////////////////  query 5000 rows  //////////////////////////////////////////////BenchmarkGetAllS_GORM-42443247546ns/op8796840B/op234784allocs/opBenchmarkGetAllS-42854401426.8ns/op224B/op1allocs/opBenchmarkGetAllM_GORM-42446329242ns/op9433050B/op334631allocs/opBenchmarkGetAllM-44076317283.4ns/op224B/op1allocs/opBenchmarkGetRowS_GORM-411445101107ns/op5962B/op142allocs/opBenchmarkGetRowS-41344831848.4ns/op336B/op7allocs/opBenchmarkGetRowM_GORM-410000100969ns/op7440B/op203allocs/opBenchmarkGetRowM-41721742688.5ns/op336B/op7allocs/opBenchmarkPagination10_GORM-47500156208ns/op19423B/op549allocs/opBenchmarkPagination10-41253757952.3ns/op400B/op7allocs/opBenchmarkPagination100_GORM-41564749408ns/op165766B/op4704allocs/opBenchmarkPagination100-41236270957.5ns/op400B/op7allocs/opBenchmarkGetAllTables-44439938625.43ns/op0B/op0allocs/opBenchmarkGetAllColumns-42790639241.45ns/op0B/op0allocs/op////////////////////////////////////////////  query 1000 rows  //////////////////////////////////////////////BenchmarkGetAllS_GORM-41636766871ns/op1683919B/op46735allocs/opBenchmarkGetAllS-42882660399.0ns/op224B/op1allocs/opBenchmarkGetAllM_GORM-41408344988ns/op1886922B/op66626allocs/opBenchmarkGetAllM-43826730296.5ns/op224B/op1allocs/opBenchmarkGetRowS_GORM-41194097725ns/op5935B/op142allocs/opBenchmarkGetRowS-41333258903.0ns/op336B/op7allocs/opBenchmarkGetRowM_GORM-410000106079ns/op7408B/op203allocs/opBenchmarkGetRowM-41601274748.2ns/op336B/op7allocs/opBenchmarkPagination10_GORM-47534159991ns/op19409B/op549allocs/opBenchmarkPagination10-411539821022ns/op400B/op7allocs/opBenchmarkPagination100_GORM-41468766269ns/op165876B/op4705allocs/opBenchmarkPagination100-410000001016ns/op400B/op7allocs/opBenchmarkGetAllTables-45620029725.36ns/op0B/op0allocs/opBenchmarkGetAllColumns-42547867941.30ns/op0B/op0allocs/op////////////////////////////////////////////  query 300 rows  //////////////////////////////////////////////BenchmarkGetAllS_GORM-45582046830ns/op458475B/op13823allocs/opBenchmarkGetAllS-42798872411.5ns/op224B/op1allocs/opBenchmarkGetAllM_GORM-44282605646ns/op567011B/op19721allocs/opBenchmarkGetAllM-44093662287.9ns/op224B/op1allocs/opBenchmarkGetRowS_GORM-41218297764ns/op5966B/op142allocs/opBenchmarkGetRowS-41347084886.4ns/op336B/op7allocs/opBenchmarkGetRowM_GORM-410000105311ns/op7440B/op203allocs/opBenchmarkGetRowM-41390363780.0ns/op336B/op7allocs/opBenchmarkPagination10_GORM-47502155949ns/op19437B/op549allocs/opBenchmarkPagination10-410000001046ns/op400B/op7allocs/opBenchmarkPagination100_GORM-41479779700ns/op165679B/op4705allocs/opBenchmarkPagination100-410000001054ns/op400B/op7allocs/opBenchmarkGetAllTables-45225570426.00ns/op0B/op0allocs/opBenchmarkGetAllColumns-42929236842.09ns/op0B/op0allocs/op////////////////////////////////////////////    MONGO       //////////////////////////////////////////////BenchmarkGetAllS-43121384385.6ns/op224B/op1allocs/opBenchmarkGetAllM-44570059264.2ns/op224B/op1allocs/opBenchmarkGetRowS-41404399866.6ns/op336B/op7allocs/opBenchmarkGetRowM-41691026722.6ns/op336B/op7allocs/opBenchmarkGetAllTables-44742448925.34ns/op0B/op0allocs/opBenchmarkGetAllColumns-42703963242.22ns/op0B/op0allocs/op//////////////////////////////////////////////////////////////////////////////////////////////////////////

Available Tags by struct field type:

String Field:

Without parameter                With parameter                           
*  text (create column as TEXT not VARCHAR)*  notnull*  unique*   iunique // insensitive unique*  index, +index, index+ (INDEX ascending)*  index-, -index (INDEX descending)*  default (DEFAULT '')
* default:'any' (DEFAULT 'any')*mindex:...* uindex:username,Iemail // CREATE UNIQUE INDEX ON users (username,LOWER(email)) // email is lower because of 'I' meaning Insensitive for email* fk:...* size:50  (VARCHAR(50))* check:...

Int, Uint, Int64, Uint64 Fields:

Without parameter                
*   -   (To Ignore a field)*   autoinc, pk  (PRIMARY KEY)*   notnull      (NOT NULL)*  index, +index, index+ (CREATE INDEX ON COLUMN)*  index-, -index(CREATE INDEX DESC ON COLUMN)     *   unique  (CREATE UNIQUE INDEX ON COLUMN) *   default (DEFAULT 0)
With parameter                           
Available 'on_delete' and 'on_update' options: cascade,(donothing,noaction),(setnull,null),(setdefault,default)*   fk:{table}.{column}:{on_delete}:{on_update} *   check: len(to_check) > 10 ; check: is_used=true (You can chain checks or keep it in the same CHECK separated by AND)*   mindex: first_name, last_name (CREATE MULTI INDEX ON COLUMN + first_name + last_name)*   uindex: first_name, last_name (CREATE MULTI UNIQUE INDEX ON COLUMN + first_name + last_name) *   default:5 (DEFAULT 5)

Bool : bool is INTEGER NOT NULL checked between 0 and 1 (in order to be consistent accross sql dialects)

Without parameter                With parameter                           
*  index, +index, index+ (CREATE INDEX ON COLUMN)*  index-, -index(CREATE INDEX DESC ON COLUMN)  *   default (DEFAULT 0)
*   default:1 (DEFAULT 1)*   mindex:...*   fk:...

time.Time :

Without parameter                With parameter
*  index, +index, index+ (CREATE INDEX ON COLUMN)*  index-, -index(CREATE INDEX DESC ON COLUMN)  *   unique*   now (NOT NULL and defaulted to current unix timestamp)*   update (NOT NULL DEFAULT UNIX_TIMESTAMP ON UPDATE UNIX_TIMESTAMP)
*   fk:...*   check:...*   uindex:...

Float64 :

Without parameter                With parameter                           
*   notnull*  index, +index, index+ (CREATE INDEX ON COLUMN)*  index-, -index(CREATE INDEX DESC ON COLUMN)  *   unique*   default
*   default:...*   fk:...*   mindex:...*   uindex:...*   check:...

JSON

typeJsonOptionstruct {AsstringDialectstringDatabasestringParams   []any}funcJSON_EXTRACT(dataJsonstring,opt...JsonOption)stringfuncJSON_REMOVE(dataJsonstring,opt...JsonOption)stringfuncJSON_SET(dataJsonstring,opt...JsonOption)stringfuncJSON_ARRAY(values []any,asstring,dialect...string)stringfuncJSON_OBJECT(values []any,asstring,dialect...string)stringfuncJSON_CAST(valuestring,asstring,dialect...string)string// create query jsonq:=korm.JSON_EXTRACT(`{"a": {"c": 3}, "b": 2}`, korm.JsonOption{As:"data",Params: []any{"a.c","b"},})fmt.Println("q ==",q)// q == JSON_EXTRACT('{"a": {"c": 3}, "b": 2}','$.a.c','$.b') AS datavardata []map[string]anyerr:=korm.To(&data).Query("SELECT "+q)lg.CheckError(err)fmt.Println("data=",data)// data= [map[data:[3,2]]]

🔗 Links

portfoliolinkedin


Licence

LicenceBSD-3

About

KORM, an elegant and lightning-fast ORM for all your concurrent and async needs. Inspired by the highly popular Django Framework, KORM offers similar functionality with the added bonus of performance

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors2

  •  
  •  

Languages


[8]ページ先頭

©2009-2025 Movatter.jp