{"id":36799699,"url":"https://github.com/kamalshkeir/korm","last_synced_at":"2026-01-12T13:33:03.207Z","repository":{"id":64297495,"uuid":"564599107","full_name":"kamalshkeir/korm","owner":"kamalshkeir","description":"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","archived":false,"fork":false,"pushed_at":"2026-01-11T01:47:35.000Z","size":740,"stargazers_count":184,"open_issues_count":0,"forks_count":7,"subscribers_count":5,"default_branch":"master","last_synced_at":"2026-01-11T09:29:23.783Z","etag":null,"topics":["admin","admin-dashboard","blazingly-fast","builder","eventbus","foreign-keys","generics","go","golang","hooks","many-to-many","mux-router","mysql","orm","postgres","shell","sql","sqlbuilder","sqlite","triggers"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kamalshkeir.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-11T03:49:29.000Z","updated_at":"2026-01-11T01:47:39.000Z","dependencies_parsed_at":"2023-02-16T14:20:19.642Z","dependency_job_id":"bb18c1bd-44a1-42e8-b3ae-bd7309261fc1","html_url":"https://github.com/kamalshkeir/korm","commit_stats":null,"previous_names":[],"tags_count":195,"template":false,"template_full_name":null,"purl":"pkg:github/kamalshkeir/korm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fkorm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fkorm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fkorm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fkorm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kamalshkeir","download_url":"https://codeload.github.com/kamalshkeir/korm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kamalshkeir%2Fkorm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28339174,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T12:22:26.515Z","status":"ssl_error","status_checked_at":"2026-01-12T12:22:10.856Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin","admin-dashboard","blazingly-fast","builder","eventbus","foreign-keys","generics","go","golang","hooks","many-to-many","mux-router","mysql","orm","postgres","shell","sql","sqlbuilder","sqlite","triggers"],"created_at":"2026-01-12T13:33:02.999Z","updated_at":"2026-01-12T13:33:03.175Z","avatar_url":"https://github.com/kamalshkeir.png","language":"Go","funding_links":["https://www.buymeacoffee.com/kamalshkeir"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\t\u003cimg src=\"./korm.png\" width=\"auto\" style=\"margin:0 auto 0 auto;\"/\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\u003cdiv align=\"center\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/go-mod/go-version/kamalshkeir/korm\" width=\"auto\" height=\"20px\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/languages/code-size/kamalshkeir/korm\" width=\"auto\" height=\"20px\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/License-BSD%20v3-blue.svg\" width=\"auto\" height=\"20px\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/v/tag/kamalshkeir/korm\" width=\"auto\" height=\"20px\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/stars/kamalshkeir/korm?style=social\" width=\"auto\" height=\"20px\"\u003e\n\t\u003cimg src=\"https://img.shields.io/github/forks/kamalshkeir/korm?style=social\" width=\"auto\" height=\"20px\"\u003e\n\u003c/div\u003e\n\n\n\u003cdiv align=\"center\"\u003e\n\t\u003ca href=\"https://www.youtube.com/watch?v=KMnnwly3Mpc\" style=\"display:flex;justify-content:center;align-items:center;gap:10px;\"\u003e\n\t\u003cimg src=\"https://user-images.githubusercontent.com/54605903/217871012-9c5dc1da-25bd-47d5-ac9e-c3acee7178d5.svg\" width=\"auto\" height=\"50px\"\u003e\n\t\u003cspan\u003e\u003cstrong\u003e Simple Example video with bus \u003c/strong\u003e\u003c/span\u003e\n\t\u003c/a\u003e\n\u003c/div\u003e\n\n\n\n\u003csvg height=\"50px\" width=\"50px\"\u003e\n\t\u003cg\u003e\n\t\t\u003cpath style=\"fill:#F61C0D;\" d=\"M365.257,67.393H95.744C42.866,67.393,0,110.259,0,163.137v134.728\n\t\t\tc0,52.878,42.866,95.744,95.744,95.744h269.513c52.878,0,95.744-42.866,95.744-95.744V163.137\n\t\t\tC461.001,110.259,418.135,67.393,365.257,67.393z M300.506,237.056l-126.06,60.123c-3.359,1.602-7.239-0.847-7.239-4.568V168.607\n\t\t\tc0-3.774,3.982-6.22,7.348-4.514l126.06,63.881C304.363,229.873,304.298,235.248,300.506,237.056z\"/\u003e\n\t\u003c/g\u003e\n\u003c/svg\u003e\n\n\u003cbr\u003e\n\u003cdiv align=\"center\"\u003e\n\t\u003ca href=\"https://kamalshkeir.dev\" target=\"_blank\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/my_portfolio-000?style=for-the-badge\u0026logo=ko-fi\u0026logoColor=white\" width=\"auto\" height=\"32px\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://www.linkedin.com/in/kamal-shkeir/\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/linkedin-0A66C2?style=for-the-badge\u0026logo=linkedin\u0026logoColor=white\" width=\"auto\" height=\"30px\"\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://www.buymeacoffee.com/kamalshkeir\" target=\"_blank\"\u003e\u003cimg src=\"https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png\" alt=\"Buy Me A Coffee\" width=\"auto\" height=\"32px\" \u003e\u003c/a\u003e\n\n\t\n\u003c/div\u003e\n\n---\n### Introducing Korm - the elegant, lightning-fast ORM/Framework for all your needs, see [benchmarks](#benchmarks-vs-gorm). Inspired by the highly popular Django Framework, Korm offers similar functionality with the added bonus of performance\n\n### It is also composable, allowing for integration with a network websocket PubSub using [WithBus](#example-with-bus-between-2-korm) when you want to synchronise your data between multiple Korm or [WithDashboard](#example-with-dashboard-you-dont-need-kormwithbus-with-it-because-withdashboard-already-call-it-and-return-the-server-bus-for-you) to have a complete setup of server bus and Admin Dashboard.\n\n#### Why settle for less when you can have the best ?\n- 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\n- Django can handle at most 300 request per second, Go handle 44,000 requests per second (benchmarks done on my machine)\n- The API is also more user-friendly and less verbose than Django's\n- 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\n- Additionally, its caching system uses goroutines and channels to efficiently to clean the cache when rows or tables are created, updated, deleted, or dropped\n\n### It Has :\n- \u003cstrong\u003eNew:\u003c/strong\u003e  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\n\n- \u003cstrong\u003eNew:\u003c/strong\u003e  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, use `korm.EnableCheck()` to enable it\n\n- \u003cstrong\u003eNew:\u003c/strong\u003e  [Handle Nested or Embeded structs](#example-nested-or-embeded-structs) and slice of structs through joins, like sqlx, but sqlx doesn't handle slice of structs\n\n- \u003cstrong\u003eNew:\u003c/strong\u003e 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\"}) \n\n- \u003cstrong\u003eNew:\u003c/strong\u003e  korm.LogsQueries() that log statements and time tooked by sql queries \n\n- \u003ca href=\"#swagger-documentation\"\u003eAuto Docs with Model API and video tutoriel\n\u003c/a\u003e\u003ca href=\"https://www.youtube.com/watch?v=r7rbMrTkVek\"\u003e\n\t\u003cimg src=\"https://user-images.githubusercontent.com/54605903/217871012-9c5dc1da-25bd-47d5-ac9e-c3acee7178d5.svg\" width=\"auto\" height=\"50px\"\u003e\n\u003c/a\u003e\n\n- \u003ca href=\"#swagger-documentation\"\u003eSwagger Documentation and tutorial\n\u003c/a\u003e\u003ca href=\"https://www.youtube.com/watch?v=RupARTkPzf4\"\u003e\n\t\u003cimg src=\"https://user-images.githubusercontent.com/54605903/217871012-9c5dc1da-25bd-47d5-ac9e-c3acee7178d5.svg\" width=\"auto\" height=\"50px\"\u003e\n\u003c/a\u003e\n\n- [PPROF](#pprof) Go profiling tool and [Metrics Prometheus](#metrics-prometheus)\n\n- [Logs Middleware](#logs-middleware)\n\n- [Admin dashboard](#example-with-dashboard-you-dont-need-kormwithbus-with-it-because-withdashboard-already-call-it-and-return-the-server-bus-for-you) with ready offline and installable PWA (using /static/sw.js and /static/manifest.webmanifest). All statics mentionned in `sw.js` will be cached and served by the service worker, you can inspect the Network Tab in the browser to check it\n\n- 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, see [Example](#example-with-bus-between-2-korm) \n\n- [Built-in Authentication](#auth-middleware-example) using `korm.Auth` , `korm.Admin` or `korm.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 example [admin_nav.html](#example-admin-and-auth-user-model-and-isauthenticated) \n\n- [Interactive Shell](#interactive-shell), to CRUD in your databases from command line, use `korm.WithShell()` , ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\n- [AutoMigrate](#automigrate) directly from struct\n\n- 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\n\n- [Router/Mux](https://github.com/kamalshkeir/ksmux) accessible from the serverBus after calling `korm.WithBus(...opts)` or `korm.WithDashboard(addr, ...opts)`\n\n- [Hooks](#hooks) : OnInsert OnSet OnDelete and OnDrop\n\n- [many to many](#manytomany-relationships-example) relationships\n\n- [GENERATED ALWAYS AS](#example-generated-tag) tag added (all dialects)\n\n- [Concatination and Length](#example-concat-and-len-from-korm_testgo) support for `Where` and for tags: `check` and `generated` (all dialects)\n\n- Support for foreign keys, indexes , checks,... [See all](#automigrate)\n\n- [Kenv](#example-not-required-load-config-from-env-directly-to-struct-using-kenv) load env vars to struct\n\n- [Python Bus Client](#python-bus-client-example) `pip install ksbus`\n\n####  All drivers concurrent safe read and write\n#### Supported databases:\n- Sqlite\n- Mysql\n- Maria\n- Postgres\n- Cockroach\n\n\n---\n# Installation\n\n```sh\ngo get -u github.com/kamalshkeir/korm@latest\n```\n\n# Drivers moved outside this package to not get them all in your go.mod file\n```sh\ngo get -u github.com/kamalshkeir/sqlitedriver@latest\ngo get -u github.com/kamalshkeir/pgdriver@latest\ngo get -u github.com/kamalshkeir/mysqldriver@latest\n```\n\n### Global Vars\n```go\n// Debug when true show extra useful logs for queries executed for migrations and queries statements\nDebug = false\n// FlushCacheEvery execute korm.FlushCache() every 10 min by default, you should not worry about it, but useful that you can change it\nFlushCacheEvery = 10 * time.Minute\n// SetCacheMaxMemory set max size of each cache cacheAllS AllM ...\nkorm.SetCacheMaxMemory(megaByte int) // default maximum of 50 Mb , cannot be lower\n// Connection pool\nMaxOpenConns = 20\nMaxIdleConns = 20\nMaxLifetime = 30 * time.Minute\nMaxIdleTime = 15 * time.Minute\n```\n\n### Connect to a database\n```go\n// sqlite\n// go get github.com/kamalshkeir/sqlitedriver\nerr := korm.New(korm.SQLITE, \"dbName\", sqlitedriver.Use()) // Connect\n// postgres, cockroach\n// go get github.com/kamalshkeir/pgdriver\nerr := korm.New(korm.POSTGRES,\"dbName\", pgdriver.Use(), \"user:password@localhost:5432\") // Connect\n// mysql, maria\n// go get github.com/kamalshkeir/mysqldriver\nerr := korm.New(korm.MYSQL,\"dbName\", mysqldriver.Use(), \"user:password@localhost:3306\") // Connect\n\nkorm.Shutdown(databasesName ...string) error\n```\n\n### Hello world example\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/korm\"\n\t\"github.com/kamalshkeir/sqlitedriver\"\n)\n\ntype Class struct {\n\tId       uint `korm:\"pk\"`\n\tName     string\n\tStudents []Student\n}\n\ntype Student struct {\n\tId      uint `korm:\"pk\"`\n\tName    string\n\tClass   uint `korm:\"fk:classes.id:cascade:cascade\"`\n\tClasses Class\n}\n\nfunc main() {\n\terr := korm.New(korm.SQLITE, \"db\", sqlitedriver.Use())\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n\tdefer korm.Shutdown()\n\n\tserver := korm.WithDashboard(\":9313\")\n\tkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\n\terr = korm.AutoMigrate[Class](\"classes\")\n\tlg.CheckError(err)\n\n\terr = korm.AutoMigrate[Student](\"students\")\n\tlg.CheckError(err)\n\n\t// go run main.go shell to createsuperuser\n\t// connect to admin and create some data to query\n\n\t// nested structs with joins, scan the result to the channel directly after each row\n\t// so instead of receiving a slice, you will receive data on the channel[0] of the passed slice\n\tstudentsChan := []chan Student{make(chan Student)}\n\tgo func() {\n\t\tfor s := range studentsChan[0] {\n\t\t\tfmt.Println(\"chan students:\", s)\n\t\t}\n\t}()\n\terr = korm.To(\u0026studentsChan).Query(\"select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class\")\n\tlg.CheckError(err)\n\tfmt.Println()\n\n\t// Named with nested (second argument of 'To') filled automatically from join, support nested slices and structs\n\tclasses := []Class{}\n\tquery := \"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\"\n\terr = korm.To(\u0026classes, true).Named(query,map[string]any{\n\t\t\"order_here\": \"classes.id\",\n\t})\n\tlg.CheckError(err)\n\tfor _, s := range classes {\n\t\tfmt.Println(\"class:\", s)\n\t}\n\tfmt.Println()\n\n\t// // not nested, only remove second arg true from 'To' method\n\tstudents := []Student{}\n\terr = korm.To(\u0026students, true).Query(\"select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class\")\n\tlg.CheckError(err)\n\tfor _, s := range students {\n\t\tfmt.Println(\"student:\", s)\n\t}\n\tfmt.Println()\n\n\tmaps := []map[string]any{}\n\terr = korm.To(\u0026maps).Query(\"select * from students\")\n\tlg.CheckError(err)\n\tfmt.Println(\"maps =\", maps)\n\tfmt.Println()\n\n\tnames := []*string{}\n\terr = korm.To(\u0026names).Query(\"select name from students\")\n\tlg.CheckError(err)\n\tfmt.Println(\"names =\", names)\n\tfmt.Println()\n\n\tids := []int{}\n\terr = korm.To(\u0026ids).Query(\"select id from students\")\n\tlg.CheckError(err)\n\tfmt.Println(\"ids =\", ids)\n\tfmt.Println()\n\n\tbools := []bool{}\n\terr = korm.To(\u0026bools).Query(\"select is_admin from users\")\n\tlg.CheckError(err)\n\tfmt.Println(\"bools =\", bools)\n\tfmt.Println()\n\n\ttimes := []time.Time{}\n\terr = korm.To(\u0026times).Query(\"select created_at from users\")\n\tlg.CheckError(err)\n\tfmt.Println(\"times =\", times)\n\n\tserver.Run()\n}\n\n// OUTPUT\n// chan students: {1 student-1 1 {1 Math []}}\n// chan students: {2 student-2 2 {2 French []}}\n// chan students: {3 student-3 1 {1 Math []}}\n// chan students: {4 student-4 2 {2 French []}}\n\n// class: {1 Math [{1 student-1 0 {0  []}} {3 student-3 0 {0  []}}]}\n// class: {2 French [{2 student-2 0 {0  []}} {4 student-4 0 {0  []}}]}\n\n// student: \u0026{1 student-1 1 {1 Math []}}\n// student: \u0026{2 student-2 2 {2 French []}}\n// student: \u0026{3 student-3 1 {1 Math []}}\n// student: \u0026{4 student-4 2 {2 French []}}\n\n// 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]]\n\n// names = [student-1 student-2 student-3 student-4]\n\n// ids = [1 2 3 4]\n\n// bools = [true]\n\n// times = [2023-04-30 19:19:32 +0200 CEST]\n```\n\n\n### AutoMigrate \n\n[Available Tags](#available-tags-by-struct-field-type) (SQL)\n\nSQL:\n```go\nkorm.AutoMigrate[T comparable](tableName string, dbName ...string) error \n\nerr := korm.AutoMigrate[User](\"users\")\nerr := korm.AutoMigrate[Bookmark ](\"bookmarks\")\n\ntype User struct {\n\tId        int       `korm:\"pk\"` // AUTO Increment ID primary key\n\tUuid      string    `korm:\"size:40\"` // VARCHAR(50)\n\tEmail     string    `korm:\"size:50;iunique\"` // insensitive unique\n\tPassword  string    `korm:\"size:150\"` // VARCHAR(150)\n\tIsAdmin   bool      `korm:\"default:false\"` // DEFAULT 0\n\tImage     string    `korm:\"size:100;default:''\"`\n\tCreatedAt time.Time `korm:\"now\"` // auto now\n    Ignored   string    `korm:\"-\"`\n}\n\ntype Bookmark struct {\n\tId      uint   `korm:\"pk\"`\n\tUserId  int    `korm:\"fk:users.id:cascade:setnull\"` // options cascade,donothing/noaction, setnull/null, setdefault/default\n\tIsDone\tbool   \n\tToCheck string `korm:\"size:50; notnull; check: len(to_check) \u003e 2 AND len(to_check) \u003c 10; check: is_done=true\"`  // column type will be VARCHAR(50)\n\tContent string `korm:\"text\"` // column type will be TEXT not VARCHAR\n\tUpdatedAt time.Time `korm:\"update\"` // will update when model updated, handled by triggers for sqlite, cockroach and postgres, and on migration for mysql\n\tCreatedAt time.Time `korm:\"now\"` // now is default to current timestamp and of type TEXT for sqlite\n}\n\nall, _ := korm.Model[User]()\n                   .Where(\"id = ?\",id) \n                   .Select(\"item1\",\"item2\")\n                   .OrderBy(\"created\")\n\t\t\t\t   .Limit(8)\n\t\t\t\t   .Page(2)\n                   .All()\n```\n\n\n### API\n```go\nkorm.New(dbType Dialect, dbName string, dbDriver driver.Driver, dbDSN ...string) error\nkorm.LogQueries()\nkorm.GetConnection(dbName ...string) *sql.DB\nkorm.To[T any](dest *[]T, nestedSlice ...bool) *Selector[T] // scan query to any type slice, even channels and slices with nested structs and joins\n(sl *Selector[T]) Ctx(ct context.Context) *Selector[T]\n(sl *Selector[T]) Query(statement string, args ...any) error\n(sl *Selector[T]) Named(statement string, args map[string]any, unsafe ...bool) error\nkorm.WithBus(...opts) *ksbus.Server // Usage: WithBus(...opts) or share an existing one\nkorm.WithDashboard(address, ...opts) *ksbus.Server\nkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\nkorm.WithDocs(generateJsonDocs bool, outJsonDocs string, handlerMiddlewares ...func(handler kmux.Handler) kmux.Handler) *ksbus.Server\nkorm.WithEmbededDocs(embeded embed.FS, embededDirPath string, handlerMiddlewares ...func(handler kmux.Handler) kmux.Handler) *ksbus.Server\nkorm.WithMetrics(httpHandler http.Handler) *ksbus.Server\nkorm.WithPprof(path ...string) *ksbus.Server\nkorm.Transaction(dbName ...string) (*sql.Tx, error)\nkorm.Exec(dbName, query string, args ...any) error\nkorm.ExecContext(ctx context.Context, dbName, query string, args ...any) error\nkorm.ExecNamed(query string, args map[string]any, dbName ...string) error\nkorm.ExecContextNamed(ctx context.Context, query string, args map[string]any, dbName ...string) error\nkorm.BeforeServersData(fn func(data any, conn *ws.Conn))\nkorm.BeforeDataWS(fn func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) bool)\nkorm.GetAllTables(dbName ...string) []string\nkorm.GetAllColumnsTypes(table string, dbName ...string) map[string]string\nkorm.GetMemoryTable(tbName string, dbName ...string) (TableEntity, error)\nkorm.GetMemoryTables(dbName ...string) ([]TableEntity, error)\nkorm.GetMemoryDatabases() []DatabaseEntity\nkorm.GetMemoryDatabase(dbName string) (*DatabaseEntity, error)\nkorm.Shutdown(databasesName ...string) error\nkorm.FlushCache()\nkorm.DisableCache() \nkorm.ManyToMany(table1, table2 string, dbName ...string) error // add table relation m2m \n```\n#### Builder `Struct`:\n```go\nkorm.Exec(dbName, query string, args ...any) error\nkorm.Transaction(dbName ...string) (*sql.Tx, error)\n// Model is a starter for Buider\nfunc Model[T comparable](tableName ...string) *BuilderS[T]\n// Database allow to choose database to execute query on\nfunc (b *BuilderS[T]) Database(dbName string) *BuilderS[T]\n// Insert insert a row into a table and return inserted PK\nfunc (b *BuilderS[T]) Insert(model *T) (int, error)\n// InsertR add row to a table using input struct, and return the inserted row\nfunc (b *BuilderS[T]) InsertR(model *T) (T, error)\n// BulkInsert insert many row at the same time in one query\nfunc (b *BuilderS[T]) BulkInsert(models ...*T) ([]int, error)\n// 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 them\nfunc (b *BuilderS[T]) AddRelated(relatedTable string, whereRelatedTable string, whereRelatedArgs ...any) (int, error)\n// DeleteRelated delete a relations many to many\nfunc (b *BuilderS[T]) DeleteRelated(relatedTable string, whereRelatedTable string, whereRelatedArgs ...any) (int, error)\n// GetRelated used for many to many to get related classes to a student or related students to a class\nfunc (b *BuilderS[T]) GetRelated(relatedTable string, dest any) error\n// JoinRelated same as get, but it join data\nfunc (b *BuilderS[T]) JoinRelated(relatedTable string, dest any) error\n// Set used to update, Set(\"email,is_admin\",\"example@mail.com\",true) or Set(\"email = ? AND is_admin = ?\",\"example@mail.com\",true)\nfunc (b *BuilderS[T]) Set(query string, args ...any) (int, error)\n// Delete data from database, can be multiple, depending on the where, return affected rows(Not every database or database driver may support affected rows)\nfunc (b *BuilderS[T]) Delete() (int, error)\n// Drop drop table from db\nfunc (b *BuilderS[T]) Drop() (int, error)\n// Select usage: Select(\"email\",\"password\")\nfunc (b *BuilderS[T]) Select(columns ...string) *BuilderS[T]\n// Where can be like : Where(\"id \u003e ?\",1) or Where(\"id\",1) = Where(\"id = ?\",1)\nfunc (b *BuilderS[T]) Where(query string, args ...any) *BuilderS[T]\n// Limit set limit\nfunc (b *BuilderS[T]) Limit(limit int) *BuilderS[T]\n// Context allow to query or execute using ctx\nfunc (b *BuilderS[T]) Context(ctx context.Context) *BuilderS[T]\n// Page return paginated elements using Limit for specific page\nfunc (b *BuilderS[T]) Page(pageNumber int) *BuilderS[T]\n// OrderBy can be used like: OrderBy(\"-id\",\"-email\") OrderBy(\"id\",\"-email\") OrderBy(\"+id\",\"email\")\nfunc (b *BuilderS[T]) OrderBy(fields ...string) *BuilderS[T]\n// Debug print prepared statement and values for this operation\nfunc (b *BuilderS[T]) Debug() *BuilderS[T]\n// All get all data\nfunc (b *BuilderS[T]) All() ([]T, error)\n// One get single row\nfunc (b *BuilderS[T]) One() (T, error)\n\nExamples:\nkorm.Model[models.User]().Select(\"email\",\"uuid\").OrderBy(\"-id\").Limit(PAGINATION_PER).Page(1).All()\n\n// INSERT\nuuid,_ := korm.GenerateUUID()\nhashedPass,_ := argon.Hash(password)\nkorm.Model[models.User]().Insert(\u0026models.User{\n\tUuid: uuid,\n\tEmail: \"test@example.com\",\n\tPassword: hashedPass,\n\tIsAdmin: false,\n\tImage: \"\",\n\tCreatedAt: time.Now(),\n})\n\n//if using more than one db\nkorm.Database[models.User](\"dbNameHere\").Where(\"id = ? AND email = ?\",1,\"test@example.com\").All() \n\n// where\nkorm.Model[models.User]().Where(\"id = ? AND email = ?\",1,\"test@example.com\").One() \n\n// delete\nkorm.Model[models.User]().Where(\"id = ? AND email = ?\",1,\"test@example.com\").Delete()\n\n// drop table\nkorm.Model[models.User]().Drop()\n\n// update\nkorm.Model[models.User]().Where(\"id = ?\",1).Set(\"email = ?\",\"new@example.com\")\n```\n#### Builder `map[string]any`:\n```go\n// BuilderM is query builder map string any\ntype BuilderM struct\n// Table is a starter for BuiderM\nfunc Table(tableName string) *BuilderM\n// Database allow to choose database to execute query on\nfunc (b *BuilderM) Database(dbName string) *BuilderM\n// Select select table columns to return\nfunc (b *BuilderM) Select(columns ...string) *BuilderM\n// Where can be like: Where(\"id \u003e ?\",1) or Where(\"id\",1) = Where(\"id = ?\",1)\nfunc (b *BuilderM) Where(query string, args ...any) *BuilderM\n// Limit set limit\nfunc (b *BuilderM) Limit(limit int) *BuilderM\n// Page return paginated elements using Limit for specific page\nfunc (b *BuilderM) Page(pageNumber int) *BuilderM\n// OrderBy can be used like: OrderBy(\"-id\",\"-email\") OrderBy(\"id\",\"-email\") OrderBy(\"+id\",\"email\")\nfunc (b *BuilderM) OrderBy(fields ...string) *BuilderM\n// Context allow to query or execute using ctx\nfunc (b *BuilderM) Context(ctx context.Context) *BuilderM\n// Debug print prepared statement and values for this operation\nfunc (b *BuilderM) Debug() *BuilderM\n// All get all data\nfunc (b *BuilderM) All() ([]map[string]any, error)\n// One get single row\nfunc (b *BuilderM) One() (map[string]any, error)\n// Insert add row to a table using input map, and return PK of the inserted row\nfunc (b *BuilderM) Insert(rowData map[string]any) (int, error)\n// InsertR add row to a table using input map, and return the inserted row\nfunc (b *BuilderM) InsertR(rowData map[string]any) (map[string]any, error)\n// BulkInsert insert many row at the same time in one query\nfunc (b *BuilderM) BulkInsert(rowsData ...map[string]any) ([]int, error)\n// Set used to update, Set(\"email,is_admin\",\"example@mail.com\",true) or Set(\"email = ? AND is_admin = ?\",\"example@mail.com\",true)\nfunc (b *BuilderM) Set(query string, args ...any) (int, error)\n// Delete data from database, can be multiple, depending on the where, return affected rows(Not every database or database driver may support affected rows)\nfunc (b *BuilderM) Delete() (int, error)\n// Drop drop table from db\nfunc (b *BuilderM) Drop() (int, error)\n// 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 them\nfunc (b *BuilderM) AddRelated(relatedTable string, whereRelatedTable string, whereRelatedArgs ...any) (int, error)\n// GetRelated used for many to many to get related classes to a student or related students to a class\nfunc (b *BuilderM) GetRelated(relatedTable string, dest *[]map[string]any) error\n// JoinRelated same as get, but it join data\nfunc (b *BuilderM) JoinRelated(relatedTable string, dest *[]map[string]any) error\n// DeleteRelated delete a relations many to many\nfunc (b *BuilderM) DeleteRelated(relatedTable string, whereRelatedTable string, whereRelatedArgs ...any) (int, error)\n\n\nExamples:\n\nsliceMapStringAny,err := korm.Table(\"users\")\n\t\t\t\t\t\t\t.Select(\"email\",\"uuid\")\n\t\t\t\t\t\t\t.OrderBy(\"-id\")\n\t\t\t\t\t\t\t.Limit(PAGINATION_PER)\n\t\t\t\t\t\t\t.Page(1)\n\t\t\t\t\t\t\t.All()\n\n// INSERT\nuuid,_ := korm.GenerateUUID()\nhashedPass,_ := argon.Hash(\"password\") // github.com/kamalshkeir/argon\n\nkorm.Table(\"users\").Insert(map[string]any{\n\t\"uuid\":uuid,\n\t\"email\":\"test@example.com\",\n\t ...\n})\n\n//if using more than one db\nkorm.Database(\"dbNameHere\").Table(\"tableName\").Where(\"id = ? AND email = ?\",1,\"test@example.com\").All() \n\n// where\nWhere(\"id = ? AND email = ?\",1,\"test@example.com\") // this work\nWhere(\"id,email\",1,\"test@example.com\") // and this work\n\nkorm.Table(\"tableName\").Where(\"id = ? AND email = ?\",1,\"test@example.com\").One() \n\n// delete\nkorm.Table(\"tableName\").Where(\"id = ? AND email = ?\",1,\"test@example.com\").Delete() \n\n// drop table\nkorm.Table(\"tableName\").Drop()\n\n// update\nkorm.Table(\"tableName\").Where(\"id = ?\",1).Set(\"email = ?\",\"new@example.com\") \nkorm.Table(\"tableName\").Where(\"id\",1).Set(\"email\",\"new@example.com\") \n```\n\n### Dashboard defaults you can set\n```go\nkorm.PaginationPer      = 10\nkorm.DocsUrl           = \"docs\"\nkorm.EmbededDashboard   = false\nkorm.MediaDir           = \"media\"\nkorm.AssetsDir          = \"assets\"\nkorm.StaticDir          = path.Join(AssetsDir, \"/\", \"static\")\nkorm.TemplatesDir       = path.Join(AssetsDir, \"/\", \"templates\")\nkorm.RepoUser           = \"kamalshkeir\"\nkorm.RepoName           = \"korm-dash\"\nkorm.adminPathNameGroup = \"/admin\" // korm.SetAdminPath(\"/another\")\n// so you can create a custom dashboard, upload it to your repos and change like like above korm.RepoUser and korm.RepoName\n```\n\n### Example With Dashboard (you don't need korm.WithBus with it, because WithDashboard already call it and return the server bus for you)\n\n```go\npackage main\n\nimport (\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/ksmux\"\n\t\"github.com/kamalshkeir/korm\"\n\t\"github.com/kamalshkeir/sqlitedriver\"\n)\n\nfunc main() {\n\terr := korm.New(korm.SQLITE, \"db\", sqlitedriver.Use())\n\tlg.CheckError(err)\n\n\n\n\tserverBus := korm.WithDashboard(\"localhost:9313\")\n\tkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\t// you can overwrite Admin and Auth middleware used for dashboard (dash_middlewares.go) \n\t//korm.Auth = func(handler ksmux.Handler) ksmux.Handler {}\n\t//korm.Admin = func(handler ksmux.Handler) ksmux.Handler {}\n\n\t// and also all handlers (dash_views.go)\n\t//korm.LoginView = func(c *ksmux.Context) {\n\t//\tc.Html(\"admin/new_admin_login.html\", nil)\n\t//}\n\n\t// add extra static directory if you want\n\t//serverBus.App.LocalStatics(\"assets/mystatic\",\"myassets\") // will be available at /myassets/*\n\t//serverBus.App.LocalTemplates(\"assets/templates\") // will make them available to use with c.Html\n\n\t// serve HTML \n\t// serverBus.App.Get(\"/\",func(c *ksmux.Context) {\n\t// \tc.Html(\"index.html\", map[string]any{\n\t// \t\t\"data\": data,\n\t// \t})\n\t// })\n\tserverBus.Run()\n\t// OR run https if you have certificates\n\tserverBus.RunTLS(cert string, certKey string)\n\n\t// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more infos\n\tserverBus.RunAutoTLS(subDomains ...string)\n}\n```\nThen create admin user to connect to the dashboard\n```sh\ngo run main.go shell\n\ncreatesuperuser\n```\n\nThen you can visit `/admin`\n\n\n### Auth middleware example\n```go\nfunc main() {\n\terr := korm.New(korm.SQLITE, \"db\", sqlitedriver.Use())\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n\tdefer korm.Shutdown()\n\t\n\tsrv := korm.WithDashboard(\"localhost:9313\")\n\tkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\tlg.Printfs(\"mgrunning on http://localhost:9313\\n\")\n\tapp := srv.App\n\n\tapp.Get(\"/\", korm.Auth(func(c *ksmux.Context) { // work with korm.Admin also\n\t\t// c.IsAuthenticated also return bool\n\t\tif v, ok := c.User(); ok {\n\t\t\tc.Json(map[string]any{\n\t\t\t\t\"msg\": \"Authenticated\",\n\t\t\t\t\"v\":   v.(korm.User).Email,\n\t\t\t})\n\t\t} else {\n\t\t\tc.Json(map[string]any{\n\t\t\t\t\"error\": \"not auth\",\n\t\t\t})\n\t\t}\n\t}))\n\n\tsrv.Run()\n}\n```\n\n### Example Admin Auth User and IsAuthenticated\n```html\n{{define \"admin_nav\"}}\n\u003cheader id=\"admin-header\"\u003e\n  \u003cnav\u003e\n    \u003ca href=\"/\"\u003e\n      \u003ch1\u003eKORM\u003c/h1\u003e\n    \u003c/a\u003e \n    \n    \u003cul\u003e\n        \u003cli\u003e\n          \u003ca {{if eq .Request.URL.Path \"/\" }}class=\"active\"{{end}} href=\"/\"\u003eHome\u003c/a\u003e\n        \u003c/li\u003e\n\n        \u003cli\u003e\n          \u003ca {{if contains .Request.URL.Path .admin_path }}class=\"active\"{{end}} href=\"{{.admin_path}}\"\u003eAdmin\u003c/a\u003e\n        \u003c/li\u003e\n\n        {{if .IsAuthenticated}}\n            \u003cli\u003e\n              \u003ca href=\"{{.admin_path}}/logout\"\u003eLogout\u003c/a\u003e\n            \u003c/li\u003e\n            \n            {{if .User.Email}}\n              \u003cli\u003e\n                \u003cspan\u003eHello {{.User.Email}}\u003c/span\u003e\n              \u003c/li\u003e\n            {{end}}\n        {{end}}\n    \u003c/ul\u003e\n  \u003c/nav\u003e\n\u003c/header\u003e\n{{end}}\n\n\n```\n\n### Admin middlewares\n\n```go\n// dash_middlewares.go\npackage korm\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/kamalshkeir/aes\"\n\t\"github.com/kamalshkeir/ksmux\"\n)\n\nvar Auth = func(handler ksmux.Handler) ksmux.Handler {\n\treturn func(c *ksmux.Context) {\n\t\tsession, err := c.GetCookie(\"session\")\n\t\tif err != nil || session == \"\" {\n\t\t\t// NOT AUTHENTICATED\n\t\t\tc.DeleteCookie(\"session\")\n\t\t\thandler(c)\n\t\t\treturn\n\t\t}\n\t\tsession, err = aes.Decrypt(session)\n\t\tif err != nil {\n\t\t\thandler(c)\n\t\t\treturn\n\t\t}\n\t\t// Check session\n\t\tuser, err := Model[User]().Where(\"uuid = ?\", session).One()\n\t\tif err != nil {\n\t\t\t// session fail\n\t\t\thandler(c)\n\t\t\treturn\n\t\t}\n\n\t\t// AUTHENTICATED AND FOUND IN DB\n\t\tc.SetKey(\"korm-user\", user)\n\t\thandler(c)\n\t}\n}\n\nvar Admin = func(handler ksmux.Handler) ksmux.Handler {\n\treturn func(c *ksmux.Context) {\n\t\tsession, err := c.GetCookie(\"session\")\n\t\tif err != nil || session == \"\" {\n\t\t\t// NOT AUTHENTICATED\n\t\t\tc.DeleteCookie(\"session\")\n\t\t\tc.Status(http.StatusTemporaryRedirect).Redirect(adminPathNameGroup + \"/login\")\n\t\t\treturn\n\t\t}\n\t\tsession, err = aes.Decrypt(session)\n\t\tif err != nil {\n\t\t\tc.Status(http.StatusTemporaryRedirect).Redirect(adminPathNameGroup + \"/login\")\n\t\t\treturn\n\t\t}\n\t\tuser, err := Model[User]().Where(\"uuid = ?\", session).One()\n\n\t\tif err != nil {\n\t\t\t// AUTHENTICATED BUT NOT FOUND IN DB\n\t\t\tc.Status(http.StatusTemporaryRedirect).Redirect(adminPathNameGroup + \"/login\")\n\t\t\treturn\n\t\t}\n\n\t\t// Not admin\n\t\tif !user.IsAdmin {\n\t\t\tc.Status(403).Text(\"Middleware : Not allowed to access this page\")\n\t\t\treturn\n\t\t}\n\t\tc.SetKey(\"korm-user\", user)\n\t\thandler(c)\n\t}\n}\n\nvar BasicAuth = func(handler ksmux.Handler) ksmux.Handler {\n\treturn ksmux.BasicAuth(handler, BASIC_AUTH_USER, BASIC_AUTH_PASS)\n}\n\n```\n\n### Example With Bus between 2 KORM\nKORM 1:\n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/ksmux\"\n\t\"github.com/kamalshkeir/ksmux/ws\"\n\t\"github.com/kamalshkeir/korm\"\n\t\"github.com/kamalshkeir/ksbus\"\n\t\"github.com/kamalshkeir/sqlitedriver\"\n)\n\nfunc main() {\n\terr := korm.New(korm.SQLITE,\"db1\", sqlitedriver.Use())\n\tif lg.CheckError(err) {return}\n\n\tkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\tserverBus := korm.WithBus(ksbus.ServerOpts{\n\t\tID              string\n\t\tAddress         string\n\t\tPath            string\n\t\tOnWsClose       func(connID string)\n\t\tOnDataWS        func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) error\n\t\tOnServerData    func(data any, conn *ws.Conn)\n\t\tOnId            func(data map[string]any)\n\t\tOnUpgradeWs     func(r *http.Request) bool\n\t\tWithOtherRouter *ksmux.Router\n\t\tWithOtherBus    *Bus\n\t})\n\t// handler authentication\t\n\tkorm.BeforeDataWS(func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) bool {\n\t\tlg.Info(\"handle authentication here\")\n\t\treturn true\n\t})\n\t// handler data from other KORM\n\tkorm.BeforeServersData(func(data any, conn *ws.Conn) {\n\t\tlg.Info(\"recv orm:\", \"data\", data)\n\t})\n\n\t// built in router to the bus, check it at https://github.com/kamalshkeir/ksbus\n\tserverBus.App.Get(\"/\",func(c *ksmux.Context) {\n\t\tserverBus.SendToServer(\"localhost:9314\",map[string]any{\n\t\t\t\"msg\":\"hello from server 1\",\n\t\t})\n\t\tc.Text(\"ok\")\n\t})\n\n\t\n\tserverBus.Run(\"localhost:9313\")\n\t// OR run https if you have certificates\n\tserverBus.RunTLS(addr string, cert string, certKey string)\n\t// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more details\n\tserverBus.RunAutoTLS(domainName string, subDomains ...string)\n}\n```\nKORM 2:\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/ksmux\"\n\t\"github.com/kamalshkeir/ksmux/ws\"\n\t\"github.com/kamalshkeir/korm\"\n\t\"github.com/kamalshkeir/sqlitedriver\"\n)\n\nfunc main() {\n\terr := korm.New(korm.SQLITE,\"db2\",sqlitedriver.Use())\n\tif lg.CheckError(err) {return}\n\n\tkorm.WithShell() // if dashboard used, this line should be after it\n\tserverBus := korm.WithBus(ksbus.ServerOpts{\n\t\tID              string\n\t\tAddress         string\n\t\tPath            string\n\t\tOnWsClose       func(connID string)\n\t\tOnDataWS        func(data map[string]any, conn *ws.Conn, originalRequest *http.Request) error\n\t\tOnServerData    func(data any, conn *ws.Conn)\n\t\tOnId            func(data map[string]any)\n\t\tOnUpgradeWs     func(r *http.Request) bool\n\t\tWithOtherRouter *ksmux.Router\n\t\tWithOtherBus    *Bus\n\t})\n\n\tkorm.BeforeServersData(func(data any, conn *ws.Conn) {\n        lg.Info(\"recv\", \"data\", data)\n\t})\n\n\t// built in router to the bus, check it at https://github.com/kamalshkeir/ksbus\n\tserverBus.App.GET(\"/\",func(c *ksmux.Context) {\n\t\tserverBus.SendToServer(\"localhost:9314\",map[string]any{\n\t\t\t\"msg\":\"hello from server 2\",\n\t\t})\n\t\tc.Status(200).Text(\"ok\")\n\t})\n\n\n    // Run Server Bus\n\tserverBus.Run(\"localhost:9314\")\n\n\t// OR run https if you have certificates\n\tserverBus.RunTLS(addr string, cert string, certKey string)\n\n\t// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more infos\n\tserverBus.RunAutoTLS(domainName string, subDomains ...string)\n}\n```\n\n### Example generated tag\n```go\n// generated example using concatination and length\ntype TestUser struct {\n\tId        *uint   `korm:\"pk\"`\n\tUuid      string  `korm:\"size:40;iunique\"`\n\tEmail     *string `korm:\"size:100;iunique\"`\n\tGen       string  `korm:\"size:250;generated: concat(uuid,'working',len(password))\"`\n\tPassword  string\n\tIsAdmin   *bool\n\tCreatedAt time.Time `korm:\"now\"`\n\tUpdatedAt time.Time `korm:\"update\"`\n}\n\nfunc TestGeneratedAs(t *testing.T) {\n\tu, err := Model[TestUser]().Limit(3).All()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif len(u) != 3 {\n\t\tt.Error(\"len not 20\")\n\t}\n\tif u[0].Gen != u[0].Uuid+\"working\"+fmt.Sprintf(\"%d\", len(u[0].Password)) {\n\t\tt.Error(\"generated not working:\", u[0].Gen)\n\t}\n}\n```\n\n\n### Example concat and len from korm_test.go\n```go\n// Where example\nfunc TestConcatANDLen(t *testing.T) {\n\tgroupes, err := Model[Group]().Where(\"name = concat(?,'min') AND len(name) = ?\", \"ad\", 5).Debug().All()\n\t// translated to select * from groups WHERE name = 'ad' || 'min'  AND  length(name) = 5 (sqlite)\n\t// translated to select * from groups WHERE name = concat('ad','min')  AND  char_length(name) = 5 (postgres, mysql)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif len(groupes) != 1 || groupes[0].Name != \"admin\" {\n\t\tt.Error(\"len(groupes) != 1 , got: \", groupes)\n\t}\n}\n```\n\n\n\n\n## Router/Mux \nLearn more about [Ksmux](https://github.com/kamalshkeir/ksmux)\n```go\nfunc main() {\n\terr := korm.New(korm.SQLITE, \"db\", sqlitedriver.Use())\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tserverBus := korm.WithDashboard(\"localhost:9313\")\n\tkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\tmux := serverBus.App\n\t// add global middlewares\n\tmux.Use((midws ...func(http.Handler) http.Handler))\n\t...\n}\n\n```\n\n### Pprof\n```go\n\nserverBus := korm.WithDashboard(\"localhost:9313\")\n// or srv := korm.WithBus()\nserverBus.WithPprof(path ...string) // path is 'debug' by default\n\nwill enable:\n\t- /debug/pprof\n\t- /debug/profile\n\t- /debug/heap\n\t- /debug/trace\n```\nTo execute profile cpu: `go tool pprof -http=\":8000\" pprofbin http://localhost:9313/debug/profile?seconds=18`\nTo execute profile memory: `go tool pprof -http=\":8000\" pprofbin http://localhost:9313/debug/heap?seconds=18`\nTo execute generate trace: go to endpoint `http://localhost:9313/debug/trace?seconds=18` from browser , this will download the trace of 18 seconds\nThen to see the trace : `go tool trace path/to/trace`\n\n### Metrics Prometheus\n```go\n// or srv := korm.WithBus()\n//srv.WithMetrics(httpHandler http.Handler, path ...string) path default to 'metrics'\nsrv.WithMetrics(promhttp.Handler())\n\nwill enable:\n\t- /metrics\n```\n\n### Logs middleware\n```go\n// or srv := korm.WithBus()\n//srv.WithMetrics(httpHandler http.Handler, path ...string) path default to 'metrics'\nsrv.App.Use(ksmux.Logs()) // it take an optional callback executed on each request if you want to add log to a file or send\nsrv.App.Use(ksmux.Logs(func(method, path, remote string, status int, took time.Duration) {\n\t// save somewhere\n}))\n\nwill enable:\n\t- /metrics\n```\n\n# Hooks\n```go\nkorm.OnInsert(func(database, table string, data map[string]any) error {\n\tfmt.Println(\"inserting into\", database, table, data)\n\t// if error returned, it will not insert\n\treturn nil\n})\n\nkorm.OnSet(func(database, table string, data map[string]any) error {\n\tfmt.Println(\"set into\", database, table, data)\n\treturn nil\n})\n\nkorm.OnDelete(func(database, table, query string, args ...any) error {})\n\nkorm.OnDrop(func(database, table string) error {})\n```\n\n\n## Python bus client example\n```sh\npip install ksbus==1.1.0\n# if it doesn't work , execute it again \n```\n```py\nfrom ksbus import Bus\n\n\n# onOpen callback that let you know when connection is ready, it take the bus as param\ndef OnOpen(bus):\n    print(\"connected\")\n    # bus.autorestart=True\n    # Publish publish to topic\n    bus.Publish(\"top\", {\n        \"data\": \"hello from python\"\n    })\n    # Subscribe, it also return the subscription\n    bus.Subscribe(\"python\", pythonTopicHandler)\n    # SendToNamed publish to named topic\n    bus.SendToNamed(\"top:srv\", {\n        \"data\": \"hello again from python\"\n    })\n    # bus.Unsubscribe(\"python\")\n    print(\"finish everything\")\n\n\n# pythonTopicHandler handle topic 'python'\ndef pythonTopicHandler(data, subs):\n    print(\"recv on topic python:\", data)\n    # Unsubscribe\n    #subs.Unsubscribe()\n\nif __name__ == \"__main__\":\n    Bus(\"localhost:9313\", onOpen=OnOpen) # blocking\n    print(\"prorgram exited\")\n```\n\n# ManyToMany Relationships Example\n\n```go\ntype Class struct {\n\tId          uint   `korm:\"pk\"`\n\tName        string `korm:\"size:100\"`\n\tIsAvailable bool\n\tCreatedAt   time.Time `korm:\"now\"`\n}\n\ntype Student struct {\n\tId        uint      `korm:\"pk\"`\n\tName      string    `korm:\"size:100\"`\n\tCreatedAt time.Time `korm:\"now\"`\n}\n\n// migrate\nfunc migrate() {\n\terr := korm.AutoMigrate[Class](\"classes\")\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n\terr = korm.AutoMigrate[Student](\"students\")\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n\terr = korm.ManyToMany(\"classes\", \"students\")\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n}\n\n// korm.ManyToMany create relation table named m2m_classes_students\n\n// then you can use it like so to get related data\n\n// get related to map to struct\nstd := []Student{}\nerr = korm.Model[Class]().Where(\"name = ?\", \"Math\").Select(\"name\").OrderBy(\"-name\").Limit(1).GetRelated(\"students\", \u0026std)\n\n// get related to map\nstd := []map[string]any{}\nerr = korm.Table(\"classes\").Where(\"name = ?\", \"Math\").Select(\"name\").OrderBy(\"-name\").Limit(1).GetRelated(\"students\", \u0026std)\n\n// join related to map\nstd := []map[string]any{}\nerr = korm.Table(\"classes\").Where(\"name = ?\", \"Math\").JoinRelated(\"students\", \u0026std)\n\n// join related to strcu\ncu := []JoinClassUser{}\nerr = korm.Model[Class]().Where(\"name = ?\", \"Math\").JoinRelated(\"students\", \u0026cu)\n\n// to add relation\n_, err = korm.Model[Class]().AddRelated(\"students\", \"name = ?\", \"hisName\")\n_, err = korm.Model[Student]().AddRelated(\"classes\", \"name = ?\", \"French\")\n_, err = korm.Table(\"students\").AddRelated(\"classes\", \"name = ?\", \"French\")\n\n// delete relation\n_, err = korm.Model[Class]().Where(\"name = ?\", \"Math\").DeleteRelated(\"students\", \"name = ?\", \"hisName\")\n_, err = korm.Table(\"classes\").Where(\"name = ?\", \"Math\").DeleteRelated(\"students\", \"name = ?\", \"hisName\")\n\n```\n\n\n### Swagger documentation\n\n\u003cimg src=\"docs.png\"\u003e\n\u003cdiv style=\"display:flex;justify-content:center;align-items:center;gap:20px;margin:20px 0\"\u003e\n\u003ca href=\"https://www.youtube.com/watch?v=RupARTkPzf4\"\u003e\n\t\u003cimg src=\"https://user-images.githubusercontent.com/54605903/217871012-9c5dc1da-25bd-47d5-ac9e-c3acee7178d5.svg\" width=\"auto\" height=\"50px\"\u003e\n\u003c/a\u003e\n\u003c/div\u003e\n\n\n```go\nkorm.DocsUrl = \"docs\" // default endpoint '/docs' \nkorm.BASIC_AUTH_USER = \"test\"\nkorm.BASIC_AUTH_PASS = \"pass\"\nkorm.WithDocs(generate, dirPath, korm.BasicAuth)\nkorm.WithDocs(true, \"\", korm.BasicAuth) // dirPath default to 'assets/static/docs'\nkorm.WithEmbededDocs(embeded embed.FS, dirPath, korm.BasicAuth)\n// dirPath default to 'assets/static/docs' if empty\n```\n\n\n### Interactive shell\n```shell\nCommands :  \n[databases, use, tables, columns, migrate, createsuperuser, createuser, query, getall, get, drop, delete, clear/cls, q/quit/exit, help/commands]\n  'databases':\n\t  list all connected databases\n\n  'use':\n\t  use a specific database\n\n  'tables':\n\t  list all tables in database\n\n  'columns':\n\t  list all columns of a table\n\t  (accept but not required extra param like : 'columns' or 'columns users')\n\n  'migrate':\n\t  migrate or execute sql file\n\n  'createsuperuser': (only with dashboard)\n\t  create a admin user\n  \n  'createuser': (only with dashboard)\n\t  create a regular user\n\n  'query': \n\t  query data from database \n\t  (accept but not required extra param like : 'query' or 'query select * from users where ...')\n\n\n  'getall': \n\t  get all rows given a table name\n\t  (accept but not required extra param like : 'getall' or 'getall users')\n\n  'get':\n\t  get single row \n\t  (accept but not required extra param like : 'get' or 'get users email like \"%anything%\"')\n\n  'delete':\n\t  delete rows where field equal_to\n\t  (accept but not required extra param like : 'delete' or 'delete users email=\"email@example.com\"')\n\n  'drop':\n\t  drop a table given table name\n\t  (accept but not required extra param like : 'drop' or 'drop users')\n\n  'clear / cls':\n\t  clear shell console\n\n  'q / quit / exit / q!':\n\t  exit shell\n\n  'help':\n\t  show this help message\n```\n\n\n# Example, not required, Load config from env directly to struct using Kenv\n```go\nimport \"github.com/kamalshkeir/kenv\"\n\ntype EmbedS struct {\n\tStatic    bool `kenv:\"EMBED_STATIC|false\"`\n\tTemplates bool `kenv:\"EMBED_TEMPLATES|false\"`\n}\n\ntype GlobalConfig struct {\n\tHost       string `kenv:\"HOST|localhost\"` // DEFAULT to 'localhost': if HOST not found in env\n\tPort       string `kenv:\"PORT|9313\"`\n\tEmbed \t   EmbedS\n\tDb struct {\n\t\tName     string `kenv:\"DB_NAME|db\"` // NOT REQUIRED: if DB_NAME not found, defaulted to 'db'\n\t\tType     string `kenv:\"DB_TYPE\"` // REEQUIRED: this env var is required, you will have error if empty\n\t\tDSN      string `kenv:\"DB_DSN|\"` // NOT REQUIRED: if DB_DSN not found it's not required, it's ok to stay empty\n\t}\n\tSmtp struct {\n\t\tEmail string `kenv:\"SMTP_EMAIL|\"`\n\t\tPass  string `kenv:\"SMTP_PASS|\"`\n\t\tHost  string `kenv:\"SMTP_HOST|\"`\n\t\tPort  string `kenv:\"SMTP_PORT|\"`\n\t}\n\tProfiler   bool   `kenv:\"PROFILER|false\"`\n\tDocs       bool   `kenv:\"DOCS|false\"`\n\tLogs       bool   `kenv:\"LOGS|false\"`\n\tMonitoring bool   `kenv:\"MONITORING|false\"`\n}\n\n\nkenv.Load(\".env\") // load env file\n\n// Fill struct from env loaded before:\nConfig := \u0026GlobalConfig{}\nerr := kenv.Fill(Config) // fill struct with env vars loaded before\n```\n\n# Example nested or embeded structs\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kamalshkeir/lg\"\n\t\"github.com/kamalshkeir/korm\"\n\t\"github.com/kamalshkeir/sqlitedriver\"\n)\n\ntype Class struct {\n\tId       uint `korm:\"pk\"`\n\tName     string\n\tStudents []Student\n}\n\ntype Student struct {\n\tId      uint `korm:\"pk\"`\n\tName    string\n\tClass   uint `korm:\"fk:classes.id:cascade:cascade\"`\n\tClasses Class\n}\n\nfunc main() {\n\terr := korm.New(korm.SQLITE, \"db\", sqlitedriver.Use())\n\tif lg.CheckError(err) {\n\t\treturn\n\t}\n\tdefer korm.Shutdown()\n\n\tserver := korm.WithDashboard(\"localhost:9313\")\n\tkorm.WithShell() // ⚠️ if dashboard used, korm.WithShell should be after WithDashboard\n\n\terr = korm.AutoMigrate[Class](\"classes\")\n\tlg.CheckError(err)\n\n\terr = korm.AutoMigrate[Student](\"students\")\n\tlg.CheckError(err)\n\n\t// go run main.go shell to createsuperuser\n\t// connect to admin and create some data to query\n\n\t// nested structs with joins, scan the result to the channel directly after each row\n\t// so instead of receiving a slice, you will receive data on the channel[0] of the passed slice\n\tstudentsChan := []chan Student{make(chan Student)}\n\tgo func() {\n\t\tfor s := range studentsChan[0] {\n\t\t\tfmt.Println(\"chan students:\", s)\n\t\t}\n\t}()\n\terr = korm.To(\u0026studentsChan).Query(\"select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class\")\n\tlg.CheckError(err)\n\tfmt.Println()\n\n\t// nested (second argument of 'Scan') filled automatically from join, support nested slices and structs\n\tclasses := []Class{}\n\terr = korm.To(\u0026classes, 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\")\n\tlg.CheckError(err)\n\tfor _, s := range classes {\n\t\tfmt.Println(\"class:\", s)\n\t}\n\tfmt.Println()\n\n\t// // not nested, only remove second arg true from Scan method\n\tstudents := []Student{}\n\terr = korm.To(\u0026students, true).Query(\"select students.*,classes.id as 'classes.id',classes.name as 'classes.name'  from students join classes where classes.id = students.class\")\n\tlg.CheckError(err)\n\tfor _, s := range students {\n\t\tfmt.Println(\"student:\", s)\n\t}\n\tfmt.Println()\n\n\tmaps := []map[string]any{}\n\terr = korm.To(\u0026maps).Query(\"select * from students\")\n\tlg.CheckError(err)\n\tfmt.Println(\"maps =\", maps)\n\tfmt.Println()\n\n\tnames := []*string{}\n\terr = korm.To(\u0026names).Query(\"select name from students\")\n\tlg.CheckError(err)\n\tfmt.Println(\"names =\", names)\n\tfmt.Println()\n\n\tids := []int{}\n\terr = korm.To(\u0026ids).Query(\"select id from students\")\n\tlg.CheckError(err)\n\tfmt.Println(\"ids =\", ids)\n\tfmt.Println()\n\n\tbools := []bool{}\n\terr = korm.To(\u0026bools).Query(\"select is_admin from users\")\n\tlg.CheckError(err)\n\tfmt.Println(\"bools =\", bools)\n\tfmt.Println()\n\n\ttimes := []time.Time{}\n\terr = korm.To(\u0026times).Query(\"select created_at from users\")\n\tlg.CheckError(err)\n\tfmt.Println(\"times =\", times)\n\n\tserver.Run()\n}\n\n// OUTPUT\n// chan students: {1 student-1 1 {1 Math []}}\n// chan students: {2 student-2 2 {2 French []}}\n// chan students: {3 student-3 1 {1 Math []}}\n// chan students: {4 student-4 2 {2 French []}}\n\n// class: {1 Math [{1 student-1 0 {0  []}} {3 student-3 0 {0  []}}]}\n// class: {2 French [{2 student-2 0 {0  []}} {4 student-4 0 {0  []}}]}\n\n// student: \u0026{1 student-1 1 {1 Math []}}\n// student: \u0026{2 student-2 2 {2 French []}}\n// student: \u0026{3 student-3 1 {1 Math []}}\n// student: \u0026{4 student-4 2 {2 French []}}\n\n// 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]]\n\n// names = [student-1 student-2 student-3 student-4]\n\n// ids = [1 2 3 4]\n\n// bools = [true]\n\n// times = [2023-04-30 19:19:32 +0200 CEST]\n\n```\n\n\n# Benchmark vs Tarantool, Pgx, Gorm\n\n[https://github.com/kamalshkeir/korm-vs-gorm-vs-tarantool-vs-pgx](https://github.com/kamalshkeir/korm-vs-gorm-vs-tarantool-vs-pgx)\n\n# Benchmarks vs Gorm\n```sh\ngoos: windows\ngoarch: amd64\npkg: github.com/kamalshkeir/korm/benchmarks\ncpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz\n```\n\nTo execute these benchmarks on your machine, very easy :\n\n- git clone https://github.com/kamalshkeir/korm.git\n- cd korm\n- uncomment commented code at benchmarks/bench-test.go and Save\n- go mod tidy\n- go test -bench ^ .\\benchmarks\\ -benchmem\n\n```go\ntype TestTable struct {\n\tId        uint `korm:\"pk\"`\n\tEmail     string\n\tContent   string\n\tPassword  string\n\tIsAdmin   bool\n\tCreatedAt time.Time `korm:\"now\"`\n\tUpdatedAt time.Time `korm:\"update\"`\n}\n\ntype TestTableGorm struct {\n\tId        uint `gorm:\"primarykey\"`\n\tEmail     string\n\tContent   string\n\tPassword  string\n\tIsAdmin   bool\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n}\n////////////////////////////////////////////  query 7000 rows  //////////////////////////////////////////////\nBenchmarkGetAllS_GORM-4                       19          56049832 ns/op        12163316 B/op     328790 allocs/op\nBenchmarkGetAllS-4                       2708934               395.3 ns/op           224 B/op          1 allocs/op\nBenchmarkGetAllM_GORM-4                       18          62989567 ns/op        13212278 B/op     468632 allocs/op\nBenchmarkGetAllM-4                       4219461               273.5 ns/op           224 B/op          1 allocs/op\nBenchmarkGetRowS_GORM-4                    12188             96988 ns/op            5930 B/op        142 allocs/op\nBenchmarkGetRowS-4                       1473164               805.1 ns/op           336 B/op          7 allocs/op\nBenchmarkGetRowM_GORM-4                    11402            101638 ns/op            7408 B/op        203 allocs/op\nBenchmarkGetRowM-4                       1752652               671.9 ns/op           336 B/op          7 allocs/op\nBenchmarkPagination10_GORM-4                7714            153304 ns/op           19357 B/op        549 allocs/op\nBenchmarkPagination10-4                  1285722               934.5 ns/op           400 B/op          7 allocs/op\nBenchmarkPagination100_GORM-4               1364            738934 ns/op          165423 B/op       4704 allocs/op\nBenchmarkPagination100-4                 1278724               956.5 ns/op           400 B/op          7 allocs/op\nBenchmarkQueryS-4                        5781499               207.7 ns/op             4 B/op          1 allocs/op\nBenchmarkQueryM-4                        4643155               227.2 ns/op             4 B/op          1 allocs/op\nBenchmarkGetAllTables-4                 47465865                25.48 ns/op            0 B/op          0 allocs/op\nBenchmarkGetAllColumns-4                23657019                42.82 ns/op            0 B/op          0 allocs/op\n////////////////////////////////////////////  query 5000 rows  //////////////////////////////////////////////\nBenchmarkGetAllS_GORM-4                       24          43247546 ns/op         8796840 B/op     234784 allocs/op\nBenchmarkGetAllS-4                       2854401               426.8 ns/op           224 B/op          1 allocs/op\nBenchmarkGetAllM_GORM-4                       24          46329242 ns/op         9433050 B/op     334631 allocs/op\nBenchmarkGetAllM-4                       4076317               283.4 ns/op           224 B/op          1 allocs/op\nBenchmarkGetRowS_GORM-4                    11445            101107 ns/op            5962 B/op        142 allocs/op\nBenchmarkGetRowS-4                       1344831               848.4 ns/op           336 B/op          7 allocs/op\nBenchmarkGetRowM_GORM-4                    10000            100969 ns/op            7440 B/op        203 allocs/op\nBenchmarkGetRowM-4                       1721742               688.5 ns/op           336 B/op          7 allocs/op\nBenchmarkPagination10_GORM-4                7500            156208 ns/op           19423 B/op        549 allocs/op\nBenchmarkPagination10-4                  1253757               952.3 ns/op           400 B/op          7 allocs/op\nBenchmarkPagination100_GORM-4               1564            749408 ns/op          165766 B/op       4704 allocs/op\nBenchmarkPagination100-4                 1236270               957.5 ns/op           400 B/op          7 allocs/op\nBenchmarkGetAllTables-4                 44399386                25.43 ns/op            0 B/op          0 allocs/op\nBenchmarkGetAllColumns-4                27906392                41.45 ns/op            0 B/op          0 allocs/op\n////////////////////////////////////////////  query 1000 rows  //////////////////////////////////////////////\nBenchmarkGetAllS_GORM-4                      163           6766871 ns/op         1683919 B/op      46735 allocs/op\nBenchmarkGetAllS-4                       2882660               399.0 ns/op           224 B/op          1 allocs/op\nBenchmarkGetAllM_GORM-4                      140           8344988 ns/op         1886922 B/op      66626 allocs/op\nBenchmarkGetAllM-4                       3826730               296.5 ns/op           224 B/op          1 allocs/op\nBenchmarkGetRowS_GORM-4                    11940             97725 ns/op            5935 B/op        142 allocs/op\nBenchmarkGetRowS-4                       1333258               903.0 ns/op           336 B/op          7 allocs/op\nBenchmarkGetRowM_GORM-4                    10000            106079 ns/op            7408 B/op        203 allocs/op\nBenchmarkGetRowM-4                       1601274               748.2 ns/op           336 B/op          7 allocs/op\nBenchmarkPagination10_GORM-4                7534            159991 ns/op           19409 B/op        549 allocs/op\nBenchmarkPagination10-4                  1153982              1022 ns/op             400 B/op          7 allocs/op\nBenchmarkPagination100_GORM-4               1468            766269 ns/op          165876 B/op       4705 allocs/op\nBenchmarkPagination100-4                 1000000              1016 ns/op             400 B/op          7 allocs/op\nBenchmarkGetAllTables-4                 56200297                25.36 ns/op            0 B/op          0 allocs/op\nBenchmarkGetAllColumns-4                25478679                41.30 ns/op            0 B/op          0 allocs/op\n////////////////////////////////////////////  query 300 rows  //////////////////////////////////////////////\nBenchmarkGetAllS_GORM-4                      558           2046830 ns/op          458475 B/op      13823 allocs/op\nBenchmarkGetAllS-4                       2798872               411.5 ns/op           224 B/op          1 allocs/op\nBenchmarkGetAllM_GORM-4                      428           2605646 ns/op          567011 B/op      19721 allocs/op\nBenchmarkGetAllM-4                       4093662               287.9 ns/op           224 B/op          1 allocs/op\nBenchmarkGetRowS_GORM-4                    12182             97764 ns/op            5966 B/op        142 allocs/op\nBenchmarkGetRowS-4                       1347084               886.4 ns/op           336 B/op          7 allocs/op\nBenchmarkGetRowM_GORM-4                    10000            105311 ns/op            7440 B/op        203 allocs/op\nBenchmarkGetRowM-4                       1390363               780.0 ns/op           336 B/op          7 allocs/op\nBenchmarkPagination10_GORM-4                7502            155949 ns/op           19437 B/op        549 allocs/op\nBenchmarkPagination10-4                  1000000              1046 ns/op             400 B/op          7 allocs/op\nBenchmarkPagination100_GORM-4               1479            779700 ns/op          165679 B/op       4705 allocs/op\nBenchmarkPagination100-4                 1000000              1054 ns/op             400 B/op          7 allocs/op\nBenchmarkGetAllTables-4                 52255704                26.00 ns/op            0 B/op          0 allocs/op\nBenchmarkGetAllColumns-4                29292368                42.09 ns/op            0 B/op          0 allocs/op\n////////////////////////////////////////////    MONGO       //////////////////////////////////////////////\nBenchmarkGetAllS-4               3121384               385.6 ns/op           224 B/op          1 allocs/op\nBenchmarkGetAllM-4               4570059               264.2 ns/op           224 B/op          1 allocs/op\nBenchmarkGetRowS-4               1404399               866.6 ns/op           336 B/op          7 allocs/op\nBenchmarkGetRowM-4               1691026               722.6 ns/op           336 B/op          7 allocs/op\nBenchmarkGetAllTables-4         47424489                25.34 ns/op            0 B/op          0 allocs/op\nBenchmarkGetAllColumns-4        27039632                42.22 ns/op            0 B/op          0 allocs/op\n//////////////////////////////////////////////////////////////////////////////////////////////////////////\n```\n\n\n\n---\n### Available Tags by struct field type:\n\n# String Field:\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eWithout parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003cth\u003eWith parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n \n```\n*  \ttext (create column as TEXT not VARCHAR)\n*  \tnotnull\n*  \tunique\n*   iunique // insensitive unique\n*  \tindex, +index, index+ (INDEX ascending)\n*  \tindex-, -index (INDEX descending)\n*  \tdefault (DEFAULT '')\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\n* \tdefault:'any' (DEFAULT 'any')\n*\tmindex:...\n* \tuindex:username,Iemail // CREATE UNIQUE INDEX ON users (username,LOWER(email)) \n\t// \temail is lower because of 'I' meaning Insensitive for email\n* \tfk:...\n* \tsize:50  (VARCHAR(50))\n* \tcheck:...\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\n---\n\n\n\n# Int, Uint, Int64, Uint64 Fields:\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eWithout parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n \n```\n*   -  \t\t\t (To Ignore a field)\n*   autoinc, pk  (PRIMARY KEY)\n*   notnull      (NOT NULL)\n*  \tindex, +index, index+ (CREATE INDEX ON COLUMN)\n*  \tindex-, -index(CREATE INDEX DESC ON COLUMN)     \n*   unique \t\t (CREATE UNIQUE INDEX ON COLUMN) \n*   default\t\t (DEFAULT 0)\n```\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\u003cth\u003eWith parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```\nAvailable 'on_delete' and 'on_update' options: cascade,(donothing,noaction),(setnull,null),(setdefault,default)\n\n*   fk:{table}.{column}:{on_delete}:{on_update} \n*   check: len(to_check) \u003e 10 ; check: is_used=true (You can chain checks or keep it in the same CHECK separated by AND)\n*   mindex: first_name, last_name (CREATE MULTI INDEX ON COLUMN + first_name + last_name)\n*   uindex: first_name, last_name (CREATE MULTI UNIQUE INDEX ON COLUMN + first_name + last_name) \n*   default:5 (DEFAULT 5)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n\n# Bool : bool is INTEGER NOT NULL checked between 0 and 1 (in order to be consistent accross sql dialects)\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eWithout parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003cth\u003eWith parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n \n```\n*  \tindex, +index, index+ (CREATE INDEX ON COLUMN)\n*  \tindex-, -index(CREATE INDEX DESC ON COLUMN)  \n*   default (DEFAULT 0)\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\n*   default:1 (DEFAULT 1)\n*   mindex:...\n*   fk:...\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n# time.Time :\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eWithout parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003cth\u003eWith parameter\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n \n```\n*  \tindex, +index, index+ (CREATE INDEX ON COLUMN)\n*  \tindex-, -index(CREATE INDEX DESC ON COLUMN)  \n*   unique\n*   now (NOT NULL and defaulted to current unix timestamp)\n*   update (NOT NULL DEFAULT UNIX_TIMESTAMP ON UPDATE UNIX_TIMESTAMP)\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\n*   fk:...\n*   check:...\n*   uindex:...\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n# Float64 :\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth\u003eWithout parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003cth\u003eWith parameter\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n \n```\n*   notnull\n*  \tindex, +index, index+ (CREATE INDEX ON COLUMN)\n*  \tindex-, -index(CREATE INDEX DESC ON COLUMN)  \n*   unique\n*   default\n```\n\u003c/td\u003e\n\u003ctd\u003e\n\n```\n*   default:...\n*   fk:...\n*   mindex:...\n*   uindex:...\n*   check:...\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\n\n# JSON\n\n```go\ntype JsonOption struct {\n\tAs       string\n\tDialect  string\n\tDatabase string\n\tParams   []any\n}\nfunc JSON_EXTRACT(dataJson string, opt ...JsonOption) string\nfunc JSON_REMOVE(dataJson string, opt ...JsonOption) string\nfunc JSON_SET(dataJson string, opt ...JsonOption) string\nfunc JSON_ARRAY(values []any, as string, dialect ...string) string\nfunc JSON_OBJECT(values []any, as string, dialect ...string) string\nfunc JSON_CAST(value string, as string, dialect ...string) string\n\n// create query json\nq := korm.JSON_EXTRACT(`{\"a\": {\"c\": 3}, \"b\": 2}`, korm.JsonOption{\n\tAs:     \"data\",\n\tParams: []any{\"a.c\", \"b\"},\n})\nfmt.Println(\"q ==\", q) // q == JSON_EXTRACT('{\"a\": {\"c\": 3}, \"b\": 2}','$.a.c','$.b') AS data\n\nvar data []map[string]any\nerr := korm.To(\u0026data).Query(\"SELECT \" + q)\nlg.CheckError(err)\n\nfmt.Println(\"data=\", data) // data= [map[data:[3,2]]]\n```\n\n\n---\n\n\n# 🔗 Links\n[![portfolio](https://img.shields.io/badge/my_portfolio-000?style=for-the-badge\u0026logo=ko-fi\u0026logoColor=white)](https://kamalshkeir.dev/) [![linkedin](https://img.shields.io/badge/linkedin-0A66C2?style=for-the-badge\u0026logo=linkedin\u0026logoColor=white)](https://www.linkedin.com/in/kamal-shkeir/)\n\n\n---\n\n# Licence\nLicence [BSD-3](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamalshkeir%2Fkorm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkamalshkeir%2Fkorm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkamalshkeir%2Fkorm/lists"}