Skip to content

Commit 0b1842c

Browse files
Fix empty objects (#5)
A top level empty object returns a TRUE condition as you're not filtering on anything. Nested empty objects will return an error as they most likely indicate an error. --------- Co-authored-by: Koen Bollen <[email protected]>
1 parent ec4961e commit 0b1842c

File tree

6 files changed

+73
-1
lines changed

6 files changed

+73
-1
lines changed

examples/basic_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ func ExampleNewConverter() {
2929
// []interface {}{"2020-01-01T00:00:00Z", "John"}
3030
}
3131

32+
func ExampleNewConverter_emptyfilter() {
33+
converter := filter.NewConverter(filter.WithEmptyCondition("TRUE")) // The default is FALSE if you don't change it.
34+
35+
mongoFilterQuery := `{}`
36+
conditions, _, err := converter.Convert([]byte(mongoFilterQuery), 1)
37+
if err != nil {
38+
// handle error
39+
}
40+
41+
fmt.Println(conditions)
42+
// Output:
43+
// TRUE
44+
}
45+
3246
func ExampleNewConverter_nonIsolatedConditions() {
3347
converter := filter.NewConverter()
3448

filter/converter.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ type Converter struct {
2626
driver.Valuer
2727
sql.Scanner
2828
}
29+
emptyCondition string
2930
}
3031

3132
// NewConverter creates a new Converter with optional nested JSONB field mapping.
3233
//
3334
// Note: When using github.com/lib/pq, the filter.WithArrayDriver should be set to pq.Array.
3435
func NewConverter(options ...Option) *Converter {
35-
converter := &Converter{}
36+
converter := &Converter{
37+
emptyCondition: "FALSE",
38+
}
3639
for _, option := range options {
3740
if option != nil {
3841
option(converter)
@@ -56,6 +59,10 @@ func (c *Converter) Convert(query []byte, startAtParameterIndex int) (conditions
5659
return "", nil, err
5760
}
5861

62+
if len(mongoFilter) == 0 {
63+
return c.emptyCondition, []any{}, nil
64+
}
65+
5966
conditions, values, err = c.convertFilter(mongoFilter, startAtParameterIndex)
6067
if err != nil {
6168
return "", nil, err
@@ -68,6 +75,10 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string
6875
var conditions []string
6976
var values []any
7077

78+
if len(filter) == 0 {
79+
return "", nil, fmt.Errorf("empty objects not allowed")
80+
}
81+
7182
keys := []string{}
7283
for key := range filter {
7384
keys = append(keys, key)

filter/converter_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,21 @@ func TestConverter_Convert(t *testing.T) {
193193
[]any{[]any{}},
194194
nil,
195195
},
196+
{
197+
"empty filter",
198+
nil,
199+
`{}`,
200+
`FALSE`,
201+
[]any{},
202+
nil,
203+
}, {
204+
"empty or conditions",
205+
nil,
206+
`{"$or": [{}, {}]}`,
207+
``,
208+
nil,
209+
fmt.Errorf("empty objects not allowed"),
210+
},
196211
}
197212

198213
for _, tt := range tests {
@@ -245,3 +260,17 @@ func TestConverter_Convert_startAtParameterIndex(t *testing.T) {
245260
t.Errorf("Converter.Convert(..., 1234551231231231231) error = %v, want nil", err)
246261
}
247262
}
263+
264+
func TestConverter_WithEmptyCondition(t *testing.T) {
265+
c := filter.NewConverter(filter.WithEmptyCondition("TRUE"))
266+
conditions, values, err := c.Convert([]byte(`{}`), 1)
267+
if err != nil {
268+
t.Fatal(err)
269+
}
270+
if want := "TRUE"; conditions != want {
271+
t.Errorf("Converter.Convert() conditions = %v, want %v", conditions, want)
272+
}
273+
if len(values) != 0 {
274+
t.Errorf("Converter.Convert() values = %v, want nil", values)
275+
}
276+
}

filter/options.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ func WithArrayDriver(f func(a any) interface {
3939
c.arrayDriver = f
4040
}
4141
}
42+
43+
// WithEmptyCondition is an option to specify the condition to be used when the
44+
// input query filter is empty. (e.g. you have a query with no conditions)
45+
//
46+
// The default value is `FALSE`, because it's the safer choice in most cases.
47+
func WithEmptyCondition(condition string) Option {
48+
return func(c *Converter) {
49+
c.emptyCondition = condition
50+
}
51+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
go test fuzz v1
2+
string("{\"$or\":[{},{}]}")

integration/postgres_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ func TestIntegration_BasicOperators(t *testing.T) {
291291
nil,
292292
errors.New("pq: invalid input syntax for type integer: \"town1\""),
293293
},
294+
{
295+
`empty object`,
296+
`{}`, // Should return FALSE as the condition.
297+
[]int{},
298+
nil,
299+
},
294300
}
295301

296302
for _, tt := range tests {

0 commit comments

Comments
 (0)