Skip to content

Commit 547271c

Browse files
authored
Introducing JSON to Lettuce (#2933) (#2992)
* Most of the API layer * Add the JSON.TYPE command * Kotlin coroutines added; Started JSON README.md; Parser registry is now part of the Connection; formatting; chained all commands; extracted commands in their own unit RedisJsonCommandBuilder; * Implemented 90% of commands from top 10 * All but SET are implemented * Integrated Jackson, finished up the SET command * Left out a few files by mistake * Adding some JavaDoc * Formatting * Implemented all JSON commands * Introducing test containers to the testing fw * Complete coverage with integration tests, straight scenarios * Added Pathv1 tests * Added RedisCE cluster support for the JSON.MGET and JSON.MSET commands * Handle null values * No longer using K for the JSON object keys * Polishing * JsonType introduced to help typization * Remove the RedisCodec from the JsonValue/JsonParser abstraction, add configuration for custom parsers * Extend API surface with methods that reduce the amount of required arguments * Adding unit tests, addressing changes to README.md * Implemented object-mapping functionality * Addresses Ali's comments * Addressed last bunch of comments from Ali, changed ports to not colide with existing infra * Forgot to change ports and stop debug log * Polishing touches
1 parent 2267bfe commit 547271c

File tree

121 files changed

+8673
-311
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+8673
-311
lines changed

pom.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<brave.version>5.13.11</brave.version>
5151
<commons-lang3.version>3.13.0</commons-lang3.version>
5252
<commons-pool.version>2.12.0</commons-pool.version>
53+
<jackson-version>2.17.0</jackson-version>
5354
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
5455
<javax.servlet-api.version>4.0.1</javax.servlet-api.version>
5556
<junit5.version>5.10.2</junit5.version>
@@ -238,6 +239,13 @@
238239
<optional>true</optional>
239240
</dependency>
240241

242+
<dependency>
243+
<groupId>com.fasterxml.jackson.core</groupId>
244+
<artifactId>jackson-databind</artifactId>
245+
<version>${jackson-version}</version>
246+
<optional>true</optional>
247+
</dependency>
248+
241249
<!-- OS-native transports -->
242250

243251
<dependency>
@@ -523,6 +531,21 @@
523531
<scope>test</scope>
524532
</dependency>
525533

534+
<!-- TEST CONTAINERS -->
535+
536+
<dependency>
537+
<groupId>org.testcontainers</groupId>
538+
<artifactId>testcontainers</artifactId>
539+
<version>1.20.1</version>
540+
<scope>test</scope>
541+
</dependency>
542+
<dependency>
543+
<groupId>org.testcontainers</groupId>
544+
<artifactId>junit-jupiter</artifactId>
545+
<version>1.20.1</version>
546+
<scope>test</scope>
547+
</dependency>
548+
526549
</dependencies>
527550

528551
<build>

src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java

Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
import io.lettuce.core.codec.Base16;
2727
import io.lettuce.core.codec.RedisCodec;
2828
import io.lettuce.core.internal.LettuceAssert;
29+
import io.lettuce.core.json.JsonParser;
30+
import io.lettuce.core.json.JsonType;
31+
import io.lettuce.core.json.JsonValue;
32+
import io.lettuce.core.json.arguments.JsonGetArgs;
33+
import io.lettuce.core.json.arguments.JsonMsetArgs;
34+
import io.lettuce.core.json.JsonPath;
35+
import io.lettuce.core.json.arguments.JsonRangeArgs;
36+
import io.lettuce.core.json.arguments.JsonSetArgs;
2937
import io.lettuce.core.models.stream.ClaimedMessages;
3038
import io.lettuce.core.models.stream.PendingMessage;
3139
import io.lettuce.core.models.stream.PendingMessages;
@@ -71,21 +79,27 @@ public abstract class AbstractRedisAsyncCommands<K, V> implements RedisAclAsyncC
7179
RedisKeyAsyncCommands<K, V>, RedisStringAsyncCommands<K, V>, RedisListAsyncCommands<K, V>, RedisSetAsyncCommands<K, V>,
7280
RedisSortedSetAsyncCommands<K, V>, RedisScriptingAsyncCommands<K, V>, RedisServerAsyncCommands<K, V>,
7381
RedisHLLAsyncCommands<K, V>, BaseRedisAsyncCommands<K, V>, RedisTransactionalAsyncCommands<K, V>,
74-
RedisGeoAsyncCommands<K, V>, RedisClusterAsyncCommands<K, V> {
82+
RedisGeoAsyncCommands<K, V>, RedisClusterAsyncCommands<K, V>, RedisJsonAsyncCommands<K, V> {
7583

7684
private final StatefulConnection<K, V> connection;
7785

7886
private final RedisCommandBuilder<K, V> commandBuilder;
7987

88+
private final RedisJsonCommandBuilder<K, V> jsonCommandBuilder;
89+
90+
private final JsonParser parser;
91+
8092
/**
8193
* Initialize a new instance.
8294
*
8395
* @param connection the connection to operate on
8496
* @param codec the codec for command encoding
8597
*/
86-
public AbstractRedisAsyncCommands(StatefulConnection<K, V> connection, RedisCodec<K, V> codec) {
98+
public AbstractRedisAsyncCommands(StatefulConnection<K, V> connection, RedisCodec<K, V> codec, JsonParser parser) {
99+
this.parser = parser;
87100
this.connection = connection;
88101
this.commandBuilder = new RedisCommandBuilder<>(codec);
102+
this.jsonCommandBuilder = new RedisJsonCommandBuilder<>(codec, parser);
89103
}
90104

91105
@Override
@@ -1453,6 +1467,176 @@ public boolean isOpen() {
14531467
return connection.isOpen();
14541468
}
14551469

1470+
@Override
1471+
public RedisFuture<List<Long>> jsonArrappend(K key, JsonPath jsonPath, JsonValue... values) {
1472+
return dispatch(jsonCommandBuilder.jsonArrappend(key, jsonPath, values));
1473+
}
1474+
1475+
@Override
1476+
public RedisFuture<List<Long>> jsonArrappend(K key, JsonValue... values) {
1477+
return dispatch(jsonCommandBuilder.jsonArrappend(key, JsonPath.ROOT_PATH, values));
1478+
}
1479+
1480+
@Override
1481+
public RedisFuture<List<Long>> jsonArrindex(K key, JsonPath jsonPath, JsonValue value, JsonRangeArgs range) {
1482+
return dispatch(jsonCommandBuilder.jsonArrindex(key, jsonPath, value, range));
1483+
}
1484+
1485+
@Override
1486+
public RedisFuture<List<Long>> jsonArrindex(K key, JsonPath jsonPath, JsonValue value) {
1487+
return dispatch(jsonCommandBuilder.jsonArrindex(key, jsonPath, value, JsonRangeArgs.Builder.defaults()));
1488+
}
1489+
1490+
@Override
1491+
public RedisFuture<List<Long>> jsonArrinsert(K key, JsonPath jsonPath, int index, JsonValue... values) {
1492+
return dispatch(jsonCommandBuilder.jsonArrinsert(key, jsonPath, index, values));
1493+
}
1494+
1495+
@Override
1496+
public RedisFuture<List<Long>> jsonArrlen(K key, JsonPath jsonPath) {
1497+
return dispatch(jsonCommandBuilder.jsonArrlen(key, jsonPath));
1498+
}
1499+
1500+
@Override
1501+
public RedisFuture<List<Long>> jsonArrlen(K key) {
1502+
return dispatch(jsonCommandBuilder.jsonArrlen(key, JsonPath.ROOT_PATH));
1503+
}
1504+
1505+
@Override
1506+
public RedisFuture<List<JsonValue>> jsonArrpop(K key, JsonPath jsonPath, int index) {
1507+
return dispatch(jsonCommandBuilder.jsonArrpop(key, jsonPath, index));
1508+
}
1509+
1510+
@Override
1511+
public RedisFuture<List<JsonValue>> jsonArrpop(K key, JsonPath jsonPath) {
1512+
return dispatch(jsonCommandBuilder.jsonArrpop(key, jsonPath, -1));
1513+
}
1514+
1515+
@Override
1516+
public RedisFuture<List<JsonValue>> jsonArrpop(K key) {
1517+
return dispatch(jsonCommandBuilder.jsonArrpop(key, JsonPath.ROOT_PATH, -1));
1518+
}
1519+
1520+
@Override
1521+
public RedisFuture<List<Long>> jsonArrtrim(K key, JsonPath jsonPath, JsonRangeArgs range) {
1522+
return dispatch(jsonCommandBuilder.jsonArrtrim(key, jsonPath, range));
1523+
}
1524+
1525+
@Override
1526+
public RedisFuture<Long> jsonClear(K key, JsonPath jsonPath) {
1527+
return dispatch(jsonCommandBuilder.jsonClear(key, jsonPath));
1528+
}
1529+
1530+
@Override
1531+
public RedisFuture<Long> jsonClear(K key) {
1532+
return dispatch(jsonCommandBuilder.jsonClear(key, JsonPath.ROOT_PATH));
1533+
}
1534+
1535+
@Override
1536+
public RedisFuture<Long> jsonDel(K key, JsonPath jsonPath) {
1537+
return dispatch(jsonCommandBuilder.jsonDel(key, jsonPath));
1538+
}
1539+
1540+
@Override
1541+
public RedisFuture<Long> jsonDel(K key) {
1542+
return dispatch(jsonCommandBuilder.jsonDel(key, JsonPath.ROOT_PATH));
1543+
}
1544+
1545+
@Override
1546+
public RedisFuture<List<JsonValue>> jsonGet(K key, JsonGetArgs options, JsonPath... jsonPaths) {
1547+
return dispatch(jsonCommandBuilder.jsonGet(key, options, jsonPaths));
1548+
}
1549+
1550+
@Override
1551+
public RedisFuture<List<JsonValue>> jsonGet(K key, JsonPath... jsonPaths) {
1552+
return dispatch(jsonCommandBuilder.jsonGet(key, JsonGetArgs.Builder.defaults(), jsonPaths));
1553+
}
1554+
1555+
@Override
1556+
public RedisFuture<String> jsonMerge(K key, JsonPath jsonPath, JsonValue value) {
1557+
return dispatch(jsonCommandBuilder.jsonMerge(key, jsonPath, value));
1558+
}
1559+
1560+
@Override
1561+
public RedisFuture<List<JsonValue>> jsonMGet(JsonPath jsonPath, K... keys) {
1562+
return dispatch(jsonCommandBuilder.jsonMGet(jsonPath, keys));
1563+
}
1564+
1565+
@Override
1566+
public RedisFuture<String> jsonMSet(List<JsonMsetArgs<K, V>> arguments) {
1567+
return dispatch(jsonCommandBuilder.jsonMSet(arguments));
1568+
}
1569+
1570+
@Override
1571+
public RedisFuture<List<Number>> jsonNumincrby(K key, JsonPath jsonPath, Number number) {
1572+
return dispatch(jsonCommandBuilder.jsonNumincrby(key, jsonPath, number));
1573+
}
1574+
1575+
@Override
1576+
public RedisFuture<List<V>> jsonObjkeys(K key, JsonPath jsonPath) {
1577+
return dispatch(jsonCommandBuilder.jsonObjkeys(key, jsonPath));
1578+
}
1579+
1580+
@Override
1581+
public RedisFuture<List<V>> jsonObjkeys(K key) {
1582+
return dispatch(jsonCommandBuilder.jsonObjkeys(key, JsonPath.ROOT_PATH));
1583+
}
1584+
1585+
@Override
1586+
public RedisFuture<List<Long>> jsonObjlen(K key, JsonPath jsonPath) {
1587+
return dispatch(jsonCommandBuilder.jsonObjlen(key, jsonPath));
1588+
}
1589+
1590+
@Override
1591+
public RedisFuture<List<Long>> jsonObjlen(K key) {
1592+
return dispatch(jsonCommandBuilder.jsonObjlen(key, JsonPath.ROOT_PATH));
1593+
}
1594+
1595+
@Override
1596+
public RedisFuture<String> jsonSet(K key, JsonPath jsonPath, JsonValue value, JsonSetArgs options) {
1597+
return dispatch(jsonCommandBuilder.jsonSet(key, jsonPath, value, options));
1598+
}
1599+
1600+
@Override
1601+
public RedisFuture<String> jsonSet(K key, JsonPath jsonPath, JsonValue value) {
1602+
return dispatch(jsonCommandBuilder.jsonSet(key, jsonPath, value, JsonSetArgs.Builder.defaults()));
1603+
}
1604+
1605+
@Override
1606+
public RedisFuture<List<Long>> jsonStrappend(K key, JsonPath jsonPath, JsonValue value) {
1607+
return dispatch(jsonCommandBuilder.jsonStrappend(key, jsonPath, value));
1608+
}
1609+
1610+
@Override
1611+
public RedisFuture<List<Long>> jsonStrappend(K key, JsonValue value) {
1612+
return dispatch(jsonCommandBuilder.jsonStrappend(key, JsonPath.ROOT_PATH, value));
1613+
}
1614+
1615+
@Override
1616+
public RedisFuture<List<Long>> jsonStrlen(K key, JsonPath jsonPath) {
1617+
return dispatch(jsonCommandBuilder.jsonStrlen(key, jsonPath));
1618+
}
1619+
1620+
@Override
1621+
public RedisFuture<List<Long>> jsonStrlen(K key) {
1622+
return dispatch(jsonCommandBuilder.jsonStrlen(key, JsonPath.ROOT_PATH));
1623+
}
1624+
1625+
@Override
1626+
public RedisFuture<List<Long>> jsonToggle(K key, JsonPath jsonPath) {
1627+
return dispatch(jsonCommandBuilder.jsonToggle(key, jsonPath));
1628+
}
1629+
1630+
@Override
1631+
public RedisFuture<List<JsonType>> jsonType(K key, JsonPath jsonPath) {
1632+
return dispatch(jsonCommandBuilder.jsonType(key, jsonPath));
1633+
}
1634+
1635+
@Override
1636+
public RedisFuture<List<JsonType>> jsonType(K key) {
1637+
return dispatch(jsonCommandBuilder.jsonType(key, JsonPath.ROOT_PATH));
1638+
}
1639+
14561640
@Override
14571641
public RedisFuture<List<K>> keys(K pattern) {
14581642
return dispatch(commandBuilder.keys(pattern));
@@ -3194,6 +3378,11 @@ public RedisFuture<List<Map<String, Object>>> clusterLinks() {
31943378
return dispatch(commandBuilder.clusterLinks());
31953379
}
31963380

3381+
@Override
3382+
public JsonParser getJsonParser() {
3383+
return this.parser;
3384+
}
3385+
31973386
private byte[] encodeFunction(String functionCode) {
31983387
LettuceAssert.notNull(functionCode, "Function code must not be null");
31993388
LettuceAssert.notEmpty(functionCode, "Function code script must not be empty");

0 commit comments

Comments
 (0)