From e002a44a0928ae600f6b19d1a8f76e14da6a1eed Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Mon, 4 Mar 2024 18:28:18 +0800 Subject: [PATCH 1/9] feat: zset cmd zremrangebyrank --- src/base_cmd.h | 3 +++ src/cmd_table_manager.cc | 4 ++++ src/cmd_zset.cc | 48 ++++++++++++++++++++++++++++++++++++++++ src/cmd_zset.h | 24 ++++++++++++++++++++ tests/zset_test.go | 20 +++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 src/cmd_zset.cc create mode 100644 src/cmd_zset.h diff --git a/src/base_cmd.h b/src/base_cmd.h index 0bbbbb193..7e3cb055e 100644 --- a/src/base_cmd.h +++ b/src/base_cmd.h @@ -102,6 +102,9 @@ const std::string kCmdNameLTrim = "ltrim"; const std::string kCmdNameLSet = "lset"; const std::string kCmdNameLInsert = "linsert"; +//zset cmd +const std::string kCmdNameZRemRangeByRank = "zremrangebyrank"; + enum CmdFlags { kCmdFlagsWrite = (1 << 0), // May modify the dataset kCmdFlagsReadonly = (1 << 1), // Doesn't modify the dataset diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index 720641e1b..6af770d9b 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -14,6 +14,7 @@ #include "cmd_list.h" #include "cmd_set.h" #include "cmd_table_manager.h" +#include "cmd_zset.h" namespace pikiwidb { @@ -107,6 +108,9 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(LTrim, 4); ADD_COMMAND(LSet, 4); ADD_COMMAND(LInsert, 5); + + //zset + ADD_COMMAND(ZRemRangeByRank,4); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc new file mode 100644 index 000000000..f5a506339 --- /dev/null +++ b/src/cmd_zset.cc @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2023-present, Qihoo, Inc. All rights reserved. +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ + +#include "cmd_zset.h" + +#include + +#include "pstd/pstd_string.h" +#include "store.h" + +namespace pikiwidb { + +ZRemRangeByRankCmd::ZRemRangeByRankCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategoryString) {} + +bool ZRemRangeByRankCmd::DoInitial(pikiwidb::PClient* client) { + int64_t start = 0; + int64_t end = 0; + + if (pstd::String2int(client->argv_[2].data(), client->argv_[2].size(), &start) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return false; + } + if (pstd::String2int(client->argv_[3].data(), client->argv_[3].size(), &end) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return false; + } + + client->SetKey(client->argv_[1]); + return true; +} + +void ZRemRangeByRankCmd::DoCmd(pikiwidb::PClient* client) { + int32_t ret = 0; + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB())->ZRemrangebyrank(client->Key(), client->argv_[2], client->argv_[3], &ret); + if (s.ok() || s.IsNotFound()) { + client->AppendInteger(ret); + } else { + client->SetRes(CmdRes::kErrOther, s.ToString()); + } +} + +} \ No newline at end of file diff --git a/src/cmd_zset.h b/src/cmd_zset.h new file mode 100644 index 000000000..eb3009020 --- /dev/null +++ b/src/cmd_zset.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2023-present, Qihoo, Inc. All rights reserved. +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ + +#pragma once +#include "base_cmd.h" + +namespace pikiwidb { + +class ZRemRangeByRankCmd : public BaseCmd { + public: + ZRemRangeByRankCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +} // namespace pikiwidb \ No newline at end of file diff --git a/tests/zset_test.go b/tests/zset_test.go index 23faf0190..676f83535 100644 --- a/tests/zset_test.go +++ b/tests/zset_test.go @@ -70,4 +70,24 @@ var _ = Describe("Zset", Ordered, func() { log.Println("Cmd ZADD Begin") Expect(client.ZAdd(ctx, "myset", redis.Z{Score: 1, Member: "one"}).Val()).NotTo(Equal("FooBar")) }) + + It("should ZRemRangeByRank", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + zRemRangeByRank := client.ZRemRangeByRank(ctx, "zset", 0, 1) + Expect(zRemRangeByRank.Err()).NotTo(HaveOccurred()) + Expect(zRemRangeByRank.Val()).To(Equal(int64(2))) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 3, + Member: "three", + }})) + }) }) From 024e1888320e34cec9c44e1ed2a04c544e7dd232 Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Mon, 4 Mar 2024 20:44:03 +0800 Subject: [PATCH 2/9] format --- src/base_cmd.h | 2 +- src/cmd_table_manager.cc | 4 ++-- src/cmd_zset.cc | 15 ++++++++------- src/cmd_zset.h | 12 ++++++------ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/base_cmd.h b/src/base_cmd.h index 7e3cb055e..959e0676c 100644 --- a/src/base_cmd.h +++ b/src/base_cmd.h @@ -102,7 +102,7 @@ const std::string kCmdNameLTrim = "ltrim"; const std::string kCmdNameLSet = "lset"; const std::string kCmdNameLInsert = "linsert"; -//zset cmd +// zset cmd const std::string kCmdNameZRemRangeByRank = "zremrangebyrank"; enum CmdFlags { diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index 6af770d9b..19074d685 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -109,8 +109,8 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(LSet, 4); ADD_COMMAND(LInsert, 5); - //zset - ADD_COMMAND(ZRemRangeByRank,4); + // zset + ADD_COMMAND(ZRemRangeByRank, 4); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index f5a506339..00909a43c 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -1,9 +1,9 @@ /* -* Copyright (c) 2023-present, Qihoo, Inc. All rights reserved. -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -*/ + * Copyright (c) 2023-present, Qihoo, Inc. All rights reserved. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #include "cmd_zset.h" @@ -37,7 +37,8 @@ bool ZRemRangeByRankCmd::DoInitial(pikiwidb::PClient* client) { void ZRemRangeByRankCmd::DoCmd(pikiwidb::PClient* client) { int32_t ret = 0; storage::Status s; - s = PSTORE.GetBackend(client->GetCurrentDB())->ZRemrangebyrank(client->Key(), client->argv_[2], client->argv_[3], &ret); + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRemrangebyrank(client->Key(), client->argv_[2], client->argv_[3], &ret); if (s.ok() || s.IsNotFound()) { client->AppendInteger(ret); } else { @@ -45,4 +46,4 @@ void ZRemRangeByRankCmd::DoCmd(pikiwidb::PClient* client) { } } -} \ No newline at end of file +} // namespace pikiwidb \ No newline at end of file diff --git a/src/cmd_zset.h b/src/cmd_zset.h index eb3009020..baa70e72d 100644 --- a/src/cmd_zset.h +++ b/src/cmd_zset.h @@ -1,9 +1,9 @@ /* -* Copyright (c) 2023-present, Qihoo, Inc. All rights reserved. -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -*/ + * Copyright (c) 2023-present, Qihoo, Inc. All rights reserved. + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #pragma once #include "base_cmd.h" @@ -21,4 +21,4 @@ class ZRemRangeByRankCmd : public BaseCmd { void DoCmd(PClient *client) override; }; -} // namespace pikiwidb \ No newline at end of file +} // namespace pikiwidb \ No newline at end of file From 895d8cc2e5111786c68330dc6dc5203afb773431 Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Mon, 4 Mar 2024 21:01:16 +0800 Subject: [PATCH 3/9] fix --- src/cmd_zset.cc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index 00909a43c..a87416d79 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -18,27 +18,27 @@ ZRemRangeByRankCmd::ZRemRangeByRankCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategoryString) {} bool ZRemRangeByRankCmd::DoInitial(pikiwidb::PClient* client) { - int64_t start = 0; - int64_t end = 0; - - if (pstd::String2int(client->argv_[2].data(), client->argv_[2].size(), &start) == 0) { - client->SetRes(CmdRes::kInvalidInt); - return false; - } - if (pstd::String2int(client->argv_[3].data(), client->argv_[3].size(), &end) == 0) { - client->SetRes(CmdRes::kInvalidInt); - return false; - } - client->SetKey(client->argv_[1]); return true; } void ZRemRangeByRankCmd::DoCmd(pikiwidb::PClient* client) { int32_t ret = 0; + + int32_t start = 0; + int32_t end = 0; + + if (pstd::String2int(client->argv_[2], &start) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + if (pstd::String2int(client->argv_[3], &end) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + storage::Status s; - s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRemrangebyrank(client->Key(), client->argv_[2], client->argv_[3], &ret); + s = PSTORE.GetBackend(client->GetCurrentDB())->ZRemrangebyrank(client->Key(), start, end, &ret); if (s.ok() || s.IsNotFound()) { client->AppendInteger(ret); } else { From 17af279c75299cd6c10df76c99af0e927af5c35e Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Thu, 7 Mar 2024 10:48:27 +0800 Subject: [PATCH 4/9] fix --- tests/zset_test.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/zset_test.go b/tests/zset_test.go index f13563fc2..7384d7687 100644 --- a/tests/zset_test.go +++ b/tests/zset_test.go @@ -71,26 +71,6 @@ var _ = Describe("Zset", Ordered, func() { Expect(client.ZAdd(ctx, "myset", redis.Z{Score: 1, Member: "one"}).Val()).NotTo(Equal("FooBar")) }) - It("should ZRemRangeByRank", func() { - err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() - Expect(err).NotTo(HaveOccurred()) - err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() - Expect(err).NotTo(HaveOccurred()) - - zRemRangeByRank := client.ZRemRangeByRank(ctx, "zset", 0, 1) - Expect(zRemRangeByRank.Err()).NotTo(HaveOccurred()) - Expect(zRemRangeByRank.Val()).To(Equal(int64(2))) - - vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() - Expect(err).NotTo(HaveOccurred()) - Expect(vals).To(Equal([]redis.Z{{ - Score: 3, - Member: "three", - }})) - }) - It("should ZAdd", func() { Expect(client.Del(ctx, "zset").Err()).NotTo(HaveOccurred()) added, err := client.ZAdd(ctx, "zset", redis.Z{ @@ -194,4 +174,24 @@ var _ = Describe("Zset", Ordered, func() { Expect(zRevRange.Err()).NotTo(HaveOccurred()) Expect(zRevRange.Val()).To(Equal([]string{"two", "one"})) }) + + It("should ZRemRangeByRank", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + zRemRangeByRank := client.ZRemRangeByRank(ctx, "zset", 0, 1) + Expect(zRemRangeByRank.Err()).NotTo(HaveOccurred()) + Expect(zRemRangeByRank.Val()).To(Equal(int64(2))) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 3, + Member: "three", + }})) + }) }) From 260e86ae6d7369fa81ee167e9ef02fbb495378ce Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Sun, 10 Mar 2024 16:37:08 +0800 Subject: [PATCH 5/9] feat: add zrange, zscore not ready for review yet --- src/base_cmd.h | 2 + src/cmd_table_manager.cc | 2 + src/cmd_zset.cc | 140 +++++++++++++++++++++++++- src/cmd_zset.h | 23 +++++ src/storage/include/storage/storage.h | 3 + src/storage/src/redis.h | 2 + src/storage/src/redis_zsets.cc | 58 +++++++++++ src/storage/src/storage.cc | 7 ++ tests/zset_test.go | 57 +++++++++++ 9 files changed, 291 insertions(+), 3 deletions(-) diff --git a/src/base_cmd.h b/src/base_cmd.h index dc0e205af..c5a14fb4f 100644 --- a/src/base_cmd.h +++ b/src/base_cmd.h @@ -111,6 +111,8 @@ const std::string kCmdNameZRevrange = "zrevrange"; const std::string kCmdNameZRangebyscore = "zrangebyscore"; const std::string kCmdNameZRemRangeByRank = "zremrangebyrank"; const std::string kCmdNameZCard = "zcard"; +const std::string kCmdNameZScore = "zscore"; +const std::string kCmdNameZRange = "zrange"; enum CmdFlags { kCmdFlagsWrite = (1 << 0), // May modify the dataset diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index 1a82ec110..be97a7415 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -118,6 +118,8 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(ZRangebyscore, -4); ADD_COMMAND(ZRemRangeByRank, 4); ADD_COMMAND(ZCard, 2); + ADD_COMMAND(ZScore,3); + ADD_COMMAND(ZRange,-4); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index 8dcfe02c2..a2114f2bc 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -238,14 +238,13 @@ void ZCardCmd::DoCmd(PClient* client) { ZRemRangeByRankCmd::ZRemRangeByRankCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategoryString) {} -bool ZRemRangeByRankCmd::DoInitial(pikiwidb::PClient* client) { +bool ZRemRangeByRankCmd::DoInitial(PClient* client) { client->SetKey(client->argv_[1]); return true; } -void ZRemRangeByRankCmd::DoCmd(pikiwidb::PClient* client) { +void ZRemRangeByRankCmd::DoCmd(PClient* client) { int32_t ret = 0; - int32_t start = 0; int32_t end = 0; @@ -267,4 +266,139 @@ void ZRemRangeByRankCmd::DoCmd(pikiwidb::PClient* client) { } } +ZRangeCmd::ZRangeCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsReadonly, kAclCategoryRead | kAclCategorySortedSet) {} + +bool ZRangeCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRangeCmd::DoCmd(PClient* client) { + double start = 0; + double stop = 0; + int64_t count = -1; + int64_t offset = 0; + bool with_scores = false; + bool by_score = false; + bool by_lex = false; + bool left_close = false; + bool right_close = false; + bool is_rev = false; + int32_t ret = DoScoreStrRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &start, &stop); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "start or stop is not a float"); + return; + } + size_t argc = client->argv_.size(); + if (argc >= 5) { + size_t index = 4; + while (index < argc) { + if (strcasecmp(client->argv_[index].data(), "byscore") == 0) { + by_score = true; + } else if (strcasecmp(client->argv_[index].data(), "bylex") == 0) { + by_lex = true; + } else if (strcasecmp(client->argv_[index].data(), "rev") == 0) { + is_rev = true; + } else if (strcasecmp(client->argv_[index].data(), "withscores") == 0) { + with_scores = true; + } else if (strcasecmp(client->argv_[index].data(), "limit") == 0) { + if (index + 3 > argc) { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + index++; + if (pstd::String2int(client->argv_[index].data(), client->argv_[index].size(), &offset) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + index++; + if (pstd::String2int(client->argv_[index].data(), client->argv_[index].size(), &count) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + } else { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + index++; + } + } + if (by_score && by_lex) { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + + std::vector score_members; + std::vector members; + storage::Status s; + if (!is_rev) { + if (by_score) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRangebyscore(client->Key(), start, stop, left_close, right_close, count, offset, &score_members); + } else if (by_lex) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, &members); + } else { + s = PSTORE.GetBackend(client->GetCurrentDB())->ZRange(client->Key(), start, stop, &score_members); + } + } else { + if (by_score) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRevrangebyscore(client->Key(), start, stop, left_close, right_close, count, offset, &score_members); + } else if (by_lex) { + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRevrangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, count, offset, &members); + } else { + s = PSTORE.GetBackend(client->GetCurrentDB())->ZRevrange(client->Key(), start, stop, &score_members); + } + } + if (!s.ok() && !s.IsNotFound()) { + client->SetRes(CmdRes::kErrOther, s.ToString()); + return; + } + //TODO(taota csx) bylex cmd's return. + FitLimit(count, offset, static_cast(score_members.size())); + size_t m_start = offset; + size_t m_end = offset + count; + if (with_scores) { + char buf[32]; + int64_t len = 0; + client->AppendArrayLen(count * 2); + for (; m_start < m_end; m_start++) { + client->AppendStringLenUint64(score_members[m_start].member.size()); + client->AppendContent(score_members[m_start].member); + len = pstd::D2string(buf, sizeof(buf), score_members[m_start].score); + client->AppendStringLen(len); + client->AppendContent(buf); + } + } else { + client->AppendArrayLen(count); + for (; m_start < m_end; m_start++) { + client->AppendStringLenUint64(score_members[m_start].member.size()); + client->AppendContent(score_members[m_start].member); + } + } +} + +ZScoreCmd::ZScoreCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsReadonly, kAclCategoryRead | kAclCategoryString) {} + +bool ZScoreCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZScoreCmd::DoCmd(PClient* client) { + double score = 0; + + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB())->ZScore(client->Key(), client->argv_[2], &score); + if (s.ok() || s.IsNotFound()) { + client->AppendString(std::to_string(score)); + } else { + client->SetRes(CmdRes::kErrOther, s.ToString()); + } +} + } // namespace pikiwidb \ No newline at end of file diff --git a/src/cmd_zset.h b/src/cmd_zset.h index 45fb0504d..6b6edcb4f 100644 --- a/src/cmd_zset.h +++ b/src/cmd_zset.h @@ -55,6 +55,7 @@ class ZRemRangeByRankCmd : public BaseCmd { private: void DoCmd(PClient *client) override; }; + class ZCardCmd : public BaseCmd { public: ZCardCmd(const std::string &name, int16_t arity); @@ -66,4 +67,26 @@ class ZCardCmd : public BaseCmd { void DoCmd(PClient *client) override; }; +class ZRangeCmd : public BaseCmd { + public: + ZRangeCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZScoreCmd : public BaseCmd { + public: + ZScoreCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + } // namespace pikiwidb \ No newline at end of file diff --git a/src/storage/include/storage/storage.h b/src/storage/include/storage/storage.h index beb1a4fb3..180ffdda3 100644 --- a/src/storage/include/storage/storage.h +++ b/src/storage/include/storage/storage.h @@ -837,6 +837,9 @@ class Storage { Status ZRevrangebyscore(const Slice& key, double min, double max, bool left_close, bool right_close, int64_t count, int64_t offset, std::vector* score_members); + Status ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, + int64_t count, int64_t offset, std::vector* members); + // Returns the rank of member in the sorted set stored at key, with the scores // ordered from high to low. The rank (or index) is 0-based, which means that // the member with the highest score has rank 0. diff --git a/src/storage/src/redis.h b/src/storage/src/redis.h index e5439042a..8dad79684 100644 --- a/src/storage/src/redis.h +++ b/src/storage/src/redis.h @@ -270,6 +270,8 @@ class Redis { Status ZRevrange(const Slice& key, int32_t start, int32_t stop, std::vector* score_members); Status ZRevrangebyscore(const Slice& key, double min, double max, bool left_close, bool right_close, int64_t count, int64_t offset, std::vector* score_members); + Status ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, + int64_t count, int64_t offset, std::vector* members); Status ZRevrank(const Slice& key, const Slice& member, int32_t* rank); Status ZScore(const Slice& key, const Slice& member, double* score); Status ZGetAll(const Slice& key, double weight, std::map* value_to_dest); diff --git a/src/storage/src/redis_zsets.cc b/src/storage/src/redis_zsets.cc index ea4864ae1..54f1ac119 100644 --- a/src/storage/src/redis_zsets.cc +++ b/src/storage/src/redis_zsets.cc @@ -971,6 +971,64 @@ Status Redis::ZRevrangebyscore(const Slice& key, double min, double max, bool le return s; } +Status Redis::ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, + int64_t count, int64_t offset, std::vector* members) { + members->clear(); + rocksdb::ReadOptions read_options; + const rocksdb::Snapshot* snapshot = nullptr; + + std::string meta_value; + ScopeSnapshot ss(db_, &snapshot); + read_options.snapshot = snapshot; + + bool left_no_limit = min.compare("-") == 0; + bool right_not_limit = max.compare("+") == 0; + + BaseMetaKey base_meta_key(key); + Status s = db_->Get(read_options, handles_[kZsetsMetaCF], base_meta_key.Encode(), &meta_value); + if (s.ok()) { + ParsedZSetsMetaValue parsed_zsets_meta_value(&meta_value); + if (parsed_zsets_meta_value.IsStale() || parsed_zsets_meta_value.Count() == 0) { + return Status::NotFound(); + } else { + uint64_t version = parsed_zsets_meta_value.Version(); + int32_t left = parsed_zsets_meta_value.Count(); + int64_t skipped = 0; + ZSetsMemberKey zsets_member_key(key, version, Slice()); + KeyStatisticsDurationGuard guard(this, DataType::kZSets, key.ToString()); + rocksdb::Iterator* iter = db_->NewIterator(read_options, handles_[kZsetsDataCF]); + for (iter->SeekForPrev(zsets_member_key.Encode()); iter->Valid() && left > 0; iter->Prev(), --left) { + bool left_pass = false; + bool right_pass = false; + ParsedZSetsMemberKey parsed_zsets_member_key(iter->key()); + Slice member = parsed_zsets_member_key.member(); + if (left_no_limit || (left_close && min.compare(member) >= 0) || (!left_close && min.compare(member) > 0)) { + left_pass = true; + } + if (right_not_limit || (right_close && max.compare(member) <= 0) || (!right_close && max.compare(member) < 0)) { + right_pass = true; + } + if (left_pass && right_pass) { + // skip offset + if (skipped < offset) { + ++skipped; + continue; + } + members->push_back(member.ToString()); + if (count > 0 && members->size() == static_cast(count)) { + break; + } + } + if (!right_pass) { + break; + } + } + delete iter; + } + } + return s; +} + Status Redis::ZRevrank(const Slice& key, const Slice& member, int32_t* rank) { *rank = -1; rocksdb::ReadOptions read_options; diff --git a/src/storage/src/storage.cc b/src/storage/src/storage.cc index bf4d54ca6..133b9803a 100644 --- a/src/storage/src/storage.cc +++ b/src/storage/src/storage.cc @@ -1007,6 +1007,13 @@ Status Storage::ZRangebylex(const Slice& key, const Slice& min, const Slice& max return inst->ZRangebylex(key, min, max, left_close, right_close, members); } +Status Storage::ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, + int64_t count, int64_t offset, std::vector* members){ + members->clear(); + auto& inst = GetDBInstance(key); + return inst->ZRevrangebylex(key, min, max, left_close, right_close, count, offset, members); +} + Status Storage::ZLexcount(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, int32_t* ret) { auto& inst = GetDBInstance(key); diff --git a/tests/zset_test.go b/tests/zset_test.go index 6e39f2efa..5c6b52d26 100644 --- a/tests/zset_test.go +++ b/tests/zset_test.go @@ -211,4 +211,61 @@ var _ = Describe("Zset", Ordered, func() { Expect(err).NotTo(HaveOccurred()) Expect(card).To(Equal(int64(2))) }) + + It("should ZRange", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + zRange := client.ZRange(ctx, "zset", 0, -1) + Expect(zRange.Err()).NotTo(HaveOccurred()) + Expect(zRange.Val()).To(Equal([]string{"one", "two", "three"})) + + zRange = client.ZRange(ctx, "zset", 2, 3) + Expect(zRange.Err()).NotTo(HaveOccurred()) + Expect(zRange.Val()).To(Equal([]string{"three"})) + + zRange = client.ZRange(ctx, "zset", -2, -1) + Expect(zRange.Err()).NotTo(HaveOccurred()) + Expect(zRange.Val()).To(Equal([]string{"two", "three"})) + }) + + It("should ZRangeWithScores", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + vals, err := client.ZRangeWithScores(ctx, "zset", 0, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 1, + Member: "one", + }, { + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + + vals, err = client.ZRangeWithScores(ctx, "zset", 2, 3).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{Score: 3, Member: "three"}})) + + vals, err = client.ZRangeWithScores(ctx, "zset", -2, -1).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + }) }) From 474104f85d5888995f7fbedc5e32f603a006ad29 Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Sun, 10 Mar 2024 16:40:22 +0800 Subject: [PATCH 6/9] format --- src/cmd_table_manager.cc | 4 ++-- src/cmd_zset.cc | 5 +++-- src/cmd_zset.h | 2 +- src/storage/include/storage/storage.h | 2 +- src/storage/src/redis.h | 2 +- src/storage/src/storage.cc | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index be97a7415..7dbab8980 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -118,8 +118,8 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(ZRangebyscore, -4); ADD_COMMAND(ZRemRangeByRank, 4); ADD_COMMAND(ZCard, 2); - ADD_COMMAND(ZScore,3); - ADD_COMMAND(ZRange,-4); + ADD_COMMAND(ZScore, 3); + ADD_COMMAND(ZRange, -4); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index a2114f2bc..853d753c8 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -348,7 +348,8 @@ void ZRangeCmd::DoCmd(PClient* client) { ->ZRevrangebyscore(client->Key(), start, stop, left_close, right_close, count, offset, &score_members); } else if (by_lex) { s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRevrangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, count, offset, &members); + ->ZRevrangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, count, + offset, &members); } else { s = PSTORE.GetBackend(client->GetCurrentDB())->ZRevrange(client->Key(), start, stop, &score_members); } @@ -357,7 +358,7 @@ void ZRangeCmd::DoCmd(PClient* client) { client->SetRes(CmdRes::kErrOther, s.ToString()); return; } - //TODO(taota csx) bylex cmd's return. + // TODO(taota csx) bylex cmd's return. FitLimit(count, offset, static_cast(score_members.size())); size_t m_start = offset; size_t m_end = offset + count; diff --git a/src/cmd_zset.h b/src/cmd_zset.h index 6b6edcb4f..e0e73d7bb 100644 --- a/src/cmd_zset.h +++ b/src/cmd_zset.h @@ -67,7 +67,7 @@ class ZCardCmd : public BaseCmd { void DoCmd(PClient *client) override; }; -class ZRangeCmd : public BaseCmd { +class ZRangeCmd : public BaseCmd { public: ZRangeCmd(const std::string &name, int16_t arity); diff --git a/src/storage/include/storage/storage.h b/src/storage/include/storage/storage.h index 180ffdda3..60b389084 100644 --- a/src/storage/include/storage/storage.h +++ b/src/storage/include/storage/storage.h @@ -838,7 +838,7 @@ class Storage { int64_t offset, std::vector* score_members); Status ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members); + int64_t count, int64_t offset, std::vector* members); // Returns the rank of member in the sorted set stored at key, with the scores // ordered from high to low. The rank (or index) is 0-based, which means that diff --git a/src/storage/src/redis.h b/src/storage/src/redis.h index 8dad79684..3de2ad78c 100644 --- a/src/storage/src/redis.h +++ b/src/storage/src/redis.h @@ -271,7 +271,7 @@ class Redis { Status ZRevrangebyscore(const Slice& key, double min, double max, bool left_close, bool right_close, int64_t count, int64_t offset, std::vector* score_members); Status ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members); + int64_t count, int64_t offset, std::vector* members); Status ZRevrank(const Slice& key, const Slice& member, int32_t* rank); Status ZScore(const Slice& key, const Slice& member, double* score); Status ZGetAll(const Slice& key, double weight, std::map* value_to_dest); diff --git a/src/storage/src/storage.cc b/src/storage/src/storage.cc index 133b9803a..c7ec264d2 100644 --- a/src/storage/src/storage.cc +++ b/src/storage/src/storage.cc @@ -1008,7 +1008,7 @@ Status Storage::ZRangebylex(const Slice& key, const Slice& min, const Slice& max } Status Storage::ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members){ + int64_t count, int64_t offset, std::vector* members) { members->clear(); auto& inst = GetDBInstance(key); return inst->ZRevrangebylex(key, min, max, left_close, right_close, count, offset, members); From 70cb3ffcb65f2dca32247637b6b1757a7a6dd012 Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Mon, 11 Mar 2024 20:30:02 +0800 Subject: [PATCH 7/9] zrangebylex & zrevrangebylex --- src/base_cmd.h | 6 ++-- src/cmd_table_manager.cc | 6 ++-- src/cmd_zset.cc | 78 +++++++++++++++++++++++++++------------- src/cmd_zset.h | 30 +++++++++++++--- 4 files changed, 88 insertions(+), 32 deletions(-) diff --git a/src/base_cmd.h b/src/base_cmd.h index 5baf2b63e..0b0162871 100644 --- a/src/base_cmd.h +++ b/src/base_cmd.h @@ -109,11 +109,13 @@ const std::string kCmdNameLInsert = "linsert"; const std::string kCmdNameZAdd = "zadd"; const std::string kCmdNameZRevrange = "zrevrange"; const std::string kCmdNameZRangebyscore = "zrangebyscore"; -const std::string kCmdNameZRemRangeByRank = "zremrangebyrank"; -const std::string kCmdNameZRevRangeByScore = "zrevrangebyscore"; +const std::string kCmdNameZRemrangebyrank = "zremrangebyrank"; +const std::string kCmdNameZRevrangebyscore = "zrevrangebyscore"; const std::string kCmdNameZCard = "zcard"; const std::string kCmdNameZScore = "zscore"; const std::string kCmdNameZRange = "zrange"; +const std::string kCmdNameZRangebylex = "zrangebylex"; +const std::string kCmdNameZRevrangebylex = "zrevrangebylex"; enum CmdFlags { kCmdFlagsWrite = (1 << 0), // May modify the dataset diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index a583226de..69bcb5207 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -116,11 +116,13 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(ZAdd, -4); ADD_COMMAND(ZRevrange, -4); ADD_COMMAND(ZRangebyscore, -4); - ADD_COMMAND(ZRemRangeByRank, 4); - ADD_COMMAND(ZRevRangeByScore, -4); + ADD_COMMAND(ZRemrangebyrank, 4); + ADD_COMMAND(ZRevrangebyscore, -4); ADD_COMMAND(ZCard, 2); ADD_COMMAND(ZScore, 3); ADD_COMMAND(ZRange, -4); + ADD_COMMAND(ZRangebylex,-3); + ADD_COMMAND(ZRevrangebylex,-3); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index 73fe0ce75..432360cd4 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -235,15 +235,15 @@ void ZCardCmd::DoCmd(PClient* client) { client->AppendInteger(reply_Num); } -ZRemRangeByRankCmd::ZRemRangeByRankCmd(const std::string& name, int16_t arity) +ZRemrangebyrankCmd::ZRemrangebyrankCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategoryString) {} -bool ZRemRangeByRankCmd::DoInitial(PClient* client) { +bool ZRemrangebyrankCmd::DoInitial(PClient* client) { client->SetKey(client->argv_[1]); return true; } -void ZRemRangeByRankCmd::DoCmd(PClient* client) { +void ZRemrangebyrankCmd::DoCmd(PClient* client) { int32_t ret = 0; int32_t start = 0; int32_t end = 0; @@ -330,7 +330,7 @@ void ZRangeCmd::DoCmd(PClient* client) { } std::vector score_members; - std::vector members; + std::vector lex_members; storage::Status s; if (!is_rev) { if (by_score) { @@ -338,7 +338,7 @@ void ZRangeCmd::DoCmd(PClient* client) { ->ZRangebyscore(client->Key(), start, stop, left_close, right_close, count, offset, &score_members); } else if (by_lex) { s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, &members); + ->ZRangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, &lex_members); } else { s = PSTORE.GetBackend(client->GetCurrentDB())->ZRange(client->Key(), start, stop, &score_members); } @@ -349,7 +349,7 @@ void ZRangeCmd::DoCmd(PClient* client) { } else if (by_lex) { s = PSTORE.GetBackend(client->GetCurrentDB()) ->ZRevrangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, count, - offset, &members); + offset, &lex_members); } else { s = PSTORE.GetBackend(client->GetCurrentDB())->ZRevrange(client->Key(), start, stop, &score_members); } @@ -358,26 +358,36 @@ void ZRangeCmd::DoCmd(PClient* client) { client->SetRes(CmdRes::kErrOther, s.ToString()); return; } - // TODO(taota csx) bylex cmd's return. FitLimit(count, offset, static_cast(score_members.size())); size_t m_start = offset; size_t m_end = offset + count; - if (with_scores) { - char buf[32]; - int64_t len = 0; - client->AppendArrayLen(count * 2); - for (; m_start < m_end; m_start++) { - client->AppendStringLenUint64(score_members[m_start].member.size()); - client->AppendContent(score_members[m_start].member); - len = pstd::D2string(buf, sizeof(buf), score_members[m_start].score); - client->AppendStringLen(len); - client->AppendContent(buf); + if (by_lex) { + if (with_scores) { + client->SetRes(CmdRes::kSyntaxErr, "by lex not support with scores"); + } else { + client->AppendArrayLen(count); + for (; m_start < m_end; m_start++) { + client->AppendContent(lex_members[m_start]); + } } } else { - client->AppendArrayLen(count); - for (; m_start < m_end; m_start++) { - client->AppendStringLenUint64(score_members[m_start].member.size()); - client->AppendContent(score_members[m_start].member); + if (with_scores) { + char buf[32]; + int64_t len = 0; + client->AppendArrayLen(count * 2); + for (; m_start < m_end; m_start++) { + client->AppendStringLenUint64(score_members[m_start].member.size()); + client->AppendContent(score_members[m_start].member); + len = pstd::D2string(buf, sizeof(buf), score_members[m_start].score); + client->AppendStringLen(len); + client->AppendContent(buf); + } + } else { + client->AppendArrayLen(count); + for (; m_start < m_end; m_start++) { + client->AppendStringLenUint64(score_members[m_start].member.size()); + client->AppendContent(score_members[m_start].member); + } } } } @@ -402,15 +412,15 @@ void ZScoreCmd::DoCmd(PClient* client) { } } -ZRevRangeByScoreCmd::ZRevRangeByScoreCmd(const std::string& name, int16_t arity) +ZRevrangebyscoreCmd::ZRevrangebyscoreCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} -bool ZRevRangeByScoreCmd::DoInitial(PClient* client) { +bool ZRevrangebyscoreCmd::DoInitial(PClient* client) { client->SetKey(client->argv_[1]); return true; } -void ZRevRangeByScoreCmd::DoCmd(PClient* client) { +void ZRevrangebyscoreCmd::DoCmd(PClient* client) { double min_score = 0; double max_score = 0; bool right_close = true; @@ -486,4 +496,24 @@ void ZRevRangeByScoreCmd::DoCmd(PClient* client) { } } +ZRangebylexCmd::ZRangebylexCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} + +bool ZRangebylexCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRangebylexCmd::DoCmd(PClient* client) {} + +ZRevrangebylexCmd::ZRevrangebylexCmd(const std::string& name, int16_t arity) + : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} + +bool ZRevrangebylexCmd::DoInitial(PClient* client) { + client->SetKey(client->argv_[1]); + return true; +} + +void ZRevrangebylexCmd::DoCmd(PClient* client) {} + } // namespace pikiwidb \ No newline at end of file diff --git a/src/cmd_zset.h b/src/cmd_zset.h index 451d6b841..d8d2d62c3 100644 --- a/src/cmd_zset.h +++ b/src/cmd_zset.h @@ -45,9 +45,9 @@ class ZRangebyscoreCmd : public BaseCmd { void DoCmd(PClient *client) override; }; -class ZRemRangeByRankCmd : public BaseCmd { +class ZRemrangebyrankCmd : public BaseCmd { public: - ZRemRangeByRankCmd(const std::string &name, int16_t arity); + ZRemrangebyrankCmd(const std::string &name, int16_t arity); protected: bool DoInitial(PClient *client) override; @@ -56,9 +56,9 @@ class ZRemRangeByRankCmd : public BaseCmd { void DoCmd(PClient *client) override; }; -class ZRevRangeByScoreCmd : public BaseCmd { +class ZRevrangebyscoreCmd : public BaseCmd { public: - ZRevRangeByScoreCmd(const std::string &name, int16_t arity); + ZRevrangebyscoreCmd(const std::string &name, int16_t arity); protected: bool DoInitial(PClient *client) override; @@ -100,4 +100,26 @@ class ZScoreCmd : public BaseCmd { void DoCmd(PClient *client) override; }; +class ZRangebylexCmd : public BaseCmd { + public: + ZRangebylexCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + +class ZRevrangebylexCmd : public BaseCmd { + public: + ZRevrangebylexCmd(const std::string &name, int16_t arity); + + protected: + bool DoInitial(PClient *client) override; + + private: + void DoCmd(PClient *client) override; +}; + } // namespace pikiwidb \ No newline at end of file From e9d360e397c65f2d8885fa8e875b3bf6a527bef7 Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Thu, 21 Mar 2024 13:58:13 +0800 Subject: [PATCH 8/9] add cmd --- src/cmd_zset.cc | 166 ++++++++++++++++++++++++-- src/storage/include/storage/storage.h | 3 - src/storage/src/redis.h | 2 - src/storage/src/redis_zsets.cc | 58 --------- src/storage/src/storage.cc | 7 -- tests/zset_test.go | 98 +++++++++++++++ 6 files changed, 252 insertions(+), 82 deletions(-) diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index 432360cd4..5bc1a72fb 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -48,6 +48,40 @@ int32_t DoScoreStrRange(std::string begin_score, std::string end_score, bool* le return 0; } +static int32_t DoMemberRange(const std::string& raw_min_member, const std::string& raw_max_member, bool* left_close, + bool* right_close, std::string* min_member, std::string* max_member) { + if (raw_min_member == "-") { + *min_member = "-"; + } else if (raw_min_member == "+") { + *min_member = "+"; + } else { + if (!raw_min_member.empty() && raw_min_member.at(0) == '(') { + *left_close = false; + } else if (!raw_min_member.empty() && raw_min_member.at(0) == '[') { + *left_close = true; + } else { + return -1; + } + min_member->assign(raw_min_member.begin() + 1, raw_min_member.end()); + } + + if (raw_max_member == "+") { + *max_member = "+"; + } else if (raw_max_member == "-") { + *max_member = "-"; + } else { + if (!raw_max_member.empty() && raw_max_member.at(0) == '(') { + *right_close = false; + } else if (!raw_max_member.empty() && raw_max_member.at(0) == '[') { + *right_close = true; + } else { + return -1; + } + max_member->assign(raw_max_member.begin() + 1, raw_max_member.end()); + } + return 0; +} + ZAddCmd::ZAddCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} @@ -285,11 +319,6 @@ void ZRangeCmd::DoCmd(PClient* client) { bool left_close = false; bool right_close = false; bool is_rev = false; - int32_t ret = DoScoreStrRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &start, &stop); - if (ret == -1) { - client->SetRes(CmdRes::kErrOther, "start or stop is not a float"); - return; - } size_t argc = client->argv_.size(); if (argc >= 5) { size_t index = 4; @@ -329,27 +358,43 @@ void ZRangeCmd::DoCmd(PClient* client) { return; } + int32_t ret = 0; + std::string lex_min; + std::string lex_max; + if (by_lex) { + ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close,&lex_min,&lex_max); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "min or max not valid string range item"); + return; + } + } else { + ret = DoScoreStrRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &start, &stop); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "start or stop is not a float"); + return; + } + } + std::vector score_members; std::vector lex_members; storage::Status s; if (!is_rev) { if (by_score) { s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRangebyscore(client->Key(), start, stop, left_close, right_close, count, offset, &score_members); + ->ZRangebyscore(client->Key(), start, stop, left_close, right_close, &score_members); } else if (by_lex) { s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, &lex_members); + ->ZRangebylex(client->Key(), lex_min, lex_max, left_close, right_close, &lex_members); } else { s = PSTORE.GetBackend(client->GetCurrentDB())->ZRange(client->Key(), start, stop, &score_members); } } else { if (by_score) { s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRevrangebyscore(client->Key(), start, stop, left_close, right_close, count, offset, &score_members); + ->ZRevrangebyscore(client->Key(), start, stop, left_close, right_close, &score_members); } else if (by_lex) { s = PSTORE.GetBackend(client->GetCurrentDB()) - ->ZRevrangebylex(client->Key(), client->argv_[2], client->argv_[3], left_close, right_close, count, - offset, &lex_members); + ->ZRangebylex(client->Key(), lex_min, lex_max, left_close, right_close, &lex_members); } else { s = PSTORE.GetBackend(client->GetCurrentDB())->ZRevrange(client->Key(), start, stop, &score_members); } @@ -504,7 +549,58 @@ bool ZRangebylexCmd::DoInitial(PClient* client) { return true; } -void ZRangebylexCmd::DoCmd(PClient* client) {} +void ZRangebylexCmd::DoCmd(PClient* client) { + if(strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { + client->AppendContent("*0"); + } + + size_t argc = client->argv_.size(); + int64_t count = -1; + int64_t offset = 0; + bool left_close = true; + bool right_close = true; + if (argc == 7 && strcasecmp(client->argv_[4].data(), "limit") == 0) { + if (pstd::String2int(client->argv_[5].data(), client->argv_[5].size(), &offset) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + if (pstd::String2int(client->argv_[6].data(), client->argv_[6].size(), &count) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + } else if ( argc == 4 ) { + + } else { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + + std::string min_member; + std::string max_member; + int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close,&min_member,&max_member); + if (ret == -1) { + client->SetRes(CmdRes::kErrOther, "min or max not valid string range item"); + return; + } + std::vector members; + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRangebylex(client->Key(), min_member, max_member, left_close, right_close, &members); + if (!s.ok() && !s.IsNotFound()) { + client->SetRes(CmdRes::kErrOther, s.ToString()); + return; + } + + FitLimit(count, offset, static_cast(members.size())); + size_t index = offset; + size_t end = offset + count; + + client->AppendArrayLen(static_cast(members.size())); + for (; index < end; index++) { + client->AppendStringLenUint64(members[index].size()); + client->AppendContent(members[index]); + } +} ZRevrangebylexCmd::ZRevrangebylexCmd(const std::string& name, int16_t arity) : BaseCmd(name, arity, kCmdFlagsWrite, kAclCategoryWrite | kAclCategorySortedSet) {} @@ -514,6 +610,52 @@ bool ZRevrangebylexCmd::DoInitial(PClient* client) { return true; } -void ZRevrangebylexCmd::DoCmd(PClient* client) {} +void ZRevrangebylexCmd::DoCmd(PClient* client) { + if(strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { + client->AppendContent("*0"); + } + + size_t argc = client->argv_.size(); + int64_t count = -1; + int64_t offset = 0; + bool left_close = true; + bool right_close = true; + if (argc == 7 && strcasecmp(client->argv_[4].data(), "limit") == 0) { + if (pstd::String2int(client->argv_[5].data(), client->argv_[5].size(), &offset) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + if (pstd::String2int(client->argv_[6].data(), client->argv_[6].size(), &count) == 0) { + client->SetRes(CmdRes::kInvalidInt); + return; + } + } else if ( argc == 4 ) { + + } else { + client->SetRes(CmdRes::kSyntaxErr); + return; + } + + std::string min_member; + std::string max_member; + int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close,&min_member,&max_member); + std::vector members; + storage::Status s; + s = PSTORE.GetBackend(client->GetCurrentDB()) + ->ZRangebylex(client->Key(), min_member, max_member, left_close, right_close, &members); + if (!s.ok() && !s.IsNotFound()) { + client->SetRes(CmdRes::kErrOther, s.ToString()); + return; + } + + FitLimit(count, offset, static_cast(members.size())); + size_t index = offset + count - 1; + size_t start = offset; + client->AppendArrayLen(static_cast(members.size())); + for (; index >= start; index--) { + client->AppendStringLenUint64(members[index].size()); + client->AppendContent(members[index]); + } +} } // namespace pikiwidb \ No newline at end of file diff --git a/src/storage/include/storage/storage.h b/src/storage/include/storage/storage.h index 60b389084..beb1a4fb3 100644 --- a/src/storage/include/storage/storage.h +++ b/src/storage/include/storage/storage.h @@ -837,9 +837,6 @@ class Storage { Status ZRevrangebyscore(const Slice& key, double min, double max, bool left_close, bool right_close, int64_t count, int64_t offset, std::vector* score_members); - Status ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members); - // Returns the rank of member in the sorted set stored at key, with the scores // ordered from high to low. The rank (or index) is 0-based, which means that // the member with the highest score has rank 0. diff --git a/src/storage/src/redis.h b/src/storage/src/redis.h index 3de2ad78c..e5439042a 100644 --- a/src/storage/src/redis.h +++ b/src/storage/src/redis.h @@ -270,8 +270,6 @@ class Redis { Status ZRevrange(const Slice& key, int32_t start, int32_t stop, std::vector* score_members); Status ZRevrangebyscore(const Slice& key, double min, double max, bool left_close, bool right_close, int64_t count, int64_t offset, std::vector* score_members); - Status ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members); Status ZRevrank(const Slice& key, const Slice& member, int32_t* rank); Status ZScore(const Slice& key, const Slice& member, double* score); Status ZGetAll(const Slice& key, double weight, std::map* value_to_dest); diff --git a/src/storage/src/redis_zsets.cc b/src/storage/src/redis_zsets.cc index 54f1ac119..ea4864ae1 100644 --- a/src/storage/src/redis_zsets.cc +++ b/src/storage/src/redis_zsets.cc @@ -971,64 +971,6 @@ Status Redis::ZRevrangebyscore(const Slice& key, double min, double max, bool le return s; } -Status Redis::ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members) { - members->clear(); - rocksdb::ReadOptions read_options; - const rocksdb::Snapshot* snapshot = nullptr; - - std::string meta_value; - ScopeSnapshot ss(db_, &snapshot); - read_options.snapshot = snapshot; - - bool left_no_limit = min.compare("-") == 0; - bool right_not_limit = max.compare("+") == 0; - - BaseMetaKey base_meta_key(key); - Status s = db_->Get(read_options, handles_[kZsetsMetaCF], base_meta_key.Encode(), &meta_value); - if (s.ok()) { - ParsedZSetsMetaValue parsed_zsets_meta_value(&meta_value); - if (parsed_zsets_meta_value.IsStale() || parsed_zsets_meta_value.Count() == 0) { - return Status::NotFound(); - } else { - uint64_t version = parsed_zsets_meta_value.Version(); - int32_t left = parsed_zsets_meta_value.Count(); - int64_t skipped = 0; - ZSetsMemberKey zsets_member_key(key, version, Slice()); - KeyStatisticsDurationGuard guard(this, DataType::kZSets, key.ToString()); - rocksdb::Iterator* iter = db_->NewIterator(read_options, handles_[kZsetsDataCF]); - for (iter->SeekForPrev(zsets_member_key.Encode()); iter->Valid() && left > 0; iter->Prev(), --left) { - bool left_pass = false; - bool right_pass = false; - ParsedZSetsMemberKey parsed_zsets_member_key(iter->key()); - Slice member = parsed_zsets_member_key.member(); - if (left_no_limit || (left_close && min.compare(member) >= 0) || (!left_close && min.compare(member) > 0)) { - left_pass = true; - } - if (right_not_limit || (right_close && max.compare(member) <= 0) || (!right_close && max.compare(member) < 0)) { - right_pass = true; - } - if (left_pass && right_pass) { - // skip offset - if (skipped < offset) { - ++skipped; - continue; - } - members->push_back(member.ToString()); - if (count > 0 && members->size() == static_cast(count)) { - break; - } - } - if (!right_pass) { - break; - } - } - delete iter; - } - } - return s; -} - Status Redis::ZRevrank(const Slice& key, const Slice& member, int32_t* rank) { *rank = -1; rocksdb::ReadOptions read_options; diff --git a/src/storage/src/storage.cc b/src/storage/src/storage.cc index c7ec264d2..bf4d54ca6 100644 --- a/src/storage/src/storage.cc +++ b/src/storage/src/storage.cc @@ -1007,13 +1007,6 @@ Status Storage::ZRangebylex(const Slice& key, const Slice& min, const Slice& max return inst->ZRangebylex(key, min, max, left_close, right_close, members); } -Status Storage::ZRevrangebylex(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, - int64_t count, int64_t offset, std::vector* members) { - members->clear(); - auto& inst = GetDBInstance(key); - return inst->ZRevrangebylex(key, min, max, left_close, right_close, count, offset, members); -} - Status Storage::ZLexcount(const Slice& key, const Slice& min, const Slice& max, bool left_close, bool right_close, int32_t* ret) { auto& inst = GetDBInstance(key); diff --git a/tests/zset_test.go b/tests/zset_test.go index 43410837a..5db081e53 100644 --- a/tests/zset_test.go +++ b/tests/zset_test.go @@ -288,4 +288,102 @@ var _ = Describe("Zset", Ordered, func() { Member: "three", }})) }) + + It("should ZRangeByScoreWithScoresMap", func() { + err := client.ZAdd(ctx, "zset", redis.Z{Score: 1, Member: "one"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 2, Member: "two"}).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zset", redis.Z{Score: 3, Member: "three"}).Err() + Expect(err).NotTo(HaveOccurred()) + + vals, err := client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "-inf", + Max: "+inf", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 1, + Member: "one", + }, { + Score: 2, + Member: "two", + }, { + Score: 3, + Member: "three", + }})) + + vals, err = client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "1", + Max: "2", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{ + Score: 1, + Member: "one", + }, { + Score: 2, + Member: "two", + }})) + + vals, err = client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "(1", + Max: "2", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{{Score: 2, Member: "two"}})) + + vals, err = client.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ + Min: "(1", + Max: "(2", + }).Result() + Expect(err).NotTo(HaveOccurred()) + Expect(vals).To(Equal([]redis.Z{})) + }) + + It("should ZRangeByLex", func() { + err := client.ZAdd(ctx, "zsetrangebylex", redis.Z{ + Score: 0, + Member: "a", + }).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zsetrangebylex", redis.Z{ + Score: 0, + Member: "b", + }).Err() + Expect(err).NotTo(HaveOccurred()) + err = client.ZAdd(ctx, "zsetrangebylex", redis.Z{ + Score: 0, + Member: "c", + }).Err() + Expect(err).NotTo(HaveOccurred()) + + zRangeByLex := client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "-", + Max: "+", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{"a", "b", "c"})) + + zRangeByLex = client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "[a", + Max: "[b", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{"a", "b"})) + + zRangeByLex = client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "(a", + Max: "[b", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{"b"})) + + zRangeByLex = client.ZRangeByLex(ctx, "zsetrangebylex", &redis.ZRangeBy{ + Min: "(a", + Max: "(b", + }) + Expect(zRangeByLex.Err()).NotTo(HaveOccurred()) + Expect(zRangeByLex.Val()).To(Equal([]string{})) + }) }) From 1d93c92194a8ad125c1b1689c07eeb8673b98c19 Mon Sep 17 00:00:00 2001 From: Tao_ta <745103130@qq.com> Date: Thu, 21 Mar 2024 14:01:12 +0800 Subject: [PATCH 9/9] format --- src/cmd_table_manager.cc | 4 ++-- src/cmd_zset.cc | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/cmd_table_manager.cc b/src/cmd_table_manager.cc index 69bcb5207..d9ab16fba 100644 --- a/src/cmd_table_manager.cc +++ b/src/cmd_table_manager.cc @@ -121,8 +121,8 @@ void CmdTableManager::InitCmdTable() { ADD_COMMAND(ZCard, 2); ADD_COMMAND(ZScore, 3); ADD_COMMAND(ZRange, -4); - ADD_COMMAND(ZRangebylex,-3); - ADD_COMMAND(ZRevrangebylex,-3); + ADD_COMMAND(ZRangebylex, -3); + ADD_COMMAND(ZRevrangebylex, -3); } std::pair CmdTableManager::GetCommand(const std::string& cmdName, PClient* client) { diff --git a/src/cmd_zset.cc b/src/cmd_zset.cc index 5bc1a72fb..b2641c895 100644 --- a/src/cmd_zset.cc +++ b/src/cmd_zset.cc @@ -362,7 +362,7 @@ void ZRangeCmd::DoCmd(PClient* client) { std::string lex_min; std::string lex_max; if (by_lex) { - ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close,&lex_min,&lex_max); + ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &lex_min, &lex_max); if (ret == -1) { client->SetRes(CmdRes::kErrOther, "min or max not valid string range item"); return; @@ -550,7 +550,7 @@ bool ZRangebylexCmd::DoInitial(PClient* client) { } void ZRangebylexCmd::DoCmd(PClient* client) { - if(strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { + if (strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { client->AppendContent("*0"); } @@ -568,8 +568,7 @@ void ZRangebylexCmd::DoCmd(PClient* client) { client->SetRes(CmdRes::kInvalidInt); return; } - } else if ( argc == 4 ) { - + } else if (argc == 4) { } else { client->SetRes(CmdRes::kSyntaxErr); return; @@ -577,7 +576,7 @@ void ZRangebylexCmd::DoCmd(PClient* client) { std::string min_member; std::string max_member; - int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close,&min_member,&max_member); + int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &min_member, &max_member); if (ret == -1) { client->SetRes(CmdRes::kErrOther, "min or max not valid string range item"); return; @@ -611,7 +610,7 @@ bool ZRevrangebylexCmd::DoInitial(PClient* client) { } void ZRevrangebylexCmd::DoCmd(PClient* client) { - if(strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { + if (strcasecmp(client->argv_[2].data(), "+") == 0 || strcasecmp(client->argv_[3].data(), "-") == 0) { client->AppendContent("*0"); } @@ -629,8 +628,7 @@ void ZRevrangebylexCmd::DoCmd(PClient* client) { client->SetRes(CmdRes::kInvalidInt); return; } - } else if ( argc == 4 ) { - + } else if (argc == 4) { } else { client->SetRes(CmdRes::kSyntaxErr); return; @@ -638,7 +636,7 @@ void ZRevrangebylexCmd::DoCmd(PClient* client) { std::string min_member; std::string max_member; - int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close,&min_member,&max_member); + int32_t ret = DoMemberRange(client->argv_[2], client->argv_[3], &left_close, &right_close, &min_member, &max_member); std::vector members; storage::Status s; s = PSTORE.GetBackend(client->GetCurrentDB())