前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊claudb的zset command

聊聊claudb的zset command

原创
作者头像
code4it
修改2020-08-31 10:01:02
1860
修改2020-08-31 10:01:02
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究一下zset command

SortedSetAddCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetAddCommand.java

代码语言:javascript
复制
@Command("zadd")
@ParamLength(3)
@ParamType(DataType.ZSET)
public class SortedSetAddCommand implements DBCommand {
​
  @Override
  public RedisToken execute(Database db, Request request) {
    try {
      DatabaseValue initial = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET);
      DatabaseValue result = db.merge(safeKey(request.getParam(0)), parseInput(request),
          (oldValue, newValue) -> {
            Set<Entry<Double, SafeString>> merge = new SortedSet();
            merge.addAll(oldValue.getSortedSet());
            merge.addAll(newValue.getSortedSet());
            return zset(merge);
          });
      return integer(changed(initial.getSortedSet(), result.getSortedSet()));
    } catch (NumberFormatException e) {
      return error("ERR value is not a valid float");
    }
  }
​
  private int changed(Set<Entry<Double, SafeString>> input, Set<Entry<Double, SafeString>> result) {
    return result.size() - input.size();
  }
​
  private DatabaseValue parseInput(Request request) {
    Set<Entry<Double, SafeString>> set = new SortedSet();
    SafeString score = null;
    for (SafeString string : request.getParams().stream().skip(1).collect(toList())) {
      if (score != null) {
        set.add(score(parseFloat(score.toString()), string));
        score =  null;
      } else {
        score = string;
      }
    }
    return zset(set);
  }
}
  • SortedSetAddCommand实现了DBCommand接口,其execute方法先获取initial,然后执行db.merge方法,它先添加oldValue.getSortedSet()、再添加newValue.getSortedSet()

SortedSetCardinalityCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetCardinalityCommand.java

代码语言:javascript
复制
@ReadOnly
@Command("zcard")
@ParamLength(1)
@ParamType(DataType.ZSET)
public class SortedSetCardinalityCommand implements DBCommand {
​
  @Override
  public RedisToken execute(Database db, Request request) {
    DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET);
    Set<Entry<Double, SafeString>> set = value.getSortedSet();
    return integer(set.size());
  }
}
  • SortedSetCardinalityCommand实现了DBCommand接口,其execute方法先通过db.getOrDefault获取value,在获取value.getSortedSet()

SortedSetRemoveCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetRemoveCommand.java

代码语言:javascript
复制
@Command("zrem")
@ParamLength(2)
@ParamType(DataType.ZSET)
public class SortedSetRemoveCommand implements DBCommand {
​
  @Override
  public RedisToken execute(Database db, Request request) {
    List<SafeString> items =  request.getParams().stream().skip(1).collect(toList());
    List<SafeString> removed = new LinkedList<>();
    db.merge(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET,
             (oldValue, newValue) -> {
               Set<Entry<Double, SafeString>> merge = new SortedSet();
               merge.addAll(oldValue.getSortedSet());
               for (SafeString item : items) {
                 if (merge.remove(score(0, item))) {
                   removed.add(item);
                 }
               }
               return zset(merge);
             });
​
    return integer(removed.size());
  }
}
  • SortedSetRemoveCommand实现了DBCommand接口,其execute方法先从request参数提取items,然后执行db.merge,该方法遍历items挨个执行merge.remove(score(0, item))

SortedSetRangeCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetRangeCommand.java

代码语言:javascript
复制
@ReadOnly
@Command("zrange")
@ParamLength(3)
@ParamType(DataType.ZSET)
public class SortedSetRangeCommand implements DBCommand {
​
  private static final String PARAM_WITHSCORES = "WITHSCORES";
​
  @Override
  public RedisToken execute(Database db, Request request) {
    try {
      DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET);
      NavigableSet<Entry<Double, SafeString>> set = value.getSortedSet();
​
      int from = Integer.parseInt(request.getParam(1).toString());
      if (from < 0) {
        from = set.size() + from;
      }
      int to = Integer.parseInt(request.getParam(2).toString());
      if (to < 0) {
        to = set.size() + to;
      }
​
      List<Object> result = emptyList();
      if (from <= to) {
        Option<SafeString> withScores = request.getOptionalParam(3);
        if (withScores.isPresent() && withScores.get().toString().equalsIgnoreCase(PARAM_WITHSCORES)) {
          result = set.stream().skip(from).limit((to - from) + 1l)
              .flatMap(entry -> Stream.of(entry.getValue(), entry.getKey())).collect(toList());
        } else {
          result = set.stream().skip(from).limit((to - from) + 1l)
              .map(Entry::getValue).collect(toList());
        }
      }
​
      return convert(result);
    } catch (NumberFormatException e) {
      return error("ERR value is not an integer or out of range");
    }
  }
}
  • SortedSetRangeCommand实现了DBCommand接口,其execute方法执行db.getOrDefault获取value,然后通过set.stream().skip(from).limit((to - from) + 1l)获取result

SortedSetRangeByScoreCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetRangeByScoreCommand.java

代码语言:javascript
复制
@ReadOnly
@Command("zrangebyscore")
@ParamLength(3)
@ParamType(DataType.ZSET)
public class SortedSetRangeByScoreCommand implements DBCommand {
​
  private static final String EXCLUSIVE = "(";
  private static final String MINUS_INFINITY = "-inf";
  private static final String INIFITY = "+inf";
  private static final String PARAM_WITHSCORES = "WITHSCORES";
  private static final String PARAM_LIMIT = "LIMIT";
​
  @Override
  public RedisToken execute(Database db, Request request) {
    try {
      DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET);
      NavigableSet<Entry<Double, SafeString>> set = value.getSortedSet();
​
      float from = parseRange(request.getParam(1).toString());
      float to = parseRange(request.getParam(2).toString());
​
      Options options = parseOptions(request);
​
      Set<Entry<Double, SafeString>> range = set.subSet(
          score(from, SafeString.EMPTY_STRING), inclusive(request.getParam(1)),
          score(to, SafeString.EMPTY_STRING), inclusive(request.getParam(2)));
​
      List<Object> result = emptyList();
      if (from <= to) {
        if (options.withScores) {
          result = range.stream().flatMap(
              entry -> Stream.of(entry.getValue(), entry.getKey())).collect(toList());
        } else {
          result = range.stream().map(Entry::getValue).collect(toList());
        }
​
        if (options.withLimit) {
          result = result.stream().skip(options.offset).limit(options.count).collect(toList());
        }
      }
​
      return convert(result);
    } catch (NumberFormatException e) {
      return error("ERR value is not an float or out of range");
    }
  }
​
  private Options parseOptions(Request request) {
    Options options = new Options();
    for (int i = 3; i < request.getLength(); i++) {
      String param = request.getParam(i).toString();
      if (param.equalsIgnoreCase(PARAM_LIMIT)) {
        options.withLimit = true;
        options.offset = parseInt(request.getParam(++i).toString());
        options.count = parseInt(request.getParam(++i).toString());
      } else if (param.equalsIgnoreCase(PARAM_WITHSCORES)) {
        options.withScores = true;
      }
    }
    return options;
  }
​
  private boolean inclusive(SafeString param) {
    return !param.toString().startsWith(EXCLUSIVE);
  }
​
  private float parseRange(String param) {
    switch (param) {
    case INIFITY:
      return Float.MAX_VALUE;
    case MINUS_INFINITY:
      return Float.MIN_VALUE;
    default:
      if (param.startsWith(EXCLUSIVE)) {
        return Float.parseFloat(param.substring(1));
      }
      return Float.parseFloat(param);
    }
  }
​
  private static class Options {
    private boolean withScores;
    private boolean withLimit;
    private int offset;
    private int count;
  }
}
  • SortedSetRangeByScoreCommand实现了DBCommand接口,其execute方法执行db.getOrDefault获取value,然后通过set.subSet(score(from, SafeString.EMPTY_STRING), inclusive(request.getParam(1)),score(to, SafeString.EMPTY_STRING), inclusive(request.getParam(2)))获取range,最后从range提取result

SortedSetReverseRangeCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetReverseRangeCommand.java

代码语言:javascript
复制
@ReadOnly
@Command("zrevrange")
@ParamLength(3)
@ParamType(DataType.ZSET)
public class SortedSetReverseRangeCommand implements DBCommand {
​
  private static final String PARAM_WITHSCORES = "WITHSCORES";
​
  @Override
  public RedisToken execute(Database db, Request request) {
    try {
      DatabaseValue value = db.getOrDefault(safeKey(request.getParam(0)), DatabaseValue.EMPTY_ZSET);
      NavigableSet<Entry<Double, SafeString>> set = value.getSortedSet();
​
      int from = Integer.parseInt(request.getParam(2).toString());
      if (from < 0) {
        from = set.size() + from;
      }
      int to = Integer.parseInt(request.getParam(1).toString());
      if (to < 0) {
        to = set.size() + to;
      }
​
      List<Object> result = emptyList();
      if (from <= to) {
        Option<SafeString> withScores = request.getOptionalParam(3);
        if (withScores.isPresent() && withScores.get().toString().equalsIgnoreCase(PARAM_WITHSCORES)) {
          result = set.stream().skip(from).limit((to - from) + 1l)
              .flatMap(item -> Stream.of(item.getValue(), item.getKey())).collect(toList());
        } else {
          result = set.stream().skip(from).limit((to - from) + 1l)
              .map(Entry::getValue).collect(toList());
        }
      }
      reverse(result);
​
      return convert(result);
    } catch (NumberFormatException e) {
      return error("ERR value is not an integer or out of range");
    }
  }
}
  • SortedSetReverseRangeCommand实现了DBCommand接口,其execute方法执行db.getOrDefault获取value,然后通过set.stream().skip(from).limit((to - from) + 1l)获取result

SortedSetIncrementByCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/zset/SortedSetIncrementByCommand.java

代码语言:javascript
复制
@Command("zincrby")
@ParamLength(3)
@ParamType(DataType.ZSET)
public class SortedSetIncrementByCommand implements DBCommand {
​
  @Override
  public RedisToken execute(Database db, Request request) {
    try {
      DatabaseKey zkey = safeKey(request.getParam(0));
      DatabaseValue value = db.getOrDefault(zkey, DatabaseValue.EMPTY_ZSET);
      NavigableSet<Entry<Double, SafeString>> set = value.getSortedSet();
​
      SafeString key = request.getParam(2);
      Double increment = Double.parseDouble(request.getParam(1).toString());
​
      Entry<Double, SafeString> newValue = merge(set, key, increment);
​
      SortedSet result = new SortedSet();
      result.addAll(set);
      result.remove(newValue);
      result.add(newValue);
      db.put(zkey, zset(result));
​
      return string(newValue.getKey().toString());
    } catch (NumberFormatException e) {
      return error("ERR value is not an integer or out of range");
    }
  }
​
  private Entry<Double, SafeString>
          merge(NavigableSet<Entry<Double, SafeString>> set, SafeString key, Double increment) {
    return score(findByKey(set, key).getKey() + increment, key);
  }
​
  private Entry<Double, SafeString> findByKey(NavigableSet<Entry<Double, SafeString>> set, SafeString key) {
    // TODO: O(n) search, to fix forget the NavigableSet and use directly the SortedSet to get by key
    return set.stream().filter(entry -> entry.getValue().equals(key)).findFirst().orElse(score(0, key));
  }
}
  • SortedSetIncrementByCommand实现了DBCommand接口,其execute方法执行db.getOrDefault获取value,然后通过merge获取newValue

小结

claudb sorted set相关的command有SortedSetAddCommand、SortedSetCardinalityCommand、SortedSetRemoveCommand、SortedSetRangeCommand、SortedSetRangeByScoreCommand、SortedSetReverseRangeCommand、SortedSetIncrementByCommand

doc

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SortedSetAddCommand
  • SortedSetCardinalityCommand
  • SortedSetRemoveCommand
  • SortedSetRangeCommand
  • SortedSetRangeByScoreCommand
  • SortedSetReverseRangeCommand
  • SortedSetIncrementByCommand
  • 小结
  • doc
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档