序
本文次要钻研一下claudb的string command
GetCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/GetCommand.java
@ReadOnly@Command("get")@ParamLength(1)@ParamType(DataType.STRING)public class GetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { return convert(db.get(safeKey(request.getParam(0)))); }}
- GetCommand实现了DBCommand接口,其execute办法执行db.get(safeKey(request.getParam(0)))
MultiGetCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/MultiGetCommand.java
@ReadOnly@Command("mget")@ParamLength(1)public class MultiGetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { ImmutableArray<DatabaseValue> result = request.getParams() .map(DatabaseKey::safeKey) .filter(key -> db.isType(key, DataType.STRING)) .map(db::get); return convert(result); }}
- MultiGetCommand实现了DBCommand接口,其execute办法遍历request.getParams()取出type为DataType.STRING,而后挨个执行db::get
SetCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/SetCommand.java
@Command("set")@ParamLength(2)public class SetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { return com.github.tonivade.purefun.type.Try.of(() -> parse(request)) .map(params -> onSuccess(db, request, params)) .recover(this::onFailure) .get(); } private RedisToken onSuccess(Database db, Request request, Parameters parameters) { DatabaseKey key = safeKey(request.getParam(0)); DatabaseValue value = parseValue(request, parameters); return value.equals(saveValue(db, parameters, key, value)) ? responseOk() : nullString(); } private DatabaseValue parseValue(Request request, Parameters parameters) { DatabaseValue value = string(request.getParam(1)); if (parameters.ttl != null) { value = value.expiredAt(Instant.now().plus(parameters.ttl)); } return value; } private RedisToken onFailure(Throwable e) { return Pattern1.<Throwable, RedisToken>build() .when(instanceOf(SyntaxException.class)) .returns(error("syntax error")) .when(instanceOf(NumberFormatException.class)) .returns(error("value is not an integer or out of range")) .otherwise() .returns(error("error: " + e.getMessage())) .apply(e); } private DatabaseValue saveValue(Database db, Parameters params, DatabaseKey key, DatabaseValue value) { DatabaseValue savedValue = null; if (params.ifExists) { savedValue = putValueIfExists(db, key, value); } else if (params.ifNotExists) { savedValue = putValueIfNotExists(db, key, value); } else { savedValue = putValue(db, key, value); } return savedValue; } private DatabaseValue putValue(Database db, DatabaseKey key, DatabaseValue value) { db.put(key, value); return value; } private DatabaseValue putValueIfExists(Database db, DatabaseKey key, DatabaseValue value) { DatabaseValue oldValue = db.get(key); if (oldValue != null) { return putValue(db, key, value); } return oldValue; } private DatabaseValue putValueIfNotExists(Database db, DatabaseKey key, DatabaseValue value) { return db.merge(key, value, (oldValue, newValue) -> oldValue); } private Parameters parse(Request request) { Parameters parameters = new Parameters(); if (request.getLength() > 2) { for (int i = 2; i < request.getLength(); i++) { SafeString option = request.getParam(i); if (match("EX", option)) { if (parameters.ttl != null) { throw new SyntaxException(); } parameters.ttl = parseTtl(request, ++i) .map(Duration::ofSeconds) .getOrElseThrow(SyntaxException::new); } else if (match("PX", option)) { if (parameters.ttl != null) { throw new SyntaxException(); } parameters.ttl = parseTtl(request, ++i) .map(Duration::ofMillis) .getOrElseThrow(SyntaxException::new); } else if (match("NX", option)) { if (parameters.ifExists) { throw new SyntaxException(); } parameters.ifNotExists = true; } else if (match("XX", option)) { if (parameters.ifNotExists) { throw new SyntaxException(); } parameters.ifExists = true; } else { throw new SyntaxException(); } } } return parameters; } private boolean match(String string, SafeString option) { return string.equalsIgnoreCase(option.toString()); } private Option<Integer> parseTtl(Request request, int i) { Option<SafeString> ttlOption = request.getOptionalParam(i); return ttlOption.map(SafeString::toString).map(Integer::parseInt); } private static class Parameters { private boolean ifExists; private boolean ifNotExists; private TemporalAmount ttl; } private static class SyntaxException extends RuntimeException { private static final long serialVersionUID = 6960370945568192189L; }}
- SetCommand实现了DBCommand接口,其execute办法先通过parse办法解析成parameters,这里反对EX、PX、NX、XX参数,若语法错误抛出SyntaxException,而后onFailure会针对不同异样进行解决;onSuccess办法则通过parseValue(request, parameters)结构DatabaseValue,而后执行saveValue办法,该办法会针对ifExists执行putValueIfExists,针对ifNotExists执行putValueIfNotExists,其余的执行putValue
MultiSetCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/MultiSetCommand.java
@Command("mset")@ParamLength(2)public class MultiSetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { SafeString key = null; for (SafeString value : request.getParams()) { if (key != null) { db.put(safeKey(key), string(value)); key = null; } else { key = value; } } return status("OK"); }}
- MultiSetCommand实现了DBCommand接口,其execute办法遍历request.getParams(),挨个执行db.put(safeKey(key), string(value))
GetSetCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/GetSetCommand.java
@Command("getset")@ParamLength(2)@ParamType(DataType.STRING)public class GetSetCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { return convert(db.put(safeKey(request.getParam(0)), string(request.getParam(1)))); }}
- GetSetCommand实现了DBCommand接口,其execute办法执行db.put(safeKey(request.getParam(0)), string(request.getParam(1))),同时将返回值转为RedisToken返回
IncrementCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/IncrementCommand.java
@Command("incr")@ParamLength(1)@ParamType(DataType.STRING)public class IncrementCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { DatabaseValue value = db.merge(safeKey(request.getParam(0)), string("1"), (oldValue, newValue) -> { int current = Integer.parseInt(oldValue.getString().toString()); return string(String.valueOf(current + 1)); }); return integer(Integer.parseInt(value.getString().toString())); } catch (NumberFormatException e) { return error("ERR value is not an integer or out of range"); } }}
- IncrementCommand实现了DBCommand接口,其execute办法执行db.merge,先通过oldValue获取current,再执行String.valueOf(current + 1)
IncrementByCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/IncrementByCommand.java
@Command("incrby")@ParamLength(2)@ParamType(DataType.STRING)public class IncrementByCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { DatabaseValue value = db.merge(safeKey(request.getParam(0)), string(request.getParam(1)), (oldValue, newValue) -> { int increment = Integer.parseInt(newValue.getString().toString()); int current = Integer.parseInt(oldValue.getString().toString()); return string(String.valueOf(current + increment)); }); return integer(Integer.parseInt(value.getString().toString())); } catch (NumberFormatException e) { return error("ERR value is not an integer or out of range"); } }}
- IncrementByCommand实现了DBCommand接口,其execute办法执行db.merge,先通过newValue获取increment,再oldValue获取current,再执行String.valueOf(current + increment)
DecrementCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/DecrementCommand.java
@Command("decr")@ParamLength(1)@ParamType(DataType.STRING)public class DecrementCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { DatabaseValue value = db.merge(safeKey(request.getParam(0)), string("-1"), (oldValue, newValue) -> { int current = Integer.parseInt(oldValue.getString().toString()); return string(String.valueOf(current - 1)); }); return integer(Integer.parseInt(value.getString().toString())); } catch (NumberFormatException e) { return error("ERR value is not an integer or out of range"); } }}
- DecrementCommand实现了DBCommand接口,其execute办法执行db.merge,先通过oldValue获取current,再执行String.valueOf(current - 1)
DecrementByCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/DecrementByCommand.java
@Command("decrby")@ParamLength(2)@ParamType(DataType.STRING)public class DecrementByCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { DatabaseValue value = db.merge(safeKey(request.getParam(0)), string("-" + request.getParam(1)), (oldValue, newValue) -> { int decrement = Integer.parseInt(newValue.getString().toString()); int current = Integer.parseInt(oldValue.getString().toString()); return string(String.valueOf(current + decrement)); }); return integer(Integer.parseInt(value.getString().toString())); } catch (NumberFormatException e) { return error("ERR value is not an integer or out of range"); } }}
- DecrementByCommand实现了DBCommand接口,其execute办法执行db.merge,先通过newValue获取decrement,再oldValue获取current,再执行String.valueOf(current + decrement),留神这里将request.getParam(1)转为正数了
StringLengthCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/StringLengthCommand.java
@ReadOnly@Command("strlen")@ParamLength(1)@ParamType(DataType.STRING)public class StringLengthCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_STRING); SafeString string = value.getString(); return integer(string.length()); }}
- StringLengthCommand实现了DBCommand接口,其execute办法先通过db.getOrDefault获取DatabaseValue,在获取string.length()
SetExpiredCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/SetExpiredCommand.java
@Command("setex")@ParamLength(3)public class SetExpiredCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { db.put(safeKey(request.getParam(0)), string(request.getParam(2)) .expiredAt(parseTtl(request.getParam(1)))); return responseOk(); } catch (NumberFormatException e) { return error("ERR value is not an integer or out of range"); } } private int parseTtl(SafeString safeString) { return Integer.parseInt(safeString.toString()); }}
- SetExpiredCommand实现了DBCommand接口,其execute办法执行db.put,这里对value执行了expiredAt(parseTtl(request.getParam(1)))
BitCountCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/bitset/BitCountCommand.java
@Command("bitcount")@ParamLength(1)@ParamType(DataType.STRING)public class BitCountCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), bitset()); BitSet bitSet = BitSet.valueOf(value.getString().getBuffer()); return integer(bitSet.cardinality()); }}
- BitCountCommand实现了DBCommand接口,其execute办法先通过db.getOrDefault获取DatabaseValue,而后通过BitSet.valueOf(value.getString().getBuffer())转为bitSet,最初返回bitSet.cardinality()
SetBitCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/bitset/SetBitCommand.java
@Command("setbit")@ParamLength(3)@ParamType(DataType.STRING)public class SetBitCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { int offset = Integer.parseInt(request.getParam(1).toString()); int bit = Integer.parseInt(request.getParam(2).toString()); Queue<Boolean> queue = new LinkedList<>(); db.merge(safeKey(request.getParam(0)), bitset(), (oldValue, newValue) -> { BitSet bitSet = BitSet.valueOf(oldValue.getString().getBuffer()); queue.add(bitSet.get(offset)); bitSet.set(offset, bit != 0); return oldValue; }); return integer(queue.poll()); } catch (NumberFormatException e) { return error("bit or offset is not an integer"); } }}
- SetBitCommand实现了DBCommand接口,其execute办法先解析参数获取offset、bit,之后执行db.merge,先将oldValue转为bitSet,再执行bitSet.set(offset, bit != 0)
GetBitCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/bitset/GetBitCommand.java
@Command("getbit")@ParamLength(2)@ParamType(DataType.STRING)public class GetBitCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { try { int offset = Integer.parseInt(request.getParam(1).toString()); DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), bitset()); BitSet bitSet = BitSet.valueOf(value.getString().getBuffer()); return integer(bitSet.get(offset)); } catch (NumberFormatException e) { return error("bit offset is not an integer"); } }}
- GetBitCommand实现了DBCommand接口,其execute办法先通过db.getOrDefault获取DatabaseValue,而后转为bitSet,最初返回bitSet.get(offset)
SetIfNotExistsCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/SetIfNotExistsCommand.java
@Command("setnx")@ParamLength(2)public class SetIfNotExistsCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { DatabaseKey key = safeKey(request.getParam(0)); DatabaseValue value = string(request.getParam(1)); return integer(putValueIfNotExists(db, key, value).equals(value)); } private DatabaseValue putValueIfNotExists(Database db, DatabaseKey key, DatabaseValue value) { return db.merge(key, value, (oldValue, newValue) -> oldValue); }}
- SetIfNotExistsCommand实现了DBCommand接口,其execute办法执行putValueIfNotExists,该办法的remappingFunction返回oldValue
MultiSetIfNotExistsCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/string/MultiSetIfNotExistsCommand.java
@Command("msetnx")@ParamLength(2)public class MultiSetIfNotExistsCommand implements DBCommand { @Override public RedisToken execute(Database db, Request request) { Set<Tuple2<SafeString, SafeString>> pairs = toPairs(request); if (noneExists(db, pairs)) { pairs.forEach(entry -> db.put(safeKey(entry.get1()), string(entry.get2()))); return integer(1); } return integer(0); } private boolean noneExists(Database db, Set<Tuple2<SafeString, SafeString>> pairs) { return pairs.stream() .map(Tuple2::get1) .map(DatabaseKey::safeKey) .noneMatch(db::containsKey); } private Set<Tuple2<SafeString, SafeString>> toPairs(Request request) { Set<Tuple2<SafeString, SafeString>> pairs = new HashSet<>(); SafeString key = null; for (SafeString value : request.getParams()) { if (key != null) { pairs.add(entry(key, value)); key = null; } else { key = value; } } return pairs; }}
- MultiSetIfNotExistsCommand实现了DBCommand接口,其execute办法在noneExists(db, pairs)为true时遍历pairs挨个执行db.put(safeKey(entry.get1()), string(entry.get2()))
小结
claudb string相干的command有GetCommand、MultiGetCommand、SetCommand、MultiSetCommand、GetSetCommand、IncrementCommand、IncrementByCommand、DecrementCommand、DecrementByCommand、StringLengthCommand、SetExpiredCommand、BitCountCommand、GetBitCommand、SetIfNotExistsCommand、MultiSetIfNotExistsCommand
doc
- command/string