Skip to content
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

Add the support of the HRANDFIELD command #1565

Merged
merged 13 commits into from
Jul 16, 2023
49 changes: 48 additions & 1 deletion src/commands/cmd_hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,52 @@ class CommandHScan : public CommandSubkeyScanBase {
return Status::OK();
}
};
class CommandHRandField : public Commander {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
public:
Status Parse(const std::vector<std::string> &args) override {
if (args.size() >= 3) {
auto parse_result = ParseInt<int64_t>(args[2], 10);
if (!parse_result) {
return {Status::RedisParseErr, errValueNotInteger};
}
if (*parse_result >= 0) {
count_ = *parse_result;
} else {
count_ = -*parse_result;
uniq_ = false;
}
//如果有withvalue这个参数,就一定有前面的count
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
if (args.size() > 4 || (args.size() == 4 && strcasecmp(args[3].c_str(), "withvalues"))) {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
return {Status::RedisParseErr, errInvalidSyntax};
} else if (args.size() == 4) {
withvalues_ = true;
}
}
return Commander::Parse(args);
}
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
Status Execute(Server *svr, Connection *conn, std::string *output) override {
redis::Hash hash_db(svr->storage, conn->GetNamespace());
std::vector<FieldValue> field_values;
auto s = hash_db.RandField(args_[1], &field_values, count_, uniq_,
withvalues_ ? HashFetchType::kAll : HashFetchType::kOnlyKey);
if (!s.ok()) {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
return {Status::RedisExecErr, s.ToString()};
}
std::vector<std::string> values;
values.reserve(field_values.size());
for (const auto &p : field_values) {
values.emplace_back(p.field);
if (withvalues_) values.emplace_back(p.value);
}
*output = MultiBulkString(values, false);
return Status::OK();
}

private:
bool withvalues_ = false;
bool uniq_ = true;
uint64_t count_ = 1;
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
};

REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandHGet>("hget", 3, "read-only", 1, 1, 1),
MakeCmdAttr<CommandHIncrBy>("hincrby", 4, "write", 1, 1, 1),
Expand All @@ -392,6 +438,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandHGet>("hget", 3, "read-only", 1, 1, 1
MakeCmdAttr<CommandHVals>("hvals", 2, "read-only", 1, 1, 1),
MakeCmdAttr<CommandHGetAll>("hgetall", 2, "read-only", 1, 1, 1),
MakeCmdAttr<CommandHScan>("hscan", -3, "read-only", 1, 1, 1),
MakeCmdAttr<CommandHRangeByLex>("hrangebylex", -4, "read-only", 1, 1, 1), )
MakeCmdAttr<CommandHRangeByLex>("hrangebylex", -4, "read-only", 1, 1, 1),
MakeCmdAttr<CommandHRandField>("hrandfield", -2, "read-only", 1, 1, 1), )

} // namespace redis
41 changes: 41 additions & 0 deletions src/types/redis_hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -386,5 +386,46 @@ rocksdb::Status Hash::Scan(const Slice &user_key, const std::string &cursor, uin
std::vector<std::string> *values) {
return SubKeyScanner::Scan(kRedisHash, user_key, cursor, limit, field_prefix, fields, values);
}
rocksdb::Status Hash::RandField(const Slice &user_key, std::vector<FieldValue> *field_values, uint64_t count, bool uniq,
HashFetchType type) {
field_values->clear();
std::string ns_key;
AppendNamespacePrefix(user_key, &ns_key);
HashMetadata metadata(false);
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
rocksdb::Status s = GetMetadata(ns_key, &metadata);
if (!s.ok()) return s.IsNotFound() ? rocksdb::Status::OK() : s;
std::string prefix_key, next_version_prefix_key;
InternalKey(ns_key, "", metadata.version, storage_->IsSlotIdEncoded()).Encode(&prefix_key);
InternalKey(ns_key, "", metadata.version + 1, storage_->IsSlotIdEncoded()).Encode(&next_version_prefix_key);

git-hulk marked this conversation as resolved.
Show resolved Hide resolved
rocksdb::ReadOptions read_options;
LatestSnapShot ss(storage_);
read_options.snapshot = ss.GetSnapShot();
rocksdb::Slice upper_bound(next_version_prefix_key);
read_options.iterate_upper_bound = &upper_bound;
storage_->SetReadOptions(read_options);

// case1:count is negative, randomly select elements in the amount of count
if (!uniq) {
}
/*case2:The count was positive,The number of requested elements is greater than the number of
elements inside the hash: simply return the whole hash*/
else if (metadata.size <= count) {
auto iter = util::UniqueIterator(storage_, read_options);
for (iter->Seek(prefix_key); iter->Valid() && iter->key().starts_with(prefix_key); iter->Next()) {
if (type == HashFetchType::kOnlyKey) {
InternalKey ikey(iter->key(), storage_->IsSlotIdEncoded());
field_values->emplace_back(ikey.GetSubKey().ToString(), "");
} else {
InternalKey ikey(iter->key(), storage_->IsSlotIdEncoded());
field_values->emplace_back(ikey.GetSubKey().ToString(), iter->value().ToString());
}
}
}
/* case3:The number of elements inside the hash is not greater than the number of
elements inside the hash*/
else {
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
}
return rocksdb::Status::OK();
}
git-hulk marked this conversation as resolved.
Show resolved Hide resolved
} // namespace redis
2 changes: 2 additions & 0 deletions src/types/redis_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class Hash : public SubKeyScanner {
rocksdb::Status Scan(const Slice &user_key, const std::string &cursor, uint64_t limit,
const std::string &field_prefix, std::vector<std::string> *fields,
std::vector<std::string> *values = nullptr);
rocksdb::Status RandField(const Slice &user_key, std::vector<FieldValue> *field_values, uint64_t count, bool uniq,
HashFetchType type = HashFetchType::kOnlyKey);

private:
rocksdb::Status GetMetadata(const Slice &ns_key, HashMetadata *metadata);
Expand Down