Skip to content

Commit 1060e97

Browse files
committed
add string command decr, decrby, getrange, incrbyfloat, mset
1 parent 44cb670 commit 1060e97

File tree

12 files changed

+445
-7
lines changed

12 files changed

+445
-7
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package dev.keva.core.command.impl.string;
2+
3+
import dev.keva.core.command.annotation.CommandImpl;
4+
import dev.keva.core.command.annotation.Execute;
5+
import dev.keva.core.command.annotation.ParamLength;
6+
import dev.keva.core.exception.CommandException;
7+
import dev.keva.ioc.annotation.Autowired;
8+
import dev.keva.ioc.annotation.Component;
9+
import dev.keva.protocol.resp.reply.IntegerReply;
10+
import dev.keva.store.KevaDatabase;
11+
12+
import java.nio.charset.StandardCharsets;
13+
14+
import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;
15+
16+
17+
@Component
18+
@CommandImpl("decr")
19+
@ParamLength(type = EXACT, value = 1)
20+
public class Decr {
21+
private final KevaDatabase database;
22+
23+
@Autowired
24+
public Decr(KevaDatabase database) {
25+
this.database = database;
26+
}
27+
28+
@Execute
29+
public IntegerReply execute(byte[] key) {
30+
byte[] newVal;
31+
try {
32+
newVal = database.decrby(key, 1L);
33+
} catch (NumberFormatException ex) {
34+
throw new CommandException("value is not an integer or out of range");
35+
}
36+
return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8)));
37+
}
38+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package dev.keva.core.command.impl.string;
2+
3+
import dev.keva.core.command.annotation.CommandImpl;
4+
import dev.keva.core.command.annotation.Execute;
5+
import dev.keva.core.command.annotation.ParamLength;
6+
import dev.keva.core.exception.CommandException;
7+
import dev.keva.ioc.annotation.Autowired;
8+
import dev.keva.ioc.annotation.Component;
9+
import dev.keva.protocol.resp.reply.IntegerReply;
10+
import dev.keva.store.KevaDatabase;
11+
import lombok.var;
12+
13+
import java.nio.charset.StandardCharsets;
14+
15+
import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;
16+
17+
@Component
18+
@CommandImpl("decrby")
19+
@ParamLength(type = EXACT, value = 2)
20+
public class Decrby {
21+
private final KevaDatabase database;
22+
23+
@Autowired
24+
public Decrby(KevaDatabase database) {
25+
this.database = database;
26+
}
27+
28+
@Execute
29+
public IntegerReply execute(byte[] key, byte[] decrBy) {
30+
var amount = Long.parseLong(new String(decrBy, StandardCharsets.UTF_8));
31+
byte[] newVal;
32+
try {
33+
newVal = database.decrby(key, amount);
34+
} catch (NumberFormatException ex) {
35+
throw new CommandException("value is not an integer or out of range");
36+
}
37+
return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8)));
38+
}
39+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dev.keva.core.command.impl.string;
2+
3+
import dev.keva.core.command.annotation.CommandImpl;
4+
import dev.keva.core.command.annotation.Execute;
5+
import dev.keva.core.command.annotation.ParamLength;
6+
import dev.keva.core.command.impl.key.manager.ExpirationManager;
7+
import dev.keva.ioc.annotation.Autowired;
8+
import dev.keva.ioc.annotation.Component;
9+
import dev.keva.protocol.resp.reply.BulkReply;
10+
import dev.keva.store.KevaDatabase;
11+
12+
import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;
13+
14+
@Component
15+
@CommandImpl("getrange")
16+
@ParamLength(type = EXACT, value = 3)
17+
public class GetRange {
18+
private final KevaDatabase database;
19+
private final ExpirationManager expirationManager;
20+
21+
@Autowired
22+
public GetRange(KevaDatabase database, ExpirationManager expirationManager) {
23+
this.database = database;
24+
this.expirationManager = expirationManager;
25+
}
26+
27+
@Execute
28+
public BulkReply execute(byte[] key, byte[] start, byte[] end) {
29+
return new BulkReply(database.getrange(key, start, end));
30+
}
31+
}

core/src/main/java/dev/keva/core/command/impl/key/Incr.java renamed to core/src/main/java/dev/keva/core/command/impl/string/Incr.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.keva.core.command.impl.key;
1+
package dev.keva.core.command.impl.string;
22

33
import dev.keva.core.command.annotation.CommandImpl;
44
import dev.keva.core.command.annotation.Execute;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dev.keva.core.command.impl.string;
2+
3+
import dev.keva.core.command.annotation.CommandImpl;
4+
import dev.keva.core.command.annotation.Execute;
5+
import dev.keva.core.command.annotation.Mutate;
6+
import dev.keva.core.command.annotation.ParamLength;
7+
import dev.keva.core.exception.CommandException;
8+
import dev.keva.ioc.annotation.Autowired;
9+
import dev.keva.ioc.annotation.Component;
10+
import dev.keva.protocol.resp.reply.BulkReply;
11+
import dev.keva.protocol.resp.reply.IntegerReply;
12+
import dev.keva.store.KevaDatabase;
13+
import lombok.var;
14+
15+
import java.nio.charset.StandardCharsets;
16+
17+
import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;
18+
19+
@Component
20+
@CommandImpl("incrbyfloat")
21+
@ParamLength(type = EXACT, value = 2)
22+
@Mutate
23+
public class IncrByFloat {
24+
private final KevaDatabase database;
25+
26+
@Autowired
27+
public IncrByFloat(KevaDatabase database) {
28+
this.database = database;
29+
}
30+
31+
@Execute
32+
public BulkReply execute(byte[] key, byte[] incr) {
33+
byte[] newVal;
34+
try {
35+
double amount = Double.parseDouble(new String(incr, StandardCharsets.UTF_8));
36+
newVal = database.incrbyfloat(key, amount);
37+
} catch (NumberFormatException ex) {
38+
throw new CommandException("Value is not a valid float");
39+
}
40+
return new BulkReply(newVal);
41+
}
42+
}

core/src/main/java/dev/keva/core/command/impl/key/Incrby.java renamed to core/src/main/java/dev/keva/core/command/impl/string/Incrby.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.keva.core.command.impl.key;
1+
package dev.keva.core.command.impl.string;
22

33
import dev.keva.core.command.annotation.CommandImpl;
44
import dev.keva.core.command.annotation.Execute;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package dev.keva.core.command.impl.string;
2+
3+
import dev.keva.core.command.annotation.CommandImpl;
4+
import dev.keva.core.command.annotation.Execute;
5+
import dev.keva.core.command.annotation.ParamLength;
6+
import dev.keva.core.command.impl.key.manager.ExpirationManager;
7+
import dev.keva.core.exception.CommandException;
8+
import dev.keva.ioc.annotation.Autowired;
9+
import dev.keva.ioc.annotation.Component;
10+
import dev.keva.protocol.resp.reply.BulkReply;
11+
import dev.keva.protocol.resp.reply.MultiBulkReply;
12+
import dev.keva.protocol.resp.reply.StatusReply;
13+
import dev.keva.store.KevaDatabase;
14+
15+
import java.nio.charset.StandardCharsets;
16+
17+
import static dev.keva.core.command.annotation.ParamLength.Type.AT_LEAST;
18+
19+
@Component
20+
@CommandImpl("mset")
21+
@ParamLength(type = AT_LEAST, value = 2)
22+
public class MSet {
23+
private final KevaDatabase database;
24+
private final ExpirationManager expirationManager;
25+
26+
@Autowired
27+
public MSet(KevaDatabase database, ExpirationManager expirationManager) {
28+
this.database = database;
29+
this.expirationManager = expirationManager;
30+
}
31+
32+
@Execute
33+
public StatusReply execute(byte[]... keys) {
34+
if(keys.length % 2 != 0) {
35+
throw new CommandException("Wrong number of arguments for MSET");
36+
}
37+
database.mset(keys);
38+
return StatusReply.OK;
39+
}
40+
}

core/src/test/java/dev/keva/core/server/AbstractServerTest.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,38 @@ void dumpAndRestore() {
941941
}
942942
}
943943

944+
@Test
945+
void decr() {
946+
try {
947+
val set1 = jedis.set("mykey", "10");
948+
assertEquals(set1, "OK");
949+
val decr1 = jedis.decr("mykey");
950+
assertEquals(decr1, 9);
951+
val set2 = jedis.set("errKey", "foobar");
952+
assertThrows(JedisDataException.class, () -> jedis.decr("errKey"));
953+
954+
} catch (Exception e) {
955+
fail(e);
956+
}
957+
}
958+
959+
@Test
960+
void decrBy() {
961+
try {
962+
val set1 = jedis.set("mykey", "10");
963+
assertEquals(set1, "OK");
964+
val decrby1 = jedis.decrBy("mykey", 5);
965+
assertEquals(decrby1, 5);
966+
val decrby2 = jedis.decrBy("mykey", 10);
967+
assertEquals(decrby2, -5);
968+
val set2 = jedis.set("mykey2", "abc123");
969+
970+
assertThrows(JedisDataException.class, () -> jedis.decrBy("mykey2", 10));
971+
} catch (Exception e) {
972+
fail(e);
973+
}
974+
}
975+
944976
@Test
945977
void type() {
946978
try {
@@ -958,4 +990,60 @@ void type() {
958990
fail(e);
959991
}
960992
}
993+
994+
@Test
995+
void getRange() {
996+
try {
997+
val set1 = jedis.set("mykey", "This is a string");
998+
val getrange1 = jedis.getrange("mykey", 0, 3);
999+
assertEquals("This", getrange1);
1000+
val getrange2 = jedis.getrange("mykey", -3, -1);
1001+
assertEquals("ing", getrange2);
1002+
val getrange3 = jedis.getrange("mykey", 0, -1);
1003+
assertEquals("This is a string", getrange3);
1004+
val getrange4 = jedis.getrange("mykey", 10, 100);
1005+
assertEquals("string", getrange4);
1006+
val getrange5 = jedis.getrange("mykey", 10, 5);
1007+
assertEquals("", getrange5);
1008+
val getrange6 = jedis.getrange("mykey", -10, 10);
1009+
assertEquals("s a s", getrange6);
1010+
} catch (Exception e) {
1011+
fail(e);
1012+
}
1013+
}
1014+
1015+
@Test
1016+
void incrByFloat() {
1017+
try {
1018+
String set1 = jedis.set("mykey", "10.50");
1019+
assertEquals(set1, "OK");
1020+
Double incrbyfloat1 = jedis.incrByFloat("mykey", 0.1);
1021+
assertEquals(incrbyfloat1, 10.6);
1022+
Double incrbyfloat2 = jedis.incrByFloat("mykey", -5);
1023+
assertEquals(incrbyfloat2, 5.6);
1024+
String set2 = jedis.set("mykey", "5.0e3");
1025+
assertEquals(set2, "OK");
1026+
Double incrbyfloat3 = jedis.incrByFloat("mykey", 2.0e2);
1027+
assertEquals(incrbyfloat3, 5200);
1028+
String set3 = jedis.set("mykey3", "abc");
1029+
assertEquals(set3, "OK");
1030+
assertThrows(JedisDataException.class, () -> jedis.incrByFloat("mykey3", 123));
1031+
} catch (Exception e) {
1032+
fail(e);
1033+
}
1034+
}
1035+
1036+
@Test
1037+
void mset() {
1038+
try {
1039+
String mset1 = jedis.mset("key1", "Hello", "key2", "World");
1040+
assertEquals("OK", mset1);
1041+
String get1 = jedis.get("key1");
1042+
assertEquals("Hello", get1);
1043+
String get2 = jedis.get("key2");
1044+
assertEquals("World", get2);
1045+
} catch (Exception e) {
1046+
fail(e);
1047+
}
1048+
}
9611049
}

docs/src/guide/overview/commands.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ Implemented commands:
5151
- MGET
5252
- STRLEN
5353
- SETRANGE
54+
- DECR
55+
- DECRBY
56+
- GETRANGE
57+
- MSET
58+
- INCRBYFLOAT
5459

5560
</details>
5661

store/src/main/java/dev/keva/store/KevaDatabase.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,13 @@ public interface KevaDatabase {
7777
Double zincrby(byte[] key, Double score, BytesKey e, int flags);
7878

7979
Double zscore(byte[] key, byte[] member);
80+
81+
byte[] decrby(byte[] key, long amount);
82+
83+
byte[] getrange(byte[] key, byte[] start, byte[] end);
84+
85+
byte[] incrbyfloat(byte[] key, double amount);
86+
87+
void mset(byte[]... key);
88+
8089
}

0 commit comments

Comments
 (0)