1. 程式人生 > 實用技巧 >beego高階查詢

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 varDefaultRowsLimit = 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 varDefaultRelsDepth = 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 varusers []*User ... qs := o.QueryTable("user") i, _ := qs.PrepareInsert() for_, user :=rangeusers { id, err := i.Insert(user) iferr == 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 varusers []*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 typePoststruct{ Id int Title string Content string Status int } // 只返回 Id 和 Title varposts []Post o.QueryTable("post").Filter("Status", 1).All(&posts,"Id","Title")

物件的其它欄位值將會是對應型別的預設值。

(16)one

闡述返回單條記錄

1 2 3 4 5 6 7 8 9 10 varuser User err := o.QueryTable("user").Filter("name","slene").One(&user) iferr == orm.ErrMultiRows { // 多條的時候報錯 fmt.Printf("Returned Multi Rows Not One") } iferr == orm.ErrNoRows { // 沒有找到記錄 fmt.Printf("Not row found") }

可以指定返回的欄位。

1 2 3 // 只返回 Id 和 Title varpost 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 varmaps []orm.Params num, err := o.QueryTable("user").Values(&maps) iferr == nil { fmt.Printf("Result Nums: %d\n", num) for_, m :=rangemaps { fmt.Println(m["Id"], m["Name"]) } }

返回指定的 Field 資料

TODO: 暫不支援級聯查詢RelatedSel直接返回 Values

但可以直接指定 expr 級聯返回需要的資料

1 2 3 4 5 6 7 8 9 varmaps []orm.Params num, err := o.QueryTable("user").Values(&maps,"id","name","profile","profile__age") iferr == nil { fmt.Printf("Result Nums: %d\n", num) for_, m :=rangemaps { 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 varlists []orm.ParamsList num, err := o.QueryTable("user").ValuesList(&lists) iferr == nil { fmt.Printf("Result Nums: %d\n", num) for_, row :=rangelists { fmt.Println(row) } }

當然也可以指定 expr 返回指定的 Field

1 2 3 4 5 6 7 8 varlists []orm.ParamsList num, err := o.QueryTable("user").ValuesList(&lists,"name","profile__age") iferr == nil { fmt.Printf("Result Nums: %d\n", num) for_, row :=rangelists { fmt.Printf("Name: %s, Age: %s\m", row[0], row[1]) } }

(19)ValueFlat

只返回特定的 Field 值,將結果集展開到單個 slice 裡

1 2 3 4 5 6 varlist orm.ParamsList num, err := o.QueryTable("user").ValuesFlat(&list,"name") iferr == 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) ifuser.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 varprofile Profile err := o.QueryTable("profile").Filter("User__Id", 1).One(&profile) iferr == nil { fmt.Println(profile) }

  

(2)Post 和 User 是 ManyToOne 關係,也就是 ForeignKey 為 User

1 2 3 4 5 6 typePoststruct{ Id int Title string User *User `orm:"rel(fk)"` Tags []*Tag `orm:"rel(m2m)"` }

  

1 2 3 4 5 6 7 8 9 varposts []*Post num, err := o.QueryTable("post").Filter("User", 1).RelatedSel().All(&posts) iferr == nil { fmt.Printf("%d posts read\n", num) for_, post :=rangeposts { 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 varuser User err := o.QueryTable("user").Filter("Post__Title","The Title").Limit(1).One(&user) iferr == nil { fmt.Printf(user) }

  

(3)Post 和 Tag 是 ManyToMany 關係

設定 rel(m2m) 以後,ORM 會自動建立中間表

1 2 3 4 5 6 7 8 9 10 11 12 typePoststruct{ Id int Title string User *User `orm:"rel(fk)"` Tags []*Tag `orm:"rel(m2m)"` } typeTagstruct{ Id int Name string Posts []*Post `orm:"reverse(many)"` }

一條 Post 紀錄可能對應不同的 Tag 紀錄,一條 Tag 紀錄可能對應不同的 Post 紀錄,所以 Post 和 Tag 屬於多對多關係,通過 tag name 查詢哪些 post 使用了這個 tag

1 2 varposts []*Post num, err := dORM.QueryTable("post").Filter("Tags__Tag__Name","golang").All(&posts)

通過 post title 查詢這個 post 有哪些 tag

1 2 vartags []*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 typeUserstruct{ 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 :=rangeuser.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) iferr == nil { fmt.Println("Added nums: ", num) }

 Add 支援多種型別 Tag *Tag []*Tag []Tag []interface{} 

1 2 3 4 5 6 7 8 9 10 vartags []*Tag ... // 讀取 tags 以後 ... num, err := m2m.Add(tags) iferr == 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 vartags []*Tag ... // 讀取 tags 以後 ... num, err := m2m.Remove(tags) iferr == nil { fmt.Println("Removed nums: ", num) } // 也可以多個作為引數傳入 // m2m.Remove(tag1, tag2, tag3)

  

QueryM2Mer Exist

判斷 Tag 是否存在於 M2M 關係中

1 2 3 ifm2m.Exist(&Tag{Id: 2}) { fmt.Println("Tag Exist") }

  

QueryM2Mer Clear

清除所有 M2M 關係

1 2 3 4 nums, err := m2m.Clear() iferr == nil { fmt.Println("Removed Tag Nums: ", nums) }

  

QueryM2Mer Count

計算 Tag 的數量

1 2 3 4 nums, err := m2m.Count() iferr == nil { fmt.Println("Total Nums: ", nums) }

轉自:https://www.cnblogs.com/yangmingxianshen/p/10125586.html