Repository: DiceDB/dice
File: internal/eval/eval.go (Go)

Code before:
	}
	if err != nil {
		return diceerrors.NewErrWithMessage("could not serialize result")
	}
	return clientio.Encode(string(resultBytes), false)
}

// evalJSONTOGGLE toggles a boolean value stored at the specified key and path.
// args must contain at least the key and path (where the boolean is located).
// If the key does not exist or is expired, it returns response.RespNIL.
// If the field at the specified path is not a boolean, it returns an encoded error response.
// If the boolean is `true`, it toggles to `false` (returns :0), and if `false`, it toggles to `true` (returns :1).
// Returns an encoded error response if the incorrect number of arguments is provided.
func evalJSONTOGGLE(args []string, store *dstore.Store) []byte {
    if len(args) < 2 {
        return diceerrors.NewErrArity("JSON.TOGGLE")
    }

    key := args[0]
    path := args[1]

    // Retrieve the object from the database
    obj := store.Get(key)
    if obj == nil {
        return clientio.RespNIL
    }

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

    jsonData := obj.Value

    // Parse the JSONPath expression
    expr, err := jp.ParseString(path)
    if err != nil {
        return diceerrors.NewErrWithMessage("invalid JSONPath")
    }

    // Execute the JSONPath query
    results := expr.Get(jsonData)
    if len(results) == 0 {
        return clientio.Encode([]interface{}{}, false)  // Return empty array if no matches
    }

    for _, result := range results {
        switch v := result.(type) {
        case bool:
            newValue := !v

Review comment (suggestion):
In redis. if the key's is not exist. should return an error. like following:
`

         127.0.0.1:6379> JSON.SET user $ '{"age":13,"high":1.60,"pet":null,"language":["python","golang"],"flag":false,"partner":{"name":"tom","language":["rust"]}}'
      OK
      127.0.0.1:6379> JSON.TOGGLE user111 $.aaa
    (error) ERR could not perform this operation on a key that doesn't exist


`

Code after:
	// Create a new object with the updated JSON data
	newObj := store.NewObj(jsonData, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
	store.Put(key, newObj)
	return clientio.Encode(len(results), false)
}

// evalJSONCLEAR Clear container values (arrays/objects) and set numeric values to 0,
// Already cleared values are ignored for empty containers and zero numbers
// args must contain at least the key;  (path unused in this implementation)
// Returns encoded error if key is expired or it does not exist
// Returns encoded error response if incorrect number of arguments
// Returns an integer reply specifying the number of matching JSON arrays
// and objects cleared + number of matching JSON numerical values zeroed.
func evalJSONCLEAR(args []string, store *dstore.Store) []byte {
	if len(args) < 1 {
		return diceerrors.NewErrArity("JSON.CLEAR")
	}
	key := args[0]

	// Default path is root if not specified
	path := defaultRootPath
	if len(args) > 1 {
		path = args[1]
	}

	// Retrieve the object from the database
	obj := store.Get(key)
	if obj == nil {
		return diceerrors.NewErrWithMessage("could not perform this operation on a key that doesn't exist")
	}

	errWithMessage := object.AssertTypeAndEncoding(obj.TypeEncoding, object.ObjTypeJSON, object.ObjEncodingJSON)
	if errWithMessage != nil {
		return errWithMessage
	}

	jsonData := obj.Value

	_, err := sonic.Marshal(jsonData)
	if err != nil {
		return diceerrors.NewErrWithMessage("Existing key has wrong Dice type")
	}

	var countClear uint64 = 0
	if len(args) == 1 || path == defaultRootPath {
		if jsonData != struct{}{} {
			// If path is root and len(args) == 1, return it instantly
			newObj := store.NewObj(struct{}{}, -1, object.ObjTypeJSON, object.ObjEncodingJSON)
			store.Put(key, newObj)
			countClear++
			return clientio.Encode(countClear, false)