dialect/sql: add has-key for json predicates (#723)

This commit is contained in:
Ariel Mashraki
2020-09-02 17:57:20 +03:00
committed by GitHub
parent 6299166f3b
commit 6f960ec392
2 changed files with 65 additions and 20 deletions

View File

@@ -897,6 +897,18 @@ func (p *Predicate) EQ(col string, arg interface{}) *Predicate {
})
}
// JSONHasKey calls Predicate.JSONHasKey.
func JSONHasKey(col string, path ...string) *Predicate {
return P().JSONHasKey(col, path...)
}
// JSONHasKey return a predicate for checking that a JSON key exists and not NULL;
func (p *Predicate) JSONHasKey(col string, path ...string) *Predicate {
return p.Append(func(b *Builder) {
b.JSONPath(col, Path(path...)).WriteOp(OpNotNull)
})
}
// NEQ returns a "<>" predicate.
func NEQ(col string, value interface{}) *Predicate {
return P().NEQ(col, value)
@@ -1891,33 +1903,42 @@ func (b *Builder) WriteString(s string) *Builder {
type Op int
const (
OpEQ Op = iota // logical and.
OpNEQ // <>
OpGT // >
OpGTE // >=
OpLT // <
OpLTE // <=
OpIn // IN
OpNotIn // NOT IN
OpLike // LIKE
OpEQ Op = iota // logical and.
OpNEQ // <>
OpGT // >
OpGTE // >=
OpLT // <
OpLTE // <=
OpIn // IN
OpNotIn // NOT IN
OpLike // LIKE
OpIsNull // IS NULL
OpNotNull // IS NOT NULL
)
var ops = [...]string{
OpEQ: "=",
OpNEQ: "<>",
OpGT: ">",
OpGTE: ">=",
OpLT: "<",
OpLTE: "<=",
OpIn: "IN",
OpNotIn: "NOT IN",
OpLike: "LIKE",
OpEQ: "=",
OpNEQ: "<>",
OpGT: ">",
OpGTE: ">=",
OpLT: "<",
OpLTE: "<=",
OpIn: "IN",
OpNotIn: "NOT IN",
OpLike: "LIKE",
OpIsNull: "IS NULL",
OpNotNull: "IS NOT NULL",
}
// WriteOp writes an operator to the builder.
func (b *Builder) WriteOp(op Op) *Builder {
if op >= OpEQ && op <= OpLike {
switch {
case op >= OpEQ && op <= OpLike:
b.Pad().WriteString(ops[op]).Pad()
case op == OpIsNull || op == OpNotNull:
b.Pad().WriteString(ops[op])
default:
panic(fmt.Sprintf("invalid op %d", op))
}
return b
}
@@ -2008,12 +2029,13 @@ func (p *JSONPath) writeTo(b *Builder) {
//
// b.JSONPath("column", Path("a", "b", "[1]", "c"), Cast("int"))
//
func (b *Builder) JSONPath(ident string, opts ...JSONOption) {
func (b *Builder) JSONPath(ident string, opts ...JSONOption) *Builder {
path := &JSONPath{ident: ident}
for i := range opts {
opts[i](path)
}
path.writeTo(b)
return b
}
// Arg appends an input argument to the builder.

View File

@@ -1259,6 +1259,29 @@ WHERE
wantQuery: `SELECT * FROM "test" WHERE nlevel("path") > $1`,
wantArgs: []interface{}{1},
},
{
input: Dialect(dialect.Postgres).
Select("*").
From(Table("test")).
Where(P(func(b *Builder) {
b.WriteString("nlevel(").Ident("path").WriteByte(')').WriteOp(OpGT).Arg(1)
})),
wantQuery: `SELECT * FROM "test" WHERE nlevel("path") > $1`,
wantArgs: []interface{}{1},
},
{
input: Select("*").
From(Table("test")).
Where(JSONHasKey("j", "a", "*", "c")),
wantQuery: "SELECT * FROM `test` WHERE JSON_EXTRACT(`j`, \"$.a.*.c\") IS NOT NULL",
},
{
input: Dialect(dialect.Postgres).
Select("*").
From(Table("test")).
Where(JSONHasKey("j", "a", "b", "c")),
wantQuery: `SELECT * FROM "test" WHERE "j"->'a'->'b'->'c' IS NOT NULL`,
},
}
for i, tt := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {