新增功能:使用 korm.WithDashboard 时,现在您可以在使用 lg pkg 登录时从管理仪表板实时访问所有日志(websockets)。默认情况下,仅将最后 10 条日志保留在内存中,您可以使用 lg.SaveLogs(50) 来增加它以保留最后 50 条日志
新增:根据数据库表自动检查结构(模式),提示您进行更改,因此它可以通过向结构添加或删除字段来添加或删除列,默认情况下禁用,使用korm.EnableCheck()
启用它
新增内容:通过连接处理嵌套或嵌入结构以及结构切片,如 sqlx,但 sqlx 不处理结构切片
新功能: korm.QueryNamed、QueryNamedS、korm.ExecNamed、korm.ExecContextNamed 和WhereNamed(query string, args map[string]any) 类似:Where("email = :email",map[string]any{"email":" [email protected]"})
新功能: korm.LogsQueries(),记录 SQL 查询语句和所花费的时间
带有模型 API 和视频教程的 Auto Docs
PPROF Go 分析工具和 Prometheus Metrics
日志中间件
管理仪表板具有离线且可安装的 PWA(使用 /static/sw.js 和 /static/manifest.webmanifest)。 sw.js
中提到的所有静态信息都将由 Service Worker 缓存并提供服务,您可以检查浏览器中的“网络”选项卡来检查它
共享网络总线允许您在 ORM 之间使用 pubsub websockets 实时发送和接收数据,因此您可以决定如何在不同数据库之间分配数据,请参阅示例
使用korm.Auth
、 korm.Admin
或korm.BasicAuth
中间件进行内置身份验证,每当使用 Auth 和 Admin 中间件时,您都可以从任何模板 html 访问.User
模型和变量.IsAuthenticated
如本示例 admin_nav.html
交互式 Shell,要从命令行在数据库中进行 CRUD,请使用korm.WithShell()
直接从结构自动迁移
与官方数据库/sql 兼容,因此您可以使用 sql.DB `korm.GetConnection()`` 自己进行查询,并且总体上使用数据库/sql 轻松集成现有代码库
调用korm.WithBus(...opts)
或korm.WithDashboard(addr, ...opts)
后可从 serverBus 访问 Router/Mux
钩子:OnInsert OnSet OnDelete 和 OnDrop
多对多关系
添加了 GENERATED ALWAYS AS 标签(所有方言)
对Where
和标签的连接和长度支持: check
和generated
(所有方言)
支持外键、索引、检查... 查看全部
Kenv 加载环境变量到结构体
Python 总线客户端pip install ksbus
go get -u github.com/kamalshkeir/korm@latest
go get -u github.com/kamalshkeir/sqlitedriver@latest
go get -u github.com/kamalshkeir/pgdriver@latest
go get -u github.com/kamalshkeir/mysqldriver@latest
// Debug when true show extra useful logs for queries executed for migrations and queries statements
Debug = false
// FlushCacheEvery execute korm.FlushCache() every 10 min by default, you should not worry about it, but useful that you can change it
FlushCacheEvery = 10 * time . Minute
// SetCacheMaxMemory set max size of each cache cacheAllS AllM ...
korm . SetCacheMaxMemory ( megaByte int ) // default maximum of 50 Mb , cannot be lower
// Connection pool
MaxOpenConns = 20
MaxIdleConns = 20
MaxLifetime = 30 * time . Minute
MaxIdleTime = 15 * time . Minute
// sqlite
// go get github.com/kamalshkeir/sqlitedriver
err := korm . New ( korm . SQLITE , "dbName" , sqlitedriver . Use ()) // Connect
// postgres, cockroach
// go get github.com/kamalshkeir/pgdriver
err := korm . New ( korm . POSTGRES , "dbName" , pgdriver . Use (), "user:password@localhost:5432" ) // Connect
// mysql, maria
// go get github.com/kamalshkeir/mysqldriver
err := korm . New ( korm . MYSQL , "dbName" , mysqldriver . Use (), "user:password@localhost:3306" ) // Connect
korm . Shutdown ( databasesName ... string ) error
package main
import (
"fmt"
"time"
"github.com/kamalshkeir/lg"
"github.com/kamalshkeir/korm"
"github.com/kamalshkeir/sqlitedriver"
)
type Class struct {
Id uint `korm:"pk"`
Name string
Students [] Student
}
type Student struct {
Id uint `korm:"pk"`
Name string
Class uint `korm:"fk:classes.id:cascade:cascade"`
Classes Class
}
func main () {
err := korm . New ( korm . SQLITE , "db" , sqlitedriver . Use ())
if lg . CheckError ( err ) {
return
}
defer korm . 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 slice
studentsChan := [] chan Student { make ( chan Student )}
go func () {
for s := range studentsChan [ 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 structs
classes := [] 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 := range classes {
fmt . Println ( "class:" , s )
}
fmt . Println ()
// // not nested, only remove second arg true from 'To' method
students := [] 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 := range students {
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]
可用标签 (SQL)
SQL:
korm. AutoMigrate [ T comparable ]( tableName string , dbName ... string ) error
err := korm. AutoMigrate [ User ]( "users" )
err := korm. AutoMigrate [ Bookmark ]( "bookmarks" )
type User struct {
Id int `korm:"pk"` // AUTO Increment ID primary key
Uuid string `korm:"size:40"` // VARCHAR(50)
Email string `korm:"size:50;iunique"` // insensitive unique
Password string `korm:"size:150"` // VARCHAR(150)
IsAdmin bool `korm:"default:false"` // DEFAULT 0
Image string `korm:"size:100;default:''"`
CreatedAt time. Time `korm:"now"` // auto now
Ignored string `korm:"-"`
}
type Bookmark struct {
Id uint `korm:"pk"`
UserId int `korm:"fk:users.id:cascade:setnull"` // options cascade,donothing/noaction, setnull/null, setdefault/default
IsDone bool
ToCheck string `korm:"size:50; notnull; check: len(to_check) > 2 AND len(to_check) < 10; check: is_done=true"` // column type will be VARCHAR(50)
Content string `korm:"text"` // column type will be TEXT not VARCHAR
UpdatedAt time. Time `korm:"update"` // will update when model updated, handled by triggers for sqlite, cockroach and postgres, and on migration for mysql
CreatedAt 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 ()
korm . New ( dbType Dialect , dbName string , dbDriver driver . Driver , dbDSN ... string ) error
korm . LogQueries ()
korm . GetConnection ( dbName ... string ) * sql . DB
korm. To [ T any ]( 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 ( ct context . Context ) * Selector [ T ]
( sl * Selector [ T ]) Query ( statement string , args ... any ) error
( sl * Selector [ T ]) Named ( statement string , args map [ string ] any , unsafe ... bool ) error
korm. WithBus ( ... opts ) * ksbus . Server // Usage: WithBus(...opts) or share an existing one
korm. WithDashboard ( address , ... opts ) * ksbus . Server
korm . WithShell ()
korm . WithDocs ( generateJsonDocs bool , outJsonDocs string , handlerMiddlewares ... func ( handler kmux . Handler ) kmux . Handler ) * ksbus . Server
korm . WithEmbededDocs ( embeded embed . FS , embededDirPath string , handlerMiddlewares ... func ( handler kmux. Handler ) kmux. Handler ) * ksbus . Server
korm . WithMetrics ( httpHandler http . Handler ) * ksbus . Server
korm . WithPprof ( path ... string ) * ksbus . Server
korm . Transaction ( dbName ... string ) ( * sql . Tx , error )
korm . Exec ( dbName , query string , args ... any ) error
korm . ExecContext ( ctx context . Context , dbName , query string , args ... any ) error
korm . ExecNamed ( query string , args map [ string ] any , dbName ... string ) error
korm . ExecContextNamed ( ctx context . Context , query string , args map [ string ] any , dbName ... string ) error
korm. BeforeServersData ( fn func ( data any , conn * ws. Conn ))
korm. BeforeDataWS ( fn func ( data map [ string ] any , conn * ws. Conn , originalRequest * http. Request ) bool )
korm . GetAllTables ( dbName ... string ) [] string
korm . GetAllColumnsTypes ( table string , dbName ... string ) map [ string ]string
korm . GetMemoryTable ( tbName string , dbName ... string ) ( TableEntity , error )
korm . GetMemoryTables ( dbName ... string ) ([] TableEntity , error )
korm . GetMemoryDatabases () [] DatabaseEntity
korm . GetMemoryDatabase ( dbName string ) ( * DatabaseEntity , error )
korm . Shutdown ( databasesName ... string ) error
korm . FlushCache ()
korm . DisableCache ()
korm . ManyToMany ( table1 , table2 string , dbName ... string ) error // add table relation m2m
Struct
: korm . Exec ( dbName , query string , args ... any ) error
korm . Transaction ( dbName ... string ) ( * sql . Tx , error )
// Model is a starter for Buider
func Model [ T comparable ]( tableName ... string ) * BuilderS [ T ]
// Database allow to choose database to execute query on
func ( b * BuilderS [ T ]) Database ( dbName string ) * BuilderS [ T ]
// Insert insert a row into a table and return inserted PK
func ( b * BuilderS [ T ]) Insert ( model * T ) ( int , error )
// InsertR add row to a table using input struct, and return the inserted row
func ( b * BuilderS [ T ]) InsertR ( model * T ) ( T , error )
// BulkInsert insert many row at the same time in one query
func ( 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 them
func ( b * BuilderS [ T ]) AddRelated ( relatedTable string , whereRelatedTable string , whereRelatedArgs ... any ) ( int , error )
// DeleteRelated delete a relations many to many
func ( b * BuilderS [ T ]) DeleteRelated ( relatedTable string , whereRelatedTable string , whereRelatedArgs ... any ) ( int , error )
// GetRelated used for many to many to get related classes to a student or related students to a class
func ( b * BuilderS [ T ]) GetRelated ( relatedTable string , dest any ) error
// JoinRelated same as get, but it join data
func ( b * BuilderS [ T ]) JoinRelated ( relatedTable string , dest any ) error
// Set used to update, Set("email,is_admin","[email protected]",true) or Set("email = ? AND is_admin = ?","[email protected]",true)
func ( b * BuilderS [ T ]) Set ( query string , 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 db
func ( 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 ( query string , args ... any ) * BuilderS [ T ]
// Limit set limit
func ( b * BuilderS [ T ]) Limit ( limit int ) * BuilderS [ T ]
// Context allow to query or execute using ctx
func ( b * BuilderS [ T ]) Context ( ctx context. Context ) * BuilderS [ T ]
// Page return paginated elements using Limit for specific page
func ( b * BuilderS [ T ]) Page ( pageNumber int ) * 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 operation
func ( b * BuilderS [ T ]) Debug () * BuilderS [ T ]
// All get all data
func ( b * BuilderS [ T ]) All () ([] T , error )
// One get single row
func ( b * BuilderS [ T ]) One () ( T , error )
Examples:
korm . Model [models. User ](). Select ( "email" , "uuid" ). OrderBy ( "-id" ). Limit ( PAGINATION_PER ). Page ( 1 ). All ()
// INSERT
uuid , _ := korm . GenerateUUID ()
hashedPass , _ := argon . Hash ( password )
korm . Model [models. User ](). Insert ( & models. User {
Uuid : uuid ,
Email : "[email protected]" ,
Password : hashedPass ,
IsAdmin : false ,
Image : "" ,
CreatedAt : time . Now (),
})
//if using more than one db
korm. Database [models. User ]( "dbNameHere" ). Where ( "id = ? AND email = ?" , 1 , "[email protected]" ). All ()
// where
korm . Model [models. User ](). Where ( "id = ? AND email = ?" , 1 , "[email protected]" ). One ()
// delete
korm . Model [models. User ](). Where ( "id = ? AND email = ?" , 1 , "[email protected]" ). Delete ()
// drop table
korm . Model [models. User ](). Drop ()
// update
korm . Model [models. User ](). Where ( "id = ?" , 1 ). Set ( "email = ?" , "[email protected]" )
map[string]any
: // BuilderM is query builder map string any
type BuilderM struct
// Table is a starter for BuiderM
func Table ( tableName string ) * BuilderM
// Database allow to choose database to execute query on
func ( b * BuilderM ) Database ( dbName string ) * BuilderM
// Select select table columns to return
func ( b * BuilderM ) Select ( columns ... string ) * BuilderM
// Where can be like: Where("id > ?",1) or Where("id",1) = Where("id = ?",1)
func ( b * BuilderM ) Where ( query string , args ... any ) * BuilderM
// Limit set limit
func ( b * BuilderM ) Limit ( limit int ) * BuilderM
// Page return paginated elements using Limit for specific page
func ( b * BuilderM ) Page ( pageNumber int ) * 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 ctx
func ( b * BuilderM ) Context ( ctx context. Context ) * BuilderM
// Debug print prepared statement and values for this operation
func ( b * BuilderM ) Debug () * BuilderM
// All get all data
func ( b * BuilderM ) All () ([] map [ string ] any , error )
// One get single row
func ( b * BuilderM ) One () ( map [ string ] any , error )
// Insert add row to a table using input map, and return PK of the inserted row
func ( b * BuilderM ) Insert ( rowData map [ string ] any ) ( int , error )
// InsertR add row to a table using input map, and return the inserted row
func ( b * BuilderM ) InsertR ( rowData map [ string ] any ) ( map [ string ] any , error )
// BulkInsert insert many row at the same time in one query
func ( b * BuilderM ) BulkInsert ( rowsData ... map [ string ] any ) ([] int , error )
// Set used to update, Set("email,is_admin","[email protected]",true) or Set("email = ? AND is_admin = ?","[email protected]",true)
func ( b * BuilderM ) Set ( query string , 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 db
func ( 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 them
func ( b * BuilderM ) AddRelated ( relatedTable string , whereRelatedTable string , whereRelatedArgs ... any ) ( int , error )
// GetRelated used for many to many to get related classes to a student or related students to a class
func ( b * BuilderM ) GetRelated ( relatedTable string , dest * [] map [ string ] any ) error
// JoinRelated same as get, but it join data
func ( b * BuilderM ) JoinRelated ( relatedTable string , dest * [] map [ string ] any ) error
// DeleteRelated delete a relations many to many
func ( b * BuilderM ) DeleteRelated ( relatedTable string , whereRelatedTable string , whereRelatedArgs ... any ) ( int , error )
Examples:
sliceMapStringAny , err := korm . Table ( "users" )
. Select ( "email" , "uuid" )
. OrderBy ( "-id" )
. Limit ( PAGINATION_PER )
. Page ( 1 )
. All ()
// INSERT
uuid , _ := korm . GenerateUUID ()
hashedPass , _ := argon . Hash ( "password" ) // github.com/kamalshkeir/argon
korm . Table ( "users" ). Insert ( map [ string ] any {
"uuid" : uuid ,
"email" : "[email protected]" ,
...
})
//if using more than one db
korm . Database ( "dbNameHere" ). Table ( "tableName" ). Where ( "id = ? AND email = ?" , 1 , "[email protected]" ). All ()
// where
Where ( "id = ? AND email = ?" , 1 , "[email protected]" ) // this work
Where ( "id,email" , 1 , "[email protected]" ) // and this work
korm . Table ( "tableName" ). Where ( "id = ? AND email = ?" , 1 , "[email protected]" ). One ()
// delete
korm . Table ( "tableName" ). Where ( "id = ? AND email = ?" , 1 , "[email protected]" ). Delete ()
// drop table
korm . Table ( "tableName" ). Drop ()
// update
korm . Table ( "tableName" ). Where ( "id = ?" , 1 ). Set ( "email = ?" , "[email protected]" )
korm . Table ( "tableName" ). Where ( "id" , 1 ). Set ( "email" , "[email protected]" )
korm . PaginationPer = 10
korm . DocsUrl = "docs"
korm . EmbededDashboard = false
korm . 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
package main
import (
"github.com/kamalshkeir/lg"
"github.com/kamalshkeir/ksmux"
"github.com/kamalshkeir/korm"
"github.com/kamalshkeir/sqlitedriver"
)
func main () {
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 certificates
serverBus . RunTLS ( cert string , certKey string )
// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more infos
serverBus . RunAutoTLS ( subDomains ... string )
}
然后创建管理员用户以连接到仪表板
go run main.go shell
createsuperuser
然后就可以访问/admin
func main () {
err := korm . New ( korm . SQLITE , "db" , sqlitedriver . Use ())
if lg . CheckError ( err ) {
return
}
defer korm . Shutdown ()
srv := korm . WithDashboard ( "localhost:9313" )
korm . WithShell ()
lg . Printfs ( "mgrunning on http://localhost:9313 n " )
app := srv . App
app . Get ( "/" , korm . Auth ( func ( c * ksmux. Context ) { // work with korm.Admin also
// c.IsAuthenticated also return bool
if v , 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 ()
}
{{define "admin_nav"}}
< header id =" admin-header " >
< nav >
< a href =" / " >
< h1 > KORM </ h1 >
</ a >
< ul >
< li >
< a {{if eq .Request.URL.Path "/" }}class=" active " {{end}} href =" / " > Home </ a >
</ li >
< li >
< a {{if contains .Request.URL.Path .admin_path }}class =" active " {{end}} href =" {{.admin_path}} " > Admin </ a >
</ li >
{{if .IsAuthenticated}}
< li >
< a href =" {{.admin_path}}/logout " > Logout </ a >
</ li >
{{if .User.Email}}
< li >
< span > Hello {{.User.Email}} </ span >
</ li >
{{end}}
{{end}}
</ ul >
</ nav >
</ header >
{{end}}
// dash_middlewares.go
package korm
import (
"context"
"net/http"
"github.com/kamalshkeir/aes"
"github.com/kamalshkeir/ksmux"
)
var Auth = func ( handler ksmux. Handler ) ksmux. Handler {
return func ( c * ksmux. Context ) {
session , err := c . GetCookie ( "session" )
if err != nil || session == "" {
// NOT AUTHENTICATED
c . DeleteCookie ( "session" )
handler ( c )
return
}
session , err = aes . Decrypt ( session )
if err != nil {
handler ( c )
return
}
// Check session
user , err := Model [ User ](). Where ( "uuid = ?" , session ). One ()
if err != nil {
// session fail
handler ( c )
return
}
// AUTHENTICATED AND FOUND IN DB
c . SetKey ( "korm-user" , user )
handler ( c )
}
}
var Admin = func ( handler ksmux. Handler ) ksmux. Handler {
return func ( c * ksmux. Context ) {
session , err := c . GetCookie ( "session" )
if err != nil || session == "" {
// NOT AUTHENTICATED
c . DeleteCookie ( "session" )
c . Status ( http . StatusTemporaryRedirect ). Redirect ( adminPathNameGroup + "/login" )
return
}
session , err = aes . Decrypt ( session )
if err != nil {
c . Status ( http . StatusTemporaryRedirect ). Redirect ( adminPathNameGroup + "/login" )
return
}
user , err := Model [ User ](). Where ( "uuid = ?" , session ). One ()
if err != nil {
// AUTHENTICATED BUT NOT FOUND IN DB
c . Status ( http . StatusTemporaryRedirect ). Redirect ( adminPathNameGroup + "/login" )
return
}
// Not admin
if ! user . IsAdmin {
c . Status ( 403 ). Text ( "Middleware : Not allowed to access this page" )
return
}
c . SetKey ( "korm-user" , user )
handler ( c )
}
}
var BasicAuth = func ( handler ksmux. Handler ) ksmux. Handler {
return ksmux . BasicAuth ( handler , BASIC_AUTH_USER , BASIC_AUTH_PASS )
}
科姆1:
package main
import (
"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"
)
func main () {
err := korm . New ( korm . SQLITE , "db1" , sqlitedriver . Use ())
if lg . CheckError ( err ) { return }
korm . WithShell ()
serverBus := korm . WithBus (ksbus. ServerOpts {
ID string
Address string
Path string
OnWsClose func ( connID string )
OnDataWS func ( data map [ string ] any , conn * ws. Conn , originalRequest * http. Request ) error
OnServerData func ( data any , conn * ws. Conn )
OnId func ( data map [ string ] any )
OnUpgradeWs func ( r * http. Request ) bool
WithOtherRouter * ksmux. Router
WithOtherBus * Bus
})
// handler authentication
korm . BeforeDataWS ( func ( data map [ string ] any , conn * ws. Conn , originalRequest * http. Request ) bool {
lg . Info ( "handle authentication here" )
return true
})
// handler data from other KORM
korm . BeforeServersData ( func ( data any , conn * ws. Conn ) {
lg . Info ( "recv orm:" , "data" , data )
})
// built in router to the bus, check it at https://github.com/kamalshkeir/ksbus
serverBus . 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 certificates
serverBus . RunTLS ( addr string , cert string , certKey string )
// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more details
serverBus . RunAutoTLS ( domainName string , subDomains ... string )
}
科尔姆2:
package main
import (
"net/http"
"github.com/kamalshkeir/lg"
"github.com/kamalshkeir/ksmux"
"github.com/kamalshkeir/ksmux/ws"
"github.com/kamalshkeir/korm"
"github.com/kamalshkeir/sqlitedriver"
)
func main () {
err := korm . New ( korm . SQLITE , "db2" , sqlitedriver . Use ())
if lg . CheckError ( err ) { return }
korm . WithShell () // if dashboard used, this line should be after it
serverBus := korm . WithBus (ksbus. ServerOpts {
ID string
Address string
Path string
OnWsClose func ( connID string )
OnDataWS func ( data map [ string ] any , conn * ws. Conn , originalRequest * http. Request ) error
OnServerData func ( data any , conn * ws. Conn )
OnId func ( data map [ string ] any )
OnUpgradeWs func ( r * http. Request ) bool
WithOtherRouter * ksmux. Router
WithOtherBus * Bus
})
korm . BeforeServersData ( func ( data any , conn * ws. Conn ) {
lg . Info ( "recv" , "data" , data )
})
// built in router to the bus, check it at https://github.com/kamalshkeir/ksbus
serverBus . 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 Bus
serverBus . Run ( "localhost:9314" )
// OR run https if you have certificates
serverBus . RunTLS ( addr string , cert string , certKey string )
// OR generate certificates let's encrypt for a domain name, check https://github.com/kamalshkeir/ksbus for more infos
serverBus . RunAutoTLS ( domainName string , subDomains ... string )
}
// generated example using concatination and length
type TestUser struct {
Id * uint `korm:"pk"`
Uuid string `korm:"size:40;iunique"`
Email * string `korm:"size:100;iunique"`
Gen string `korm:"size:250;generated: concat(uuid,'working',len(password))"`
Password string
IsAdmin * bool
CreatedAt time. Time `korm:"now"`
UpdatedAt time. Time `korm:"update"`
}
func TestGeneratedAs ( t * testing. T ) {
u , err := Model [ TestUser ](). Limit ( 3 ). All ()
if err != nil {
t . Error ( err )
}
if len ( u ) != 3 {
t . Error ( "len not 20" )
}
if u [ 0 ]. Gen != u [ 0 ]. Uuid + "working" + fmt . Sprintf ( "%d" , len ( u [ 0 ]. Password )) {
t . Error ( "generated not working:" , u [ 0 ]. Gen )
}
}
// Where example
func TestConcatANDLen ( 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)
if err != nil {
t . Error ( err )
}
if len ( groupes ) != 1 || groupes [ 0 ]. Name != "admin" {
t . Error ( "len(groupes) != 1 , got: " , groupes )
}
}
了解有关 Ksmux 的更多信息
func main () {
err := korm . New ( korm . SQLITE , "db" , sqlitedriver . Use ())
if err != nil {
log . Fatal ( err )
}
serverBus := korm . WithDashboard ( "localhost:9313" )
korm . WithShell ()
mux := serverBus . App
// add global middlewares
mux . Use (( midws ... func (http. Handler ) http. Handler ))
...
}
serverBus := korm . WithDashboard ( "localhost:9313" )
// or srv := korm.WithBus()
serverBus . WithPprof ( path ... string ) // path is 'debug' by default
will enable :
- / debug / pprof
- / debug / profile
- / debug / heap
- / debug / trace
执行 cpu 分析: go tool pprof -http=":8000" pprofbin http://localhost:9313/debug/profile?seconds=18
执行内存分析: go tool pprof -http=":8000" pprofbin http://localhost:9313/debug/heap?seconds=18
:9313/debug/profile?seconds=18 go tool pprof -http=":8000" pprofbin http://localhost:9313/debug/heap?seconds=18
要执行生成跟踪:从浏览器转到端点http://localhost:9313/debug/trace?seconds=18
,这将下载18 秒的跟踪然后查看跟踪: go tool trace path/to/trace
// or srv := korm.WithBus()
//srv.WithMetrics(httpHandler http.Handler, path ...string) path default to 'metrics'
srv . WithMetrics ( promhttp . Handler ())
will enable :
- / metrics
// 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 send
srv . App . Use ( ksmux . Logs ( func ( method , path , remote string , status int , took time. Duration ) {
// save somewhere
}))
will enable :
- / metrics
korm . OnInsert ( func ( database , table string , data map [ string ] any ) error {
fmt . Println ( "inserting into" , database , table , data )
// if error returned, it will not insert
return nil
})
korm . OnSet ( func ( database , table string , data map [ string ] any ) error {
fmt . Println ( "set into" , database , table , data )
return nil
})
korm . OnDelete ( func ( database , table , query string , args ... any ) error {})
korm . OnDrop ( func ( database , table string ) error {})
pip install ksbus==1.1.0
# if it doesn't work , execute it again
from ksbus import Bus
# onOpen callback that let you know when connection is ready, it take the bus as param
def OnOpen ( bus ):
print ( "connected" )
# bus.autorestart=True
# Publish publish to topic
bus . Publish ( "top" , {
"data" : "hello from python"
})
# Subscribe, it also return the subscription
bus . Subscribe ( "python" , pythonTopicHandler )
# SendToNamed publish to named topic
bus . SendToNamed ( "top:srv" , {
"data" : "hello again from python"
})
# bus.Unsubscribe("python")
print ( "finish everything" )
# pythonTopicHandler handle topic 'python'
def pythonTopicHandler ( data , subs ):
print ( "recv on topic python:" , data )
# Unsubscribe
#subs.Unsubscribe()
if __name__ == "__main__" :
Bus ( "localhost:9313" , onOpen = OnOpen ) # blocking
print ( "prorgram exited" )
type Class struct {
Id uint `korm:"pk"`
Name string `korm:"size:100"`
IsAvailable bool
CreatedAt time. Time `korm:"now"`
}
type Student struct {
Id uint `korm:"pk"`
Name string `korm:"size:100"`
CreatedAt time. Time `korm:"now"`
}
// migrate
func migrate () {
err := korm. AutoMigrate [ Class ]( "classes" )
if lg . CheckError ( err ) {
return
}
err = korm. AutoMigrate [ Student ]( "students" )
if lg . CheckError ( err ) {
return
}
err = korm . ManyToMany ( "classes" , "students" )
if lg . 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 struct
std := [] Student {}
err = korm . Model [ Class ](). Where ( "name = ?" , "Math" ). Select ( "name" ). OrderBy ( "-name" ). Limit ( 1 ). GetRelated ( "students" , & std )
// get related to map
std := [] map [ string ] any {}
err = korm . Table ( "classes" ). Where ( "name = ?" , "Math" ). Select ( "name" ). OrderBy ( "-name" ). Limit ( 1 ). GetRelated ( "students" , & std )
// join related to map
std := [] map [ string ] any {}
err = korm . Table ( "classes" ). Where ( "name = ?" , "Math" ). JoinRelated ( "students" , & std )
// join related to strcu
cu := [] 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" )
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 ( embeded embed . FS , dirPath , korm . BasicAuth )
// dirPath default to 'assets/static/docs' if empty
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 tables in 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 protected]" ' )
' 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 this help message
import "github.com/kamalshkeir/kenv"
type EmbedS struct {
Static bool `kenv:"EMBED_STATIC|false"`
Templates bool `kenv:"EMBED_TEMPLATES|false"`
}
type GlobalConfig struct {
Host string `kenv:"HOST|localhost"` // DEFAULT to 'localhost': if HOST not found in env
Port string `kenv:"PORT|9313"`
Embed EmbedS
Db struct {
Name string `kenv:"DB_NAME|db"` // NOT REQUIRED: if DB_NAME not found, defaulted to 'db'
Type string `kenv:"DB_TYPE"` // REEQUIRED: this env var is required, you will have error if empty
DSN string `kenv:"DB_DSN|"` // NOT REQUIRED: if DB_DSN not found it's not required, it's ok to stay empty
}
Smtp struct {
Email string `kenv:"SMTP_EMAIL|"`
Pass string `kenv:"SMTP_PASS|"`
Host string `kenv:"SMTP_HOST|"`
Port string `kenv:"SMTP_PORT|"`
}
Profiler bool `kenv:"PROFILER|false"`
Docs bool `kenv:"DOCS|false"`
Logs bool `kenv:"LOGS|false"`
Monitoring bool `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
package main
import (
"fmt"
"time"
"github.com/kamalshkeir/lg"
"github.com/kamalshkeir/korm"
"github.com/kamalshkeir/sqlitedriver"
)
type Class struct {
Id uint `korm:"pk"`
Name string
Students [] Student
}
type Student struct {
Id uint `korm:"pk"`
Name string
Class uint `korm:"fk:classes.id:cascade:cascade"`
Classes Class
}
func main () {
err := korm . New ( korm . SQLITE , "db" , sqlitedriver . Use ())
if lg . CheckError ( err ) {
return
}
defer korm . 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 slice
studentsChan := [] chan Student { make ( chan Student )}
go func () {
for s := range studentsChan [ 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 structs
classes := [] 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 := range classes {
fmt . Println ( "class:" , s )
}
fmt . Println ()
// // not nested, only remove second arg true from Scan method
students := [] 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 := range students {
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]
https://github.com/kamalshkeir/korm-vs-gorm-vs-tarantool-vs-pgx
goos: windows
goarch: amd64
pkg: github.com/kamalshkeir/korm/benchmarks
cpu: Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz
要在您的机器上执行这些基准测试,非常简单:
type TestTable struct {
Id uint `korm:"pk"`
Email string
Content string
Password string
IsAdmin bool
CreatedAt time. Time `korm:"now"`
UpdatedAt time. Time `korm:"update"`
}
type TestTableGorm struct {
Id uint `gorm:"primarykey"`
Email string
Content string
Password string
IsAdmin bool
CreatedAt time. Time
UpdatedAt time. Time
}
//////////////////////////////////////////// query 7000 rows //////////////////////////////////////////////
BenchmarkGetAllS_GORM - 4 19 56049832 ns / op 12163316 B / op 328790 allocs / op
BenchmarkGetAllS - 4 2708934 395.3 ns / op 224 B / op 1 allocs / op
BenchmarkGetAllM_GORM - 4 18 62989567 ns / op 13212278 B / op 468632 allocs / op
BenchmarkGetAllM - 4 4219461 273.5 ns / op 224 B / op 1 allocs / op
BenchmarkGetRowS_GORM - 4 12188 96988 ns / op 5930 B / op 142 allocs / op
BenchmarkGetRowS - 4 1473164 805.1 ns / op 336 B / op 7 allocs / op
BenchmarkGetRowM_GORM - 4 11402 101638 ns / op 7408 B / op 203 allocs / op
BenchmarkGetRowM - 4 1752652 671.9 ns / op 336 B / op 7 allocs / op
BenchmarkPagination10_GORM - 4 7714 153304 ns / op 19357 B / op 549 allocs / op
BenchmarkPagination10 - 4 1285722 934.5 ns / op 400 B / op 7 allocs / op
BenchmarkPagination100_GORM - 4 1364 738934 ns / op 165423 B / op 4704 allocs / op
BenchmarkPagination100 - 4 1278724 956.5 ns / op 400 B / op 7 allocs / op
BenchmarkQueryS - 4 5781499 207.7 ns / op 4 B / op 1 allocs / op
BenchmarkQueryM - 4 4643155 227.2 ns / op 4 B / op 1 allocs / op
BenchmarkGetAllTables - 4 47465865 25.48 ns / op 0 B / op 0 allocs / op
BenchmarkGetAllColumns - 4 23657019 42.82 ns / op 0 B / op 0 allocs / op
//////////////////////////////////////////// query 5000 rows //////////////////////////////////////////////
BenchmarkGetAllS_GORM - 4 24 43247546 ns / op 8796840 B / op 234784 allocs / op
BenchmarkGetAllS - 4 2854401 426.8 ns / op 224 B / op 1 allocs / op
BenchmarkGetAllM_GORM - 4 24 46329242 ns / op 9433050 B / op 334631 allocs / op
BenchmarkGetAllM - 4 4076317 283.4 ns / op 224 B / op 1 allocs / op
BenchmarkGetRowS_GORM - 4 11445 101107 ns / op 5962 B / op 142 allocs / op
BenchmarkGetRowS - 4 1344831 848.4 ns / op 336 B / op 7 allocs / op
BenchmarkGetRowM_GORM - 4 10000 100969 ns / op 7440 B / op 203 allocs / op
BenchmarkGetRowM - 4 1721742 688.5 ns / op 336 B / op 7 allocs / op
BenchmarkPagination10_GORM - 4 7500 156208 ns / op 19423 B / op 549 allocs / op
BenchmarkPagination10 - 4 1253757 952.3 ns / op 400 B / op 7 allocs / op
BenchmarkPagination100_GORM - 4 1564 749408 ns / op 165766 B / op 4704 allocs / op
BenchmarkPagination100 - 4 1236270 957.5 ns / op 400 B / op 7 allocs / op
BenchmarkGetAllTables - 4 44399386 25.43 ns / op 0 B / op 0 allocs / op
BenchmarkGetAllColumns - 4 27906392 41.45 ns / op 0 B / op 0 allocs / op
//////////////////////////////////////////// query 1000 rows //////////////////////////////////////////////
BenchmarkGetAllS_GORM - 4 163 6766871 ns / op 1683919 B / op 46735 allocs / op
BenchmarkGetAllS - 4 2882660 399.0 ns / op 224 B / op 1 allocs / op
BenchmarkGetAllM_GORM - 4 140 8344988 ns / op 1886922 B / op 66626 allocs / op
BenchmarkGetAllM - 4 3826730 296.5 ns / op 224 B / op 1 allocs / op
BenchmarkGetRowS_GORM - 4 11940 97725 ns / op 5935 B / op 142 allocs / op
BenchmarkGetRowS - 4 1333258 903.0 ns / op 336 B / op 7 allocs / op
BenchmarkGetRowM_GORM - 4 10000 106079 ns / op 7408 B / op 203 allocs / op
BenchmarkGetRowM - 4 1601274 748.2 ns / op 336 B / op 7 allocs / op
BenchmarkPagination10_GORM - 4 7534 159991 ns / op 19409 B / op 549 allocs / op
BenchmarkPagination10 - 4 1153982 1022 ns / op 400 B / op 7 allocs / op
BenchmarkPagination100_GORM - 4 1468 766269 ns / op 165876 B / op 4705 allocs / op
BenchmarkPagination100 - 4 1000000 1016 ns / op 400 B / op 7 allocs / op
BenchmarkGetAllTables - 4 56200297 25.36 ns / op 0 B / op 0 allocs / op
BenchmarkGetAllColumns - 4 25478679 41.30 ns / op 0 B / op 0 allocs / op
//////////////////////////////////////////// query 300 rows //////////////////////////////////////////////
BenchmarkGetAllS_GORM - 4 558 2046830 ns / op 458475 B / op 13823 allocs / op
BenchmarkGetAllS - 4 2798872 411.5 ns / op 224 B / op 1 allocs / op
BenchmarkGetAllM_GORM - 4 428 2605646 ns / op 567011 B / op 19721 allocs / op
BenchmarkGetAllM - 4 4093662 287.9 ns / op 224 B / op 1 allocs / op
BenchmarkGetRowS_GORM - 4 12182 97764 ns / op 5966 B / op 142 allocs / op
BenchmarkGetRowS - 4 1347084 886.4 ns / op 336 B / op 7 allocs / op
BenchmarkGetRowM_GORM - 4 10000 105311 ns / op 7440 B / op 203 allocs / op
BenchmarkGetRowM - 4 1390363 780.0 ns / op 336 B / op 7 allocs / op
BenchmarkPagination10_GORM - 4 7502 155949 ns / op 19437 B / op 549 allocs / op
BenchmarkPagination10 - 4 1000000 1046 ns / op 400 B / op 7 allocs / op
BenchmarkPagination100_GORM - 4 1479 779700 ns / op 165679 B / op 4705 allocs / op
BenchmarkPagination100 - 4 1000000 1054 ns / op 400 B / op 7 allocs / op
BenchmarkGetAllTables - 4 52255704 26.00 ns / op 0 B / op 0 allocs / op
BenchmarkGetAllColumns - 4 29292368 42.09 ns / op 0 B / op 0 allocs / op
//////////////////////////////////////////// MONGO //////////////////////////////////////////////
BenchmarkGetAllS - 4 3121384 385.6 ns / op 224 B / op 1 allocs / op
BenchmarkGetAllM - 4 4570059 264.2 ns / op 224 B / op 1 allocs / op
BenchmarkGetRowS - 4 1404399 866.6 ns / op 336 B / op 7 allocs / op
BenchmarkGetRowM - 4 1691026 722.6 ns / op 336 B / op 7 allocs / op
BenchmarkGetAllTables - 4 47424489 25.34 ns / op 0 B / op 0 allocs / op
BenchmarkGetAllColumns - 4 27039632 42.22 ns / op 0 B / op 0 allocs / op
//////////////////////////////////////////////////////////////////////////////////////////////////////////
不带参数 | 带参数 |
---|---|
| |
不带参数 |
---|
|
带参数 |
|
不带参数 | 带参数 |
---|---|
| |
不带参数 | 带参数 |
---|---|
| |
不带参数 | 带参数 |
---|---|
| |
type JsonOption struct {
As string
Dialect string
Database string
Params [] any
}
func JSON_EXTRACT ( dataJson string , opt ... JsonOption ) string
func JSON_REMOVE ( dataJson string , opt ... JsonOption ) string
func JSON_SET ( dataJson string , opt ... JsonOption ) string
func JSON_ARRAY ( values [] any , as string , dialect ... string ) string
func JSON_OBJECT ( values [] any , as string , dialect ... string ) string
func JSON_CAST ( value string , as string , dialect ... string ) string
// create query json
q := 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 data
var data [] map [ string ] any
err := korm . To ( & data ). Query ( "SELECT " + q )
lg . CheckError ( err )
fmt . Println ( "data=" , data ) // data= [map[data:[3,2]]]
许可证 BSD-3