简单等值匹配用filter足够,涉及范围、null判断、函数或自定义表达式时必须用expr;filter支持内置后缀如__gte、__in,expr需手动控制括号与参数化防注入。

Where条件里用Filter还是Expr?看字段类型和操作符
简单等值匹配用 Filter 足够,比如 Filter("status", "active");但一旦涉及范围、NULL 判断、函数或自定义表达式,Filter 就力不从心了。Expr 才是拼复杂条件的主力,它直接透传 SQL 片段,但必须配合参数化防止注入。
常见组合示例:
-
Filter("created_at__gte", time.Now().AddDate(0, 0, -7))→ 支持内置查找后缀(__gte、__in、__contains),但仅限基础类型和时间 Expr("read_count > ? AND read_count → 手写表达式 + 占位符,安全且灵活-
Expr("title LIKE ?", "%"+keyword+"%")→ 模糊匹配必须自己加%,Filter("title__contains", keyword)底层也是这么干的 -
Expr("updated_at IS NULL")→Filter("updated_at", nil)不生效,必须用Expr
Ormer.QueryTable 链式调用顺序影响最终SQL
Beego ORM 的链式调用不是“先写先执行”,而是按固定优先级生成 SQL 子句:WHERE → GROUP BY → HAVING → ORDER BY → LIMIT/OFFSET。中间任意一步出错(比如 Filter 字段名拼错),后续调用仍会继续,但可能查不到数据也不报错。
容易踩的坑:
- 多个
Filter同名字段会覆盖,不是叠加 ——Filter("id", 1).Filter("id", 2)最终只保留id = 2 -
Limit和Offset必须放在最后,否则会被忽略;OrderBy("-created_at").Limit(10)有效,反过来则无效 -
GroupBy后若要用聚合字段筛选,得用Having,不能用Filter——Filter("COUNT(*)__gt", 5)是错的,得写Having("COUNT(*) > ?", 5)
多条件动态拼接时别硬拼字符串,用Expr + 切片参数
用户搜索页常要根据前端传参动态加条件,很多人习惯用字符串拼接 SQL,这既危险又难维护。正确做法是构建 []interface{} 参数切片,配合 Expr 动态追加条件片段。
实操建议:
- 初始化一个空的
conds []string和args []interface{} - 每个条件分支只往两个切片里追加,例如:
conds = append(conds, "status = ?"); args = append(args, "published") - 最终一次性调用:
o.QueryTable("post").Expr(strings.Join(conds, " AND "), args...) - 注意:所有字段名、表名不能进参数列表,必须写死在
Expr字符串里,只有值才走参数占位
嵌套括号和逻辑优先级得靠Expr手动控制
ORM 不支持自动加括号分组,像 (a = 1 AND b = 2) OR (c = 3 AND d = 4) 这种结构,必须拆成两个 Expr 并用 Or 连接,或者全手写在一个 Expr 里。
推荐写法:
- 用
Or分隔大块逻辑:.Expr("(status = ? AND score > ?)", "active", 80).Or().Expr("(status = ? AND score > ?)", "draft", 95) - 避免混合使用
Filter和Expr做同一层逻辑,它们不会自动合并进同一组括号 ——Filter("a", 1).Expr("b > 2")生成的是WHERE a = 1 AND b > 2,没括号,但如果你本意是(a = 1 OR b > 2),那就错了 - 特别注意 MySQL 对
OR的索引使用限制,这种结构容易导致全表扫描,上线前务必 explain
最易被忽略的一点:Beego ORM 的 Expr 不校验字段是否存在,也不做语法检查,错写成 Expr("user_idd = ?")(多打了个 d)只会静默返回空结果。上线前最好用 Raw + QueryRow 对关键动态查询做一次字段存在性验证。











