beego高階查詢
ORM以QuerySeter來組織查詢,每個返回QuerySeter的方法都會獲得一個新的QuerySeter物件。
基本使用方法:
1 2 3 4 5 6 7 8 |
o := orm.NewOrm()
// 獲取 QuerySeter 物件,user 為表名
qs := o.QueryTable( "user" )
// 也可以直接使用物件作為表名
user := new(User)
qs = o.QueryTable(user) // 返回 QuerySeter
|
1.expr
QuerySeter中用於描述欄位和sql操作符,使用簡單的expr查詢方法。
欄位組合的其後順序依照表的關係,比如User表擁有Profile的外來鍵,那麼對User表查詢對應的Profile.Age為條件,則使用profile__Age,
注意欄位的分隔符使用雙下劃線__,除了描述欄位,expr的尾部可以增加操作符以執行對應的sql操作。比如profile__Age__gt代表profile.Age > 18的條件查詢。
1 2 3 4 5 6 7 8 9 10 |
qs.Filter( "id" , 1) // WHERE id = 1
qs.Filter( "profile__age" , 18) // WHERE profile.age = 18
qs.Filter( "Profile__Age" , 18) // 使用欄位名和 Field 名都是允許的
qs.Filter( "profile__age" , 18) // WHERE profile.age = 18 qs.Filter( "profile__age__gt" , 18) // WHERE profile.age > 18
qs.Filter( "profile__age__gte" , 18) // WHERE profile.age >= 18
qs.Filter( "profile__age__in" , 18, 20) // WHERE profile.age IN (18, 20)
qs.Filter( "profile__age__in" , 18, 20).Exclude( "profile__lt" , 1000)
// WHERE profile.age IN (18, 20) AND NOT profile_id < 1000 |
2.operators
當前支援的操作符號
- exact/iexact等於
- contains/icontains包含
- gt / gte大於 / 大於等於
- lt / lte小於 / 小於等於
- startswith/istartswith以…起始
- endswith/iendswith以…結束
- in
- isnull
後面以i開頭的表示:不區分大小寫。
(1)exact
Filter / Exclude / Condition expr 的預設值
1 2 3 4 |
qs.Filter( "name" , "slene" ) // WHERE name = 'slene'
qs.Filter( "name__exact" , "slene" ) // WHERE name = 'slene'
// 使用 = 匹配,大小寫是否敏感取決於資料表使用的 collation
qs.Filter( "profile_id" , nil) // WHERE profile_id IS NULL
|
(2)iexact
1 2 3 |
qs.Filter( "name__iexact" , "slene" )
// WHERE name LIKE 'slene'
// 大小寫不敏感,匹配任意 'Slene' 'sLENE'
|
(3)contains
1 2 3 |
qs.Filter( "name__contains" , "slene" )
// WHERE name LIKE BINARY '%slene%'
// 大小寫敏感, 匹配包含 slene 的字元
|
(4)icontains
1 2 3 |
qs.Filter( "name__icontains" , "slene" )
// WHERE name LIKE '%slene%'
// 大小寫不敏感, 匹配任意 'im Slene', 'im sLENE'
|
(5)in
1 2 3 4 5 6 7 8 9 |
qs.Filter( "age__in" , 17, 18, 19, 20)
// WHERE age IN (17, 18, 19, 20)
ids:=[]int{17,18,19,20}
qs.Filter( "age__in" , ids)
// WHERE age IN (17, 18, 19, 20)
// 同上效果
|
(6)gt/gte
1 2 3 4 5 |
qs.Filter( "profile__age__gt" , 17)
// WHERE profile.age > 17
qs.Filter( "profile__age__gte" , 18)
// WHERE profile.age >= 18
|
(7)li/lte
1 2 3 4 5 |
qs.Filter( "profile__age__lt" , 17)
// WHERE profile.age < 17
qs.Filter( "profile__age__lte" , 18)
// WHERE profile.age <= 18
|
(8)startswith
1 2 3 |
qs.Filter( "name__startswith" , "slene" )
// WHERE name LIKE BINARY 'slene%'
// 大小寫敏感, 匹配以 'slene' 起始的字串
|
(9)istartwith
1 2 3 |
qs.Filter( "name__istartswith" , "slene" )
// WHERE name LIKE 'slene%'
// 大小寫不敏感, 匹配任意以 'slene', 'Slene' 起始的字串
|
(10)endswith
1 2 3 |
qs.Filter( "name__endswith" , "slene" )
// WHERE name LIKE BINARY '%slene'
// 大小寫敏感, 匹配以 'slene' 結束的字串
|
(11)iendswith
1 2 3 |
qs.Filter( "name__iendswithi" , "slene" )
// WHERE name LIKE '%slene'
// 大小寫不敏感, 匹配任意以 'slene', 'Slene' 結束的字串
|
(12)isnull
1 2 3 4 5 6 |
qs.Filter( "profile__isnull" , true)
qs.Filter( "profile_id__isnull" , true)
// WHERE profile_id IS NULL
qs.Filter( "profile__isnull" , false)
// WHERE profile_id IS NOT NULL
|
3.高階查詢介面使用
QuerySeter是高階查詢使用的介面,下面是其中的一些方法
type QuerySeter interface {
- Filter(string, …interface{}) QuerySeter
- Exclude(string, …interface{}) QuerySeter
- SetCond(*Condition) QuerySeter
- Limit(int, …int64) QuerySeter
- Offset(int64) QuerySeter
- GroupBy(…string) QuerySeter
- OrderBy(…string) QuerySeter
- Distinct() QuerySeter
- RelatedSel(…interface{}) QuerySeter
- Count() (int64, error)
- Exist() bool
- Update(Params) (int64, error)
- Delete() (int64, error)
- PrepareInsert() (Inserter, error)
- All(interface{}, …string) (int64, error)
- One(interface{}, …string) error
- Values(*[]Params, …string) (int64, error)
- ValuesList(*[]ParamsList, …string) (int64, error)
- ValuesFlat(*ParamsList, string) (int64, error)
}
每個返回QuerySeter的api呼叫時都會新建一個QuerySeter,不影響之前建立的。
高階查詢使用Filter和Exclude來做常用的條件查詢,囊括兩種清晰的過濾規則:包含、排除
(1)Filter
用來過濾查詢結果,起到包含條件的作用,多個Filter之間使用AND連線。
1 2 |
qs.Filter( "profile__isnull" , true).Filter( "name" , "slene" )
// WHERE profile_id IS NULL AND name = 'slene'
|
(2)Exclude
用來過濾查詢結果,起到排除條件的作用。
使用NOT排除條件,多個Exclude之間使用AND連線。
1 2 |
qs.Exclude( "profile__isnull" , true).Filter( "name" , "slene" )
// WHERE NOT profile_id IS NULL AND name = 'slene'
|
(3)SetCond
自定義條件表示式
1 2 3 4 5 6 7 8 9 10 |
cond := orm.NewCondition()
cond1 := cond.And( "profile__isnull" , false).AndNot( "status__in" , 1).Or( "profile__age__gt" , 2000)
qs := orm.QueryTable( "user" )
qs = qs.SetCond(cond1)
// WHERE ... AND ... AND NOT ... OR ...
cond2 := cond.AndCond(cond1).OrCond(cond.And( "name" , "slene" ))
qs = qs.SetCond(cond2).Count()
// WHERE (... AND ... AND NOT ... OR ...) OR ( ... )
|
(4)Limit
限制最大返回資料行數,第二個引數可以設定為Offset
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var DefaultRowsLimit = 1000 // ORM 預設的 limit 值為 1000
// 預設情況下 select 查詢的最大行數為 1000
// LIMIT 1000
qs.Limit(10)
// LIMIT 10
qs.Limit(10, 20)
// LIMIT 10 OFFSET 20 注意跟 SQL 反過來的
qs.Limit(-1)
// no limit
qs.Limit(-1, 100)
// LIMIT 18446744073709551615 OFFSET 100
// 18446744073709551615 是 1<<64 - 1 用來指定無 limit 限制 但有 offset 偏移的情況
|
(5)Offset
設定偏移行數
1 2 |
qs.Offset(20)
// LIMIT 1000 OFFSET 20
|
(6)GroupBy
1 2 |
qs.GroupBy( "id" , "age" )
// GROUP BY id,age
|
(7)OrderBy
在expr前使用減號-表示DESC排列
1 2 3 4 5 |
qs.OrderBy( "id" , "-profile__age" )
// ORDER BY id ASC, profile.age DESC
qs.OrderBy( "-profile__age" , "profile" )
// ORDER BY profile.age DESC, profile_id ASC
|
(8)Distinct
對應sql的distinct語句,返回不重複的值
1 2 |
qs.Distinct()
// SELECT DISTINCT
|
(9)RelatedSel
關係查詢,引數使用expr
1 2 3 4 5 6 7 8 9 10 11 12 |
var DefaultRelsDepth = 5 // 預設情況下直接呼叫 RelatedSel 將進行最大 5 層的關係查詢
qs := o.QueryTable( "post" )
qs.RelatedSel()
// INNER JOIN user ... LEFT OUTER JOIN profile ...
qs.RelatedSel( "user" )
// INNER JOIN user ...
// 設定 expr 只對設定的欄位進行關係查詢
// 對設定 null 屬性的 Field 將使用 LEFT OUTER JOIN
|
(10)Count
依據當前的查詢條件,返回結果行數
1 2 |
cnt, err := o.QueryTable( "user" ).Count() // SELECT COUNT(*) FROM USER
fmt.Printf( "Count Num: %s, %s" , cnt, err)
|
(11)Exist
1 2 |
exist := o.QueryTable( "user" ).Filter( "UserName" , "Name" ).Exist()
fmt.Printf( "Is Exist: %s" , exist)
|
(12)Update
依據當前查詢條件,進行批量更新操作
1 2 3 4 5 |
num, err := o.QueryTable( "user" ).Filter( "name" , "slene" ).Update(orm.Params{
"name" : "astaxie" ,
})
fmt.Printf( "Affected Num: %s, %s" , num, err)
// SET name = "astaixe" WHERE name = "slene"
|
原子操作增加欄位值
1 2 3 4 5 |
// 假設 user struct 裡有一個 nums int 欄位
num, err := o.QueryTable( "user" ).Update(orm.Params{
"nums" : orm.ColValue(orm.ColAdd, 100),
})
// SET nums = nums + 100
|
orm.ColValue 支援以下操作
1 2 3 4 |
ColAdd // 加
ColMinus // 減
ColMultiply // 乘
ColExcept // 除
|
(13)Delete
依據當前查詢條件,進行批量刪除操作
1 2 3 |
num, err := o.QueryTable( "user" ).Filter( "name" , "slene" ).Delete()
fmt.Printf( "Affected Num: %s, %s" , num, err)
// DELETE FROM user WHERE name = "slene"
|
(14)PrepareInsert
用於一次 prepare 多次 insert 插入,以提高批量插入的速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var users []*User
...
qs := o.QueryTable( "user" )
i, _ := qs.PrepareInsert()
for _, user := range users {
id, err := i.Insert(user)
if err == nil {
...
}
}
// PREPARE INSERT INTO user (`name`, ...) VALUES (?, ...)
// EXECUTE INSERT INTO user (`name`, ...) VALUES ("slene", ...)
// EXECUTE ...
// ...
i.Close() // 別忘記關閉 statement
|
(15)All
返回物件的結果集物件,All的引數支援*[]Type 和 *[]*Type 兩種形式的 slice。
1 2 3 |
var users []*User
num, err := o.QueryTable( "user" ).Filter( "name" , "slene" ).All(&users)
fmt.Printf( "Returned Rows Num: %s, %s" , num, err)
|
All / Values / ValuesList / ValuesFlat 受到Limit的限制,預設最大行數為 1000
可以指定返回的欄位:
1 2 3 4 5 6 7 8 9 10 |
type Post struct {
Id int
Title string
Content string
Status int
}
// 只返回 Id 和 Title
var posts []Post
o.QueryTable( "post" ).Filter( "Status" , 1).All(&posts, "Id" , "Title" )
|
物件的其它欄位值將會是對應型別的預設值。
(16)one
闡述返回單條記錄
1 2 3 4 5 6 7 8 9 10 |
var user User
err := o.QueryTable( "user" ).Filter( "name" , "slene" ).One(&user)
if err == orm.ErrMultiRows {
// 多條的時候報錯
fmt.Printf( "Returned Multi Rows Not One" )
}
if err == orm.ErrNoRows {
// 沒有找到記錄
fmt.Printf( "Not row found" )
}
|
可以指定返回的欄位。
1 2 3 |
// 只返回 Id 和 Title
var post Post
o.QueryTable( "post" ).Filter( "Content__istartswith" , "prefix string" ).One(&post, "Id" , "Title" )
|
物件的其它欄位值將會是對應型別的預設值。
(17)Values
以鍵值對的方式返回結果集。key 為 Model 裡的 Field name,value 的值 以 string 儲存。
1 2 3 4 5 6 7 8 |
var maps []orm.Params
num, err := o.QueryTable( "user" ).Values(&maps)
if err == nil {
fmt.Printf( "Result Nums: %d\n" , num)
for _, m := range maps {
fmt.Println(m[ "Id" ], m[ "Name" ])
}
}
|
返回指定的 Field 資料
TODO: 暫不支援級聯查詢RelatedSel直接返回 Values
但可以直接指定 expr 級聯返回需要的資料
1 2 3 4 5 6 7 8 9 |
var maps []orm.Params
num, err := o.QueryTable( "user" ).Values(&maps, "id" , "name" , "profile" , "profile__age" )
if err == nil {
fmt.Printf( "Result Nums: %d\n" , num)
for _, m := range maps {
fmt.Println(m[ "Id" ], m[ "Name" ], m[ "Profile" ], m[ "Profile__Age" ])
// map 中的資料都是展開的,沒有複雜的巢狀
}
}
|
(18)ValueList
顧名思義,返回的結果集以slice儲存
結果的排列與 Model 中定義的 Field 順序一致
返回的每個元素值以 string 儲存
1 2 3 4 5 6 7 8 |
var lists []orm.ParamsList
num, err := o.QueryTable( "user" ).ValuesList(&lists)
if err == nil {
fmt.Printf( "Result Nums: %d\n" , num)
for _, row := range lists {
fmt.Println(row)
}
}
|
當然也可以指定 expr 返回指定的 Field
1 2 3 4 5 6 7 8 |
var lists []orm.ParamsList
num, err := o.QueryTable( "user" ).ValuesList(&lists, "name" , "profile__age" )
if err == nil {
fmt.Printf( "Result Nums: %d\n" , num)
for _, row := range lists {
fmt.Printf( "Name: %s, Age: %s\m" , row[0], row[1])
}
}
|
(19)ValueFlat
只返回特定的 Field 值,將結果集展開到單個 slice 裡
1 2 3 4 5 6 |
var list orm.ParamsList
num, err := o.QueryTable( "user" ).ValuesFlat(&list, "name" )
if err == nil {
fmt.Printf( "Result Nums: %d\n" , num)
fmt.Printf( "All User Names: %s" , strings.Join(list, ", " ))
}
|
4.關係查詢
(1)User 和 Profile 是 OneToOne 的關係
已經取得了 User 物件,查詢 Profile:
1 2 3 4 5 |
user := &User{Id: 1}
o.Read(user)
if user.Profile != nil {
o.Read(<strong>user.Profile</strong>)
}
|
直接關聯查詢:
1 2 3 4 5 6 7 8 |
user := &User{}
o.QueryTable( "user" ).Filter( "Id" , 1).RelatedSel().One(user)
// 自動查詢到 Profile
fmt.Println(<strong>user.Profile</strong>)
// 因為在 Profile 裡定義了反向關係的 User,所以 Profile 裡的 User 也是自動賦值過的,可以直接取用。
fmt.Println(<strong>user.Profile.User</strong>)
// [SELECT T0.`id`, T0.`name`, T0.`profile_id`, T1.`id`, T1.`age` FROM `user` T0 INNER JOIN `profile` T1 ON T1.`id` = T0.`profile_id` WHERE T0.`id` = ? LIMIT 1000] - `1`
|
通過 User 反向查詢 Profile:
1 2 3 4 5 |
var profile Profile
err := o.QueryTable( "profile" ).Filter( "User__Id" , 1).One(&profile)
if err == nil {
fmt.Println(profile)
}
|
(2)Post 和 User 是 ManyToOne 關係,也就是 ForeignKey 為 User
1 2 3 4 5 6 |
type Post struct {
Id int
Title string
User *User `orm: "rel(fk)" `
Tags []*Tag `orm: "rel(m2m)" `
}
|
1 2 3 4 5 6 7 8 9 |
var posts []*Post
num, err := o.QueryTable( "post" ).Filter( "User" , 1).RelatedSel().All(&posts)
if err == nil {
fmt.Printf( "%d posts read\n" , num)
for _, post := range posts {
fmt.Printf( "Id: %d, UserName: %d, Title: %s\n" , post.Id, post.User.UserName, post.Title)
}
}
// [SELECT T0.`id`, T0.`title`, T0.`user_id`, T1.`id`, T1.`name`, T1.`profile_id`, T2.`id`, T2.`age` FROM `post` T0 INNER JOIN `user` T1 ON T1.`id` = T0.`user_id` INNER JOIN `profile` T2 ON T2.`id` = T1.`profile_id` WHERE T0.`user_id` = ? LIMIT 1000] - `1`
|
根據 Post.Title 查詢對應的 User:
RegisterModel 時,ORM 也會自動建立 User 中 Post 的反向關係,所以可以直接進行查詢
1 2 3 4 5 |
var user User
err := o.QueryTable( "user" ).Filter( "Post__Title" , "The Title" ).Limit(1).One(&user)
if err == nil {
fmt.Printf(user)
}
|
(3)Post 和 Tag 是 ManyToMany 關係
設定 rel(m2m) 以後,ORM 會自動建立中間表
1 2 3 4 5 6 7 8 9 10 11 12 |
type Post struct {
Id int
Title string
User *User `orm: "rel(fk)" `
Tags []*Tag `orm: "rel(m2m)" `
}
type Tag struct {
Id int
Name string
Posts []*Post `orm: "reverse(many)" `
}
|
一條 Post 紀錄可能對應不同的 Tag 紀錄,一條 Tag 紀錄可能對應不同的 Post 紀錄,所以 Post 和 Tag 屬於多對多關係,通過 tag name 查詢哪些 post 使用了這個 tag
1 2 |
var posts []*Post
num, err := dORM.QueryTable( "post" ).Filter( "Tags__Tag__Name" , "golang" ).All(&posts)
|
通過 post title 查詢這個 post 有哪些 tag
1 2 |
var tags []*Tag
num, err := dORM.QueryTable( "tag" ).Filter( "Posts__Post__Title" , "Introduce Beego ORM" ).All(&tags)
|
5.載入關係欄位
LoadRelated 用於載入模型的關係欄位,包括所有的 rel/reverse - one/many 關係
ManyToMany 關係欄位載入
1 2 3 4 5 6 7 8 9 |
// 載入相應的 Tags
post := Post{Id: 1}
err := o.Read(&post)
num, err := o.LoadRelated(&post, "Tags" )
// 載入相應的 Posts
tag := Tag{Id: 1}
err := o.Read(&tag)
num, err := o.LoadRelated(&tag, "Posts" )
|
User 是 Post 的 ForeignKey,對應的 ReverseMany 關係欄位載入
1 2 3 4 5 6 7 8 9 10 11 12 |
type User struct {
Id int
Name string
Posts []*Post `orm: "reverse(many)" `
}
user := User{Id: 1}
err := dORM.Read(&user)
num, err := dORM.LoadRelated(&user, "Posts" )
for _, post := range user.Posts {
//...
}
|
6.多對多關係操作
type QueryM2Mer interface {
- Add(…interface{}) (int64, error)
- Remove(…interface{}) (int64, error)
- Exist(interface{}) bool
- Clear() (int64, error)
- Count() (int64, error)
}
建立一個 QueryM2Mer 物件
1 2 3 4 5 6 |
o := orm.NewOrm()
post := Post{Id: 1}
m2m := o.QueryM2M(&post, "Tags" )
// 第一個引數的物件,主鍵必須有值
// 第二個引數為物件需要操作的 M2M 欄位
// QueryM2Mer 的 api 將作用於 Id 為 1 的 Post
|
QueryM2Mer Add
1 2 3 4 5 6 7 |
tag := &Tag{Name: "golang" }
o.Insert(tag)
num, err := m2m.Add(tag)
if err == nil {
fmt.Println( "Added nums: " , num)
}
|
Add 支援多種型別 Tag *Tag []*Tag []Tag []interface{}
1 2 3 4 5 6 7 8 9 10 |
var tags []*Tag
...
// 讀取 tags 以後
...
num, err := m2m.Add(tags)
if err == nil {
fmt.Println( "Added nums: " , num)
}
// 也可以多個作為引數傳入
// m2m.Add(tag1, tag2, tag3)
|
QueryM2Mer Remove
從M2M關係中刪除 tag
Remove 支援多種型別 Tag *Tag []*Tag []Tag []interface{}
1 2 3 4 5 6 7 8 9 10 |
var tags []*Tag
...
// 讀取 tags 以後
...
num, err := m2m.Remove(tags)
if err == nil {
fmt.Println( "Removed nums: " , num)
}
// 也可以多個作為引數傳入
// m2m.Remove(tag1, tag2, tag3)
|
QueryM2Mer Exist
判斷 Tag 是否存在於 M2M 關係中
1 2 3 |
if m2m.Exist(&Tag{Id: 2}) {
fmt.Println( "Tag Exist" )
}
|
QueryM2Mer Clear
清除所有 M2M 關係
1 2 3 4 |
nums, err := m2m.Clear()
if err == nil {
fmt.Println( "Removed Tag Nums: " , nums)
}
|
QueryM2Mer Count
計算 Tag 的數量
1 2 3 4 |
nums, err := m2m.Count()
if err == nil {
fmt.Println( "Total Nums: " , nums)
}
|
轉自:https://www.cnblogs.com/yangmingxianshen/p/10125586.html