Skip to content

Commit ca07988

Browse files
Dragomir-Ivanovsmyrman
authored andcommitted
Fix filtering for null fields
1 parent e5f1d6c commit ca07988

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

rest/method_get_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,60 @@ func TestGetListFieldHandler(t *testing.T) {
286286
t.Run(n, tc.Test)
287287
}
288288
}
289+
290+
func TestGetListFilter(t *testing.T) {
291+
sharedInit := func() *requestTestVars {
292+
s := mem.NewHandler()
293+
s.Insert(context.TODO(), []*resource.Item{
294+
{ID: "1", Payload: map[string]interface{}{"id": 1, "foo": "bar"}},
295+
{ID: "2", Payload: map[string]interface{}{"id": 2, "foo": nil}},
296+
{ID: "3", Payload: map[string]interface{}{"id": 3, "foo2": "bar2"}},
297+
})
298+
299+
idx := resource.NewIndex()
300+
idx.Bind("foo", schema.Schema{
301+
Fields: schema.Fields{
302+
"foo": {
303+
Filterable: true,
304+
Validator: &schema.AnyOf{&schema.Null{}, &schema.String{}},
305+
},
306+
},
307+
}, s, resource.DefaultConf)
308+
309+
return &requestTestVars{
310+
Index: idx,
311+
Storers: map[string]resource.Storer{"foo": s},
312+
}
313+
}
314+
315+
tests := map[string]requestTest{
316+
`filter:string`: {
317+
Init: sharedInit,
318+
NewRequest: func() (*http.Request, error) {
319+
return http.NewRequest("GET", `/foo?filter={foo:""}`, nil)
320+
},
321+
ResponseCode: 200,
322+
ResponseBody: `[]`,
323+
},
324+
`filter:string2`: {
325+
Init: sharedInit,
326+
NewRequest: func() (*http.Request, error) {
327+
return http.NewRequest("GET", `/foo?filter={foo:"bar"}`, nil)
328+
},
329+
ResponseCode: 200,
330+
ResponseBody: `[{"foo":"bar","id":1}]`,
331+
},
332+
`filter:null`: {
333+
Init: sharedInit,
334+
NewRequest: func() (*http.Request, error) {
335+
return http.NewRequest("GET", `/foo?filter={foo:null}`, nil)
336+
},
337+
ResponseCode: 200,
338+
ResponseBody: `[{"foo":null,"id":2},{"foo2":"bar2","id":3}]`,
339+
},
340+
}
341+
for n, tc := range tests {
342+
tc := tc // capture range variable
343+
t.Run(n, tc.Test)
344+
}
345+
}

schema/query/predicate_parser.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ func (p *predicateParser) parseValue() (Value, error) {
305305
return p.parseValues()
306306
case 't', 'f':
307307
return p.parseBool()
308+
case 'n':
309+
return p.parseNull()
308310
default:
309311
if (c >= '0' && c <= '9') || c == '-' {
310312
// Parse a number
@@ -394,6 +396,16 @@ func (p *predicateParser) parseBool() (bool, error) {
394396
return false, errors.New("not a boolean")
395397
}
396398

399+
// parseNull parses a JSON Null value.
400+
func (p *predicateParser) parseNull() (Value, error) {
401+
c := p.peek()
402+
if c == 'n' && p.pos+4 <= len(p.query) && p.query[p.pos:p.pos+4] == "null" {
403+
p.pos += 4
404+
return nil, nil
405+
}
406+
return nil, errors.New("not null")
407+
}
408+
397409
// parseNumber parses a number as float.
398410
func (p *predicateParser) parseNumber() (float64, error) {
399411
end := p.pos

schema/query/predicate_parser_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ func TestParse(t *testing.T) {
4242
Predicate{&Equal{Field: "foo", Value: false}},
4343
nil,
4444
},
45+
{
46+
`{"foo": null}`,
47+
Predicate{&Equal{Field: "foo", Value: nil}},
48+
nil,
49+
},
4550
{
4651
`{"foo": "bar \n\" ❤️"}`,
4752
Predicate{&Equal{Field: "foo", Value: "bar \n\" ❤️"}},
@@ -214,6 +219,11 @@ func TestParse(t *testing.T) {
214219
Predicate{},
215220
errors.New("char 8: foo: not a string: unexpected EOF"),
216221
},
222+
{
223+
`{"foo": nul`,
224+
Predicate{},
225+
errors.New("char 8: foo: not null"),
226+
},
217227
{
218228
`{"foo": {"$ne": {"bar", "baz"}}}`,
219229
Predicate{},

0 commit comments

Comments
 (0)