mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 10:25:47 +02:00
feat(server): SCAN command add ATTR options (#4766)
This commit is contained in:
parent
3829ca87d7
commit
28fa1783db
4 changed files with 76 additions and 1 deletions
|
@ -308,6 +308,19 @@ OpResult<ScanOpts> ScanOpts::TryFrom(CmdArgList args) {
|
|||
if (!absl::SimpleAtoi(ArgS(args, i + 1), &scan_opts.bucket_id)) {
|
||||
return facade::OpStatus::INVALID_INT;
|
||||
}
|
||||
} else if (opt == "ATTR") {
|
||||
string_view mask = ArgS(args, i + 1);
|
||||
if (mask == "v") {
|
||||
scan_opts.mask = ScanOpts::Mask::Volatile;
|
||||
} else if (mask == "p") {
|
||||
scan_opts.mask = ScanOpts::Mask::Permanent;
|
||||
} else if (mask == "a") {
|
||||
scan_opts.mask = ScanOpts::Mask::Accessed;
|
||||
} else if (mask == "u") {
|
||||
scan_opts.mask = ScanOpts::Mask::Untouched;
|
||||
} else {
|
||||
return facade::OpStatus::SYNTAX_ERR;
|
||||
}
|
||||
} else {
|
||||
return facade::OpStatus::SYNTAX_ERR;
|
||||
}
|
||||
|
|
|
@ -312,6 +312,14 @@ struct ScanOpts {
|
|||
size_t limit = 10;
|
||||
std::optional<CompactObjType> type_filter;
|
||||
unsigned bucket_id = UINT_MAX;
|
||||
enum class Mask {
|
||||
Volatile, // volatile, keys that have ttl
|
||||
Permanent, // permanent, keys that do not have ttl
|
||||
Accessed, // accessed, the key has been accessed since the last load/flush event, or the last
|
||||
// time a flag was reset.
|
||||
Untouched, // untouched, the key has not been accessed/touched.
|
||||
};
|
||||
std::optional<Mask> mask;
|
||||
|
||||
bool Matches(std::string_view val_name) const;
|
||||
static OpResult<ScanOpts> TryFrom(CmdArgList args);
|
||||
|
|
|
@ -599,7 +599,17 @@ bool ScanCb(const OpArgs& op_args, PrimeIterator prime_it, const ScanOpts& opts,
|
|||
}
|
||||
|
||||
bool matches = !opts.type_filter || it->second.ObjType() == opts.type_filter;
|
||||
|
||||
if (opts.mask.has_value()) {
|
||||
if (opts.mask == ScanOpts::Mask::Volatile) {
|
||||
matches &= it->second.HasExpire();
|
||||
} else if (opts.mask == ScanOpts::Mask::Permanent) {
|
||||
matches &= !it->second.HasExpire();
|
||||
} else if (opts.mask == ScanOpts::Mask::Accessed) {
|
||||
matches &= it->first.WasTouched();
|
||||
} else if (opts.mask == ScanOpts::Mask::Untouched) {
|
||||
matches &= !it->first.WasTouched();
|
||||
}
|
||||
}
|
||||
if (!matches)
|
||||
return false;
|
||||
|
||||
|
@ -1747,6 +1757,7 @@ void GenericFamily::Echo(CmdArgList args, const CommandContext& cmd_cntx) {
|
|||
}
|
||||
|
||||
// SCAN cursor [MATCH <glob>] [TYPE <type>] [COUNT <count>] [BUCKET <bucket_id>]
|
||||
// [ATTR <mask>]
|
||||
void GenericFamily::Scan(CmdArgList args, const CommandContext& cmd_cntx) {
|
||||
string_view token = ArgS(args, 0);
|
||||
uint64_t cursor = 0;
|
||||
|
|
|
@ -412,6 +412,49 @@ TEST_F(GenericFamilyTest, Scan) {
|
|||
EXPECT_EQ(resp, "");
|
||||
}
|
||||
|
||||
TEST_F(GenericFamilyTest, ScanWithAttr) {
|
||||
Run({"flushdb"});
|
||||
|
||||
Run({"set", "hello", "world"});
|
||||
Run({"set", "foo", "bar"});
|
||||
|
||||
Run({"expire", "hello", "1000"});
|
||||
|
||||
auto resp = Run({"scan", "0", "attr", "v"});
|
||||
auto vec = StrArray(resp.GetVec()[1]);
|
||||
ASSERT_EQ(1, vec.size());
|
||||
EXPECT_EQ(vec[0], "hello");
|
||||
|
||||
resp = Run({"scan", "0", "attr", "p"});
|
||||
vec = StrArray(resp.GetVec()[1]);
|
||||
ASSERT_EQ(1, vec.size());
|
||||
EXPECT_EQ(vec[0], "foo");
|
||||
|
||||
// before run get "foo", scan with a attr should return "hello", because set "hello" expire before
|
||||
resp = Run({"scan", "0", "attr", "a"});
|
||||
vec = StrArray(resp.GetVec()[1]);
|
||||
ASSERT_EQ(1, vec.size());
|
||||
EXPECT_EQ(vec[0], "hello");
|
||||
|
||||
// before run get "foo", scan with a attr should return "foo"
|
||||
resp = Run({"scan", "0", "attr", "u"});
|
||||
vec = StrArray(resp.GetVec()[1]);
|
||||
ASSERT_EQ(1, vec.size());
|
||||
EXPECT_EQ(vec[0], "foo");
|
||||
|
||||
ASSERT_THAT(Run({"get", "foo"}), "bar");
|
||||
|
||||
// after run get "foo", scan with a attr should return "foo" and "hello"
|
||||
resp = Run({"scan", "0", "attr", "a"});
|
||||
vec = StrArray(resp.GetVec()[1]);
|
||||
ASSERT_EQ(2, vec.size());
|
||||
|
||||
// after run get "foo", scan with a attr should return empty set
|
||||
resp = Run({"scan", "0", "attr", "u"});
|
||||
vec = StrArray(resp.GetVec()[1]);
|
||||
ASSERT_EQ(0, vec.size());
|
||||
}
|
||||
|
||||
TEST_F(GenericFamilyTest, Sort) {
|
||||
// Test list sort with params
|
||||
Run({"del", "list-1"});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue