feat(hash): support NOVALUES in HSCAN (#3109)
diff --git a/src/commands/cmd_hash.cc b/src/commands/cmd_hash.cc
index 13d13de..35e7c98 100644
--- a/src/commands/cmd_hash.cc
+++ b/src/commands/cmd_hash.cc
@@ -408,17 +408,23 @@
std::vector<std::string> values;
auto key_name = srv->GetKeyNameFromCursor(cursor_, CursorType::kTypeHash);
- auto s = hash_db.Scan(ctx, key_, key_name, limit_, prefix_, &fields, &values);
+ auto s = hash_db.Scan(ctx, key_, key_name, limit_, prefix_, &fields, no_values_ ? nullptr : &values);
if (!s.ok() && !s.IsNotFound()) {
return {Status::RedisExecErr, s.ToString()};
}
auto cursor = GetNextCursor(srv, fields, CursorType::kTypeHash);
std::vector<std::string> entries;
- entries.reserve(2 * fields.size());
+ if (no_values_) {
+ entries.reserve(fields.size());
+ } else {
+ entries.reserve(2 * fields.size());
+ }
for (size_t i = 0; i < fields.size(); i++) {
entries.emplace_back(redis::BulkString(fields[i]));
- entries.emplace_back(redis::BulkString(values[i]));
+ if (!no_values_) {
+ entries.emplace_back(redis::BulkString(values[i]));
+ }
}
*output = redis::Array({redis::BulkString(cursor), redis::Array(entries)});
return Status::OK();
diff --git a/src/commands/scan_base.h b/src/commands/scan_base.h
index 5a0d4ca..d80c1e4 100644
--- a/src/commands/scan_base.h
+++ b/src/commands/scan_base.h
@@ -64,6 +64,8 @@
} else {
return {Status::RedisExecErr, "Invalid type"};
}
+ } else if (parser.EatEqICase("novalues")) {
+ no_values_ = true;
} else {
return parser.InvalidSyntax();
}
@@ -102,6 +104,7 @@
std::string suffix_glob_ = "*";
int limit_ = 20;
RedisType type_ = kRedisNone;
+ bool no_values_ = false;
};
class CommandSubkeyScanBase : public CommandScanBase {
diff --git a/tests/gocase/unit/type/hash/hash_test.go b/tests/gocase/unit/type/hash/hash_test.go
index 8d15367..eb568b2 100644
--- a/tests/gocase/unit/type/hash/hash_test.go
+++ b/tests/gocase/unit/type/hash/hash_test.go
@@ -775,6 +775,23 @@
require.Equal(t, "b", rdb.HGet(ctx, "hash", strings.Repeat("k", 336)).Val())
})
+ t.Run("HSCAN without NOVALUES", func(t *testing.T) {
+ rdb.Del(ctx, "langhash")
+ rdb.HMSet(ctx, "langhash", []string{"lang1", "C++", "lang2", "JavaScript", "lang3", "Python", "lang4", "GoLanguage"})
+ res, _, _ := rdb.HScan(ctx, "langhash", 0, "lang1", 0).Result()
+ require.Equal(t, 2, len(res))
+ require.Equal(t, "lang1", res[0])
+ require.Equal(t, "C++", res[1])
+ })
+
+ t.Run("HSCAN with NOVALUES", func(t *testing.T) {
+ rdb.Del(ctx, "langhash")
+ rdb.HMSet(ctx, "langhash", []string{"lang1", "C++", "lang2", "JavaScript", "lang3", "Python", "lang4", "GoLanguage"})
+ res, _, _ := rdb.HScanNoValues(ctx, "langhash", 0, "lang1", 0).Result()
+ require.Equal(t, 1, len(res))
+ require.Equal(t, "lang1", res[0])
+ })
+
for _, size := range []int64{10, 512} {
t.Run(fmt.Sprintf("Hash fuzzing #1 - %d fields", size), func(t *testing.T) {
for times := 0; times < 10; times++ {