Skip to content

Commit 1d56cc6

Browse files
Implement $nin
1 parent 884c17f commit 1d56cc6

File tree

4 files changed

+31
-5
lines changed

4 files changed

+31
-5
lines changed

filter/converter.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,16 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string
164164
return "", nil, fmt.Errorf("$and as scalar operator not supported")
165165
case "$not":
166166
return "", nil, fmt.Errorf("$not as scalar operator not supported")
167-
case "$in":
167+
case "$in", "$nin":
168168
if !isScalarSlice(v[operator]) {
169169
return "", nil, fmt.Errorf("invalid value for $in operator (must array of primatives): %v", v[operator])
170170
}
171-
inner = append(inner, fmt.Sprintf("(%s = ANY($%d))", c.columnName(key), paramIndex))
171+
neg := ""
172+
if operator == "$nin" {
173+
// `column != ANY(...)` does not work, so we need to do `NOT column = ANY(...)` instead.
174+
neg = "NOT "
175+
}
176+
inner = append(inner, fmt.Sprintf("(%s%s = ANY($%d))", neg, c.columnName(key), paramIndex))
172177
paramIndex++
173178
if c.arrayDriver != nil {
174179
v[operator] = c.arrayDriver(v[operator])

filter/converter_test.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,31 @@ func TestConverter_Convert(t *testing.T) {
8282
nil,
8383
},
8484
{
85-
"in-array operator invalid value",
85+
"simple $in",
8686
nil,
8787
`{"status": {"$in": [{"hacker": 1}, "OPEN"]}}`,
8888
``,
8989
nil,
9090
fmt.Errorf("invalid value for $in operator (must array of primatives): [map[hacker:1] OPEN]"),
9191
},
9292
{
93-
"in-array operator scalar value",
93+
"simple $nin",
94+
nil,
95+
`{"status": {"$nin": ["NEW", "OPEN"]}}`,
96+
`(NOT "status" = ANY($1))`,
97+
[]any{[]any{"NEW", "OPEN"}},
98+
nil,
99+
},
100+
{
101+
"$in scalar value",
94102
nil,
95103
`{"status": {"$in": "text"}}`,
96104
``,
97105
nil,
98106
fmt.Errorf("invalid value for $in operator (must array of primatives): text"),
99107
},
100108
{
101-
"in-array operator with null value",
109+
"$in with null value",
102110
nil,
103111
`{"status": {"$in": ["guest", null]}}`,
104112
`("status" = ANY($1))`,

fuzz/fuzz_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func FuzzConverter(f *testing.F) {
2121
`{"b": 1, "c": 2, "a": 3}`,
2222
`{"status": {"$in": ["NEW", "OPEN"]}}`,
2323
`{"status": {"$in": [{"hacker": 1}, "OPEN"]}}`,
24+
`{"status": {"$nin": ["NEW", "OPEN"]}}`,
2425
`{"status": {"$in": "text"}}`,
2526
`{"status": {"$in": ["guest", null]}}`,
2627
`{"$or": [{"name": "John"}, {"name": "Doe"}]}`,

integration/postgres_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,18 @@ func TestIntegration_BasicOperators(t *testing.T) {
345345
[]int{1, 2, 3, 4, 5, 6, 7, 8, 10},
346346
nil,
347347
},
348+
{
349+
"$in",
350+
`{"level": {"$in": [20, 30, 40]}}`,
351+
[]int{2, 3, 4},
352+
nil,
353+
},
354+
{
355+
"$nin",
356+
`{"level": {"$nin": [20, 30, 40]}}`,
357+
[]int{1, 5, 6, 7, 8, 9, 10},
358+
nil,
359+
},
348360
}
349361

350362
for _, tt := range tests {

0 commit comments

Comments
 (0)