Skip to content

Commit

Permalink
Merge pull request #1044 from skaggmannen/master
Browse files Browse the repository at this point in the history
x/mango: Fix evaluation of `$exists: false` always failing
  • Loading branch information
flimzy authored Sep 11, 2024
2 parents 0b67452 + a797e87 commit 4c26937
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 23 deletions.
5 changes: 5 additions & 0 deletions x/collate/collate.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ func CompareObject(a, b interface{}) int {
return -1
}
return 1
case jsonTypeNull:
if b == nil {
return 0
}
return -1
case jsonTypeNumber:
return int(a.(float64) - b.(float64))
case jsonTypeString:
Expand Down
87 changes: 71 additions & 16 deletions x/mango/match_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,35 +129,43 @@ func TestMatch(t *testing.T) {
want: false,
})
tests.Add("exists", test{
sel: &conditionNode{
op: OpExists,
cond: true,
sel: &fieldNode{
field: "foo",
cond: &conditionNode{op: OpExists, cond: true},
},
doc: map[string]interface{}{
"foo": "bar",
},
doc: "foo",
want: true,
})
tests.Add("!exists", test{
sel: &conditionNode{
op: OpExists,
cond: false,
sel: &fieldNode{
field: "baz",
cond: &conditionNode{op: OpExists, cond: true},
},
doc: map[string]interface{}{
"foo": "bar",
},
doc: "foo",
want: false,
})
tests.Add("not exists", test{
sel: &conditionNode{
op: OpExists,
cond: false,
sel: &fieldNode{
field: "baz",
cond: &conditionNode{op: OpExists, cond: false},
},
doc: map[string]interface{}{
"foo": "bar",
},
doc: nil,
want: true,
})
tests.Add("!not exists", test{
sel: &conditionNode{
op: OpExists,
cond: true,
sel: &fieldNode{
field: "baz",
cond: &conditionNode{op: OpExists, cond: true},
},
doc: map[string]interface{}{
"foo": "bar",
},
doc: nil,
want: false,
})
tests.Add("type, null", test{
Expand Down Expand Up @@ -436,6 +444,53 @@ func TestMatch(t *testing.T) {
doc: "bar",
want: false,
})
tests.Add("field selector, nested", test{
sel: &fieldNode{
field: "foo.bar.baz",
cond: &conditionNode{
op: OpEqual,
cond: "hello",
},
},
doc: map[string]interface{}{
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"baz": "hello",
},
},
},
want: true,
})
tests.Add("field selector, nested, non-object", test{
sel: &fieldNode{
field: "foo.bar.baz",
cond: &conditionNode{
op: OpEqual,
cond: "hello",
},
},
doc: map[string]interface{}{
"foo": "hello",
},
want: false,
})
tests.Add("!field selector, nested", test{
sel: &fieldNode{
field: "foo.bar.baz",
cond: &conditionNode{
op: OpEqual,
cond: "hello",
},
},
doc: map[string]interface{}{
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"buzz": "hello",
},
},
},
want: false,
})
tests.Add("elemMatch", test{
sel: &fieldNode{
field: "foo",
Expand Down
20 changes: 13 additions & 7 deletions x/mango/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,20 @@ func (f *fieldNode) String() string {
}

func (f *fieldNode) Match(doc interface{}) bool {
m, ok := doc.(map[string]interface{})
if !ok {
return false
}
val, ok := m[f.field]
if !ok {
return false
val := doc

// Traverse nested fields (e.g. "foo.bar.baz")
segments := strings.Split(f.field, ".")
for _, segment := range segments {
m, ok := val.(map[string]interface{})
if !ok {
return false
}

val = m[segment]
}

// Even if the field does not exist we need to pass it to the condition expression because of `$exists`
return f.cond.Match(val)
}

Expand Down

0 comments on commit 4c26937

Please sign in to comment.