Skip to content

Conversation

@bsbodden
Copy link

@bsbodden bsbodden commented Dec 23, 2025

Implement all JSON commands with complete feature parity to redis-py:

  • JSON.GET, JSON.SET, JSON.DEL, JSON.MGET, JSON.MSET
  • JSON.ARRAPPEND, JSON.ARRINDEX, JSON.ARRINSERT, JSON.ARRLEN, JSON.ARRPOP, JSON.ARRTRIM
  • JSON.OBJKEYS, JSON.OBJLEN, JSON.STRLEN, JSON.STRAPPEND
  • JSON.NUMINCRBY, JSON.NUMMULTBY
  • JSON.TOGGLE, JSON.CLEAR, JSON.TYPE
  • JSON.RESP, JSON.DEBUG
  • Support for both legacy (.) and modern ($) JSONPath syntax
  • Add json_forget alias for json_del

Testing:

  • Add 97 tests covering all commands
  • All edge cases and error conditions tested
  • JSONPath query tests for both syntaxes
  • Array and object manipulation tests
  • Numeric operation tests

Documentation:

  • Add json_tutorial.rb example demonstrating all features
  • Add search_with_json.rb example showing JSON with Search
  • Include practical examples for common use cases

@bsbodden bsbodden force-pushed the redis/json branch 2 times, most recently from ae1c729 to 8333b77 Compare December 23, 2025 18:07
@bsbodden bsbodden requested a review from byroot December 23, 2025 18:19
@bsbodden bsbodden self-assigned this Dec 23, 2025
@bsbodden bsbodden requested a review from uglide December 23, 2025 18:53
Copy link
Collaborator

@byroot byroot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • None of the new methods are documented.
  • The feature is only added to standalone redis, not to cluster or distributed.

args.concat(paths) unless paths.empty?
result = parse_json(send_command(args))
# Unwrap single-element arrays for JSONPath queries
result.is_a?(Array) && result.length == 1 ? result.first : result
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how convenient that really is. If I'm working with a list of paths I received, and sometimes it is of length 1, sometimes more, I get inconsistent results.

Might be better to accept either a single path or an array, and return a consistent type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed:

  1. Documentation: Added YARD docs to all methods (commit 6dd3596)

  2. Distributed support: Implemented all JSON methods in Redis::Distributed using the node_for(key) delegation pattern. Multi-key operations (json_mget, json_mset) properly use ensure_same_node to enforce same-node requirements (commit 7e8906d)

  3. Cluster support: Added test suite for Redis::Cluster. Since Cluster inherits from Redis, it automatically has access to all JSON commands without additional implementation (commit 1b1ef5c)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how convenient that really is. If I'm working with a list of paths I received, and sometimes it is of length 1, sometimes more, I get inconsistent results.

Might be better to accept either a single path or an array, and return a consistent type.

Yup. I've removed the unwrapping logic, see (commit 6dd3596).

The methods now always return the raw parsed JSON response from Redis without conditional unwrapping.

end

def json_arrpop(key, path = '$', index = -1)
parse_json(send_command(['JSON.ARRPOP', key, path, index.to_s]))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parse_json(send_command(['JSON.ARRPOP', key, path, index.to_s]))
parse_json(send_command(['JSON.ARRPOP', key, path, Integer(index).to_s]))

We should enforce the type of Integer arguments for consistency with the rest of the codebase (e.g. see linsert).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed! Changed json_arrpop to use Integer(index) for proper type enforcement, see (commit 6dd3596).


private

def parse_json(value)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned before, that helper is a smell. We should know what type to expect in return of the command we emit, instead of having a generic helper that can parse about anything.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The challenge with JSON commands is that Redis returns JSON-encoded strings that need parsing, and the return type varies by command and path query:

  • json_get can return objects, arrays, strings, numbers, booleans, or null
  • Array operations can return single values or arrays depending on JSONPath queries
  • Some commands like json_arrpop return the actual JSON value, not metadata

I kept the helper with proper error handling (raises Redis::JSONParseError on parse failures) as it centralizes the JSON parsing logic and symbolize_names option. However, I'm open to refactoring this if you have a preferred pattern - perhaps individual parsing lambdas similar to Hashify/Floatify in Commands?

bsbodden added a commit that referenced this pull request Dec 23, 2025
- Add YARD documentation for all JSON methods
- Remove inconsistent unwrapping behavior from json_get/json_mget/json_numincrby/json_nummultby
- Use Integer() to enforce type for json_arrpop index parameter
- Clarify json_forget as Redis command alias

Addresses reviewer feedback on PR #1333

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Implement all JSON commands with complete feature parity to redis-py:
- JSON.GET, JSON.SET, JSON.DEL, JSON.MGET, JSON.MSET
- JSON.ARRAPPEND, JSON.ARRINDEX, JSON.ARRINSERT, JSON.ARRLEN, JSON.ARRPOP, JSON.ARRTRIM
- JSON.OBJKEYS, JSON.OBJLEN, JSON.STRLEN, JSON.STRAPPEND
- JSON.NUMINCRBY, JSON.NUMMULTBY
- JSON.TOGGLE, JSON.CLEAR, JSON.TYPE
- JSON.RESP, JSON.DEBUG
- Support for both legacy (.) and modern (\$) JSONPath syntax
- Add json_forget alias for json_del

Testing:
- Add comprehensive test suite with 97 tests covering all commands
- All edge cases and error conditions tested
- JSONPath query tests for both syntaxes
- Array and object manipulation tests
- Numeric operation tests

Documentation:
- Add json_tutorial.rb example demonstrating all features
- Add search_with_json.rb example showing JSON with Search
- Include practical examples for common use cases
- Add YARD documentation for all JSON methods
- Remove inconsistent unwrapping behavior from json_get/json_mget/json_numincrby/json_nummultby
- Use Integer() to enforce type for json_arrpop index parameter
- Clarify json_forget as Redis command alias
Adds all JSON commands to the distributed Redis implementation,
delegating single-key operations to the appropriate node and
enforcing same-node requirements for multi-key operations
(json_mget, json_mset).

Includes comprehensive test suite for distributed JSON operations
with tests for both same-node (using key tags) and different-node
scenarios.

This addresses the reviewer feedback that JSON support was only
added to standalone redis, not to cluster or distributed.
Adds comprehensive test suite for JSON operations in cluster mode.
Since Redis::Cluster inherits from Redis, it automatically includes
all JSON commands from Redis::Commands::JSON without requiring
additional implementation.

This completes the cluster support requirement from the reviewer
feedback.
@byroot
Copy link
Collaborator

byroot commented Dec 24, 2025

You just copy pasted all the tests for distributed. That's not how it should be done. Please look at the test/lint directory and how it defines modules (like Lint::Lists) that are shared between standalone and distributed redis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants