Skip to content

Implement string command DECR, DECRBY, GETRANGE #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions core/src/main/java/dev/keva/core/command/impl/string/Decr.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
import dev.keva.core.command.annotation.ParamLength;
import dev.keva.core.exception.CommandException;
import dev.keva.ioc.annotation.Autowired;
import dev.keva.ioc.annotation.Component;
import dev.keva.protocol.resp.reply.IntegerReply;
import dev.keva.store.KevaDatabase;

import java.nio.charset.StandardCharsets;

import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;


@Component
@CommandImpl("decr")
@ParamLength(type = EXACT, value = 1)
public class Decr {
private final KevaDatabase database;

@Autowired
public Decr(KevaDatabase database) {
this.database = database;
}

@Execute
public IntegerReply execute(byte[] key) {
byte[] newVal;
try {
newVal = database.decrby(key, 1L);
} catch (NumberFormatException ex) {
throw new CommandException("value is not an integer or out of range");
}
return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8)));
}
}
39 changes: 39 additions & 0 deletions core/src/main/java/dev/keva/core/command/impl/string/Decrby.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
import dev.keva.core.command.annotation.ParamLength;
import dev.keva.core.exception.CommandException;
import dev.keva.ioc.annotation.Autowired;
import dev.keva.ioc.annotation.Component;
import dev.keva.protocol.resp.reply.IntegerReply;
import dev.keva.store.KevaDatabase;
import lombok.var;

import java.nio.charset.StandardCharsets;

import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;

@Component
@CommandImpl("decrby")
@ParamLength(type = EXACT, value = 2)
public class Decrby {
private final KevaDatabase database;

@Autowired
public Decrby(KevaDatabase database) {
this.database = database;
}

@Execute
public IntegerReply execute(byte[] key, byte[] decrBy) {
var amount = Long.parseLong(new String(decrBy, StandardCharsets.UTF_8));
byte[] newVal;
try {
newVal = database.decrby(key, amount);
} catch (NumberFormatException ex) {
throw new CommandException("value is not an integer or out of range");
}
return new IntegerReply(Long.parseLong(new String(newVal, StandardCharsets.UTF_8)));
}
}
31 changes: 31 additions & 0 deletions core/src/main/java/dev/keva/core/command/impl/string/GetRange.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
import dev.keva.core.command.annotation.ParamLength;
import dev.keva.core.command.impl.key.manager.ExpirationManager;
import dev.keva.ioc.annotation.Autowired;
import dev.keva.ioc.annotation.Component;
import dev.keva.protocol.resp.reply.BulkReply;
import dev.keva.store.KevaDatabase;

import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;

@Component
@CommandImpl("getrange")
@ParamLength(type = EXACT, value = 3)
public class GetRange {
private final KevaDatabase database;
private final ExpirationManager expirationManager;

@Autowired
public GetRange(KevaDatabase database, ExpirationManager expirationManager) {
this.database = database;
this.expirationManager = expirationManager;
}

@Execute
public BulkReply execute(byte[] key, byte[] start, byte[] end) {
return new BulkReply(database.getrange(key, start, end));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.keva.core.command.impl.key;
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
import dev.keva.core.command.annotation.Mutate;
import dev.keva.core.command.annotation.ParamLength;
import dev.keva.core.exception.CommandException;
import dev.keva.ioc.annotation.Autowired;
import dev.keva.ioc.annotation.Component;
import dev.keva.protocol.resp.reply.BulkReply;
import dev.keva.protocol.resp.reply.IntegerReply;
import dev.keva.store.KevaDatabase;
import lombok.var;

import java.nio.charset.StandardCharsets;

import static dev.keva.core.command.annotation.ParamLength.Type.EXACT;

@Component
@CommandImpl("incrbyfloat")
@ParamLength(type = EXACT, value = 2)
@Mutate
public class IncrByFloat {
private final KevaDatabase database;

@Autowired
public IncrByFloat(KevaDatabase database) {
this.database = database;
}

@Execute
public BulkReply execute(byte[] key, byte[] incr) {
byte[] newVal;
try {
double amount = Double.parseDouble(new String(incr, StandardCharsets.UTF_8));
newVal = database.incrbyfloat(key, amount);
} catch (NumberFormatException ex) {
throw new CommandException("Value is not a valid float");
}
return new BulkReply(newVal);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.keva.core.command.impl.key;
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
Expand Down
40 changes: 40 additions & 0 deletions core/src/main/java/dev/keva/core/command/impl/string/MSet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dev.keva.core.command.impl.string;

import dev.keva.core.command.annotation.CommandImpl;
import dev.keva.core.command.annotation.Execute;
import dev.keva.core.command.annotation.ParamLength;
import dev.keva.core.command.impl.key.manager.ExpirationManager;
import dev.keva.core.exception.CommandException;
import dev.keva.ioc.annotation.Autowired;
import dev.keva.ioc.annotation.Component;
import dev.keva.protocol.resp.reply.BulkReply;
import dev.keva.protocol.resp.reply.MultiBulkReply;
import dev.keva.protocol.resp.reply.StatusReply;
import dev.keva.store.KevaDatabase;

import java.nio.charset.StandardCharsets;

import static dev.keva.core.command.annotation.ParamLength.Type.AT_LEAST;

@Component
@CommandImpl("mset")
@ParamLength(type = AT_LEAST, value = 2)
public class MSet {
private final KevaDatabase database;
private final ExpirationManager expirationManager;

@Autowired
public MSet(KevaDatabase database, ExpirationManager expirationManager) {
this.database = database;
this.expirationManager = expirationManager;
}

@Execute
public StatusReply execute(byte[]... keys) {
if (keys.length % 2 != 0) {
throw new CommandException("Wrong number of arguments for MSET");
}
database.mset(keys);
return StatusReply.OK;
}
}
88 changes: 88 additions & 0 deletions core/src/test/java/dev/keva/core/server/AbstractServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,38 @@ void dumpAndRestore() {
}
}

@Test
void decr() {
try {
String set1 = jedis.set("mykey", "10");
assertEquals(set1, "OK");
Long decr1 = jedis.decr("mykey");
assertEquals(decr1, 9);
String set2 = jedis.set("errKey", "foobar");
assertThrows(JedisDataException.class, () -> jedis.decr("errKey"));

} catch (Exception e) {
fail(e);
}
}

@Test
void decrBy() {
try {
String set1 = jedis.set("mykey", "10");
assertEquals(set1, "OK");
Long decrby1 = jedis.decrBy("mykey", 5);
assertEquals(decrby1, 5);
Long decrby2 = jedis.decrBy("mykey", 10);
assertEquals(decrby2, -5);
String set2 = jedis.set("mykey2", "abc123");

assertThrows(JedisDataException.class, () -> jedis.decrBy("mykey2", 10));
} catch (Exception e) {
fail(e);
}
}

@Test
void type() {
try {
Expand All @@ -958,4 +990,60 @@ void type() {
fail(e);
}
}

@Test
void getRange() {
try {
String set1 = jedis.set("mykey", "This is a string");
String getrange1 = jedis.getrange("mykey", 0, 3);
assertEquals("This", getrange1);
String getrange2 = jedis.getrange("mykey", -3, -1);
assertEquals("ing", getrange2);
String getrange3 = jedis.getrange("mykey", 0, -1);
assertEquals("This is a string", getrange3);
String getrange4 = jedis.getrange("mykey", 10, 100);
assertEquals("string", getrange4);
String getrange5 = jedis.getrange("mykey", 10, 5);
assertEquals("", getrange5);
String getrange6 = jedis.getrange("mykey", -10, 10);
assertEquals("s a s", getrange6);
} catch (Exception e) {
fail(e);
}
}

@Test
void incrByFloat() {
try {
String set1 = jedis.set("mykey", "10.50");
assertEquals(set1, "OK");
Double incrbyfloat1 = jedis.incrByFloat("mykey", 0.1);
assertEquals(incrbyfloat1, 10.6);
Double incrbyfloat2 = jedis.incrByFloat("mykey", -5);
assertEquals(incrbyfloat2, 5.6);
String set2 = jedis.set("mykey", "5.0e3");
assertEquals(set2, "OK");
Double incrbyfloat3 = jedis.incrByFloat("mykey", 2.0e2);
assertEquals(incrbyfloat3, 5200);
String set3 = jedis.set("mykey3", "abc");
assertEquals(set3, "OK");
assertThrows(JedisDataException.class, () -> jedis.incrByFloat("mykey3", 123));
} catch (Exception e) {
fail(e);
}
}

@Test
void mset() {
try {
String mset1 = jedis.mset("key1", "Hello", "key2", "World");
assertEquals("OK", mset1);
String get1 = jedis.get("key1");
assertEquals("Hello", get1);
String get2 = jedis.get("key2");
assertEquals("World", get2);
} catch (Exception e) {
fail(e);
}
}
}
5 changes: 5 additions & 0 deletions docs/src/guide/overview/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Implemented commands:
- MGET
- STRLEN
- SETRANGE
- DECR
- DECRBY
- GETRANGE
- MSET
- INCRBYFLOAT

</details>

Expand Down
9 changes: 9 additions & 0 deletions store/src/main/java/dev/keva/store/KevaDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,13 @@ public interface KevaDatabase {
Double zincrby(byte[] key, Double score, BytesKey e, int flags);

Double zscore(byte[] key, byte[] member);

byte[] decrby(byte[] key, long amount);

byte[] getrange(byte[] key, byte[] start, byte[] end);

byte[] incrbyfloat(byte[] key, double amount);

void mset(byte[]... key);

}
Loading