ToSql 之前是手工拼接 SQL 字符串(string 类型的 conditions、手动加括号、 手动拼 AND/OR),改成用 exp.NewExpressionList 构造表达式树,跟 db-v2 其余部分统一走"先建表达式树、由方言渲染成 SQL"这条路,不再自己维护一份 字符串拼接逻辑。ConditionType/ConditionOperator 相应改成 exp.ExpressionListType/ exp.BooleanOperation 的类型别名。 TokenValue 从 condition_expr.go 里独立成 token_value.go,从 GetParam(k string) req.GlobalParams 改成 GetParam(k string) valuex.Accessor (配合 reflux/valuex),跟其它 "-v2" 项目的属性访问方式统一。engine 包 配合适配(sqlite3 用 db/engine 的新连接管理,SelectDataset 替代 db.Connection),删掉了不再需要的 EngineParam(老 GlobalParams 包装, 新 TokenValue 已经不需要这层)。 已知问题:engine.go 在没有外部传入 GlobalParams 时用 nil user 构造兜底 值,跟 NewTokenValue 现在强制要求非 nil user 冲突,TestEngine 会 panic ——这个先不修,等后面调整 Engine.Execute 让调用方能传 user 时一起解决。
234 lines
4.7 KiB
Go
234 lines
4.7 KiB
Go
package condition
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.fsdpf.net/go/db"
|
|
"git.fsdpf.net/go/db/exp"
|
|
"git.fsdpf.net/go/utils"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type ConditionType = exp.ExpressionListType
|
|
|
|
const (
|
|
AND ConditionType = exp.AndType
|
|
OR ConditionType = exp.OrType
|
|
)
|
|
|
|
type Condition struct {
|
|
parent *Condition
|
|
typ ConditionType // 分组类型
|
|
describe string // 描述
|
|
exprs []*ConditionExpr // 分组表达式成员
|
|
childrens []*Condition // 分组子集
|
|
}
|
|
|
|
func (this *Condition) AppendTo(c *Condition) {
|
|
this.parent = c
|
|
}
|
|
|
|
// 条件类型
|
|
func (this Condition) Type() ConditionType {
|
|
return this.typ
|
|
}
|
|
|
|
// 判断条件为空
|
|
func (this Condition) IsEmpty() bool {
|
|
if len(this.exprs) > 0 {
|
|
return false
|
|
}
|
|
for _, cond := range this.childrens {
|
|
if !cond.IsEmpty() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// 判断条件不为空
|
|
func (this Condition) IsNotEmpty() bool {
|
|
return !this.IsEmpty()
|
|
}
|
|
|
|
// 判断条件是否恒成立
|
|
func (this Condition) IsAlwaysRight() bool {
|
|
flag := []bool{}
|
|
|
|
for _, expr := range this.exprs {
|
|
if expr.GetOperator() != EQ {
|
|
flag = append(flag, false)
|
|
continue
|
|
}
|
|
|
|
if expr.GetTokenType() != SQL {
|
|
flag = append(flag, false)
|
|
continue
|
|
}
|
|
|
|
eRight := fmt.Sprintf("%s.%s", expr.GetFieldResource(), expr.GetField()) == strings.ReplaceAll(expr.GetTokenName(), "`", "")
|
|
|
|
if this.typ == OR && eRight {
|
|
return true
|
|
}
|
|
|
|
flag = append(flag, eRight)
|
|
}
|
|
|
|
for _, cond := range this.childrens {
|
|
cRight := cond.IsAlwaysRight()
|
|
|
|
if this.typ == OR && cRight {
|
|
return true
|
|
}
|
|
|
|
flag = append(flag, cRight)
|
|
}
|
|
|
|
if len(flag) == 0 {
|
|
return true
|
|
}
|
|
|
|
return !lo.Some(flag, []bool{false})
|
|
}
|
|
|
|
// ToSql 生成 SQL 语句
|
|
func (this Condition) ToSql(m TokenValue) db.Expression {
|
|
conditions := []exp.Expression{}
|
|
// 表达式
|
|
for _, item := range this.exprs {
|
|
if item.IsIgnoreEmptyParma(m) {
|
|
continue
|
|
}
|
|
conditions = append(conditions, item.ToSql(m))
|
|
}
|
|
// 条件子集
|
|
for _, item := range this.childrens {
|
|
conditions = append(conditions, item.ToSql(m))
|
|
}
|
|
|
|
if this.IsEmpty() {
|
|
if this.parent == nil && this.Type() == OR {
|
|
return db.V(false)
|
|
} else if this.parent == nil && this.Type() == AND {
|
|
return db.V(true)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if this.describe != "" {
|
|
return db.L("? /* ? */", exp.NewExpressionList(this.typ, conditions...), db.L(this.describe))
|
|
}
|
|
|
|
return exp.NewExpressionList(this.typ, conditions...)
|
|
}
|
|
|
|
// 设置条件表达式
|
|
func (this *Condition) SetExpr(expr *ConditionExpr) *Condition {
|
|
expr.AppendTo(this)
|
|
this.exprs = append(this.exprs, expr)
|
|
return this
|
|
}
|
|
|
|
// 设置条件子集
|
|
func (this *Condition) SetCondition(c *Condition) *Condition {
|
|
c.AppendTo(this)
|
|
this.childrens = append(this.childrens, c)
|
|
return this
|
|
}
|
|
|
|
// 设置 Token 匹配前缀
|
|
func (this *Condition) SetMatchPrefix(prefix string) *Condition {
|
|
for _, expr := range this.exprs {
|
|
expr.SetMatchPrefix(prefix)
|
|
}
|
|
for _, cond := range this.childrens {
|
|
cond.SetMatchPrefix(prefix)
|
|
}
|
|
return this
|
|
}
|
|
|
|
func (this Condition) GetFieldsValue(m TokenValue, isWithResource bool) (result map[string]any) {
|
|
if this.IsEmpty() {
|
|
return
|
|
}
|
|
|
|
// 表达式
|
|
for _, item := range this.exprs {
|
|
if item.GetOperator() != EQ {
|
|
continue
|
|
}
|
|
|
|
vField := map[string]any{item.GetField(): item.GetTokenValue(m)}
|
|
|
|
if isWithResource {
|
|
vField = map[string]any{item.GetFieldResource(): vField}
|
|
}
|
|
|
|
result = utils.MergeMap(result, vField)
|
|
}
|
|
// 条件子集
|
|
for _, item := range this.childrens {
|
|
result = utils.MergeMap(result, item.GetFieldsValue(m, isWithResource))
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* @param ConditionOperator operator
|
|
* @param TokenType types
|
|
* @return map[string][string] // 如: {"TestA.field_a": "param"}
|
|
*/
|
|
func (this Condition) GetFields(operator ConditionOperator, types ...TokenType) map[string]string {
|
|
result := make(map[string]string)
|
|
|
|
if this.IsEmpty() {
|
|
return result
|
|
}
|
|
|
|
// 表达式
|
|
for _, item := range this.exprs {
|
|
if item.GetOperator() != operator || !lo.Contains(types, item.GetTokenType()) {
|
|
continue
|
|
}
|
|
|
|
result[fmt.Sprintf("%s.%s", item.GetFieldResource(), item.GetField())] = item.GetTokenName()
|
|
}
|
|
|
|
// 条件子集
|
|
for _, item := range this.childrens {
|
|
result = lo.Assign(result, item.GetFields(operator, types...))
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (this *Condition) SetOption(opts ...Option) *Condition {
|
|
for i := 0; i < len(opts); i++ {
|
|
opts[i](this)
|
|
}
|
|
return this
|
|
}
|
|
|
|
type Option func(option *Condition)
|
|
|
|
func Type(v ConditionType) Option {
|
|
return func(option *Condition) {
|
|
option.typ = v
|
|
}
|
|
}
|
|
|
|
func Describe(v string) Option {
|
|
return func(option *Condition) {
|
|
option.describe = v
|
|
}
|
|
}
|
|
|
|
func New(opts ...Option) *Condition {
|
|
cond := &Condition{typ: AND}
|
|
cond.SetOption(opts...)
|
|
return cond
|
|
}
|