Skip to content

Commit

Permalink
#1030: Migrate JSON.RESP & JSON.DEBUG (#1277)
Browse files Browse the repository at this point in the history
  • Loading branch information
c-harish authored Nov 14, 2024
1 parent b127d96 commit 88e4961
Show file tree
Hide file tree
Showing 17 changed files with 940 additions and 358 deletions.
80 changes: 41 additions & 39 deletions docs/src/content/docs/commands/JSON.DEBUG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,90 @@ description: Documentation for the DiceDB command JSON.DEBUG

The `JSON.DEBUG` command in DiceDB is part of the DiceDBJSON module, which allows for the manipulation and querying of JSON data stored in DiceDB. This command is primarily used for debugging purposes, providing insights into the internal representation of JSON data within DiceDB.

## Parameters

### Syntax
## Syntax

```bash
JSON.DEBUG <subcommand> <key> [path]
```

### Parameters Description
## Parameters

- `subcommand`: (Required) The specific debug operation to perform. Currently, the supported subcommand is `MEMORY`.
- `key`: (Required) The key under which the JSON data is stored.
- `path`: (Optional) The JSON path to the specific part of the JSON data to debug. Defaults to the root if not provided.
| Parameter | Description | Type | Required |
| --------- | --------------------------------------------------------------------------------------- | ------ | -------- |
| `subcommand` | The specific debug operation to perform. Currently, the supported subcommand is `MEMORY`. | String | Yes |
| `key` | The key under which the JSON data is stored. | String | Yes |
| `path` | The JSON path to the specific part of the JSON data to debug. Defaults to the root if not provided. | String | No |

### Subcommands

- `MEMORY`: This subcommand returns the memory usage of the JSON value at the specified path.

## Return Value

The return value of the `JSON.DEBUG` command depends on the subcommand used:

- `MEMORY`: Returns an integer representing the memory usage in bytes of the JSON value at the specified path.
| Condition | Return Value |
| ----------------------- | ----------------------------------- |
| if `MEMORY` subcommand is used | Memory usage in bytes of the JSON value at the specified path. |

## Behaviour

When the `JSON.DEBUG` command is executed, DiceDB will perform the specified debug operation on the JSON data stored at the given key and path. For the `MEMORY` subcommand, it will calculate and return the memory usage of the JSON value at the specified path. If the path is not provided, it defaults to the root of the JSON data.
- For the `MEMORY` subcommand, it will calculate and return the memory usage of the JSON value at the specified path.
- If the path is not provided, it defaults to the root of the JSON data.

## Errors

The `JSON.DEBUG` command can raise errors in the following scenarios:

1. `Invalid Subcommand`: If an unsupported subcommand is provided, DiceDB will return an error.

- `Error Message`: `ERR unknown subcommand '<subcommand>'`

2. `Non-Existent Key`: If the specified key does not exist in the DiceDB database, DiceDB will return an error.

- `Error Message`: `ERR no such key`

3. `Invalid Path`: If the specified path does not exist within the JSON data, DiceDB will return an error.
1. `Invalid Subcommand`:
- Error Message: `ERR unknown subcommand '<subcommand>'`
- Occurs when an unsupported subcommand is provided.

2. `Invalid Path`:
- Error Message: `ERR Path '<path>' does not exist`
- If the specified path does not exist within the JSON data, DiceDB will return an error.

- `Error Message`: `ERR path '<path>' does not exist`

4. `Wrong Type`: If the key exists but does not hold JSON data, DiceDB will return an error.

- `Error Message`: `WRONGTYPE Operation against a key holding the wrong kind of value`
4. `Wrong Type`:
- Error Message: `WRONGTYPE Operation against a key holding the wrong kind of value`
- If the key exists but does not hold JSON data, DiceDB will return an error.

## Example Usage

### Debugging Memory Usage of Entire JSON Data

The `JSON.DEBUG MEMORY` command is used to get the memory usage of the entire JSON data stored under the key `myjson`. The command returns `89`, indicating that the JSON data occupies 89 bytes of memory.

```bash
127.0.0.1:7379> JSON.SET myjson $ '{"a":1}',
OK
127.0.0.1:7379> JSON.DEBUG MEMORY myjson
(integer) 256
(integer) 89
```

In this example, the `JSON.DEBUG MEMORY` command is used to get the memory usage of the entire JSON data stored under the key `myjson`. The command returns `256`, indicating that the JSON data occupies 256 bytes of memory.

### Debugging Memory Usage of a Specific Path

The `JSON.DEBUG MEMORY` command is used to get the memory usage of the JSON value at the path `$.a` within the JSON data stored under the key `myjson`. The command returns `16`, indicating that the specified JSON value occupies 16 bytes of memory.
```bash
127.0.0.1:7379> JSON.DEBUG MEMORY myjson $.store.book[0]
(integer) 64
127.0.0.1:7379> JSON.SET myjson $ '{"a":1,"b":2}',
OK
127.0.0.1:7379> JSON.DEBUG MEMORY myjson $.a
1) (integer) 16
```

In this example, the `JSON.DEBUG MEMORY` command is used to get the memory usage of the JSON value at the path `$.store.book[0]` within the JSON data stored under the key `myjson`. The command returns `64`, indicating that the specified JSON value occupies 64 bytes of memory.

### Handling Non-Existent Key

The `JSON.DEBUG MEMORY` command is used on a non-existent key `nonExistentKey`. DiceDB returns an 0 indicating that the key does not exist.

```bash
127.0.0.1:7379> JSON.DEBUG MEMORY nonExistentKey
(error) ERR no such key
(integer) 0
```

In this example, the `JSON.DEBUG MEMORY` command is used on a non-existent key `nonExistentKey`. DiceDB returns an error indicating that the key does not exist.

### Handling Invalid Path

The `JSON.DEBUG MEMORY` command is used on an invalid path `$.nonExistentPath` within the JSON data stored under the key `myjson`. DiceDB returns an error indicating that the specified path does not exist.

```bash
127.0.0.1:7379> JSON.DEBUG MEMORY myjson $.nonExistentPath
(error) ERR path '$.nonExistentPath' does not exist
(error) ERR Path '$.nonExistentPath' does not exist
```

In this example, the `JSON.DEBUG MEMORY` command is used on an invalid path `$.nonExistentPath` within the JSON data stored under the key `myjson`. DiceDB returns an error indicating that the specified path does not exist.
## Notes

- JSONPath expressions are used to navigate and specify the location within the JSON document. Familiarity with JSONPath syntax is beneficial for effective use of this command.
73 changes: 73 additions & 0 deletions docs/src/content/docs/commands/JSON.RESP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: JSON.RESP
description: Documentation for the DiceDB command JSON.RESP
---

The `JSON.RESP` command in DiceDB is part of the DiceDBJSON module, which returns the JSON in the specified key in Redis Serialization Protocol Specification (RESP) form.

## Syntax

```bash
JSON.RESP <key> [path]
```

## Parameters

| Parameter | Description | Type | Required |
| --------- | --------------------------------------------------------------------------------------- | ------ | -------- |
| `key` | The key under which the JSON data is stored. | String | Yes |
| `path` | The JSON path to the specific part of the JSON data to fetch. Defaults to the root if not provided. | String | No |

## Return Value

| Condition | Return Value |
| ----------------------- | ----------------------------------- |
| if `path` is provided | JSON value at the specified path in RESP form. |

## Behaviour

- If the path is not provided, it defaults to the root of the JSON data.

## Errors

1. `Wrong Type`:
- Error Message: `WRONGTYPE Operation against a key holding the wrong kind of value`
- If the key exists but does not hold JSON data, DiceDB will return an error.

## Example Usage

### JSON.RESP on array

The `JSON.RESP` command returns the entire JSON data stored under the key `arrayjson` in RESP form.

```bash
127.0.0.1:7379> JSON.SET arrayjson $ '["dice",10,10.5,true,null]'
OK
127.0.0.1:7379> JSON.RESP
1) [
2) "dice"
3) (integer) 10
4) "10.5"
5) true
6) (nil)
```
### JSON.RESP on nested JSON
The `JSON.RESP` command returns the JSON data stored under the key `myjson` at `$.b` in RESP form.
```bash
127.0.0.1:7379> JSON.SET myjson $ '{"a":100,"b":["dice",10,10.5,true,null]}'
OK
127.0.0.1:7379> JSON.RESP myjson $.b
1) 1) [
2) "dice"
3) (integer) 10
4) "10.5"
5) true
6) (nil)
```
## Notes
- JSONPath expressions are used to navigate and specify the location within the JSON document. Familiarity with JSONPath syntax is beneficial for effective use of this command.
2 changes: 2 additions & 0 deletions integration_tests/commands/http/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ func runIntegrationTests(t *testing.T, exec *HTTPCommandExecutor, testCases []In
case "range":
assert.True(t, result.(float64) <= out.(float64) && result.(float64) > 0, "Expected %v to be within 0 to %v", result, out)
case "json_equal":
// fmt.Println("hi expected : ", out)
// fmt.Println("hi actual :", result)
assert.JSONEq(t, out.(string), result.(string))
}
}
Expand Down
82 changes: 82 additions & 0 deletions integration_tests/commands/http/jsondebug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package http

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestJSONDEBUG(t *testing.T) {
exec := NewHTTPCommandExecutor()

testCases := []TestCase{
{
name: "jsondebug with no path",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k1", "path": "$", "json": map[string]interface{}{"a": 1}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k1"}}},
},
expected: []interface{}{"OK", float64(89)},
},
{
name: "jsondebug with a valid path",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k2", "path": "$", "json": map[string]interface{}{"a": 1, "b": 2}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k2", "$.a"}}},
},
expected: []interface{}{"OK", []interface{}{float64(16)}},
},
{
name: "jsondebug with multiple paths",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k3", "path": "$", "json": map[string]interface{}{"a": 1, "b": "dice"}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k3", "$.a", "$.b"}}},
},
expected: []interface{}{"OK", []interface{}{float64(16)}},
},
{
name: "jsondebug with multiple paths",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k4", "path": "$", "json": map[string]interface{}{"a": 1, "b": "dice"}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k4", "$.a", "$.b"}}},
},
expected: []interface{}{"OK", []interface{}{float64(16)}},
},
{
name: "jsondebug with single path for array json",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k5", "path": "$", "json": []interface{}{"roll", "the", "dices"}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k5", "$[1]"}}},
},
expected: []interface{}{"OK", []interface{}{float64(19)}},
},
{
name: "jsondebug with multiple paths for array json",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k6", "path": "$", "json": []interface{}{"roll", "the", "dices"}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k6", "$[1,2]"}}},
},
expected: []interface{}{"OK", []interface{}{float64(19), float64(21)}},
},
{
name: "jsondebug with all paths for array json",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k7", "path": "$", "json": []interface{}{"roll", "the", "dices"}}},
{Command: "JSON.DEBUG", Body: map[string]interface{}{"values": []interface{}{"MEMORY", "k7", "$[:]"}}},
},
expected: []interface{}{"OK", []interface{}{float64(20), float64(19), float64(21)}},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
assert.Equal(t, tc.expected[i], result)
}
})
}

// Cleanup the used keys
exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": []interface{}{"k1", "k2", "k3", "k4", "k5", "k6", "k7"}}})
}
50 changes: 50 additions & 0 deletions integration_tests/commands/http/jsonresp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package http

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestJSONRESP(t *testing.T) {
exec := NewHTTPCommandExecutor()

testCases := []TestCase{
{
name: "jsonresp on array with mixed types",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k1", "path": "$", "json": []interface{}{"dice", 10, 10.5, true, nil}}},
{Command: "JSON.RESP", Body: map[string]interface{}{"key": "k1", "path": "$"}},
},
expected: []interface{}{"OK", []interface{}{"[", "dice", float64(10), float64(10.5), true, nil}},
},
{
name: "jsonresp on nested array with mixed types",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k2", "path": "$", "json": map[string]interface{}{"b": []interface{}{"dice", 10, 10.5, true, nil}}}},
{Command: "JSON.RESP", Body: map[string]interface{}{"key": "k2", "path": "$.b"}},
},
expected: []interface{}{"OK", []interface{}{[]interface{}{"[", "dice", float64(10), float64(10.5), true, nil}}},
},
{
name: "jsonresp on object at root path",
commands: []HTTPCommand{
{Command: "JSON.SET", Body: map[string]interface{}{"key": "k3", "path": "$", "json": map[string]interface{}{"b": []interface{}{"dice", 10, 10.5, true, nil}}}},
{Command: "JSON.RESP", Body: map[string]interface{}{"key": "k3"}},
},
expected: []interface{}{"OK", []interface{}{"{", "b", []interface{}{"[", "dice", float64(10), float64(10.5), true, nil}}},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
assert.Equal(t, tc.expected[i], result)
}
})
}

// Cleanup the used keys
exec.FireCommand(HTTPCommand{Command: "DEL", Body: map[string]interface{}{"keys": []interface{}{"k1", "k2", "k3"}}})
}
Loading

0 comments on commit 88e4961

Please sign in to comment.