Skip to content
This repository was archived by the owner on Jan 15, 2026. It is now read-only.

Commit e3a852e

Browse files
authored
Bennett/null filters (#13)
1 parent 3ad6362 commit e3a852e

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

filter.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func (bf *BaseFilter) tpuf_SerializeFilter() interface{} {
4444
}
4545

4646
func (f *BaseFilter) MarshalJSON() ([]byte, error) {
47+
if f == nil {
48+
return []byte("null"), nil
49+
}
4750
return json.Marshal(f.tpuf_SerializeFilter())
4851
}
4952

@@ -55,15 +58,21 @@ type AndFilter struct {
5558
func (af *AndFilter) tpuf_SerializeFilter() interface{} {
5659
serialized := make([]interface{}, 2)
5760
serialized[0] = "And"
58-
subFilters := make([]interface{}, len(af.Filters))
59-
for i, filter := range af.Filters {
60-
subFilters[i] = filter.tpuf_SerializeFilter()
61+
subFilters := make([]interface{}, 0, len(af.Filters))
62+
for _, filter := range af.Filters {
63+
if filter == nil {
64+
continue
65+
}
66+
subFilters = append(subFilters, filter.tpuf_SerializeFilter())
6167
}
6268
serialized[1] = subFilters
6369
return serialized
6470
}
6571

6672
func (f *AndFilter) MarshalJSON() ([]byte, error) {
73+
if f == nil {
74+
return []byte("null"), nil
75+
}
6776
return json.Marshal(f.tpuf_SerializeFilter())
6877
}
6978

@@ -75,14 +84,20 @@ type OrFilter struct {
7584
func (of *OrFilter) tpuf_SerializeFilter() interface{} {
7685
serialized := make([]interface{}, 2)
7786
serialized[0] = "Or"
78-
subFilters := make([]interface{}, len(of.Filters))
79-
for i, filter := range of.Filters {
80-
subFilters[i] = filter.tpuf_SerializeFilter()
87+
subFilters := make([]interface{}, 0, len(of.Filters))
88+
for _, filter := range of.Filters {
89+
if filter == nil {
90+
continue
91+
}
92+
subFilters = append(subFilters, filter.tpuf_SerializeFilter())
8193
}
8294
serialized[1] = subFilters
8395
return serialized
8496
}
8597

8698
func (f *OrFilter) MarshalJSON() ([]byte, error) {
99+
if f == nil {
100+
return []byte("null"), nil
101+
}
87102
return json.Marshal(f.tpuf_SerializeFilter())
88103
}

filter_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,25 @@ func TestMarshalFilter(t *testing.T) {
105105
},
106106
expected: `["And",[["id","In",[1,2,3]],["key1","Eq","one"],["filename","NotGlob","/vendor/**"],["Or",[["filename","Glob","**.tsx"],["filename","Glob","**.js"]]]]]`,
107107
},
108+
{
109+
name: "And filter with nil subfilter",
110+
filter: &tpuf.AndFilter{
111+
Filters: []tpuf.Filter{
112+
&tpuf.BaseFilter{
113+
Attribute: "attr1",
114+
Operator: tpuf.OpEq,
115+
Value: "value1",
116+
},
117+
nil,
118+
&tpuf.BaseFilter{
119+
Attribute: "attr2",
120+
Operator: tpuf.OpGt,
121+
Value: 10,
122+
},
123+
},
124+
},
125+
expected: `["And",[["attr1","Eq","value1"],["attr2","Gt",10]]]`,
126+
},
108127
}
109128

110129
for _, tt := range tests {

query_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,28 @@ func TestQuery(t *testing.T) {
9999
{ID: "2", Dist: 0},
100100
},
101101
},
102+
{
103+
name: "nil filter",
104+
namespace: "test-namespace",
105+
request: &tpuf.QueryRequest{
106+
Filters: nil,
107+
TopK: 2,
108+
},
109+
httpResponse: &http.Response{
110+
StatusCode: http.StatusOK,
111+
Body: io.NopCloser(bytes.NewBufferString(`[
112+
{"id":"1","dist":0},
113+
{"id":"2","dist":0}
114+
]`)),
115+
},
116+
expectedMethod: http.MethodPost,
117+
expectedURL: "https://api.turbopuffer.com/v1/vectors/test-namespace/query",
118+
expectedBody: `{"top_k":2}`,
119+
expectedResult: []*tpuf.QueryResult{
120+
{ID: "1", Dist: 0},
121+
{ID: "2", Dist: 0},
122+
},
123+
},
102124
{
103125
name: "query error",
104126
namespace: "test-namespace",

0 commit comments

Comments
 (0)