<span>beego——高级查询</span>


ORM以QuerySeter来组织查询,每个返回QuerySeter的方法都会获得一个新的QuerySeter对象。

基本使用方法:

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的条件查询。


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 的默认值

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

qs.Filter(          "name__iexact"          ,           "slene"          )         

          // WHERE name LIKE 'slene'         

          // 大小写不敏感,匹配任意 'Slene' 'sLENE'



(3)contains


qs.Filter(          "name__contains"          ,           "slene"          )         

          // WHERE name LIKE BINARY '%slene%'         

          // 大小写敏感, 匹配包含 slene 的字符


(4)icontains

qs.Filter(          "name__icontains"          ,           "slene"          )         

          // WHERE name LIKE '%slene%'         

          // 大小写不敏感, 匹配任意 'im Slene', 'im sLENE'



(5)in

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


qs.Filter(          "profile__age__gt"          , 17)         

          // WHERE profile.age > 17         

                    

          qs.Filter(          "profile__age__gte"          , 18)         

          // WHERE profile.age >= 18


(7)li/lte


qs.Filter(          "profile__age__lt"          , 17)         

          // WHERE profile.age < 17         

                    

          qs.Filter(          "profile__age__lte"          , 18)         

          // WHERE profile.age <= 18



(8)startswith

qs.Filter(          "name__startswith"          ,           "slene"          )         

          // WHERE name LIKE BINARY 'slene%'         

          // 大小写敏感, 匹配以 'slene' 起始的字符串



(9)istartwith


qs.Filter(          "name__istartswith"          ,           "slene"          )         

          // WHERE name LIKE 'slene%'         

          // 大小写不敏感, 匹配任意以 'slene', 'Slene' 起始的字符串 


(10)endswith


qs.Filter(          "name__endswith"          ,           "slene"          )         

          // WHERE name LIKE BINARY '%slene'         

          // 大小写敏感, 匹配以 'slene' 结束的字符串



(11)iendswith

qs.Filter(          "name__iendswithi"          ,           "slene"          )         

          // WHERE name LIKE '%slene'         

          // 大小写不敏感, 匹配任意以 'slene', 'Slene' 结束的字符串




(12)isnull

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连接。

qs.Filter(          "profile__isnull"          , true).Filter(          "name"          ,           "slene"          )         

          // WHERE profile_id IS NULL AND name = 'slene'



(2)Exclude

用来过滤查询结果,起到排除条件的作用。

使用NOT排除条件,多个Exclude之间使用AND连接。


qs.Exclude(          "profile__isnull"          , true).Filter(          "name"          ,           "slene"          )         

          // WHERE NOT profile_id IS NULL AND name = 'slene'


(3)SetCond

自定义条件表达式

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


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

设置偏移行数

qs.Offset(20)         

          // LIMIT 1000 OFFSET 20


(6)GroupBy


qs.GroupBy(          "id"          ,           "age"          )         

          // GROUP BY id,age

(7)OrderBy

在expr前使用减号-表示DESC排列

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语句,返回不重复的值

qs.Distinct()         

          // SELECT DISTINCT


(9)RelatedSel

关系查询,参数使用 expr


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

依据当前的查询条件,返回结果行数

cnt, err := o.QueryTable(          "user"          ).Count()           // SELECT COUNT(*) FROM USER         

          fmt.Printf(          "Count Num: %s, %s"          , cnt, err)


(11)Exist


exist := o.QueryTable(          "user"          ).Filter(          "UserName"          ,           "Name"          ).Exist()         

          fmt.Printf(          "Is Exist: %s"          , exist)


(12)Update

依据当前查询条件,进行批量更新操作


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"

原子操作增加字段值


// 假设 user struct 里有一个 nums int 字段         

          num, err := o.QueryTable(          "user"          ).Update(orm.Params{         

                    "nums"          : orm.ColValue(orm.ColAdd, 100),         

          })         

          // SET nums = nums + 100

orm.ColValue 支持以下操作


ColAdd                // 加         

          ColMinus              // 减         

          ColMultiply           // 乘         

          ColExcept             // 除

(13)Delete

依据当前查询条件,进行批量删除操作

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 插入,以提高批量插入的速度。

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。


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

可以指定返回的字段:

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

阐述返回单条记录

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"          )         

          }




可以指定返回的字段。

// 只返回 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 保存。

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 级联返回需要的数据

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 保存

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

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 里

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:

user := &User{Id: 1}         

          o.Read(user)         

          if           user.Profile != nil {         

                    o.Read(<strong>user.Profile</strong>)         

          }


直接关联查询:

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:


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

type           Post           struct           {         

                    Id    int         

                    Title string         

                    User  *User  `orm:          "rel(fk)"          `         

                    Tags  []*Tag `orm:          "rel(m2m)"          `         

          }


  

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 的反向关系,所以可以直接进行查询

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 会自动创建中间表

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

var           posts []*Post         

          num, err := dORM.QueryTable(          "post"          ).Filter(          "Tags__Tag__Name"          ,           "golang"          ).All(&posts)

通过 post title 查询这个 post 有哪些 tag


var           tags []*Tag         

          num, err := dORM.QueryTable(          "tag"          ).Filter(          "Posts__Post__Title"          ,           "Introduce Beego ORM"          ).All(&tags)

  

5.载入关系字段

LoadRelated 用于载入模型的关系字段,包括所有的 rel/reverse - one/many 关系

ManyToMany 关系字段载入

// 载入相应的 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 关系字段载入

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 对象

o := orm.NewOrm()         

          post := Post{Id: 1}         

          m2m := o.QueryM2M(&post,           "Tags"          )         

          // 第一个参数的对象,主键必须有值         

          // 第二个参数为对象需要操作的 M2M 字段         

          // QueryM2Mer 的 api 将作用于 Id 为 1 的 Post


  

QueryM2Mer Add

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{} 

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{}


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 关系中


if           m2m.Exist(&Tag{Id: 2}) {         

                    fmt.Println(          "Tag Exist"          )         

          }

  

QueryM2Mer Clear

清除所有 M2M 关系

nums, err := m2m.Clear()         

          if           err == nil {         

                    fmt.Println(          "Removed Tag Nums: "          , nums)         

          }


  

QueryM2Mer Count

计算 Tag 的数量


nums, err := m2m.Count()         

          if           err == nil {         

                    fmt.Println(          "Total Nums: "          , nums)         

          }


<div id="blog_post_info">