“这种过滤是必须的,像 AND (time > ? AND time < ?) 这样的,还有更复杂的,如果写代码判断,就严重降低了效率和增加了 bug。”
→ 完全正确!这就是 xb 设计的核心理念。
xb 在多个层级实现了自动过滤,让用户无需手动判断边界条件。
doGLE())cond_builder.go 第 95-123 行
| 类型 | 被过滤的值 | 代码行 | 示例 |
|---|---|---|---|
string |
"" (空字符串) |
99-100 | Eq("name", "") → 不添加 |
int, int64, int32, … |
0 |
103-104 | Gt("count", 0) → 不添加 |
float32, float64 |
0 |
103-104 | Lt("score", 0.0) → 不添加 |
*int, *string, … |
nil |
107-110 | Eq("id", nil) → 不添加 |
| 任意类型 | nil |
118-119 | Eq("obj", nil) → 不添加 |
bool |
0 (false) |
103-104 | Eq("flag", 0) → 不添加 |
func (cb *CondBuilder) doGLE(p string, k string, v interface{}) *CondBuilder {
switch v.(type) {
case string:
if v.(string) == "" {
return cb // ⭐ 过滤空字符串
}
case uint64, uint, int64, int, int32, int16, int8, bool, byte, float64, float32:
if v == 0 {
return cb // ⭐ 过滤 0
}
case *uint64, *uint, *int64, *int, *int32, *int16, *int8, *bool, *byte, *float64, *float32:
isNil, n := NilOrNumber(v)
if isNil {
return cb // ⭐ 过滤 nil 指针
}
return cb.addBb(p, k, n)
case time.Time:
ts := v.(time.Time).Format("2006-01-02 15:04:05")
return cb.addBb(p, k, ts)
default:
if v == nil {
return cb // ⭐ 过滤 nil
}
}
return cb.addBb(p, k, v)
}
Eq(), Ne(), Gt(), Gte(), Lt(), Lte()doIn())cond_builder.go 第 33-81 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 参数为空 | nil 或 len == 0 |
34-35 | In("id") → 不添加 |
| 单个 nil 参数 | vs[0] == nil |
37-38 | In("id", nil) → 不添加 |
| 单个空字符串 | vs[0] == "" |
37-38 | In("id", "") → 不添加 |
| 数组中的 nil | 每个 nil 元素 |
45-46 | In("id", 1, nil, 2) → [1, 2] |
| 数组中的 0 | 每个 0 元素 |
56-58 | In("id", 1, 0, 2) → [1, 2] |
| 指针为 nil | 每个 nil 指针 | 61-64 | In("id", &a, nil) → [a] |
func (cb *CondBuilder) doIn(p string, k string, vs ...interface{}) *CondBuilder {
// ⭐ 过滤 1: 整个参数为空
if vs == nil || len(vs) == 0 {
return cb
}
// ⭐ 过滤 2: 单个 nil 或空字符串
if len(vs) == 1 && (vs[0] == nil || vs[0] == "") {
return cb
}
ss := []string{}
for i := 0; i < length; i++ {
v := vs[i]
// ⭐ 过滤 3: 数组中的 nil
if v == nil {
continue
}
switch v.(type) {
case string:
s := "'" + v.(string) + "'"
ss = append(ss, s)
case uint64, uint, int, int64, int32, int16, int8, byte, float64, float32:
s := N2s(v)
// ⭐ 过滤 4: 数组中的 0
if s == "0" {
continue
}
ss = append(ss, s)
case *uint64, *uint, ...:
s, isOK := Np2s(v)
// ⭐ 过滤 5: 指针为 nil
if !isOK {
continue
}
ss = append(ss, s)
}
}
bb := Bb{op: p, key: k, value: &ss}
cb.bbs = append(cb.bbs, bb)
return cb
}
// 场景 1: 全部为空/0
In("id", 0, nil, "")
// 结果: 不添加任何条件 ✅
// 场景 2: 部分有效
In("id", 1, 0, nil, 2, 3)
// 结果: IN (1, 2, 3) ✅
// 场景 3: 单个有效值
In("id", 123)
// 结果: IN (123) ✅
cond_builder.go 第 222-242 行
| 方法 | 被过滤的值 | 代码行 | 示例 |
|---|---|---|---|
Like() |
"" |
224-225 | Like("name", "") → 不添加 |
NotLike() |
"" |
230-231 | NotLike("name", "") → 不添加 |
LikeLeft() |
"" |
238-239 | LikeLeft("name", "") → 不添加 |
func (cb *CondBuilder) Like(k string, v string) *CondBuilder {
if v == "" {
return cb // ⭐ 过滤空字符串
}
return cb.doLike(LIKE, k, "%"+v+"%")
}
func (cb *CondBuilder) NotLike(k string, v string) *CondBuilder {
if v == "" {
return cb // ⭐ 过滤空字符串
}
return cb.doLike(NOT_LIKE, k, "%"+v+"%")
}
func (cb *CondBuilder) LikeLeft(k string, v string) *CondBuilder {
if v == "" {
return cb // ⭐ 过滤空字符串
}
return cb.doLike(LIKE, k, v+"%")
}
orAndSub())cond_builder.go 第 145-159 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 空 OR | len(c.bbs) == 0 |
148-149 | Or(所有条件都是 nil/0) → 不添加 |
| 空 AND | len(c.bbs) == 0 |
148-149 | And(所有条件都是 nil/0) → 不添加 |
func (cb *CondBuilder) orAndSub(orAnd string, f func(cb *CondBuilder)) *CondBuilder {
c := subCondBuilder()
f(c)
// ⭐ 如果子条件为空,不添加整个 OR/AND
if c.bbs == nil || len(c.bbs) == 0 {
return cb
}
bb := Bb{op: orAnd, key: orAnd, subs: c.bbs}
cb.bbs = append(cb.bbs, bb)
return cb
}
// 用户代码
Or(func(cb *CondBuilder) {
cb.Eq("category", "") // 空字符串,被第 1 层过滤
cb.Gt("rank", 0) // 0,被第 1 层过滤
})
// 结果: 整个 OR 被第 2 层过滤 ✅
// SQL: 不包含 OR
orAnd())cond_builder.go 第 161-175 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 条件为空时调用 OR() | length == 0 |
163-164 | .OR() 在开头 → 不添加 |
| 连续的 OR() | pre.op == OR |
167-168 | .OR().OR() → 只保留一个 |
func (cb *CondBuilder) orAnd(orAnd string) *CondBuilder {
length := len(cb.bbs)
// ⭐ 过滤 1: 条件为空
if length == 0 {
return cb
}
pre := cb.bbs[length-1]
// ⭐ 过滤 2: 连续的 OR
if pre.op == OR {
return cb
}
bb := Bb{op: orAnd}
cb.bbs = append(cb.bbs, bb)
return cb
}
// 场景 1: 在开头调用 OR()
builder.OR().Eq("name", "test")
// 结果: OR() 被过滤 ✅
// 场景 2: 连续 OR()
builder.Eq("a", 1).OR().OR().Eq("b", 2)
// 结果: 只保留一个 OR ✅
Bool())cond_builder.go 第 189-201 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 条件为 false | !preCond() |
193-194 | Bool(false, ...) → 不执行 |
func (cb *CondBuilder) Bool(preCond BoolFunc, f func(cb *CondBuilder)) *CondBuilder {
if preCond == nil {
panic("CondBuilder.Bool para of BoolFunc can not nil")
}
// ⭐ 如果条件为 false,不执行函数
if !preCond() {
return cb
}
if f == nil {
panic("CondBuilder.Bool para of func(k string, vs... interface{}) can not nil")
}
f(cb)
return cb
}
includeOptional := false
builder.
Eq("name", "test").
Bool(func() bool { return includeOptional }, func(cb *CondBuilder) {
cb.Eq("category", "optional")
})
// 结果: category 条件不添加 ✅
builder_x.go 第 100-107 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 空字符串字段 | "" |
102-103 | Select("", "name") → 只添加 “name” |
func (x *BuilderX) Select(resultKeys ...string) *BuilderX {
for _, resultKey := range resultKeys {
// ⭐ 过滤空字符串
if resultKey != "" {
x.resultKeys = append(x.resultKeys, resultKey)
}
}
return x
}
Select("id", "", "name", "")
// 结果: SELECT id, name ✅
builder_x.go 第 116-122 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 空字符串 | "" |
117-118 | GroupBy("") → 不添加 |
func (x *BuilderX) GroupBy(groupBy string) *BuilderX {
// ⭐ 过滤空字符串
if groupBy == "" {
return x
}
x.groupBys = append(x.groupBys, groupBy)
return x
}
builder_x.go 第 124-135 行
| 场景 | 被过滤 | 代码行 | 示例 |
|---|---|---|---|
| 空函数名 | "" |
125-126 | Agg("", ...) → 不添加 |
func (x *BuilderX) Agg(fn string, vs ...interface{}) *BuilderX {
// ⭐ 过滤空函数名
if fn == "" {
return x
}
bb := Bb{op: AGG, key: fn, value: vs}
x.aggs = append(x.aggs, bb)
return x
}
用户代码
↓
┌─────────────────────────────────────┐
│ 第 1 层:单个条件过滤 │
│ - 空字符串 │
│ - nil │
│ - 0 │
│ - 空数组 │
└─────────────┬───────────────────────┘
↓
┌─────────────────────────────────────┐
│ 第 2 层:组合条件过滤 │
│ - 空 OR/AND │
│ - 连续 OR() │
│ - 条件为 false 的 Bool() │
└─────────────┬───────────────────────┘
↓
┌─────────────────────────────────────┐
│ 第 3 层:字段过滤 │
│ - 空 Select 字段 │
│ - 空 GroupBy │
│ - 空 Agg 函数 │
└─────────────┬───────────────────────┘
↓
构建 SQL
// ❌ 没有自动过滤(用户需要手动判断)
if name != "" {
builder.Eq("name", name)
}
if category != "" {
builder.Eq("category", category)
}
if minScore > 0 {
builder.Gt("score", minScore)
}
if category != "" || tag != "" {
builder.Or(func(cb *CondBuilder) {
if category != "" {
cb.Eq("category", category)
}
if tag != "" {
cb.Eq("tag", tag)
}
})
}
// ✅ 有自动过滤(用户只需关注业务逻辑)
builder.
Eq("name", name).
Eq("category", category).
Gt("score", minScore).
Or(func(cb *CondBuilder) {
cb.Eq("category", category)
cb.Eq("tag", tag)
})
// 所有边界情况自动处理 ✅
没有自动过滤的问题:
SQL: WHERE name = ? AND OR () AND score > ?
❌ 有空的 OR()
❌ SQL 语法错误
SQL: WHERE name = ? AND category = '' AND score > ?
❌ 无意义的空字符串条件
❌ 性能浪费
SQL: WHERE AND time > ?
❌ 孤立的 AND
❌ SQL 语法错误
-- 有自动过滤
SELECT * FROM users
WHERE name = ? AND score > ?
-- 没有自动过滤(可能生成)
SELECT * FROM users
WHERE name = ? AND category = '' AND OR () AND score > ?
| 过滤类型 | 位置 | 被过滤的值 | 适用方法 |
|---|---|---|---|
| 单个条件 | doGLE() |
nil, 0, "" |
Eq, Gt, Lt, … |
| IN 条件 | doIn() |
nil, 0, "", 空数组 |
In, Nin |
| LIKE 条件 | Like() |
"" |
Like, NotLike, LikeLeft |
| 空 OR/AND | orAndSub() |
空子条件 | Or, And |
| OR() 连接符 | orAnd() |
空条件,连续 OR | OR() |
| Bool 条件 | Bool() |
false |
Bool |
| Select 字段 | Select() |
"" |
Select |
| GroupBy | GroupBy() |
"" |
GroupBy |
| Agg 函数 | Agg() |
"" |
Agg |
// 用户可能不填某些字段
name := request.GetString("name") // 可能为 ""
category := request.GetString("category") // 可能为 ""
minScore := request.GetFloat("minScore") // 可能为 0
tags := request.GetStrings("tags") // 可能为 []
// 无需任何判断,直接构建查询
builder := xb.Of(&Product{}).
Eq("name", name). // 自动过滤 ""
Eq("category", category). // 自动过滤 ""
Gt("score", minScore). // 自动过滤 0
In("tag", tags...) // 自动过滤空数组
sql, args := builder.Build().SqlOfSelect()
// 结果:只包含用户实际填写的条件 ✅
// 您提到的:AND (time > ? AND time < ?)
startTime := request.GetTime("startTime") // 可能为零值
endTime := request.GetTime("endTime") // 可能为零值
builder := xb.Of(&Order{}).
Eq("status", "active").
And(func(cb *CondBuilder) {
cb.Gt("created_at", startTime) // 自动过滤零值
cb.Lt("created_at", endTime) // 自动过滤零值
})
// 如果 startTime 和 endTime 都是零值:
// 整个 AND 被过滤 ✅
// SQL: WHERE status = 'active'
// 如果只有 startTime 有值:
// SQL: WHERE status = 'active' AND (created_at > ?)
// 如果都有值:
// SQL: WHERE status = 'active' AND (created_at > ? AND created_at < ?)
builder := xb.Of(&User{}).
Eq("status", "active").
Or(func(cb *CondBuilder) {
cb.And(func(cb2 *CondBuilder) {
cb2.Eq("role", role) // 可能为 ""
cb2.Eq("department", dept) // 可能为 ""
})
cb.And(func(cb2 *CondBuilder) {
cb2.Eq("level", level) // 可能为 0
cb2.Gt("score", score) // 可能为 0
})
})
// 所有嵌套的空 AND 和空 OR 都会被自动过滤 ✅
设计原则:
1. 用户只需关注业务逻辑
2. 框架自动处理所有边界情况
3. 生成的 SQL 始终干净、正确
4. 减少 Bug 和性能问题
实现方式:
1. 9 个过滤层级
2. 覆盖所有常见边界情况
3. 在构建 Bb 阶段就过滤
4. 对用户完全透明
“这种过滤是必须的,像 AND (time > ? AND time < ?) 这样的,还有更复杂的,如果写代码判断,就严重降低了效率和增加了 bug。”
→ xb 通过 9 层自动过滤机制,完美解决了这个问题! ✨
这就是 AI-First ORM 的设计哲学:智能、简洁、可靠。 🚀