diff --git a/pom.xml b/pom.xml
index 1a6c2e4c47..bc3225af6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,6 +50,7 @@
5.13.11
3.13.0
2.12.0
+ 2.17.0
1.3.2
4.0.1
5.10.2
@@ -238,6 +239,13 @@
true
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson-version}
+ true
+
+
@@ -523,6 +531,21 @@
test
+
+
+
+ org.testcontainers
+ testcontainers
+ 1.20.1
+ test
+
+
+ org.testcontainers
+ junit-jupiter
+ 1.20.1
+ test
+
+
diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java
index 60a0096644..5789812671 100644
--- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java
+++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java
@@ -26,6 +26,14 @@
import io.lettuce.core.codec.Base16;
import io.lettuce.core.codec.RedisCodec;
import io.lettuce.core.internal.LettuceAssert;
+import io.lettuce.core.json.JsonParser;
+import io.lettuce.core.json.JsonType;
+import io.lettuce.core.json.JsonValue;
+import io.lettuce.core.json.arguments.JsonGetArgs;
+import io.lettuce.core.json.arguments.JsonMsetArgs;
+import io.lettuce.core.json.JsonPath;
+import io.lettuce.core.json.arguments.JsonRangeArgs;
+import io.lettuce.core.json.arguments.JsonSetArgs;
import io.lettuce.core.models.stream.ClaimedMessages;
import io.lettuce.core.models.stream.PendingMessage;
import io.lettuce.core.models.stream.PendingMessages;
@@ -71,21 +79,27 @@ public abstract class AbstractRedisAsyncCommands implements RedisAclAsyncC
RedisKeyAsyncCommands, RedisStringAsyncCommands, RedisListAsyncCommands, RedisSetAsyncCommands,
RedisSortedSetAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands,
RedisHLLAsyncCommands, BaseRedisAsyncCommands, RedisTransactionalAsyncCommands,
- RedisGeoAsyncCommands, RedisClusterAsyncCommands {
+ RedisGeoAsyncCommands, RedisClusterAsyncCommands, RedisJsonAsyncCommands {
private final StatefulConnection connection;
private final RedisCommandBuilder commandBuilder;
+ private final RedisJsonCommandBuilder jsonCommandBuilder;
+
+ private final JsonParser parser;
+
/**
* Initialize a new instance.
*
* @param connection the connection to operate on
* @param codec the codec for command encoding
*/
- public AbstractRedisAsyncCommands(StatefulConnection connection, RedisCodec codec) {
+ public AbstractRedisAsyncCommands(StatefulConnection connection, RedisCodec codec, JsonParser parser) {
+ this.parser = parser;
this.connection = connection;
this.commandBuilder = new RedisCommandBuilder<>(codec);
+ this.jsonCommandBuilder = new RedisJsonCommandBuilder<>(codec, parser);
}
@Override
@@ -1453,6 +1467,176 @@ public boolean isOpen() {
return connection.isOpen();
}
+ @Override
+ public RedisFuture> jsonArrappend(K key, JsonPath jsonPath, JsonValue... values) {
+ return dispatch(jsonCommandBuilder.jsonArrappend(key, jsonPath, values));
+ }
+
+ @Override
+ public RedisFuture> jsonArrappend(K key, JsonValue... values) {
+ return dispatch(jsonCommandBuilder.jsonArrappend(key, JsonPath.ROOT_PATH, values));
+ }
+
+ @Override
+ public RedisFuture> jsonArrindex(K key, JsonPath jsonPath, JsonValue value, JsonRangeArgs range) {
+ return dispatch(jsonCommandBuilder.jsonArrindex(key, jsonPath, value, range));
+ }
+
+ @Override
+ public RedisFuture> jsonArrindex(K key, JsonPath jsonPath, JsonValue value) {
+ return dispatch(jsonCommandBuilder.jsonArrindex(key, jsonPath, value, JsonRangeArgs.Builder.defaults()));
+ }
+
+ @Override
+ public RedisFuture> jsonArrinsert(K key, JsonPath jsonPath, int index, JsonValue... values) {
+ return dispatch(jsonCommandBuilder.jsonArrinsert(key, jsonPath, index, values));
+ }
+
+ @Override
+ public RedisFuture> jsonArrlen(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonArrlen(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture> jsonArrlen(K key) {
+ return dispatch(jsonCommandBuilder.jsonArrlen(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public RedisFuture> jsonArrpop(K key, JsonPath jsonPath, int index) {
+ return dispatch(jsonCommandBuilder.jsonArrpop(key, jsonPath, index));
+ }
+
+ @Override
+ public RedisFuture> jsonArrpop(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonArrpop(key, jsonPath, -1));
+ }
+
+ @Override
+ public RedisFuture> jsonArrpop(K key) {
+ return dispatch(jsonCommandBuilder.jsonArrpop(key, JsonPath.ROOT_PATH, -1));
+ }
+
+ @Override
+ public RedisFuture> jsonArrtrim(K key, JsonPath jsonPath, JsonRangeArgs range) {
+ return dispatch(jsonCommandBuilder.jsonArrtrim(key, jsonPath, range));
+ }
+
+ @Override
+ public RedisFuture jsonClear(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonClear(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture jsonClear(K key) {
+ return dispatch(jsonCommandBuilder.jsonClear(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public RedisFuture jsonDel(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonDel(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture jsonDel(K key) {
+ return dispatch(jsonCommandBuilder.jsonDel(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public RedisFuture> jsonGet(K key, JsonGetArgs options, JsonPath... jsonPaths) {
+ return dispatch(jsonCommandBuilder.jsonGet(key, options, jsonPaths));
+ }
+
+ @Override
+ public RedisFuture> jsonGet(K key, JsonPath... jsonPaths) {
+ return dispatch(jsonCommandBuilder.jsonGet(key, JsonGetArgs.Builder.defaults(), jsonPaths));
+ }
+
+ @Override
+ public RedisFuture jsonMerge(K key, JsonPath jsonPath, JsonValue value) {
+ return dispatch(jsonCommandBuilder.jsonMerge(key, jsonPath, value));
+ }
+
+ @Override
+ public RedisFuture> jsonMGet(JsonPath jsonPath, K... keys) {
+ return dispatch(jsonCommandBuilder.jsonMGet(jsonPath, keys));
+ }
+
+ @Override
+ public RedisFuture jsonMSet(List> arguments) {
+ return dispatch(jsonCommandBuilder.jsonMSet(arguments));
+ }
+
+ @Override
+ public RedisFuture> jsonNumincrby(K key, JsonPath jsonPath, Number number) {
+ return dispatch(jsonCommandBuilder.jsonNumincrby(key, jsonPath, number));
+ }
+
+ @Override
+ public RedisFuture> jsonObjkeys(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonObjkeys(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture> jsonObjkeys(K key) {
+ return dispatch(jsonCommandBuilder.jsonObjkeys(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public RedisFuture> jsonObjlen(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonObjlen(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture> jsonObjlen(K key) {
+ return dispatch(jsonCommandBuilder.jsonObjlen(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public RedisFuture jsonSet(K key, JsonPath jsonPath, JsonValue value, JsonSetArgs options) {
+ return dispatch(jsonCommandBuilder.jsonSet(key, jsonPath, value, options));
+ }
+
+ @Override
+ public RedisFuture jsonSet(K key, JsonPath jsonPath, JsonValue value) {
+ return dispatch(jsonCommandBuilder.jsonSet(key, jsonPath, value, JsonSetArgs.Builder.defaults()));
+ }
+
+ @Override
+ public RedisFuture> jsonStrappend(K key, JsonPath jsonPath, JsonValue value) {
+ return dispatch(jsonCommandBuilder.jsonStrappend(key, jsonPath, value));
+ }
+
+ @Override
+ public RedisFuture> jsonStrappend(K key, JsonValue value) {
+ return dispatch(jsonCommandBuilder.jsonStrappend(key, JsonPath.ROOT_PATH, value));
+ }
+
+ @Override
+ public RedisFuture> jsonStrlen(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonStrlen(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture> jsonStrlen(K key) {
+ return dispatch(jsonCommandBuilder.jsonStrlen(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public RedisFuture> jsonToggle(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonToggle(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture> jsonType(K key, JsonPath jsonPath) {
+ return dispatch(jsonCommandBuilder.jsonType(key, jsonPath));
+ }
+
+ @Override
+ public RedisFuture> jsonType(K key) {
+ return dispatch(jsonCommandBuilder.jsonType(key, JsonPath.ROOT_PATH));
+ }
+
@Override
public RedisFuture> keys(K pattern) {
return dispatch(commandBuilder.keys(pattern));
@@ -3194,6 +3378,11 @@ public RedisFuture>> clusterLinks() {
return dispatch(commandBuilder.clusterLinks());
}
+ @Override
+ public JsonParser getJsonParser() {
+ return this.parser;
+ }
+
private byte[] encodeFunction(String functionCode) {
LettuceAssert.notNull(functionCode, "Function code must not be null");
LettuceAssert.notEmpty(functionCode, "Function code script must not be empty");
diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java
index 8614a6eb59..350fbce601 100644
--- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java
+++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java
@@ -26,6 +26,14 @@
import io.lettuce.core.codec.Base16;
import io.lettuce.core.codec.RedisCodec;
import io.lettuce.core.internal.LettuceAssert;
+import io.lettuce.core.json.JsonParser;
+import io.lettuce.core.json.JsonPath;
+import io.lettuce.core.json.JsonType;
+import io.lettuce.core.json.JsonValue;
+import io.lettuce.core.json.arguments.JsonGetArgs;
+import io.lettuce.core.json.arguments.JsonMsetArgs;
+import io.lettuce.core.json.arguments.JsonRangeArgs;
+import io.lettuce.core.json.arguments.JsonSetArgs;
import io.lettuce.core.models.stream.ClaimedMessages;
import io.lettuce.core.models.stream.PendingMessage;
import io.lettuce.core.models.stream.PendingMessages;
@@ -74,6 +82,7 @@
* @author dengliming
* @author Andrey Shlykov
* @author Ali Takavci
+ * @author Tihomir Mateev
* @since 4.0
*/
public abstract class AbstractRedisReactiveCommands
@@ -81,12 +90,16 @@ public abstract class AbstractRedisReactiveCommands
RedisStringReactiveCommands, RedisListReactiveCommands, RedisSetReactiveCommands,
RedisSortedSetReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands,
RedisHLLReactiveCommands, BaseRedisReactiveCommands, RedisTransactionalReactiveCommands,
- RedisGeoReactiveCommands, RedisClusterReactiveCommands {
+ RedisGeoReactiveCommands, RedisClusterReactiveCommands, RedisJsonReactiveCommands {
private final StatefulConnection connection;
private final RedisCommandBuilder commandBuilder;
+ private final RedisJsonCommandBuilder jsonCommandBuilder;
+
+ private final JsonParser parser;
+
private final ClientResources clientResources;
private final boolean tracingEnabled;
@@ -99,9 +112,11 @@ public abstract class AbstractRedisReactiveCommands
* @param connection the connection to operate on.
* @param codec the codec for command encoding.
*/
- public AbstractRedisReactiveCommands(StatefulConnection connection, RedisCodec codec) {
+ public AbstractRedisReactiveCommands(StatefulConnection connection, RedisCodec codec, JsonParser parser) {
this.connection = connection;
+ this.parser = parser;
this.commandBuilder = new RedisCommandBuilder<>(codec);
+ this.jsonCommandBuilder = new RedisJsonCommandBuilder<>(codec, parser);
this.clientResources = connection.getResources();
this.tracingEnabled = clientResources.tracing().isEnabled();
}
@@ -122,6 +137,11 @@ private EventExecutorGroup getScheduler() {
return this.scheduler = schedulerToUse;
}
+ @Override
+ public JsonParser getJsonParser() {
+ return parser;
+ }
+
@Override
public Mono> aclCat() {
return createMono(commandBuilder::aclCat);
@@ -1515,6 +1535,179 @@ public boolean isOpen() {
return connection.isOpen();
}
+ @Override
+ public Flux jsonArrappend(K key, JsonPath jsonPath, JsonValue... values) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrappend(key, jsonPath, values));
+ }
+
+ @Override
+ public Flux jsonArrappend(K key, JsonValue... values) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrappend(key, JsonPath.ROOT_PATH, values));
+ }
+
+ @Override
+ public Flux jsonArrindex(K key, JsonPath jsonPath, JsonValue value, JsonRangeArgs range) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrindex(key, jsonPath, value, range));
+ }
+
+ @Override
+ public Flux jsonArrindex(K key, JsonPath jsonPath, JsonValue value) {
+ final JsonRangeArgs args = JsonRangeArgs.Builder.defaults();
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrindex(key, jsonPath, value, args));
+ }
+
+ @Override
+ public Flux jsonArrinsert(K key, JsonPath jsonPath, int index, JsonValue... values) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrinsert(key, jsonPath, index, values));
+ }
+
+ @Override
+ public Flux jsonArrlen(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrlen(key, jsonPath));
+ }
+
+ @Override
+ public Flux jsonArrlen(K key) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrlen(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public Flux jsonArrpop(K key, JsonPath jsonPath, int index) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrpop(key, jsonPath, index));
+ }
+
+ @Override
+ public Flux jsonArrpop(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrpop(key, jsonPath, -1));
+ }
+
+ @Override
+ public Flux jsonArrpop(K key) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrpop(key, JsonPath.ROOT_PATH, -1));
+ }
+
+ @Override
+ public Flux jsonArrtrim(K key, JsonPath jsonPath, JsonRangeArgs range) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonArrtrim(key, jsonPath, range));
+ }
+
+ @Override
+ public Mono jsonClear(K key, JsonPath jsonPath) {
+ return createMono(() -> jsonCommandBuilder.jsonClear(key, jsonPath));
+ }
+
+ @Override
+ public Mono jsonClear(K key) {
+ return createMono(() -> jsonCommandBuilder.jsonClear(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public Mono jsonDel(K key, JsonPath jsonPath) {
+ return createMono(() -> jsonCommandBuilder.jsonDel(key, jsonPath));
+ }
+
+ @Override
+ public Mono jsonDel(K key) {
+ return createMono(() -> jsonCommandBuilder.jsonDel(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public Flux jsonGet(K key, JsonGetArgs options, JsonPath... jsonPaths) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonGet(key, options, jsonPaths));
+ }
+
+ @Override
+ public Flux jsonGet(K key, JsonPath... jsonPaths) {
+ final JsonGetArgs args = JsonGetArgs.Builder.defaults();
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonGet(key, args, jsonPaths));
+ }
+
+ @Override
+ public Mono jsonMerge(K key, JsonPath jsonPath, JsonValue value) {
+ return createMono(() -> jsonCommandBuilder.jsonMerge(key, jsonPath, value));
+ }
+
+ @Override
+ public Flux jsonMGet(JsonPath jsonPath, K... keys) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonMGet(jsonPath, keys));
+ }
+
+ @Override
+ public Mono jsonMSet(List> arguments) {
+ return createMono(() -> jsonCommandBuilder.jsonMSet(arguments));
+ }
+
+ @Override
+ public Flux jsonNumincrby(K key, JsonPath jsonPath, Number number) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonNumincrby(key, jsonPath, number));
+ }
+
+ @Override
+ public Flux jsonObjkeys(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonObjkeys(key, jsonPath));
+ }
+
+ @Override
+ public Flux jsonObjkeys(K key) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonObjkeys(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public Flux jsonObjlen(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonObjlen(key, jsonPath));
+ }
+
+ @Override
+ public Flux jsonObjlen(K key) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonObjlen(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public Mono jsonSet(K key, JsonPath jsonPath, JsonValue value, JsonSetArgs options) {
+ return createMono(() -> jsonCommandBuilder.jsonSet(key, jsonPath, value, options));
+ }
+
+ @Override
+ public Mono jsonSet(K key, JsonPath jsonPath, JsonValue value) {
+ final JsonSetArgs args = JsonSetArgs.Builder.defaults();
+ return createMono(() -> jsonCommandBuilder.jsonSet(key, jsonPath, value, args));
+ }
+
+ @Override
+ public Flux jsonStrappend(K key, JsonPath jsonPath, JsonValue value) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonStrappend(key, jsonPath, value));
+ }
+
+ @Override
+ public Flux jsonStrappend(K key, JsonValue value) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonStrappend(key, JsonPath.ROOT_PATH, value));
+ }
+
+ @Override
+ public Flux jsonStrlen(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonStrlen(key, jsonPath));
+ }
+
+ @Override
+ public Flux jsonStrlen(K key) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonStrlen(key, JsonPath.ROOT_PATH));
+ }
+
+ @Override
+ public Flux jsonToggle(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonToggle(key, jsonPath));
+ }
+
+ @Override
+ public Flux jsonType(K key, JsonPath jsonPath) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonType(key, jsonPath));
+ }
+
+ @Override
+ public Flux jsonType(K key) {
+ return createDissolvingFlux(() -> jsonCommandBuilder.jsonType(key, JsonPath.ROOT_PATH));
+ }
+
@Override
public Flux keys(K pattern) {
return createDissolvingFlux(() -> commandBuilder.keys(pattern));
diff --git a/src/main/java/io/lettuce/core/AclSetuserArgs.java b/src/main/java/io/lettuce/core/AclSetuserArgs.java
index 4b3e27a69f..c64145c100 100644
--- a/src/main/java/io/lettuce/core/AclSetuserArgs.java
+++ b/src/main/java/io/lettuce/core/AclSetuserArgs.java
@@ -606,7 +606,7 @@ public void build(CommandArgs args) {
@Override
public String toString() {
- return getClass().getSimpleName() + ": " + value.name();
+ return getClass().getSimpleName() + ": " + value.toString();
}
}
@@ -716,7 +716,7 @@ public void build(CommandArgs args) {
if (command.getSubCommand() == null) {
args.add("+" + command.getCommand().name());
} else {
- args.add("+" + command.getCommand().name() + "|" + command.getSubCommand().name());
+ args.add("+" + command.getCommand().name() + "|" + command.getSubCommand().toString());
}
}
@@ -735,7 +735,7 @@ public void build(CommandArgs args) {
if (command.getSubCommand() == null) {
args.add("-" + command.getCommand().name());
} else {
- args.add("-" + command.getCommand().name() + "|" + command.getSubCommand().name());
+ args.add("-" + command.getCommand().name() + "|" + command.getSubCommand().toString());
}
}
diff --git a/src/main/java/io/lettuce/core/ClientOptions.java b/src/main/java/io/lettuce/core/ClientOptions.java
index 3e32967bf1..9d8aeb4ad9 100644
--- a/src/main/java/io/lettuce/core/ClientOptions.java
+++ b/src/main/java/io/lettuce/core/ClientOptions.java
@@ -25,6 +25,8 @@
import io.lettuce.core.api.StatefulConnection;
import io.lettuce.core.internal.LettuceAssert;
+import io.lettuce.core.json.DefaultJsonParser;
+import io.lettuce.core.json.JsonParser;
import io.lettuce.core.protocol.DecodeBufferPolicies;
import io.lettuce.core.protocol.DecodeBufferPolicy;
import io.lettuce.core.protocol.ProtocolVersion;
@@ -69,6 +71,8 @@ public class ClientOptions implements Serializable {
public static final TimeoutOptions DEFAULT_TIMEOUT_OPTIONS = TimeoutOptions.enabled();
+ public static final JsonParser DEFAULT_JSON_PARSER = DefaultJsonParser.INSTANCE;
+
private final boolean autoReconnect;
private final boolean cancelCommandsOnReconnectFailure;
@@ -89,6 +93,8 @@ public class ClientOptions implements Serializable {
private final Charset scriptCharset;
+ private final JsonParser jsonParser;
+
private final SocketOptions socketOptions;
private final SslOptions sslOptions;
@@ -108,6 +114,7 @@ protected ClientOptions(Builder builder) {
this.readOnlyCommands = builder.readOnlyCommands;
this.requestQueueSize = builder.requestQueueSize;
this.scriptCharset = builder.scriptCharset;
+ this.jsonParser = builder.jsonParser;
this.socketOptions = builder.socketOptions;
this.sslOptions = builder.sslOptions;
this.suspendReconnectOnProtocolFailure = builder.suspendReconnectOnProtocolFailure;
@@ -125,6 +132,7 @@ protected ClientOptions(ClientOptions original) {
this.readOnlyCommands = original.getReadOnlyCommands();
this.requestQueueSize = original.getRequestQueueSize();
this.scriptCharset = original.getScriptCharset();
+ this.jsonParser = original.getJsonParser();
this.socketOptions = original.getSocketOptions();
this.sslOptions = original.getSslOptions();
this.suspendReconnectOnProtocolFailure = original.isSuspendReconnectOnProtocolFailure();
@@ -184,6 +192,8 @@ public static class Builder {
private Charset scriptCharset = DEFAULT_SCRIPT_CHARSET;
+ private JsonParser jsonParser = DEFAULT_JSON_PARSER;
+
private SocketOptions socketOptions = DEFAULT_SOCKET_OPTIONS;
private SslOptions sslOptions = DEFAULT_SSL_OPTIONS;
@@ -369,6 +379,21 @@ public Builder scriptCharset(Charset scriptCharset) {
return this;
}
+ /**
+ * Set a custom implementation for the {@link JsonParser} to use. Defaults to {@link DefaultJsonParser}.
+ *
+ * @param parser must not be {@code null}.
+ * @return {@code this}
+ * @see JsonParser
+ * @since 6.5
+ */
+ public Builder jsonParser(JsonParser parser) {
+
+ LettuceAssert.notNull(parser, "JsonParser must not be null");
+ this.jsonParser = parser;
+ return this;
+ }
+
/**
* Sets the low-level {@link SocketOptions} for the connections kept to Redis servers. See
* {@link #DEFAULT_SOCKET_OPTIONS}.
@@ -449,9 +474,9 @@ public ClientOptions.Builder mutate() {
.decodeBufferPolicy(getDecodeBufferPolicy()).disconnectedBehavior(getDisconnectedBehavior())
.readOnlyCommands(getReadOnlyCommands()).publishOnScheduler(isPublishOnScheduler())
.pingBeforeActivateConnection(isPingBeforeActivateConnection()).protocolVersion(getConfiguredProtocolVersion())
- .requestQueueSize(getRequestQueueSize()).scriptCharset(getScriptCharset()).socketOptions(getSocketOptions())
- .sslOptions(getSslOptions()).suspendReconnectOnProtocolFailure(isSuspendReconnectOnProtocolFailure())
- .timeoutOptions(getTimeoutOptions());
+ .requestQueueSize(getRequestQueueSize()).scriptCharset(getScriptCharset()).jsonParser(getJsonParser())
+ .socketOptions(getSocketOptions()).sslOptions(getSslOptions())
+ .suspendReconnectOnProtocolFailure(isSuspendReconnectOnProtocolFailure()).timeoutOptions(getTimeoutOptions());
return builder;
}
@@ -609,6 +634,16 @@ public Charset getScriptCharset() {
return scriptCharset;
}
+ /**
+ * Returns the currently set up {@link JsonParser}.
+ *
+ * @return the implementation of the {@link JsonParser} to use.
+ * @since 6.5
+ */
+ public JsonParser getJsonParser() {
+ return jsonParser;
+ }
+
/**
* Returns the {@link SocketOptions}.
*
diff --git a/src/main/java/io/lettuce/core/FutureSyncInvocationHandler.java b/src/main/java/io/lettuce/core/FutureSyncInvocationHandler.java
index a080ead113..c251a35310 100644
--- a/src/main/java/io/lettuce/core/FutureSyncInvocationHandler.java
+++ b/src/main/java/io/lettuce/core/FutureSyncInvocationHandler.java
@@ -107,8 +107,8 @@ private static boolean isTxControlMethod(String methodName, Object[] args) {
if (methodName.equals("dispatch") && args.length > 0 && args[0] instanceof ProtocolKeyword) {
ProtocolKeyword keyword = (ProtocolKeyword) args[0];
- if (keyword.name().equals(CommandType.MULTI.name()) || keyword.name().equals(CommandType.EXEC.name())
- || keyword.name().equals(CommandType.DISCARD.name())) {
+ if (keyword.toString().equals(CommandType.MULTI.name()) || keyword.toString().equals(CommandType.EXEC.name())
+ || keyword.toString().equals(CommandType.DISCARD.name())) {
return true;
}
}
diff --git a/src/main/java/io/lettuce/core/RedisAsyncCommandsImpl.java b/src/main/java/io/lettuce/core/RedisAsyncCommandsImpl.java
index aeb17c53e8..23ded92df9 100644
--- a/src/main/java/io/lettuce/core/RedisAsyncCommandsImpl.java
+++ b/src/main/java/io/lettuce/core/RedisAsyncCommandsImpl.java
@@ -4,6 +4,7 @@
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
import io.lettuce.core.codec.RedisCodec;
+import io.lettuce.core.json.JsonParser;
/**
* An asynchronous and thread-safe API for a Redis connection.
@@ -22,8 +23,8 @@ public class RedisAsyncCommandsImpl extends AbstractRedisAsyncCommands connection, RedisCodec codec) {
- super(connection, codec);
+ public RedisAsyncCommandsImpl(StatefulRedisConnection connection, RedisCodec codec, JsonParser parser) {
+ super(connection, codec, parser);
}
@Override
diff --git a/src/main/java/io/lettuce/core/RedisClient.java b/src/main/java/io/lettuce/core/RedisClient.java
index 550c5bf104..4a2c3e7bd3 100644
--- a/src/main/java/io/lettuce/core/RedisClient.java
+++ b/src/main/java/io/lettuce/core/RedisClient.java
@@ -38,6 +38,7 @@
import io.lettuce.core.internal.ExceptionFactory;
import io.lettuce.core.internal.Futures;
import io.lettuce.core.internal.LettuceAssert;
+import io.lettuce.core.json.JsonParser;
import io.lettuce.core.masterreplica.MasterReplica;
import io.lettuce.core.protocol.CommandExpiryWriter;
import io.lettuce.core.protocol.CommandHandler;
@@ -656,7 +657,7 @@ protected StatefulRedisPubSubConnectionImpl newStatefulRedisPubSubC
*/
protected StatefulRedisSentinelConnectionImpl newStatefulRedisSentinelConnection(
RedisChannelWriter channelWriter, RedisCodec codec, Duration timeout) {
- return new StatefulRedisSentinelConnectionImpl<>(channelWriter, codec, timeout);
+ return new StatefulRedisSentinelConnectionImpl<>(channelWriter, codec, timeout, getOptions().getJsonParser());
}
/**
@@ -674,7 +675,7 @@ protected StatefulRedisSentinelConnectionImpl newStatefulRedisSenti
*/
protected StatefulRedisConnectionImpl newStatefulRedisConnection(RedisChannelWriter channelWriter,
PushHandler pushHandler, RedisCodec codec, Duration timeout) {
- return new StatefulRedisConnectionImpl<>(channelWriter, pushHandler, codec, timeout);
+ return new StatefulRedisConnectionImpl<>(channelWriter, pushHandler, codec, timeout, getOptions().getJsonParser());
}
/**
diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java
index a192690d9e..d7db66b4df 100644
--- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java
+++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java
@@ -62,16 +62,6 @@
@SuppressWarnings({ "unchecked", "varargs" })
class RedisCommandBuilder extends BaseRedisCommandBuilder {
- private static final String MUST_NOT_CONTAIN_NULL_ELEMENTS = "must not contain null elements";
-
- private static final String MUST_NOT_BE_EMPTY = "must not be empty";
-
- private static final String MUST_NOT_BE_NULL = "must not be null";
-
- private static final byte[] MINUS_BYTES = { '-' };
-
- private static final byte[] PLUS_BYTES = { '+' };
-
RedisCommandBuilder(RedisCodec codec) {
super(codec);
}
@@ -4438,148 +4428,6 @@ Command>> clusterLinks() {
return createCommand(CLUSTER, (CommandOutput) new ObjectOutput<>(StringCodec.UTF8), args);
}
- private boolean allElementsInstanceOf(Object[] objects, Class> expectedAssignableType) {
-
- for (Object object : objects) {
- if (!expectedAssignableType.isAssignableFrom(object.getClass())) {
- return false;
- }
- }
-
- return true;
- }
-
- private byte[] maxValue(Range extends V> range) {
-
- Boundary extends V> upper = range.getUpper();
-
- if (upper.getValue() == null) {
- return PLUS_BYTES;
- }
-
- ByteBuffer encoded = codec.encodeValue(upper.getValue());
- ByteBuffer allocated = ByteBuffer.allocate(encoded.remaining() + 1);
- allocated.put(upper.isIncluding() ? (byte) '[' : (byte) '(').put(encoded);
-
- return allocated.array();
- }
-
- private byte[] minValue(Range extends V> range) {
-
- Boundary extends V> lower = range.getLower();
-
- if (lower.getValue() == null) {
- return MINUS_BYTES;
- }
-
- ByteBuffer encoded = codec.encodeValue(lower.getValue());
- ByteBuffer allocated = ByteBuffer.allocate(encoded.remaining() + 1);
- allocated.put(lower.isIncluding() ? (byte) '[' : (byte) '(').put(encoded);
-
- return allocated.array();
- }
-
- static void notNull(ScoredValueStreamingChannel> channel) {
- LettuceAssert.notNull(channel, "ScoredValueStreamingChannel " + MUST_NOT_BE_NULL);
- }
-
- static void notNull(KeyStreamingChannel> channel) {
- LettuceAssert.notNull(channel, "KeyValueStreamingChannel " + MUST_NOT_BE_NULL);
- }
-
- static void notNull(ValueStreamingChannel> channel) {
- LettuceAssert.notNull(channel, "ValueStreamingChannel " + MUST_NOT_BE_NULL);
- }
-
- static void notNull(KeyValueStreamingChannel, ?> channel) {
- LettuceAssert.notNull(channel, "KeyValueStreamingChannel " + MUST_NOT_BE_NULL);
- }
-
- static void notNullMinMax(String min, String max) {
- LettuceAssert.notNull(min, "Min " + MUST_NOT_BE_NULL);
- LettuceAssert.notNull(max, "Max " + MUST_NOT_BE_NULL);
- }
-
- private static void addLimit(CommandArgs, ?> args, Limit limit) {
-
- if (limit.isLimited()) {
- args.add(LIMIT).add(limit.getOffset()).add(limit.getCount());
- }
- }
-
- private static void assertNodeId(String nodeId) {
- LettuceAssert.notNull(nodeId, "NodeId " + MUST_NOT_BE_NULL);
- LettuceAssert.notEmpty(nodeId, "NodeId " + MUST_NOT_BE_EMPTY);
- }
-
- private static String max(Range extends Number> range) {
-
- Boundary extends Number> upper = range.getUpper();
-
- if (upper.getValue() == null
- || upper.getValue() instanceof Double && upper.getValue().doubleValue() == Double.POSITIVE_INFINITY) {
- return "+inf";
- }
-
- if (!upper.isIncluding()) {
- return "(" + upper.getValue();
- }
-
- return upper.getValue().toString();
- }
-
- private static String min(Range extends Number> range) {
-
- Boundary extends Number> lower = range.getLower();
-
- if (lower.getValue() == null
- || lower.getValue() instanceof Double && lower.getValue().doubleValue() == Double.NEGATIVE_INFINITY) {
- return "-inf";
- }
-
- if (!lower.isIncluding()) {
- return "(" + lower.getValue();
- }
-
- return lower.getValue().toString();
- }
-
- private static void notEmpty(Object[] keys) {
- LettuceAssert.notNull(keys, "Keys " + MUST_NOT_BE_NULL);
- LettuceAssert.notEmpty(keys, "Keys " + MUST_NOT_BE_EMPTY);
- }
-
- private static void notEmptySlots(int[] slots) {
- LettuceAssert.notNull(slots, "Slots " + MUST_NOT_BE_NULL);
- LettuceAssert.notEmpty(slots, "Slots " + MUST_NOT_BE_EMPTY);
- }
-
- private static void notEmptyValues(Object[] values) {
- LettuceAssert.notNull(values, "Values " + MUST_NOT_BE_NULL);
- LettuceAssert.notEmpty(values, "Values " + MUST_NOT_BE_EMPTY);
- }
-
- private static void notNullKey(Object key) {
- LettuceAssert.notNull(key, "Key " + MUST_NOT_BE_NULL);
- }
-
- private static void keyAndFieldsProvided(Object key, Object[] fields) {
- LettuceAssert.notNull(key, "Key " + MUST_NOT_BE_NULL);
- LettuceAssert.notEmpty(fields, "Fields " + MUST_NOT_BE_EMPTY);
- }
-
- private static void notNullLimit(Limit limit) {
- LettuceAssert.notNull(limit, "Limit " + MUST_NOT_BE_NULL);
- }
-
- private static void notNullRange(Range> range) {
- LettuceAssert.notNull(range, "Range " + MUST_NOT_BE_NULL);
- }
-
- private static void notEmptyRanges(Range>[] ranges) {
- LettuceAssert.notEmpty(ranges, "Ranges " + MUST_NOT_BE_NULL);
- }
-
enum LongCodec implements RedisCodec {
INSTANCE;
diff --git a/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java b/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java
new file mode 100644
index 0000000000..fb1e580179
--- /dev/null
+++ b/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2024, Redis Ltd. and Contributors
+ * All rights reserved.
+ *
+ * Licensed under the MIT License.
+ */
+
+package io.lettuce.core;
+
+import io.lettuce.core.codec.RedisCodec;
+import io.lettuce.core.json.JsonParser;
+import io.lettuce.core.json.JsonType;
+import io.lettuce.core.json.JsonValue;
+import io.lettuce.core.json.arguments.JsonGetArgs;
+import io.lettuce.core.json.arguments.JsonMsetArgs;
+import io.lettuce.core.json.JsonPath;
+import io.lettuce.core.json.arguments.JsonRangeArgs;
+import io.lettuce.core.json.arguments.JsonSetArgs;
+import io.lettuce.core.output.*;
+import io.lettuce.core.protocol.BaseRedisCommandBuilder;
+import io.lettuce.core.protocol.Command;
+import io.lettuce.core.protocol.CommandArgs;
+
+import java.util.List;
+
+import static io.lettuce.core.protocol.CommandType.*;
+
+/**
+ * Implementation of the {@link BaseRedisCommandBuilder} handling JSON commands.
+ *
+ * @author Tihomir Mateev
+ * @since 6.5
+ */
+class RedisJsonCommandBuilder extends BaseRedisCommandBuilder {
+
+ private final JsonParser parser;
+
+ RedisJsonCommandBuilder(RedisCodec codec, JsonParser theParser) {
+ super(codec);
+ parser = theParser;
+ }
+
+ Command> jsonArrappend(K key, JsonPath jsonPath, JsonValue... jsonValues) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ for (JsonValue value : jsonValues) {
+ args.add(value.asByteBuffer().array());
+ }
+
+ return createCommand(JSON_ARRAPPEND, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command> jsonArrindex(K key, JsonPath jsonPath, JsonValue value, JsonRangeArgs range) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ args.add(value.asByteBuffer().array());
+
+ if (range != null) {
+ // OPTIONAL as per API
+ range.build(args);
+ }
+
+ return createCommand(JSON_ARRINDEX, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command> jsonArrinsert(K key, JsonPath jsonPath, int index, JsonValue... values) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ args.add(index);
+
+ for (JsonValue value : values) {
+ args.add(value.asByteBuffer().array());
+ }
+
+ return createCommand(JSON_ARRINSERT, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command> jsonArrlen(K key, JsonPath jsonPath) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+ return createCommand(JSON_ARRLEN, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command> jsonArrpop(K key, JsonPath jsonPath, int index) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null) {
+ args.add(jsonPath.toString());
+
+ if (index != -1) {
+ args.add(index);
+ }
+ }
+
+ return createCommand(JSON_ARRPOP, new JsonValueListOutput<>(codec, parser), args);
+ }
+
+ Command> jsonArrtrim(K key, JsonPath jsonPath, JsonRangeArgs range) {
+
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ if (range != null) {
+ range.build(args);
+ }
+
+ return createCommand(JSON_ARRTRIM, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command jsonClear(K key, JsonPath jsonPath) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_CLEAR, new IntegerOutput<>(codec), args);
+ }
+
+ Command> jsonGet(K key, JsonGetArgs options, JsonPath... jsonPaths) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (options != null) {
+ options.build(args);
+ }
+
+ if (jsonPaths != null) {
+ for (JsonPath jsonPath : jsonPaths) {
+ if (jsonPath != null) {
+ args.add(jsonPath.toString());
+ }
+ }
+ }
+
+ return createCommand(JSON_GET, new JsonValueListOutput<>(codec, parser), args);
+ }
+
+ Command jsonMerge(K key, JsonPath jsonPath, JsonValue value) {
+
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ args.add(value.asByteBuffer().array());
+
+ return createCommand(JSON_MERGE, new StatusOutput<>(codec), args);
+ }
+
+ Command> jsonMGet(JsonPath jsonPath, K... keys) {
+ notEmpty(keys);
+
+ CommandArgs args = new CommandArgs<>(codec).addKeys(keys);
+
+ if (jsonPath != null) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_MGET, new JsonValueListOutput<>(codec, parser), args);
+ }
+
+ Command jsonMSet(List> arguments) {
+
+ notEmpty(arguments.toArray());
+
+ CommandArgs args = new CommandArgs<>(codec);
+
+ for (JsonMsetArgs argument : arguments) {
+ argument.build(args);
+ }
+
+ return createCommand(JSON_MSET, new StatusOutput<>(codec), args);
+ }
+
+ Command> jsonNumincrby(K key, JsonPath jsonPath, Number number) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ args.add(number.toString());
+
+ return createCommand(JSON_NUMINCRBY, new NumberListOutput<>(codec), args);
+ }
+
+ Command> jsonObjkeys(K key, JsonPath jsonPath) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_OBJKEYS, new ValueListOutput<>(codec), args);
+ }
+
+ Command> jsonObjlen(K key, JsonPath jsonPath) {
+
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_OBJLEN, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command jsonSet(K key, JsonPath jsonPath, JsonValue value, JsonSetArgs options) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ args.add(jsonPath.toString());
+
+ args.add(value.asByteBuffer().array());
+
+ if (options != null) {
+ options.build(args);
+ }
+
+ return createCommand(JSON_SET, new StatusOutput<>(codec), args);
+ }
+
+ Command> jsonStrappend(K key, JsonPath jsonPath, JsonValue value) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ args.add(value.asByteBuffer().array());
+
+ return createCommand(JSON_STRAPPEND, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command> jsonStrlen(K key, JsonPath jsonPath) {
+
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_STRLEN, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command> jsonToggle(K key, JsonPath jsonPath) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_TOGGLE, (CommandOutput) new ArrayOutput<>(codec), args);
+ }
+
+ Command jsonDel(K key, JsonPath jsonPath) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+ return createCommand(JSON_DEL, new IntegerOutput<>(codec), args);
+ }
+
+ Command> jsonType(K key, JsonPath jsonPath) {
+ notNullKey(key);
+
+ CommandArgs args = new CommandArgs<>(codec).addKey(key);
+
+ if (jsonPath != null && !jsonPath.isRootPath()) {
+ args.add(jsonPath.toString());
+ }
+
+ return createCommand(JSON_TYPE, new JsonTypeListOutput<>(codec), args);
+ }
+
+}
diff --git a/src/main/java/io/lettuce/core/RedisReactiveCommandsImpl.java b/src/main/java/io/lettuce/core/RedisReactiveCommandsImpl.java
index 6b28b8e051..23ffd71ef2 100644
--- a/src/main/java/io/lettuce/core/RedisReactiveCommandsImpl.java
+++ b/src/main/java/io/lettuce/core/RedisReactiveCommandsImpl.java
@@ -4,6 +4,7 @@
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
import io.lettuce.core.codec.RedisCodec;
+import io.lettuce.core.json.JsonParser;
/**
* A reactive and thread-safe API for a Redis Sentinel connection.
@@ -22,8 +23,8 @@ public class RedisReactiveCommandsImpl extends AbstractRedisReactiveComman
* @param codec the codec for command encoding.
*
*/
- public RedisReactiveCommandsImpl(StatefulRedisConnection connection, RedisCodec codec) {
- super(connection, codec);
+ public RedisReactiveCommandsImpl(StatefulRedisConnection connection, RedisCodec codec, JsonParser parser) {
+ super(connection, codec, parser);
}
@Override
diff --git a/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java b/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java
index 5f64e272e8..1948062a59 100644
--- a/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java
+++ b/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java
@@ -36,6 +36,7 @@
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
import io.lettuce.core.codec.RedisCodec;
import io.lettuce.core.codec.StringCodec;
+import io.lettuce.core.json.JsonParser;
import io.lettuce.core.output.MultiOutput;
import io.lettuce.core.output.StatusOutput;
import io.lettuce.core.protocol.*;
@@ -64,6 +65,8 @@ public class StatefulRedisConnectionImpl extends RedisChannelHandler
private final PushHandler pushHandler;
+ private final JsonParser parser;
+
protected MultiOutput multi;
/**
@@ -75,12 +78,13 @@ public class StatefulRedisConnectionImpl extends RedisChannelHandler
* @param timeout Maximum time to wait for a response.
*/
public StatefulRedisConnectionImpl(RedisChannelWriter writer, PushHandler pushHandler, RedisCodec codec,
- Duration timeout) {
+ Duration timeout, JsonParser parser) {
super(writer, timeout);
this.pushHandler = pushHandler;
this.codec = codec;
+ this.parser = parser;
this.async = newRedisAsyncCommandsImpl();
this.sync = newRedisSyncCommandsImpl();
this.reactive = newRedisReactiveCommandsImpl();
@@ -110,7 +114,7 @@ protected RedisCommands newRedisSyncCommandsImpl() {
* @return a new instance
*/
protected RedisAsyncCommandsImpl newRedisAsyncCommandsImpl() {
- return new RedisAsyncCommandsImpl<>(this, codec);
+ return new RedisAsyncCommandsImpl<>(this, codec, parser);
}
@Override
@@ -124,7 +128,7 @@ public RedisReactiveCommands reactive() {
* @return a new instance
*/
protected RedisReactiveCommandsImpl newRedisReactiveCommandsImpl() {
- return new RedisReactiveCommandsImpl<>(this, codec);
+ return new RedisReactiveCommandsImpl<>(this, codec, parser);
}
@Override
@@ -184,7 +188,7 @@ public RedisCommand dispatch(RedisCommand command) {
private void potentiallyEnableMulti(RedisCommand command) {
- if (command.getType().name().equals(MULTI.name())) {
+ if (command.getType().toString().equals(MULTI.name())) {
multi = (multi == null ? new MultiOutput<>(codec) : multi);
@@ -202,7 +206,7 @@ protected RedisCommand preProcessCommand(RedisCommand comm
RedisCommand local = command;
- if (local.getType().name().equals(AUTH.name())) {
+ if (local.getType().toString().equals(AUTH.name())) {
local = attachOnComplete(local, status -> {
if ("OK".equals(status)) {
@@ -219,7 +223,7 @@ protected RedisCommand preProcessCommand(RedisCommand comm
});
}
- if (local.getType().name().equals(SELECT.name())) {
+ if (local.getType().toString().equals(SELECT.name())) {
local = attachOnComplete(local, status -> {
if ("OK".equals(status)) {
Long db = CommandArgsAccessor.getFirstInteger(command.getArgs());
@@ -230,7 +234,7 @@ protected RedisCommand preProcessCommand(RedisCommand comm
});
}
- if (local.getType().name().equals(READONLY.name())) {
+ if (local.getType().toString().equals(READONLY.name())) {
local = attachOnComplete(local, status -> {
if ("OK".equals(status)) {
state.setReadOnly(true);
@@ -238,7 +242,7 @@ protected RedisCommand preProcessCommand(RedisCommand comm
});
}
- if (local.getType().name().equals(READWRITE.name())) {
+ if (local.getType().toString().equals(READWRITE.name())) {
local = attachOnComplete(local, status -> {
if ("OK".equals(status)) {
state.setReadOnly(false);
@@ -246,14 +250,14 @@ protected RedisCommand preProcessCommand(RedisCommand comm
});
}
- if (local.getType().name().equals(DISCARD.name())) {
+ if (local.getType().toString().equals(DISCARD.name())) {
if (multi != null) {
multi.cancel();
multi = null;
}
}
- if (local.getType().name().equals(EXEC.name())) {
+ if (local.getType().toString().equals(EXEC.name())) {
MultiOutput multiOutput = this.multi;
this.multi = null;
if (multiOutput == null) {
@@ -262,7 +266,7 @@ protected RedisCommand preProcessCommand(RedisCommand comm
local.setOutput((MultiOutput) multiOutput);
}
- if (multi != null && !local.getType().name().equals(MULTI.name())) {
+ if (multi != null && !local.getType().toString().equals(MULTI.name())) {
local = new TransactionalCommand<>(local);
multi.add(local);
}
diff --git a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java
index be442dd70b..6ff3ef9ad1 100644
--- a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java
+++ b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java
@@ -22,6 +22,7 @@
import io.lettuce.core.RedisFuture;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
+import io.lettuce.core.json.JsonParser;
/**
* A complete asynchronous and thread-safe Redis API with 400+ Methods.
@@ -36,7 +37,7 @@ public interface RedisAsyncCommands extends BaseRedisAsyncCommands,
RedisHashAsyncCommands, RedisHLLAsyncCommands, RedisKeyAsyncCommands, RedisListAsyncCommands,
RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands,
RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, RedisStringAsyncCommands,
- RedisTransactionalAsyncCommands {
+ RedisTransactionalAsyncCommands, RedisJsonAsyncCommands {
/**
* Authenticate to the server.
@@ -81,4 +82,10 @@ public interface RedisAsyncCommands extends BaseRedisAsyncCommands,
@Deprecated
StatefulRedisConnection getStatefulConnection();
+ /**
+ * @return the currently configured instance of the {@link JsonParser}
+ * @since 6.5
+ */
+ JsonParser getJsonParser();
+
}
diff --git a/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java
new file mode 100644
index 0000000000..74494e2718
--- /dev/null
+++ b/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2017-2024, Redis Ltd. and Contributors
+ * All rights reserved.
+ *
+ * Licensed under the MIT License.
+ */
+package io.lettuce.core.api.async;
+
+import java.util.List;
+import io.lettuce.core.RedisFuture;
+import io.lettuce.core.json.JsonPath;
+import io.lettuce.core.json.JsonType;
+import io.lettuce.core.json.JsonValue;
+import io.lettuce.core.json.arguments.JsonGetArgs;
+import io.lettuce.core.json.arguments.JsonMsetArgs;
+import io.lettuce.core.json.arguments.JsonRangeArgs;
+import io.lettuce.core.json.arguments.JsonSetArgs;
+
+/**
+ * Asynchronous executed commands for JSON documents
+ *
+ * @param Key type.
+ * @param Value type.
+ * @author Tihomir Mateev
+ * @see Redis JSON
+ * @since 6.5
+ * @generated by io.lettuce.apigenerator.CreateAsyncApi
+ */
+public interface RedisJsonAsyncCommands {
+
+ /**
+ * Append the JSON values into the array at a given {@link JsonPath} after the last element in a said array.
+ *
+ * @param key the key holding the JSON document.
+ * @param jsonPath the {@link JsonPath} pointing to the array inside the document.
+ * @param values one or more {@link JsonValue} to be appended.
+ * @return Long the resulting size of the arrays after the new data was appended, or null if the path does not exist.
+ * @since 6.5
+ */
+ RedisFuture> jsonArrappend(K key, JsonPath jsonPath, JsonValue... values);
+
+ /**
+ * Append the JSON values into the array at the {@link JsonPath#ROOT_PATH} after the last element in a said array.
+ *
+ * @param key the key holding the JSON document.
+ * @param values one or more {@link JsonValue} to be appended.
+ * @return Long the resulting size of the arrays after the new data was appended, or null if the path does not exist.
+ * @since 6.5
+ */
+ RedisFuture