xb

xb 完整的自动过滤机制

🎯 用户的洞察

“这种过滤是必须的,像 AND (time > ? AND time < ?) 这样的,还有更复杂的,如果写代码判断,就严重降低了效率和增加了 bug。”

→ 完全正确!这就是 xb 设计的核心理念。


📋 xb 的所有过滤机制

xb 在多个层级实现了自动过滤,让用户无需手动判断边界条件。


1️⃣ 单个条件过滤(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)
}

适用的方法


2️⃣ IN 条件过滤(doIn()

位置

cond_builder.go 第 33-81 行

过滤规则

场景 被过滤 代码行 示例
参数为空 nillen == 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) ✅

3️⃣ LIKE 条件过滤

位置

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+"%")
}

4️⃣ 空 OR/AND 过滤(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

5️⃣ 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 ✅

6️⃣ Bool 条件执行过滤(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 条件不添加 ✅

7️⃣ Select 字段过滤

位置

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 ✅

8️⃣ GroupBy 字段过滤

位置

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
}

9️⃣ Agg 聚合函数过滤

位置

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

🌟 为什么这么设计?

1. 用户体验优先

// ❌ 没有自动过滤(用户需要手动判断)
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)
    })
// 所有边界情况自动处理 ✅

2. 减少 Bug

没有自动过滤的问题:

SQL: WHERE name = ? AND OR () AND score > ?
     ❌ 有空的 OR()
     ❌ SQL 语法错误

SQL: WHERE name = ? AND category = '' AND score > ?
     ❌ 无意义的空字符串条件
     ❌ 性能浪费

SQL: WHERE AND time > ?
     ❌ 孤立的 AND
     ❌ SQL 语法错误

3. 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

🎯 实际应用示例

场景 1: 动态查询(用户输入)

// 用户可能不填某些字段
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()

// 结果:只包含用户实际填写的条件 ✅

场景 2: 复杂的时间范围查询

// 您提到的: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 < ?)

场景 3: 多层嵌套 OR/AND

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 都会被自动过滤 ✅

🏆 总结

xb 的过滤哲学

设计原则:
  1. 用户只需关注业务逻辑
  2. 框架自动处理所有边界情况
  3. 生成的 SQL 始终干净、正确
  4. 减少 Bug 和性能问题

实现方式:
  1. 9 个过滤层级
  2. 覆盖所有常见边界情况
  3. 在构建 Bb 阶段就过滤
  4. 对用户完全透明

您说得对

“这种过滤是必须的,像 AND (time > ? AND time < ?) 这样的,还有更复杂的,如果写代码判断,就严重降低了效率和增加了 bug。”

→ xb 通过 9 层自动过滤机制,完美解决了这个问题!


这就是 AI-First ORM 的设计哲学:智能、简洁、可靠。 🚀