From 494c513d1214dd352c706bc5397dbb3c8d1940ac Mon Sep 17 00:00:00 2001 From: "Tor-Inge J. Eriksen" Date: Fri, 25 May 2018 10:33:51 +0200 Subject: [PATCH 1/3] Add testing for GetField --- schema/query/utils_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/schema/query/utils_test.go b/schema/query/utils_test.go index e36b1769..e7b8bae7 100644 --- a/schema/query/utils_test.go +++ b/schema/query/utils_test.go @@ -34,3 +34,23 @@ func TestIsNumber(t *testing.T) { }) } } + +func TestGetField(t *testing.T) { + cases := []struct { + name string + payload map[string]interface{} + fieldName string + want string + }{ + {"foo", map[string]interface{}{"foo": "bar"}, "foo", "bar"}, + {"foo.bar", map[string]interface{}{"foo": map[string]interface{}{"bar": "baz"}}, "foo.bar", "baz"}, + } + for i := range cases { + tc := cases[i] + t.Run(tc.name, func(t *testing.T) { + if res := getField(tc.payload, tc.fieldName); res != tc.want { + t.Errorf("field = %v, wanted %v", res, tc.want) + } + }) + } +} From 5538b29e325f3efb918afdc4eb6d6d6b2c6c7144 Mon Sep 17 00:00:00 2001 From: "Tor-Inge J. Eriksen" Date: Fri, 25 May 2018 10:34:09 +0200 Subject: [PATCH 2/3] Add tests for nested fields --- schema/query/predicate_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/schema/query/predicate_test.go b/schema/query/predicate_test.go index 6ad51372..49630d08 100644 --- a/schema/query/predicate_test.go +++ b/schema/query/predicate_test.go @@ -106,6 +106,12 @@ func TestMatch(t *testing.T) { {map[string]interface{}{"bar": float64(1)}, false}, }, }, + { + `{"foo.bar": "baz"}`, []test{ + {map[string]interface{}{"foo": map[string]interface{}{"bar": "baz"}}, true}, + {map[string]interface{}{"foo": map[string]interface{}{"bar": "bar"}}, false}, + }, + }, } for i := range tests { tt := tests[i] @@ -143,6 +149,7 @@ func TestString(t *testing.T) { `{"$and": [{"foo": "bar"}, {"foo": "baz"}]}`: `{$and: [{foo: "bar"}, {foo: "baz"}]}`, `{"foo": "bar", "$or": [{"bar": "baz"}, {"bar": "foo"}]}`: `{foo: "bar", $or: [{bar: "baz"}, {bar: "foo"}]}`, `{"foo": ["bar", "baz"]}`: `{foo: ["bar","baz"]}`, + `{"foo.bar": "baz"}`: `{foo.bar: "baz"}`, } for query, want := range tests { q, err := ParsePredicate(query) From ecef32ec714ebc7f87a5010390a34f236bb2dd7e Mon Sep 17 00:00:00 2001 From: "Tor-Inge J. Eriksen" Date: Fri, 25 May 2018 10:34:33 +0200 Subject: [PATCH 3/3] Add support for $in and $nin queries against arrays --- schema/query/predicate.go | 43 +++++++++++++++++++++++++++++----- schema/query/predicate_test.go | 12 ++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/schema/query/predicate.go b/schema/query/predicate.go index 83ef7057..3b863eab 100644 --- a/schema/query/predicate.go +++ b/schema/query/predicate.go @@ -140,11 +140,27 @@ type In struct { // Match implements Expression interface. func (e In) Match(payload map[string]interface{}) bool { value := getField(payload, e.Field) - for _, v := range e.Values { - if reflect.DeepEqual(v, value) { - return true + + // The $in operator in MongoDB allows matching both single values and an + // array of values. + // https://docs.mongodb.com/manual/reference/operator/query/in/ + switch vt := value.(type) { + case []interface{}: + for _, v := range e.Values { + for _, vv := range vt { + if reflect.DeepEqual(v, vv) { + return true + } + } + } + default: + for _, v := range e.Values { + if reflect.DeepEqual(v, value) { + return true + } } } + return false } @@ -171,9 +187,24 @@ type NotIn struct { // Match implements Expression interface. func (e NotIn) Match(payload map[string]interface{}) bool { value := getField(payload, e.Field) - for _, v := range e.Values { - if reflect.DeepEqual(v, value) { - return false + + // The $nin operator in MongoDB allows matching both single values and an + // array of values. + // https://docs.mongodb.com/manual/reference/operator/query/nin/ + switch vt := value.(type) { + case []interface{}: + for _, v := range e.Values { + for _, vv := range vt { + if reflect.DeepEqual(v, vv) { + return false + } + } + } + default: + for _, v := range e.Values { + if reflect.DeepEqual(v, value) { + return false + } } } return true diff --git a/schema/query/predicate_test.go b/schema/query/predicate_test.go index 49630d08..34da7a0a 100644 --- a/schema/query/predicate_test.go +++ b/schema/query/predicate_test.go @@ -68,12 +68,24 @@ func TestMatch(t *testing.T) { {map[string]interface{}{"foo": "foo"}, false}, }, }, + { + `{"foo": {"$in": ["baz"]}}`, []test{ + {map[string]interface{}{"foo": []interface{}{"baz"}}, true}, + {map[string]interface{}{"foo": []interface{}{"bar"}}, false}, + }, + }, { `{"foo": {"$nin": ["bar", "baz"]}}`, []test{ {map[string]interface{}{"foo": "bar"}, false}, {map[string]interface{}{"foo": "foo"}, true}, }, }, + { + `{"foo": {"$nin": ["baz"]}}`, []test{ + {map[string]interface{}{"foo": []interface{}{"baz"}}, false}, + {map[string]interface{}{"foo": []interface{}{"bar"}}, true}, + }, + }, { `{"$or": [{"foo": "bar"}, {"bar": 1}]}`, []test{ {map[string]interface{}{"foo": "bar"}, true},