Skip to content

Commit

Permalink
Merge pull request #1045 from go-kivik/MangoSplitKeys
Browse files Browse the repository at this point in the history
x/mango: Split JSON keys more intelligently
  • Loading branch information
flimzy authored Sep 11, 2024
2 parents 4c26937 + 5664533 commit ada1fcc
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 1 deletion.
32 changes: 31 additions & 1 deletion x/mango/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (f *fieldNode) Match(doc interface{}) bool {
val := doc

// Traverse nested fields (e.g. "foo.bar.baz")
segments := strings.Split(f.field, ".")
segments := SplitKeys(f.field)
for _, segment := range segments {
m, ok := val.(map[string]interface{})
if !ok {
Expand Down Expand Up @@ -371,3 +371,33 @@ func cmpSelectors(a, b Node) int {
}
return 0
}

// SplitKeys splits a field into its component keys. For example,
// "foo.bar" is split into `["foo", "bar"]`. Escaped dots are not treated
// as separators, so `"foo\\.bar"` becomes `["foo.bar"]`.
func SplitKeys(field string) []string {
var escaped bool
result := []string{}
word := make([]byte, 0, len(field))
for _, ch := range field {
if escaped {
word = append(word, byte(ch))
escaped = false
continue
}
if ch == '\\' {
escaped = true
continue
}
if ch == '.' {
result = append(result, string(word))
word = word[:0]
continue
}
word = append(word, byte(ch))
}
if escaped {
word = append(word, '\\')
}
return append(result, string(word))
}
64 changes: 64 additions & 0 deletions x/mango/selector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package mango

import (
"testing"

"github.com/google/go-cmp/cmp"
)

func TestSplitKeys(t *testing.T) {
tests := []struct {
input string
want []string
}{
{
input: "foo.bar.baz",
want: []string{"foo", "bar", "baz"},
},
{
input: "foo",
want: []string{"foo"},
},
{
input: "",
want: []string{""},
},
{
input: "foo\\.bar",
want: []string{"foo.bar"},
},
{
input: "foo\\\\.bar",
want: []string{"foo\\", "bar"},
},
{
input: "foo\\",
want: []string{"foo\\"},
},
{
input: "foo.",
want: []string{"foo", ""},
},
}

for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := SplitKeys(tt.input)
if d := cmp.Diff(tt.want, got); d != "" {
t.Errorf("unexpected keys (-want, +got): %s", d)
}
})
}
}

0 comments on commit ada1fcc

Please sign in to comment.