Skip to content

Commit 018df33

Browse files
promote tags3 to the new tags implementation
1 parent cf68a1c commit 018df33

19 files changed

Lines changed: 500 additions & 1807 deletions

tags/bun.go

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,67 @@ func FromBunString(str string) Tags {
3838
return slicest.Map(strs, func(str string) Tag { return Tag(str) })
3939
}
4040

41+
// Only works as a db pre filter to reduce the number of results.
42+
// Due to wildcards matching over [bunTagDelimiter] it does not produce 100% acurate results.
43+
// Use [Expr.Eval] to ensure correct results.
4144
func ApplyToBunQuery(expr Expr, qb bun.QueryBuilder, column string) bun.QueryBuilder {
42-
return expr.applyToBunQuery(qb, column, bunAnd, false)
45+
return pushNegatesToValues(expr).applyToBunQuery(qb, column, bunAnd)
4346
}
4447

45-
func (e ValueExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunMode, negate bool) bun.QueryBuilder {
46-
sqlExpr := e.Value
48+
func (e ValueExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunMode) bun.QueryBuilder {
49+
return applyValueToBunQuery(e.Value, false, qb, column, mode)
50+
}
51+
52+
func (e AndExpr) applyToBunQuery(qb bun.QueryBuilder, column string, _ bunMode) bun.QueryBuilder {
53+
// applys all sub expressions
54+
return slicest.ReduceD(e.Exprs, qb, func(expr Expr, qb bun.QueryBuilder) bun.QueryBuilder {
55+
//
56+
switch expr.(type) {
57+
case OrExpr:
58+
return applyBracesToBunQuery(expr, qb, column, bunAnd)
59+
default:
60+
return expr.applyToBunQuery(qb, column, bunAnd)
61+
}
62+
})
63+
}
64+
65+
func (e OrExpr) applyToBunQuery(qb bun.QueryBuilder, column string, _ bunMode) bun.QueryBuilder {
66+
// applys all sub expressions
67+
return slicest.ReduceD(e.Exprs, qb, func(expr Expr, qb bun.QueryBuilder) bun.QueryBuilder {
68+
switch expr.(type) {
69+
case AndExpr, OrExpr:
70+
return applyBracesToBunQuery(expr, qb, column, bunOr)
71+
default:
72+
return expr.applyToBunQuery(qb, column, bunOr)
73+
}
74+
})
75+
}
76+
77+
func (e NotExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunMode) bun.QueryBuilder {
78+
// make sure, NotExpr contains only a ValueExpr
79+
expr, ok := e.Expr.(ValueExpr)
80+
if !ok {
81+
panic("NotExpr contained non ValueExpr. This behavior is not compatible with bun.QueryBuilder and should have been avoided by pushNegatesToValues().")
82+
}
83+
84+
return applyValueToBunQuery(expr.Value, true, qb, column, mode)
85+
}
86+
87+
func applyBracesToBunQuery(e Expr, qb bun.QueryBuilder, column string, mode bunMode) bun.QueryBuilder {
88+
var seperator string
89+
switch mode {
90+
case bunAnd:
91+
seperator = " AND "
92+
case bunOr:
93+
seperator = " OR "
94+
}
95+
96+
return qb.WhereGroup(seperator, func(qb bun.QueryBuilder) bun.QueryBuilder {
97+
return e.applyToBunQuery(qb, column, mode)
98+
})
99+
}
100+
func applyValueToBunQuery(value string, negated bool, qb bun.QueryBuilder, column string, mode bunMode) bun.QueryBuilder {
101+
sqlExpr := value
47102

48103
// escape special chars
49104
sqlExpr = slicest.ReduceD([]rune(bunEscapedChars), sqlExpr, func(char rune, sqlexpr string) string {
@@ -59,7 +114,7 @@ func (e ValueExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunM
59114

60115
// setup negation string
61116
var queryNot string
62-
if negate {
117+
if negated {
63118
queryNot = " NOT"
64119
}
65120

@@ -74,39 +129,20 @@ func (e ValueExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunM
74129
}
75130
}
76131

77-
func (e AndExpr) applyToBunQuery(qb bun.QueryBuilder, column string, _ bunMode, negate bool) bun.QueryBuilder {
78-
// applys all sub expressions
79-
return slicest.ReduceD(e.Exprs, qb, func(expr Expr, qb bun.QueryBuilder) bun.QueryBuilder {
80-
// flips mode AND to OR, if negated
81-
return expr.applyToBunQuery(qb, column, bunMode(!negate), negate)
82-
})
83-
}
84-
85-
func (e OrExpr) applyToBunQuery(qb bun.QueryBuilder, column string, _ bunMode, negate bool) bun.QueryBuilder {
86-
// applys all sub expressions
87-
return slicest.ReduceD(e.Exprs, qb, func(expr Expr, qb bun.QueryBuilder) bun.QueryBuilder {
88-
// flips mode OR to AND, if negated
89-
return expr.applyToBunQuery(qb, column, bunMode(negate), negate)
90-
})
91-
}
92-
93-
func (e NotExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunMode, negate bool) bun.QueryBuilder {
94-
// flips negate for subexpression
95-
return e.Expr.applyToBunQuery(qb, column, mode, !negate)
96-
}
97-
98-
func (e BracesExpr) applyToBunQuery(qb bun.QueryBuilder, column string, mode bunMode, negate bool) bun.QueryBuilder {
99-
var seperator string
100-
switch mode {
101-
case bunAnd:
102-
seperator = " AND "
103-
case bunOr:
104-
seperator = " OR "
132+
func pushNegatesToValues(expr Expr) Expr {
133+
switch expr := expr.(type) {
134+
case NotExpr:
135+
switch expr.Expr.(type) {
136+
case AndExpr, OrExpr:
137+
return pushNegatesToValues(expr.tryResolve())
138+
default:
139+
return expr
140+
}
141+
case AndExpr:
142+
return AndExpr{slicest.Map(expr.Exprs, func(expr Expr) Expr { return pushNegatesToValues(expr) })}
143+
case OrExpr:
144+
return OrExpr{slicest.Map(expr.Exprs, func(expr Expr) Expr { return pushNegatesToValues(expr) })}
145+
default:
146+
return expr
105147
}
106-
107-
return qb.WhereGroup(seperator, func(qb bun.QueryBuilder) bun.QueryBuilder {
108-
// braces them self can't be negated.
109-
// flipping mode and passing negated flag, has the effect of negating the whole braces content.
110-
return e.Expr.applyToBunQuery(qb, column, !bunMode(negate), negate)
111-
})
112148
}

tags/bun_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"fmt"
88
"testing"
99

10-
"github.com/toeirei/keymaster/tags"
10+
tags "github.com/toeirei/keymaster/tags"
1111
"github.com/uptrace/bun"
1212
"github.com/uptrace/bun/dialect/sqlitedialect"
1313
"github.com/uptrace/bun/schema"
@@ -38,7 +38,7 @@ var bunTests = []bunTest{
3838
// --- Complex Nesting ---
3939
{"(aws|gcp)&!legacy", "SELECT * WHERE ((tags LIKE '%|aws|%' ESCAPE '!') OR (tags LIKE '%|gcp|%' ESCAPE '!')) AND (tags NOT LIKE '%|legacy|%' ESCAPE '!')"},
4040
{"auth&(**-admin|super-**)", "SELECT * WHERE (tags LIKE '%|auth|%' ESCAPE '!') AND ((tags LIKE '%|%!-admin|%' ESCAPE '!') OR (tags LIKE '%|super!-%|%' ESCAPE '!'))"},
41-
{"!(test|stage)&prod", "SELECT * WHERE ((tags NOT LIKE '%|test|%' ESCAPE '!') AND (tags NOT LIKE '%|stage|%' ESCAPE '!')) AND (tags LIKE '%|prod|%' ESCAPE '!')"},
41+
{"!(test|stage)&prod", "SELECT * WHERE (tags NOT LIKE '%|test|%' ESCAPE '!') AND (tags NOT LIKE '%|stage|%' ESCAPE '!') AND (tags LIKE '%|prod|%' ESCAPE '!')"},
4242

4343
// --- Edge Cases ---
4444
{"**", "SELECT * WHERE (tags LIKE '%|%|%' ESCAPE '!')"},

0 commit comments

Comments
 (0)