This is an automated email from the ASF dual-hosted git repository.

maplefu pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 231b093c feat(tdigest): add TDIGEST.MAX/MIN command (#2811)
231b093c is described below

commit 231b093c979ab80c11ac5433e52d43213dc94840
Author: Uniqueyou <[email protected]>
AuthorDate: Fri Mar 7 13:58:15 2025 +0800

    feat(tdigest): add TDIGEST.MAX/MIN command (#2811)
    
    Co-authored-by: mwish <[email protected]>
---
 src/commands/cmd_tdigest.cc                    | 50 +++++++++++++++++++++++-
 tests/gocase/unit/type/tdigest/tdigest_test.go | 54 ++++++++++++++++++++++++++
 2 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/src/commands/cmd_tdigest.cc b/src/commands/cmd_tdigest.cc
index ac699ed0..4c62ffb5 100644
--- a/src/commands/cmd_tdigest.cc
+++ b/src/commands/cmd_tdigest.cc
@@ -168,7 +168,55 @@ class CommandTDigestAdd : public Commander {
   std::vector<double> values_;
 };
 
+class CommandTDigestMinMax : public Commander {
+ public:
+  explicit CommandTDigestMinMax(bool is_min) : is_min_(is_min) {}
+
+  Status Parse(const std::vector<std::string> &args) override {
+    key_name_ = args[1];
+    return Status::OK();
+  }
+
+  Status Execute(engine::Context &ctx, Server *srv, Connection *conn, 
std::string *output) override {
+    TDigest tdigest(srv->storage, conn->GetNamespace());
+    TDigestMetadata metadata;
+    auto s = tdigest.GetMetaData(ctx, key_name_, &metadata);
+    if (!s.ok()) {
+      if (s.IsNotFound()) {
+        return {Status::RedisExecErr, errKeyNotFound};
+      }
+      return {Status::RedisExecErr, s.ToString()};
+    }
+
+    if (metadata.total_observations == 0) {
+      *output = redis::BulkString("nan");
+      return Status::OK();
+    }
+
+    double value = is_min_ ? metadata.minimum : metadata.maximum;
+    *output = redis::BulkString(fmt::format("{}", value));
+    return Status::OK();
+  }
+
+ private:
+  std::string key_name_;
+  bool is_min_;
+};
+
+// Then replace the existing template implementation and type aliases with:
+class CommandTDigestMin : public CommandTDigestMinMax {
+ public:
+  CommandTDigestMin() : CommandTDigestMinMax(true) {}
+};
+
+class CommandTDigestMax : public CommandTDigestMinMax {
+ public:
+  CommandTDigestMax() : CommandTDigestMinMax(false) {}
+};
+
 REDIS_REGISTER_COMMANDS(TDigest, 
MakeCmdAttr<CommandTDigestCreate>("tdigest.create", -2, "write", 1, 1, 1),
                         MakeCmdAttr<CommandTDigestInfo>("tdigest.info", 2, 
"read-only", 1, 1, 1),
-                        MakeCmdAttr<CommandTDigestAdd>("tdigest.add", -3, 
"write", 1, 1, 1));
+                        MakeCmdAttr<CommandTDigestAdd>("tdigest.add", -3, 
"write", 1, 1, 1),
+                        MakeCmdAttr<CommandTDigestMax>("tdigest.max", 2, 
"read-only", 1, 1, 1),
+                        MakeCmdAttr<CommandTDigestMin>("tdigest.min", 2, 
"read-only", 1, 1, 1));
 }  // namespace redis
diff --git a/tests/gocase/unit/type/tdigest/tdigest_test.go 
b/tests/gocase/unit/type/tdigest/tdigest_test.go
index e9509636..0183144c 100644
--- a/tests/gocase/unit/type/tdigest/tdigest_test.go
+++ b/tests/gocase/unit/type/tdigest/tdigest_test.go
@@ -203,4 +203,58 @@ func tdigestTests(t *testing.T, configs 
util.KvrocksServerConfigs) {
                require.EqualValues(t, 100, info.Observations)
                require.Greater(t, info.TotalCompressions, int64(0))
        })
+
+       t.Run("tdigest.max with different arguments", func(t *testing.T) {
+               keyPrefix := "tdigest_max_"
+
+               // Test invalid arguments
+               require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.MAX").Err(), 
errMsgWrongNumberArg)
+               require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.MAX", 
keyPrefix+"nonexistent").Err(), errMsgKeyNotExist)
+
+               // Test with empty tdigest
+               key := keyPrefix + "test1"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               rsp := rdb.Do(ctx, "TDIGEST.MAX", key)
+               require.NoError(t, rsp.Err())
+               require.EqualValues(t, rsp.Val(), "nan")
+
+               // Test with single value
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, 
"42.5").Err())
+               rsp = rdb.Do(ctx, "TDIGEST.MAX", key)
+               require.NoError(t, rsp.Err())
+               require.Equal(t, "42.5", rsp.Val())
+
+               // Test with multiple values
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1.0", 
"100.5", "50.5", "-10.5").Err())
+               rsp = rdb.Do(ctx, "TDIGEST.MAX", key)
+               require.NoError(t, rsp.Err())
+               require.Equal(t, "100.5", rsp.Val())
+       })
+
+       t.Run("tdigest.min with different arguments", func(t *testing.T) {
+               keyPrefix := "tdigest_min_"
+
+               // Test invalid arguments
+               require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.MIN").Err(), 
errMsgWrongNumberArg)
+               require.ErrorContains(t, rdb.Do(ctx, "TDIGEST.MIN", 
keyPrefix+"nonexistent").Err(), errMsgKeyNotExist)
+
+               // Test with empty tdigest
+               key := keyPrefix + "test1"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               rsp := rdb.Do(ctx, "TDIGEST.MIN", key)
+               require.NoError(t, rsp.Err())
+               require.EqualValues(t, rsp.Val(), "nan")
+
+               // Test with single value
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, 
"42.5").Err())
+               rsp = rdb.Do(ctx, "TDIGEST.MIN", key)
+               require.NoError(t, rsp.Err())
+               require.Equal(t, "42.5", rsp.Val())
+
+               // Test with multiple values
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1.0", 
"100.5", "50.5", "-10.5").Err())
+               rsp = rdb.Do(ctx, "TDIGEST.MIN", key)
+               require.NoError(t, rsp.Err())
+               require.Equal(t, "-10.5", rsp.Val())
+       })
 }

Reply via email to