本文次要钻研一下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