Skip to content

Commit

Permalink
add error handling for no json
Browse files Browse the repository at this point in the history
  • Loading branch information
iamskp11 committed Sep 19, 2024
1 parent 06b6336 commit 144e03a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 5 deletions.
17 changes: 12 additions & 5 deletions internal/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -3574,15 +3574,22 @@ func evalJSONOBJKEYS(args []string, store *dstore.Store) []byte {
}

jsonData := obj.Value
_,err := sonic.Marshal(jsonData)
if err != nil {
return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
}
keysList := make([]interface{},0,1)
// If path is root, return all keys of the entire JSON
if path == defaultRootPath {
keys := make([]string,0)
for key := range jsonData.(map[string]interface{}) {
keys = append(keys, key)
if utils.GetJSONFieldType(jsonData) == utils.ObjectType {
keys := make([]string,0)
for key := range jsonData.(map[string]interface{}) {
keys = append(keys, key)
}
keysList = append(keysList, keys)
return clientio.Encode(keysList, false)
}
keysList = append(keysList, keys)
return clientio.Encode(keysList, false)
return diceerrors.NewErrWithFormattedMessage(diceerrors.WrongTypeErr)
}

// Parse the JSONPath expression
Expand Down
148 changes: 148 additions & 0 deletions internal/eval/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func TestEval(t *testing.T) {
testEvalGETEX(t, store)
testEvalJSONNUMINCRBY(t, store)
testEvalCOMMAND(t, store)
testEvalJSONOBJKEYS(t, store)
}

func testEvalPING(t *testing.T, store *dstore.Store) {
Expand Down Expand Up @@ -2586,3 +2587,150 @@ func testEvalCOMMAND(t *testing.T, store *dstore.Store) {

runEvalTests(t, tests, evalCommand, store)
}

func testEvalJSONOBJKEYS(t *testing.T, store *dstore.Store) {
tests := map[string]evalTestCase{
"nil value": {
setup: func() {},
input: nil,
output: []byte("-ERR wrong number of arguments for 'json.objkeys' command\r\n"),
},
"empty args": {
setup: func() {},
input: []string{},
output: []byte("-ERR wrong number of arguments for 'json.objkeys' command\r\n"),
},
"key does not exist": {
setup: func() {},
input: []string{"NONEXISTENT_KEY"},
output: clientio.RespNIL,
},
"root not object": {
setup: func() {
key := "EXISTING_KEY"
value := "[1]"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY"},
output: []byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"),
},
"root object objkeys": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY"},
output: clientio.Encode([]interface{}{[]interface{}{"name", "age", "city"},}, false),
},
"wildcard no object objkeys": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"name\":\"John\",\"age\":30,\"pets\":null,\"languages\":[\"python\",\"golang\"],\"flag\":false}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY", "$.*"},
output: []byte("*5\r\n$-1\r\n$-1\r\n$-1\r\n$-1\r\n$-1\r\n"),
},
"subpath object objkeys": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"person\":{\"name\":\"John\",\"age\":30},\"languages\":[\"python\",\"golang\"]}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY", "$.person"},
output: clientio.Encode([]interface{}{[]interface{}{"name", "age"},}, false),
},
"invalid JSONPath": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"name\":\"John\",\"age\":30}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY", "$invalid_path"},
output: []byte("-ERR parse error at 2 in $invalid_path\r\n"),
},
"incomapitable type(int)": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"person\":{\"name\":\"John\",\"age\":30},\"languages\":[\"python\",\"golang\"]}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY", "$.person.age"},
output: []byte("*1\r\n$-1\r\n"),
},
"incomapitable type(string)": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"person\":{\"name\":\"John\",\"age\":30},\"languages\":[\"python\",\"golang\"]}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY", "$.person.name"},
output: []byte("*1\r\n$-1\r\n"),
},
"incomapitable type(array)": {
setup: func() {
key := "EXISTING_KEY"
value := "{\"person\":{\"name\":\"John\",\"age\":30},\"languages\":[\"python\",\"golang\"]}"
var rootData interface{}
_ = sonic.Unmarshal([]byte(value), &rootData)
obj := store.NewObj(rootData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
store.Put(key, obj)
},
input: []string{"EXISTING_KEY", "$.languages"},
output: []byte("*1\r\n$-1\r\n"),
},
}

runEvalTests(t, tests, evalJSONOBJKEYS, store)
}

func BenchmarkEvalJSONOBJKEYS(b *testing.B) {
sizes := []int{0, 10, 100, 1000, 10000, 100000, 1000000} // Various sizes of JSON objects
store := dstore.NewStore(nil)

for _, size := range sizes {
b.Run(fmt.Sprintf("JSONObjectSize_%d", size), func(b *testing.B) {
key := fmt.Sprintf("benchmark_json_objkeys_%d", size)

// Create a large JSON object with the given size
jsonObj := make(map[string]interface{})
for i := 0; i < size; i++ {
jsonObj[fmt.Sprintf("key%d", i)] = fmt.Sprintf("value%d", i)
}

// Set the JSON object in the store
args := []string{key, "$", fmt.Sprintf("%v", jsonObj)}
evalJSONSET(args, store)

b.ResetTimer()
b.ReportAllocs()

// Benchmark the evalJSONOBJKEYS function
for i := 0; i < b.N; i++ {
_ = evalJSONOBJKEYS([]string{key, "$"}, store)
}
})
}
}

0 comments on commit 144e03a

Please sign in to comment.