GORM查询条件之不定参数的用法

为了根据用户ID(UserID)获取到相应的话题(Topic),我们定义了一个QueryByUserID 的方法,为了表意刻意传入了一个参数,同时在QueryByUserID的最后强行添加了一个args不定参数,从而接受调用时传入的附加的参数,在函数体内根据args的长度对它进行不同方式的应用,达到了优雅扩展QueryByUserID的功能。

看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Topic 话题
type Topic struct {
gorm.Model
Title string `gorm:"index"`
Content string `gorm:"type:text"`
ViewCount int `json:"view_count"`
ReplyCount int `json:"reply_count"`
UserID int `gorm:"index" json:"user_id"`
}

// QueryByUserID 根据UserID获取话题
func (t *Topic) QueryByUserID(userID int, args ...interface{}) (topics []Topic, err error) {
db := config.DB.Where("user_id = ?", userID)
if len(args) >= 2 {
db = db.Where(args[0], args[1:]...)
} else if len(args) >= 1 {
db = db.Where(args[0])
}
err = db.Find(&topics).Error
return
}

在添加了不定参数的情况下,调用QueryByUserID时如果想要进一步精细地搜索话题,比如想要获取UserID为1且最近一周发布的话题,可以这么写了:

1
2
topics, err := (&Topic{}).QueryByUserID(1, "created_at > ?", time.Now().Add(-7*24*time.Hour)
// ... 其他对topics 与 err 的处理

总之,非常灵活!

所述最佳实践中不定参数的约定

QueryByUserID 中根据传入的不定参数的数量,采用不同的传入方式,其实这里遵循了GORM中Where条件语句的作用方式

1
2
3
4
5
6
7
// ...
if len(args) >= 2 {
db = db.Where(args[0], args[1:]...)
} else if len(args) >= 1 {
db = db.Where(args[0])
}
// ...

首先我们先把这里的约定给出来

  1. 当传入的args长度为1时,如果args[0]不是一个map[string]interface{}的值,这个值必须能够作用到主键上面。比如1,表示主键ID为1(ID=1)的记录;比如[]int{1,2},表示主键ID为1和2(ID IN (1,2))的记录;
  2. 否则,当传入的args长度为1时,args[0]必须是一个map[string]interface{}的值;
  3. 当传入的args长度大于等于2时,args[0]必须是一个string类型的值,且包含了args[1:]中同等个数的占位符。打个比方,如果args[0]真实值为"created_at > ? AND deleted_at > ?",那么args[1:]则应该包含两个值,一个对应第一个问号(即创建时间),一个对应第二个问号(即删除时间)。