diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index f9fb7f5b1c..2b90cd9939 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -26,6 +26,7 @@ import io.lettuce.core.codec.Base16; import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.internal.LettuceAssert; +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; @@ -1558,7 +1559,7 @@ public RedisFuture> jsonToggle(K key, JsonPath jsonPath) { } @Override - public RedisFuture> jsonType(K key, JsonPath jsonPath) { + public RedisFuture> jsonType(K key, JsonPath jsonPath) { return dispatch(jsonCommandBuilder.jsonType(key, jsonPath)); } diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 7d117d1d7d..2252ac551e 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -27,6 +27,7 @@ import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.internal.LettuceAssert; 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; @@ -1621,7 +1622,7 @@ public Flux jsonToggle(K key, JsonPath jsonPath) { } @Override - public Flux jsonType(K key, JsonPath jsonPath) { + public Flux jsonType(K key, JsonPath jsonPath) { return createDissolvingFlux(() -> jsonCommandBuilder.jsonType(key, jsonPath)); } diff --git a/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java b/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java index 5d0ea46c55..1e19e01b10 100644 --- a/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisJsonCommandBuilder.java @@ -8,6 +8,7 @@ package io.lettuce.core; import io.lettuce.core.codec.RedisCodec; +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; @@ -309,7 +310,7 @@ Command jsonDel(K key, JsonPath jsonPath) { return createCommand(JSON_DEL, new IntegerOutput<>(codec), args); } - Command> jsonType(K key, JsonPath jsonPath) { + Command> jsonType(K key, JsonPath jsonPath) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key); @@ -318,7 +319,7 @@ Command> jsonType(K key, JsonPath jsonPath) { args.add(jsonPath.toString()); } - return createCommand(JSON_TYPE, new ValueListOutput<>(codec), args); + return createCommand(JSON_TYPE, new JsonTypeListOutput<>(codec), args); } } diff --git a/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java index 16a8513890..37cdd66779 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisJsonAsyncCommands.java @@ -9,6 +9,7 @@ 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; @@ -207,7 +208,7 @@ public interface RedisJsonAsyncCommands { * * @param key the key holding the JSON document. * @param jsonPath the {@link JsonPath} pointing to the value(s) whose key(s) we want. - * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. + * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. * @since 6.5 */ RedisFuture> jsonObjkeys(K key, JsonPath jsonPath); @@ -282,6 +283,6 @@ public interface RedisJsonAsyncCommands { * @return List the type of JSON value at the provided {@link JsonPath} * @since 6.5 */ - RedisFuture> jsonType(K key, JsonPath jsonPath); + RedisFuture> jsonType(K key, JsonPath jsonPath); } diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisJsonReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisJsonReactiveCommands.java index 94ccf55abe..115c073f3f 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisJsonReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisJsonReactiveCommands.java @@ -8,6 +8,7 @@ import java.util.List; 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; @@ -208,7 +209,7 @@ public interface RedisJsonReactiveCommands { * * @param key the key holding the JSON document. * @param jsonPath the {@link JsonPath} pointing to the value(s) whose key(s) we want. - * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. + * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. * @since 6.5 */ Flux jsonObjkeys(K key, JsonPath jsonPath); @@ -283,6 +284,6 @@ public interface RedisJsonReactiveCommands { * @return List the type of JSON value at the provided {@link JsonPath} * @since 6.5 */ - Flux jsonType(K key, JsonPath jsonPath); + Flux jsonType(K key, JsonPath jsonPath); } diff --git a/src/main/java/io/lettuce/core/api/sync/RedisJsonCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisJsonCommands.java index 9505583fd5..161317ada9 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisJsonCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisJsonCommands.java @@ -8,6 +8,7 @@ import java.util.List; 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; @@ -206,7 +207,7 @@ public interface RedisJsonCommands { * * @param key the key holding the JSON document. * @param jsonPath the {@link JsonPath} pointing to the value(s) whose key(s) we want. - * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. + * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. * @since 6.5 */ List jsonObjkeys(K key, JsonPath jsonPath); @@ -281,6 +282,6 @@ public interface RedisJsonCommands { * @return List the type of JSON value at the provided {@link JsonPath} * @since 6.5 */ - List jsonType(K key, JsonPath jsonPath); + List jsonType(K key, JsonPath jsonPath); } diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionJsonAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionJsonAsyncCommands.java index baa6fc7a3d..27dae9cfdb 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionJsonAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionJsonAsyncCommands.java @@ -8,6 +8,7 @@ import java.util.List; 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; @@ -206,7 +207,7 @@ public interface NodeSelectionJsonAsyncCommands { * * @param key the key holding the JSON document. * @param jsonPath the {@link JsonPath} pointing to the value(s) whose key(s) we want. - * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. + * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. * @since 6.5 */ AsyncExecutions> jsonObjkeys(K key, JsonPath jsonPath); @@ -281,6 +282,6 @@ public interface NodeSelectionJsonAsyncCommands { * @return List the type of JSON value at the provided {@link JsonPath} * @since 6.5 */ - AsyncExecutions> jsonType(K key, JsonPath jsonPath); + AsyncExecutions> jsonType(K key, JsonPath jsonPath); } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionJsonCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionJsonCommands.java index 85f0a6d79c..143cbf91c3 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionJsonCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionJsonCommands.java @@ -8,6 +8,7 @@ import java.util.List; 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; @@ -206,7 +207,7 @@ public interface NodeSelectionJsonCommands { * * @param key the key holding the JSON document. * @param jsonPath the {@link JsonPath} pointing to the value(s) whose key(s) we want. - * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. + * @return List the keys in the JSON document that are referenced by the given {@link JsonPath}. * @since 6.5 */ Executions> jsonObjkeys(K key, JsonPath jsonPath); @@ -281,6 +282,6 @@ public interface NodeSelectionJsonCommands { * @return List the type of JSON value at the provided {@link JsonPath} * @since 6.5 */ - Executions> jsonType(K key, JsonPath jsonPath); + Executions> jsonType(K key, JsonPath jsonPath); } diff --git a/src/main/java/io/lettuce/core/json/JsonType.java b/src/main/java/io/lettuce/core/json/JsonType.java new file mode 100644 index 0000000000..7e432ec36a --- /dev/null +++ b/src/main/java/io/lettuce/core/json/JsonType.java @@ -0,0 +1,53 @@ +/* + * Copyright 2024, Redis Ltd. and Contributors + * All rights reserved. + * + * Licensed under the MIT License. + * + * This file contains contributions from third-party contributors + * licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.lettuce.core.json; + +/** + * JSON types as returned by the JSON.TYPE command + * + * @see io.lettuce.core.api.sync.RedisCommands#jsonType + * @since 6.5 + * @author Tihomir Mateev + */ +public enum JsonType { + + OBJECT, ARRAY, STRING, INTEGER, NUMBER, BOOLEAN, UNKNOWN; + + public static JsonType fromString(String s) { + switch (s) { + case "object": + return OBJECT; + case "array": + return ARRAY; + case "string": + return STRING; + case "integer": + return INTEGER; + case "number": + return NUMBER; + case "boolean": + return BOOLEAN; + default: + return UNKNOWN; + } + } + +} diff --git a/src/main/java/io/lettuce/core/output/JsonTypeListOutput.java b/src/main/java/io/lettuce/core/output/JsonTypeListOutput.java new file mode 100644 index 0000000000..6bd2f0e713 --- /dev/null +++ b/src/main/java/io/lettuce/core/output/JsonTypeListOutput.java @@ -0,0 +1,49 @@ +/* + * Copyright 2011-Present, Redis Ltd. and Contributors + * All rights reserved. + * + * Licensed under the MIT License. + */ +package io.lettuce.core.output; + +import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.json.JsonType; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +/** + * {@link List} of {@link JsonType} output. + * + * @param Key type. + * @param Value type. + * @author Tihomir Mateev + */ +public class JsonTypeListOutput extends CommandOutput> { + + private boolean initialized; + + public JsonTypeListOutput(RedisCodec codec) { + super(codec, Collections.emptyList()); + } + + @Override + public void set(ByteBuffer bytes) { + if (!initialized) { + multi(1); + } + + output.add(JsonType.fromString(decodeAscii(bytes))); + } + + @Override + public void multi(int count) { + + if (!initialized) { + output = OutputFactory.newList(count); + initialized = true; + } + } + +} diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommands.kt index 2c00790388..de0d2ca05d 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommands.kt @@ -9,6 +9,7 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import kotlinx.coroutines.flow.Flow +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 @@ -209,7 +210,7 @@ interface RedisJsonCoroutinesCommands { * * @param key the key holding the JSON document. * @param jsonPath the [JsonPath] pointing to the value(s) whose key(s) we want. - * @return List the keys in the JSON document that are referenced by the given [JsonPath]. + * @return List the keys in the JSON document that are referenced by the given [JsonPath]. * @since 6.5 */ suspend fun jsonObjkeys(key: K, jsonPath: JsonPath): List @@ -284,7 +285,7 @@ interface RedisJsonCoroutinesCommands { * @return List the type of JSON value at the provided [JsonPath] * @since 6.5 */ - suspend fun jsonType(key: K, jsonPath: JsonPath): List + suspend fun jsonType(key: K, jsonPath: JsonPath): List } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommandsImpl.kt index 7cba803e37..f71961feb8 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisJsonCoroutinesCommandsImpl.kt @@ -10,6 +10,7 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.* import io.lettuce.core.api.reactive.RedisJsonReactiveCommands 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 @@ -74,7 +75,7 @@ internal class RedisJsonCoroutinesCommandsImpl(internal val op override suspend fun jsonMSet(arguments: List>): String? = ops.jsonMSet(arguments).awaitFirstOrNull() - override suspend fun jsonType(key: K, jsonPath: JsonPath): List = + override suspend fun jsonType(key: K, jsonPath: JsonPath): List = ops.jsonType(key, jsonPath).asFlow().toList() override suspend fun jsonToggle(key: K, jsonPath: JsonPath): List = diff --git a/src/main/templates/io/lettuce/core/api/RedisJsonCommands.java b/src/main/templates/io/lettuce/core/api/RedisJsonCommands.java index b6ff471b22..67caaffb2f 100644 --- a/src/main/templates/io/lettuce/core/api/RedisJsonCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisJsonCommands.java @@ -6,6 +6,7 @@ */ package io.lettuce.core.api; +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; @@ -281,6 +282,6 @@ public interface RedisJsonCommands { * @return List the type of JSON value at the provided {@link JsonPath} * @since 6.5 */ - List jsonType(K key, JsonPath jsonPath); + List jsonType(K key, JsonPath jsonPath); } diff --git a/src/test/java/io/lettuce/core/json/RedisJsonClusterIntegrationTests.java b/src/test/java/io/lettuce/core/json/RedisJsonClusterIntegrationTests.java index fc59154dd0..829086961e 100644 --- a/src/test/java/io/lettuce/core/json/RedisJsonClusterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/json/RedisJsonClusterIntegrationTests.java @@ -401,7 +401,7 @@ void jsonDel(String path) { void jsonType(String path) { JsonPath myPath = JsonPath.of(path); - String jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + JsonType jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); assertThat(jsonType).isEqualTo("array"); } diff --git a/src/test/java/io/lettuce/core/json/RedisJsonIntegrationTests.java b/src/test/java/io/lettuce/core/json/RedisJsonIntegrationTests.java index d0137a2f26..93593868d9 100644 --- a/src/test/java/io/lettuce/core/json/RedisJsonIntegrationTests.java +++ b/src/test/java/io/lettuce/core/json/RedisJsonIntegrationTests.java @@ -447,8 +447,32 @@ void jsonDel(String path) { void jsonType(String path) { JsonPath myPath = JsonPath.of(path); - String jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); - assertThat(jsonType).isEqualTo("array"); + JsonType jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + assertThat(jsonType).isEqualTo(JsonType.ARRAY); + } + + @Test + void jsonAllTypes() { + JsonPath myPath = JsonPath.of("$..mountain_bikes[1]"); + + JsonType jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + assertThat(jsonType).isEqualTo(JsonType.OBJECT); + + myPath = JsonPath.of("$..mountain_bikes[0:1].price"); + jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + assertThat(jsonType).isEqualTo(JsonType.INTEGER); + + myPath = JsonPath.of("$..weight"); + jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + assertThat(jsonType).isEqualTo(JsonType.NUMBER); + + myPath = JsonPath.of("$..complete"); + jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + assertThat(jsonType).isEqualTo(JsonType.BOOLEAN); + + myPath = JsonPath.of("$..inventory.owner"); + jsonType = redis.jsonType(BIKES_INVENTORY, myPath).get(0); + assertThat(jsonType).isEqualTo(JsonType.UNKNOWN); } }