Skip to content

Commit

Permalink
Linking Commit to correct author
Browse files Browse the repository at this point in the history
  • Loading branch information
N3XT14 committed Nov 13, 2024
1 parent 571b7b5 commit 0833475
Showing 1 changed file with 316 additions and 0 deletions.
316 changes: 316 additions & 0 deletions internal/eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,323 @@ func evalJSONMGET(args []string, store *dstore.Store) []byte {
return clientio.Encode(interfaceObj, false)
}

<<<<<<< HEAD
func jsonMGETHelper(store *dstore.Store, path, key string) (result interface{}, err2 []byte) {
=======
// Helper function to check if there are any non-legacy paths
func containsNonLegacyPath(paths []string) bool {
for _, path := range paths {
if isPathLegacy(path) {
return false
}
}
return true
}

func isPathLegacy(path string) bool {
return strings.HasPrefix(path, "$")
}

func jsonGETSingle(store *dstore.Store, path, key string, isLegacy bool) (results interface{}, err2 []byte) {
if isLegacy {
return jsonGETSingleLegacy(store, path, key)
}
return jsonGETSingleNormal(store, path, key)
}

func jsonGETSingleNormal(store *dstore.Store, path, key string) (results interface{}, err2 []byte) {
// Retrieve the object from the store
obj := store.Get(key)
if obj == nil {
return nil, nil // Key does not exist
}

// Ensure the object type is JSON
err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeJSON, object.ObjEncodingJSON)
if err != nil {
return nil, err
}

jsonData := obj.Value

// Parse the path using jp.ParseString
expr, parseErr := jp.ParseString(path)
if parseErr != nil {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path \"$.%s\" does not exist", path))
}

// Execute the JSONPath query
pathResults := expr.Get(jsonData)
const emptyJSONArray = "[]"
if len(pathResults) == 0 {
return emptyJSONArray, nil
}

// Serialize the result
resultBytes, marshalErr := sonic.Marshal(pathResults)
if marshalErr != nil {
return nil, diceerrors.NewErrWithMessage("could not serialize result")
}
return string(resultBytes), nil
}

func jsonGETSingleLegacy(store *dstore.Store, path, key string) (results interface{}, err2 []byte) {
// Retrieve the object from the store
obj := store.Get(key)
if obj == nil {
return nil, nil // Key does not exist
}

// Ensure the object type is JSON
err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeJSON, object.ObjEncodingJSON)
if err != nil {
return nil, err
}

jsonData := obj.Value

// Handle legacy paths
if path == "" || path == "." {
// For no path or dot path, return entire data
resultBytes, err := sonic.Marshal(jsonData)
if err != nil {
return nil, diceerrors.NewErrWithMessage("could not serialize result")
}
return string(resultBytes), nil
}

// Checking for legacy paths that begin with a dot (e.g., ".a")
if path[0] == '.' {
path = path[1:]
}

expr, parseErr := jp.ParseString(path)
if parseErr != nil {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path \"$.%s\" does not exist", path))
}

// Execute the JSONPath query for filtering
pathResults := expr.Get(jsonData)

if len(pathResults) == 0 {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path \"$.%s\" does not exist", path))
}

// Serialize the result
resultBytes, marshalErr := sonic.Marshal(pathResults[0])
if marshalErr != nil {
return nil, diceerrors.NewErrWithMessage("could not serialize result")
}
return string(resultBytes), nil
}

func jsonGETMulti(store *dstore.Store, paths []string, key string, isLegacy bool) (multiResults interface{}, err2 []byte) {
// Retrieve the object by key
obj := store.Get(key)
if obj == nil {
return nil, nil // Key does not exist
}

// Ensure the object is a valid JSON object
err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeJSON, object.ObjEncodingJSON)
if err != nil {
return nil, err
}

jsonData := obj.Value
results := make(map[string]interface{})
var missingPath string

// Process each path
for _, path := range paths {
expr, parseErr := jp.ParseString(path)
if parseErr != nil {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path '%s' does not exist", path))
}

pathResults := expr.Get(jsonData)
if len(pathResults) == 0 {
// For non-legacy mode, add an empty array if no result found
if !isLegacy {
results[path] = []interface{}{}
} else {
// In legacy mode, immediately mark as missing if no result found
missingPath = path
break
}
} else {
// Add results based on legacy mode
if isLegacy {
results[path] = pathResults[0] // Only the first result
} else {
results[path] = pathResults // All results
}
}
}

// In legacy mode, throw an error if a missing path was found
if isLegacy && missingPath != "" {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path '%s' does not exist", missingPath))
}

// Marshal the results to JSON
resultBytes, marshalErr := sonic.Marshal(results)
if marshalErr != nil {
return nil, diceerrors.NewErrWithMessage("could not serialize result")
}

return string(resultBytes), nil
}

// Convert a single value to RESP3 format
func valueToResp3(value interface{}) interface{} {
// Convert the value to the appropriate RESP3 format
switch v := value.(type) {
case string:
return []interface{}{v}
case []interface{}:
return v
default:
return []interface{}{v}
}
}

// Process a single path and convert its results to RESP3 format
func toResp3Path(path string, jsonData interface{}, isLegacy bool) (resp3Result interface{}, err2 []byte) {
expr, parseErr := jp.ParseString(path)
if parseErr != nil {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path '%s' does not exist", path))
}

// Get the results for the current path
pathResults := expr.Get(jsonData)
if len(pathResults) == 0 {
// For non-legacy mode, return an empty array if no result found
if !isLegacy {
return []interface{}{}, nil
}

return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path '%s' does not exist", path))
}

// Convert the results to RESP3 format.
resp3Results := []interface{}{}
for _, result := range pathResults {
resp3Results = append(resp3Results, valueToResp3(result))
}

return resp3Results, nil
}

func jsonGETResp3(store *dstore.Store, paths []string, key string, isLegacy bool) (resp3Result interface{}, err2 []byte) {
// Retrieve the object by key
obj := store.Get(key)
if obj == nil {
return nil, nil // Key does not exist
}

// Ensure the object is a valid JSON object
err := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeJSON, object.ObjEncodingJSON)
if err != nil {
return nil, err
}

jsonData := obj.Value

if len(paths) == 1 {
switch paths[0] {
case "$":
resultBytes, err := sonic.Marshal([]interface{}{jsonData})
if err != nil {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Could not serialize jsonData for '$': %v", err))
}
return string(resultBytes), nil

case ".":
resultBytes, err := sonic.Marshal(jsonData)
if err != nil {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Could not serialize jsonData for '.': %v", err))
}
return string(resultBytes), nil
}
}

results := make(map[string]interface{})
var missingPath string

// Process each path and convert to RESP3
for _, path := range paths {
resp3Result, respErr := toResp3Path(path, jsonData, isLegacy)
if respErr != nil {
return nil, respErr
}

// Add to the result map with correct indexing for legacy mode
if isLegacy {
// Assert that resp3Result is a []interface{} before indexing
if resultSlice, ok := resp3Result.([]interface{}); ok && len(resultSlice) > 0 {
results[path] = resultSlice[0] // Only the first result in legacy mode
} else {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Expected array for path '%s', but got different type", path))
}
} else {
results[path] = resp3Result // Store all results in non-legacy mode
}
}

// In legacy mode, throw an error if a missing path was found
if isLegacy && missingPath != "" {
return nil, diceerrors.NewErrWithMessage(fmt.Sprintf("Path '%s' does not exist", missingPath))
}

// Marshal the results to JSON (or RESP3)
resultBytes, marshalErr := sonic.Marshal(results)
if marshalErr != nil {
return nil, diceerrors.NewErrWithMessage("could not serialize result")
}

return string(resultBytes), nil
}

// Helper function to get the next argument safely.
func getNextArg(args []string) string {
if len(args) > 0 {
return args[0]
}
return ""
}

func maxStrLen(arr []string) int {
maxLen := 0
for _, str := range arr {
if len(str) > maxLen {
maxLen = len(str)
}
}
return maxLen
}

// Define the constants
const (
CmdArgNoEscape = "NOESCAPE"
CmdArgIndent = "INDENT"
CmdArgNewLine = "NEWLINE"
CmdArgSpace = "SPACE"
CmdArgFormat = "FORMAT"
)

// Calculate the max length of JSON.GET subcommands.
var JSONGetSubCommandsMaxStrLen = maxStrLen([]string{
CmdArgNoEscape,
CmdArgIndent,
CmdArgNewLine,
CmdArgSpace,
CmdArgFormat,
})

// helper function used by evalJSONGET and evalJSONMGET to prepare the results
func jsonGETHelper(store *dstore.Store, path, key string) (result interface{}, err2 []byte) {
>>>>>>> 7be9cf0 (Linking Commit to correct author)
// Retrieve the object from the database
obj := store.Get(key)
if obj == nil {
Expand Down

0 comments on commit 0833475

Please sign in to comment.