Skip to content

Commit 8e2964b

Browse files
Implement $exists
1 parent a827073 commit 8e2964b

File tree

4 files changed

+47
-0
lines changed

4 files changed

+47
-0
lines changed

filter/converter.go

+18
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,24 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string
150150
v[operator] = c.arrayDriver(v[operator])
151151
}
152152
values = append(values, v[operator])
153+
case "$exists":
154+
// $exists only works on jsonb columns, so we need to check if the key is in the JSONB data first.
155+
isNestedColumn := c.nestedColumn != ""
156+
for _, exemption := range c.nestedExemptions {
157+
if exemption == key {
158+
isNestedColumn = false
159+
break
160+
}
161+
}
162+
if !isNestedColumn {
163+
// There is no way in Postgres to check if a column exists on a table.
164+
return "", nil, fmt.Errorf("$exists operator not supported on non-nested jsonb columns")
165+
}
166+
neg := ""
167+
if v[operator] == false {
168+
neg = "NOT "
169+
}
170+
inner = append(inner, fmt.Sprintf("(%sjsonb_path_match(%s, 'exists($.%s)'))", neg, c.nestedColumn, key))
153171
default:
154172
value := v[operator]
155173
op, ok := BasicOperatorMap[operator]

filter/converter_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,22 @@ func TestConverter_Convert(t *testing.T) {
216216
nil,
217217
fmt.Errorf("invalid column name: \"bla = 1 --"),
218218
},
219+
{
220+
"$exists on normal column",
221+
nil,
222+
`{"name": {"$exists": false}}`,
223+
``,
224+
nil,
225+
fmt.Errorf("$exists operator not supported on non-nested jsonb columns"),
226+
},
227+
{
228+
"$exists jsonb column",
229+
filter.WithNestedJSONB("meta"),
230+
`{"name": {"$exists": false}}`,
231+
`(NOT jsonb_path_match(meta, 'exists($.name)'))`,
232+
nil,
233+
nil,
234+
},
219235
}
220236

221237
for _, tt := range tests {

fuzz/fuzz_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func FuzzConverter(f *testing.F) {
3333
`{"name": {}}`,
3434
`{"$or": []}`,
3535
`{"status": {"$in": []}}`,
36+
`{"name": {"$exists": false}}`,
3637
}
3738
for _, tc := range tcs {
3839
f.Add(tc)

integration/postgres_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,18 @@ func TestIntegration_BasicOperators(t *testing.T) {
303303
[]int{7, 8, 9, 10},
304304
nil,
305305
},
306+
{
307+
`jsonb exists`,
308+
`{"pet": {"$exists": false}}`,
309+
[]int{9},
310+
nil,
311+
},
312+
{
313+
`jsonb exists`,
314+
`{"pet": {"$exists": true}}`,
315+
[]int{1, 2, 3, 4, 5, 6, 7, 8, 10},
316+
nil,
317+
},
306318
}
307319

308320
for _, tt := range tests {

0 commit comments

Comments
 (0)