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

hulk 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 6cb5b3aa Minor refactor the return value of JSON methods (#1920)
6cb5b3aa is described below

commit 6cb5b3aa57805299359e73e903212b2e25dbfcad
Author: hulk <[email protected]>
AuthorDate: Wed Dec 6 09:43:45 2023 +0800

    Minor refactor the return value of JSON methods (#1920)
---
 src/commands/cmd_json.cc         | 144 ++++++++++++---------------------------
 src/types/json.h                 | 131 ++++++++++++++++++-----------------
 src/types/redis_json.cc          |  73 ++++++++++----------
 src/types/redis_json.h           |  23 +++----
 tests/cppunit/types/json_test.cc |  90 ++++++++++++------------
 5 files changed, 203 insertions(+), 258 deletions(-)

diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc
index 6602cc9f..41763bdb 100644
--- a/src/commands/cmd_json.cc
+++ b/src/commands/cmd_json.cc
@@ -32,6 +32,19 @@
 
 namespace redis {
 
+template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
+std::string OptionalsToString(Optionals<T> &opts) {
+  std::string str = MultiLen(opts.size());
+  for (const auto &opt : opts) {
+    if (opt.has_value()) {
+      str += redis::Integer(opt.value());
+    } else {
+      str += redis::NilString();
+    }
+  }
+  return str;
+}
+
 class CommandJsonSet : public Commander {
  public:
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
@@ -126,20 +139,12 @@ class CommandJsonArrAppend : public Commander {
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     redis::Json json(srv->storage, conn->GetNamespace());
 
-    std::vector<size_t> result_count;
+    Optionals<size_t> results;
 
-    auto s = json.ArrAppend(args_[1], args_[2], {args_.begin() + 3, 
args_.end()}, &result_count);
+    auto s = json.ArrAppend(args_[1], args_[2], {args_.begin() + 3, 
args_.end()}, &results);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = redis::MultiLen(result_count.size());
-    for (size_t c : result_count) {
-      if (c != 0) {
-        *output += redis::Integer(c);
-      } else {
-        *output += redis::NilString();
-      }
-    }
-
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 };
@@ -159,25 +164,17 @@ class CommandJsonArrInsert : public Commander {
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     redis::Json json(srv->storage, conn->GetNamespace());
 
-    std::vector<std::optional<uint64_t>> result_count;
+    Optionals<uint64_t> results;
     auto parse_result = ParseInt<int>(args_[3], 10);
 
-    auto s = json.ArrInsert(args_[1], args_[2], index_, {args_.begin() + 4, 
args_.end()}, &result_count);
+    auto s = json.ArrInsert(args_[1], args_[2], index_, {args_.begin() + 4, 
args_.end()}, &results);
     if (s.IsNotFound()) {
       *output = redis::NilString();
       return Status::OK();
     }
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = redis::MultiLen(result_count.size());
-    for (auto c : result_count) {
-      if (c.has_value()) {
-        *output += redis::Integer(c.value());
-      } else {
-        *output += redis::NilString();
-      }
-    }
-
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 
@@ -215,11 +212,11 @@ class CommandJsonObjkeys : public Commander {
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     redis::Json json(srv->storage, conn->GetNamespace());
 
-    std::vector<std::optional<std::vector<std::string>>> results;
+    Optionals<std::vector<std::string>> results;
 
     // If path not specified set it to $
     std::string path = (args_.size() > 2) ? args_[2] : "$";
-    auto s = json.ObjKeys(args_[1], path, results);
+    auto s = json.ObjKeys(args_[1], path, &results);
     if (!s.ok() && !s.IsNotFound()) return {Status::RedisExecErr, 
s.ToString()};
     if (s.IsNotFound()) {
       *output = redis::NilString();
@@ -268,25 +265,15 @@ class CommandJsonToggle : public Commander {
     redis::Json json(svr->storage, conn->GetNamespace());
 
     std::string path = (args_.size() > 2) ? args_[2] : "$";
-    std::vector<std::optional<bool>> results;
-    auto s = json.Toggle(args_[1], path, results);
-
+    Optionals<bool> results;
+    auto s = json.Toggle(args_[1], path, &results);
+    if (!s.ok() && !s.IsNotFound()) return {Status::RedisExecErr, 
s.ToString()};
     if (s.IsNotFound()) {
       *output = redis::NilString();
       return Status::OK();
     }
 
-    *output = redis::MultiLen(results.size());
-    for (auto it = results.rbegin(); it != results.rend(); ++it) {
-      if (it->has_value()) {
-        *output += redis::Integer(it->value());
-      } else {
-        *output += redis::NilString();
-      }
-    }
-
-    if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
-
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 };
@@ -303,23 +290,15 @@ class CommandJsonArrLen : public Commander {
       return {Status::RedisExecErr, "The number of arguments is more than 
expected"};
     }
 
-    std::vector<std::optional<uint64_t>> results;
-    auto s = json.ArrLen(args_[1], path, results);
+    Optionals<uint64_t> results;
+    auto s = json.ArrLen(args_[1], path, &results);
     if (s.IsNotFound()) {
       *output = redis::NilString();
       return Status::OK();
     }
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = redis::MultiLen(results.size());
-    for (const auto &len : results) {
-      if (len.has_value()) {
-        *output += redis::Integer(len.value());
-      } else {
-        *output += redis::NilString();
-      }
-    }
-
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 };
@@ -367,7 +346,7 @@ class CommandJsonArrPop : public Commander {
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     redis::Json json(srv->storage, conn->GetNamespace());
 
-    std::vector<std::optional<JsonValue>> results;
+    Optionals<JsonValue> results;
 
     auto s = json.ArrPop(args_[1], path_, index_, &results);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
@@ -401,23 +380,15 @@ class CommandJsonObjLen : public Commander {
       return {Status::RedisExecErr, "The number of arguments is more than 
expected"};
     }
 
-    std::vector<std::optional<uint64_t>> results;
-    auto s = json.ObjLen(args_[1], path, results);
+    Optionals<uint64_t> results;
+    auto s = json.ObjLen(args_[1], path, &results);
     if (s.IsNotFound()) {
       *output = redis::NilString();
       return Status::OK();
     }
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = redis::MultiLen(results.size());
-    for (const auto &len : results) {
-      if (len.has_value()) {
-        *output += redis::Integer(len.value());
-      } else {
-        *output += redis::NilString();
-      }
-    }
-
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 };
@@ -435,9 +406,9 @@ class CommandJsonArrTrim : public Commander {
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
     redis::Json json(srv->storage, conn->GetNamespace());
 
-    std::vector<std::optional<uint64_t>> results;
+    Optionals<uint64_t> results;
 
-    auto s = json.ArrTrim(args_[1], path_, start_, stop_, results);
+    auto s = json.ArrTrim(args_[1], path_, start_, stop_, &results);
 
     if (s.IsNotFound()) {
       *output = redis::NilString();
@@ -445,15 +416,7 @@ class CommandJsonArrTrim : public Commander {
     }
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = redis::MultiLen(results.size());
-    for (const auto &len : results) {
-      if (len.has_value()) {
-        *output += redis::Integer(len.value());
-      } else {
-        *output += redis::NilString();
-      }
-    }
-
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 
@@ -484,9 +447,9 @@ class CommanderJsonArrIndex : public Commander {
   Status Execute(Server *svr, Connection *conn, std::string *output) override {
     redis::Json json(svr->storage, conn->GetNamespace());
 
-    std::vector<ssize_t> result;
+    Optionals<ssize_t> results;
 
-    auto s = json.ArrIndex(args_[1], args_[2], args_[3], start_, end_, 
&result);
+    auto s = json.ArrIndex(args_[1], args_[2], args_[3], start_, end_, 
&results);
 
     if (s.IsNotFound()) {
       *output = redis::NilString();
@@ -495,14 +458,7 @@ class CommanderJsonArrIndex : public Commander {
 
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = redis::MultiLen(result.size());
-    for (const auto &found_index : result) {
-      if (found_index == NOT_ARRAY) {
-        *output += redis::NilString();
-        continue;
-      }
-      *output += redis::Integer(found_index);
-    }
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 
@@ -580,25 +536,13 @@ class CommandJsonStrAppend : public Commander {
       return {Status::RedisExecErr, "The number of arguments is more than 
expected"};
     }
 
-    std::vector<uint64_t> results;
-    auto s = json.StrAppend(args_[1], path, args_[3], results);
+    Optionals<uint64_t> results;
+    auto s = json.StrAppend(args_[1], path, args_[3], &results);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = IntegerArray(results);
+    *output = OptionalsToString(results);
     return Status::OK();
   }
-
-  static std::string IntegerArray(const std::vector<uint64_t> &values) {
-    std::string result = "*" + std::to_string(values.size()) + CRLF;
-    for (const auto &value : values) {
-      if (value == std::numeric_limits<uint64_t>::max()) {
-        result += NilString();
-      } else {
-        result += Integer(value);
-      }
-    }
-    return result;
-  }
 };
 
 class CommandJsonStrLen : public Commander {
@@ -613,11 +557,11 @@ class CommandJsonStrLen : public Commander {
       return {Status::RedisExecErr, "The number of arguments is more than 
expected"};
     }
 
-    std::vector<uint64_t> results;
-    auto s = json.StrLen(args_[1], path, results);
+    Optionals<uint64_t> results;
+    auto s = json.StrLen(args_[1], path, &results);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    *output = CommandJsonStrAppend::IntegerArray(results);
+    *output = OptionalsToString(results);
     return Status::OK();
   }
 };
diff --git a/src/types/json.h b/src/types/json.h
index 0163c07a..4b786834 100644
--- a/src/types/json.h
+++ b/src/types/json.h
@@ -41,8 +41,8 @@
 #include "jsoncons_ext/jsonpath/jsonpath_error.hpp"
 #include "status.h"
 
-constexpr ssize_t NOT_FOUND_INDEX = -1;
-constexpr ssize_t NOT_ARRAY = -2;
+template <class T>
+using Optionals = std::vector<std::optional<T>>;
 
 struct JsonValue {
   enum class NumOpEnum : uint8_t {
@@ -161,7 +161,8 @@ struct JsonValue {
     return Status::OK();
   }
 
-  Status StrAppend(std::string_view path, const std::string &append_value, 
std::vector<uint64_t> &append_cnt) {
+  StatusOr<Optionals<uint64_t>> StrAppend(std::string_view path, const 
std::string &append_value) {
+    Optionals<uint64_t> results;
     try {
       std::string append_str;
       jsoncons::json append_json = jsoncons::json::parse(append_value);
@@ -171,37 +172,39 @@ struct JsonValue {
         return {Status::NotOK, "STRAPPEND need input a string to append"};
       }
 
-      jsoncons::jsonpath::json_replace(
-          value, path, [&append_str, &append_cnt](const std::string & 
/*path*/, jsoncons::json &origin) {
-            if (origin.is_string()) {
-              auto origin_str = origin.as_string();
-              append_cnt.push_back(origin_str.length() + append_str.length());
-              origin = origin_str + append_str;
-            } else {
-              append_cnt.push_back(std::numeric_limits<uint64_t>::max());
-            }
-          });
+      jsoncons::jsonpath::json_replace(value, path,
+                                       [&append_str, &results](const 
std::string & /*path*/, jsoncons::json &origin) {
+                                         if (origin.is_string()) {
+                                           auto origin_str = 
origin.as_string();
+                                           
results.emplace_back(origin_str.length() + append_str.length());
+                                           origin = origin_str + append_str;
+                                         } else {
+                                           results.emplace_back(std::nullopt);
+                                         }
+                                       });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
 
-    return Status::OK();
+    std::reverse(results.begin(), results.end());
+    return results;
   }
 
-  Status StrLen(std::string_view path, std::vector<uint64_t> &str_lens) const {
+  StatusOr<Optionals<uint64_t>> StrLen(std::string_view path) const {
+    Optionals<uint64_t> results;
     try {
       jsoncons::jsonpath::json_query(value, path,
-                                     [&str_lens](const std::string & /*path*/, 
const jsoncons::json &origin) {
+                                     [&results](const std::string & /*path*/, 
const jsoncons::json &origin) {
                                        if (origin.is_string()) {
-                                         
str_lens.push_back(origin.as_string().length());
+                                         
results.emplace_back(origin.as_string().length());
                                        } else {
-                                         
str_lens.push_back(std::numeric_limits<uint64_t>::max());
+                                         results.emplace_back(std::nullopt);
                                        }
                                      });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-    return Status::OK();
+    return results;
   }
 
   StatusOr<JsonValue> Get(std::string_view path) const {
@@ -212,53 +215,52 @@ struct JsonValue {
     }
   }
 
-  StatusOr<std::vector<size_t>> ArrAppend(std::string_view path, const 
std::vector<jsoncons::json> &append_values) {
-    std::vector<size_t> result_count;
+  StatusOr<Optionals<size_t>> ArrAppend(std::string_view path, const 
std::vector<jsoncons::json> &append_values) {
+    Optionals<size_t> results;
 
     try {
       jsoncons::jsonpath::json_replace(
-          value, path, [&append_values, &result_count](const std::string & 
/*path*/, jsoncons::json &val) {
+          value, path, [&append_values, &results](const std::string & 
/*path*/, jsoncons::json &val) {
             if (val.is_array()) {
               val.insert(val.array_range().end(), append_values.begin(), 
append_values.end());
-              result_count.emplace_back(val.size());
+              results.emplace_back(val.size());
             } else {
-              result_count.emplace_back(0);
+              results.emplace_back(std::nullopt);
             }
           });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
 
-    return result_count;
+    return results;
   }
 
-  StatusOr<std::vector<std::optional<uint64_t>>> ArrInsert(std::string_view 
path, const int64_t &index,
-                                                           const 
std::vector<jsoncons::json> &insert_values) {
-    std::vector<std::optional<uint64_t>> result_count;
-
+  StatusOr<Optionals<uint64_t>> ArrInsert(std::string_view path, const int64_t 
&index,
+                                          const std::vector<jsoncons::json> 
&insert_values) {
+    Optionals<uint64_t> results;
     try {
       jsoncons::jsonpath::json_replace(
-          value, path, [&insert_values, &result_count, index](const 
std::string & /*path*/, jsoncons::json &val) {
+          value, path, [&insert_values, &results, index](const std::string & 
/*path*/, jsoncons::json &val) {
             if (val.is_array()) {
               auto len = static_cast<int64_t>(val.size());
               // When index > 0, we need index < len
               // when index < 0, we need index >= -len.
               if (index >= len || index < -len) {
-                result_count.emplace_back(std::nullopt);
+                results.emplace_back(std::nullopt);
                 return;
               }
               auto base_iter = index >= 0 ? val.array_range().begin() : 
val.array_range().end();
               val.insert(base_iter + index, insert_values.begin(), 
insert_values.end());
-              result_count.emplace_back(val.size());
+              results.emplace_back(val.size());
             } else {
-              result_count.emplace_back(std::nullopt);
+              results.emplace_back(std::nullopt);
             }
           });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
 
-    return result_count;
+    return results;
   }
 
   static std::pair<ssize_t, ssize_t> NormalizeArrIndices(ssize_t start, 
ssize_t end, ssize_t len) {
@@ -276,13 +278,13 @@ struct JsonValue {
     return {start, end};
   }
 
-  StatusOr<std::vector<ssize_t>> ArrIndex(std::string_view path, const 
jsoncons::json &needle, ssize_t start,
-                                          ssize_t end) const {
-    std::vector<ssize_t> result;
+  StatusOr<Optionals<ssize_t>> ArrIndex(std::string_view path, const 
jsoncons::json &needle, ssize_t start,
+                                        ssize_t end) const {
+    Optionals<ssize_t> results;
     try {
       jsoncons::jsonpath::json_query(value, path, [&](const std::string & 
/*path*/, const jsoncons::json &val) {
         if (!val.is_array()) {
-          result.emplace_back(NOT_ARRAY);
+          results.emplace_back(std::nullopt);
           return;
         }
         auto [pstart, pend] = NormalizeArrIndices(start, end, 
static_cast<ssize_t>(val.size()));
@@ -292,15 +294,16 @@ struct JsonValue {
         auto end_it = arr_begin + pend;
         auto it = std::find(begin_it, end_it, needle);
         if (it != end_it) {
-          result.emplace_back(it - arr_begin);
+          results.emplace_back(it - arr_begin);
           return;
         }
-        result.emplace_back(NOT_FOUND_INDEX);
+        // index not found
+        results.emplace_back(-1);
       });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-    return result;
+    return results;
   }
 
   StatusOr<std::vector<std::string>> Type(std::string_view path) const {
@@ -342,21 +345,22 @@ struct JsonValue {
     return types;
   }
 
-  StatusOr<std::vector<std::optional<bool>>> Toggle(std::string_view path) {
-    std::vector<std::optional<bool>> result;
+  StatusOr<Optionals<bool>> Toggle(std::string_view path) {
+    Optionals<bool> results;
     try {
-      jsoncons::jsonpath::json_replace(value, path, [&result](const 
std::string & /*path*/, jsoncons::json &val) {
+      jsoncons::jsonpath::json_replace(value, path, [&results](const 
std::string & /*path*/, jsoncons::json &val) {
         if (val.is_bool()) {
           val = !val.as_bool();
-          result.emplace_back(val.as_bool());
+          results.emplace_back(val.as_bool());
         } else {
-          result.emplace_back(std::nullopt);
+          results.emplace_back(std::nullopt);
         }
       });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-    return result;
+    std::reverse(results.begin(), results.end());
+    return results;
   }
 
   StatusOr<size_t> Clear(std::string_view path) {
@@ -384,21 +388,21 @@ struct JsonValue {
     return count;
   }
 
-  Status ArrLen(std::string_view path, std::vector<std::optional<uint64_t>> 
&arr_lens) const {
+  StatusOr<Optionals<uint64_t>> ArrLen(std::string_view path) const {
+    Optionals<uint64_t> results;
     try {
       jsoncons::jsonpath::json_query(value, path,
-                                     [&arr_lens](const std::string & /*path*/, 
const jsoncons::json &basic_json) {
+                                     [&results](const std::string & /*path*/, 
const jsoncons::json &basic_json) {
                                        if (basic_json.is_array()) {
-                                         
arr_lens.emplace_back(static_cast<uint64_t>(basic_json.size()));
+                                         
results.emplace_back(static_cast<uint64_t>(basic_json.size()));
                                        } else {
-                                         arr_lens.emplace_back(std::nullopt);
+                                         results.emplace_back(std::nullopt);
                                        }
                                      });
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-
-    return Status::OK();
+    return results;
   }
 
   StatusOr<bool> Merge(const std::string_view path, const std::string 
&merge_value) {
@@ -449,7 +453,8 @@ struct JsonValue {
     return is_updated;
   }
 
-  Status ObjKeys(std::string_view path, 
std::vector<std::optional<std::vector<std::string>>> &keys) const {
+  StatusOr<Optionals<std::vector<std::string>>> ObjKeys(std::string_view path) 
const {
+    Optionals<std::vector<std::string>> keys;
     try {
       jsoncons::jsonpath::json_query(value, path,
                                      [&keys](const std::string & /*path*/, 
const jsoncons::json &basic_json) {
@@ -466,10 +471,12 @@ struct JsonValue {
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-    return Status::OK();
+
+    return keys;
   }
 
-  Status ObjLen(std::string_view path, std::vector<std::optional<uint64_t>> 
&obj_lens) const {
+  StatusOr<Optionals<uint64_t>> ObjLen(std::string_view path) const {
+    Optionals<uint64_t> obj_lens;
     try {
       jsoncons::jsonpath::json_query(value, path,
                                      [&obj_lens](const std::string & /*path*/, 
const jsoncons::json &basic_json) {
@@ -482,11 +489,11 @@ struct JsonValue {
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-    return Status::OK();
+    return obj_lens;
   }
 
-  StatusOr<std::vector<std::optional<JsonValue>>> ArrPop(std::string_view 
path, int64_t index = -1) {
-    std::vector<std::optional<JsonValue>> popped_values;
+  StatusOr<Optionals<JsonValue>> ArrPop(std::string_view path, int64_t index = 
-1) {
+    Optionals<JsonValue> popped_values;
 
     try {
       jsoncons::jsonpath::json_replace(value, path,
@@ -512,7 +519,8 @@ struct JsonValue {
     return popped_values;
   }
 
-  Status ArrTrim(std::string_view path, int64_t start, int64_t stop, 
std::vector<std::optional<uint64_t>> &results) {
+  StatusOr<Optionals<uint64_t>> ArrTrim(std::string_view path, int64_t start, 
int64_t stop) {
+    Optionals<uint64_t> results;
     try {
       jsoncons::jsonpath::json_replace(
           value, path, [&results, start, stop](const std::string & /*path*/, 
jsoncons::json &val) {
@@ -540,8 +548,7 @@ struct JsonValue {
     } catch (const jsoncons::jsonpath::jsonpath_error &e) {
       return {Status::NotOK, e.what()};
     }
-
-    return Status::OK();
+    return results;
   }
 
   StatusOr<size_t> Del(const std::string &path) {
diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc
index f3532b17..8c0f28bf 100644
--- a/src/types/redis_json.cc
+++ b/src/types/redis_json.cc
@@ -165,7 +165,7 @@ rocksdb::Status Json::Get(const std::string &user_key, 
const std::vector<std::st
 }
 
 rocksdb::Status Json::ArrAppend(const std::string &user_key, const std::string 
&path,
-                                const std::vector<std::string> &values, 
std::vector<size_t> *result_count) {
+                                const std::vector<std::string> &values, 
Optionals<size_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
 
   std::vector<jsoncons::json> append_values;
@@ -186,16 +186,17 @@ rocksdb::Status Json::ArrAppend(const std::string 
&user_key, const std::string &
 
   auto append_res = value.ArrAppend(path, append_values);
   if (!append_res) return rocksdb::Status::InvalidArgument(append_res.Msg());
-  *result_count = *append_res;
+  *results = std::move(*append_res);
 
-  bool is_write = std::any_of(result_count->begin(), result_count->end(), 
[](uint64_t c) { return c > 0; });
+  bool is_write =
+      std::any_of(results->begin(), results->end(), [](std::optional<uint64_t> 
c) { return c.has_value(); });
   if (!is_write) return rocksdb::Status::OK();
 
   return write(ns_key, &metadata, value);
 }
 
 rocksdb::Status Json::ArrIndex(const std::string &user_key, const std::string 
&path, const std::string &needle,
-                               ssize_t start, ssize_t end, 
std::vector<ssize_t> *result) {
+                               ssize_t start, ssize_t end, Optionals<ssize_t> 
*results) {
   auto ns_key = AppendNamespacePrefix(user_key);
 
   auto needle_res = JsonValue::FromString(needle, 
storage_->GetConfig()->json_max_nesting_depth);
@@ -209,8 +210,8 @@ rocksdb::Status Json::ArrIndex(const std::string &user_key, 
const std::string &p
 
   auto index_res = value.ArrIndex(path, needle_value.value, start, end);
   if (!index_res) return rocksdb::Status::InvalidArgument(index_res.Msg());
-  *result = *index_res;
 
+  *results = std::move(*index_res);
   return rocksdb::Status::OK();
 }
 
@@ -282,23 +283,22 @@ rocksdb::Status Json::Clear(const std::string &user_key, 
const std::string &path
   return write(ns_key, &metadata, json_val);
 }
 
-rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string 
&path,
-                             std::vector<std::optional<uint64_t>> &arr_lens) {
+rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string 
&path, Optionals<uint64_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
   JsonMetadata metadata;
   JsonValue json_val;
   auto s = read(ns_key, &metadata, &json_val);
   if (!s.ok()) return s;
 
-  auto len_res = json_val.ArrLen(path, arr_lens);
+  auto len_res = json_val.ArrLen(path);
   if (!len_res) return rocksdb::Status::InvalidArgument(len_res.Msg());
 
+  *results = std::move(*len_res);
   return rocksdb::Status::OK();
 }
 
 rocksdb::Status Json::ArrInsert(const std::string &user_key, const std::string 
&path, const int64_t &index,
-                                const std::vector<std::string> &values,
-                                std::vector<std::optional<uint64_t>> 
*result_count) {
+                                const std::vector<std::string> &values, 
Optionals<uint64_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
 
   std::vector<jsoncons::json> insert_values;
@@ -319,17 +319,16 @@ rocksdb::Status Json::ArrInsert(const std::string 
&user_key, const std::string &
 
   auto insert_res = value.ArrInsert(path, index, insert_values);
   if (!insert_res) return rocksdb::Status::InvalidArgument(insert_res.Msg());
-  *result_count = *insert_res;
+  *results = std::move(*insert_res);
 
   bool is_write =
-      std::any_of(result_count->begin(), result_count->end(), 
[](std::optional<uint64_t> c) { return c.has_value(); });
+      std::any_of(results->begin(), results->end(), [](std::optional<uint64_t> 
c) { return c.has_value(); });
   if (!is_write) return rocksdb::Status::OK();
 
   return write(ns_key, &metadata, value);
 }
 
-rocksdb::Status Json::Toggle(const std::string &user_key, const std::string 
&path,
-                             std::vector<std::optional<bool>> &result) {
+rocksdb::Status Json::Toggle(const std::string &user_key, const std::string 
&path, Optionals<bool> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
 
   LockGuard guard(storage_->GetLockManager(), ns_key);
@@ -341,7 +340,7 @@ rocksdb::Status Json::Toggle(const std::string &user_key, 
const std::string &pat
 
   auto toggle_res = origin.Toggle(path);
   if (!toggle_res) return rocksdb::Status::InvalidArgument(toggle_res.Msg());
-  result = *toggle_res;
+  *results = std::move(*toggle_res);
 
   return write(ns_key, &metadata, origin);
 }
@@ -369,20 +368,21 @@ rocksdb::Status Json::ArrPop(const std::string &user_key, 
const std::string &pat
 }
 
 rocksdb::Status Json::ObjKeys(const std::string &user_key, const std::string 
&path,
-                              
std::vector<std::optional<std::vector<std::string>>> &keys) {
+                              Optionals<std::vector<std::string>> *keys) {
   auto ns_key = AppendNamespacePrefix(user_key);
   JsonMetadata metadata;
   JsonValue json_val;
   auto s = read(ns_key, &metadata, &json_val);
   if (!s.ok()) return s;
-  auto keys_res = json_val.ObjKeys(path, keys);
+  auto keys_res = json_val.ObjKeys(path);
   if (!keys_res) return rocksdb::Status::InvalidArgument(keys_res.Msg());
 
+  *keys = std::move(*keys_res);
   return rocksdb::Status::OK();
 }
 
 rocksdb::Status Json::ArrTrim(const std::string &user_key, const std::string 
&path, int64_t start, int64_t stop,
-                              std::vector<std::optional<uint64_t>> &results) {
+                              Optionals<uint64_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
 
   LockGuard guard(storage_->GetLockManager(), ns_key);
@@ -392,10 +392,12 @@ rocksdb::Status Json::ArrTrim(const std::string 
&user_key, const std::string &pa
   auto s = read(ns_key, &metadata, &json_val);
   if (!s.ok()) return s;
 
-  auto len_res = json_val.ArrTrim(path, start, stop, results);
+  auto len_res = json_val.ArrTrim(path, start, stop);
   if (!len_res) return rocksdb::Status::InvalidArgument(len_res.Msg());
+
+  *results = std::move(*len_res);
   bool is_write =
-      std::any_of(results.begin(), results.end(), [](const 
std::optional<uint64_t> &val) { return val.has_value(); });
+      std::any_of(results->begin(), results->end(), [](const 
std::optional<uint64_t> &val) { return val.has_value(); });
   if (!is_write) return rocksdb::Status::OK();
   return write(ns_key, &metadata, json_val);
 }
@@ -464,24 +466,19 @@ rocksdb::Status Json::numop(JsonValue::NumOpEnum op, 
const std::string &user_key
 }
 
 rocksdb::Status Json::StrAppend(const std::string &user_key, const std::string 
&path, const std::string &value,
-                                std::vector<uint64_t> &append_cnt) {
+                                Optionals<uint64_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
   JsonMetadata metadata;
   JsonValue json_val;
   auto s = read(ns_key, &metadata, &json_val);
   if (!s.ok()) return s;
 
-  auto append_res = json_val.StrAppend(path, value, append_cnt);
+  auto append_res = json_val.StrAppend(path, value);
   if (!append_res) return rocksdb::Status::InvalidArgument(append_res.Msg());
-  // need reverse order
-  std::reverse(append_cnt.begin(), append_cnt.end());
+  *results = std::move(*append_res);
 
-  bool need_overwrite = false;
-  for (auto append : append_cnt) {
-    if (append != std::numeric_limits<uint64_t>::max()) {
-      need_overwrite = true;
-    }
-  }
+  bool need_overwrite =
+      std::any_of(results->begin(), results->end(), [](const 
std::optional<uint64_t> &val) { return val.has_value(); });
   if (!need_overwrite) {
     return rocksdb::Status::OK();
   }
@@ -489,29 +486,29 @@ rocksdb::Status Json::StrAppend(const std::string 
&user_key, const std::string &
   return write(ns_key, &metadata, json_val);
 }
 
-rocksdb::Status Json::StrLen(const std::string &user_key, const std::string 
&path, std::vector<uint64_t> &str_lens) {
+rocksdb::Status Json::StrLen(const std::string &user_key, const std::string 
&path, Optionals<uint64_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
   JsonMetadata metadata;
   JsonValue json_val;
   auto s = read(ns_key, &metadata, &json_val);
   if (!s.ok()) return s;
 
-  auto append_res = json_val.StrLen(path, str_lens);
-  if (!append_res) return rocksdb::Status::InvalidArgument(append_res.Msg());
+  auto str_lens = json_val.StrLen(path);
+  if (!str_lens) return rocksdb::Status::InvalidArgument(str_lens.Msg());
+  *results = std::move(*str_lens);
   return rocksdb::Status::OK();
 }
 
-rocksdb::Status Json::ObjLen(const std::string &user_key, const std::string 
&path,
-                             std::vector<std::optional<uint64_t>> &obj_lens) {
+rocksdb::Status Json::ObjLen(const std::string &user_key, const std::string 
&path, Optionals<uint64_t> *results) {
   auto ns_key = AppendNamespacePrefix(user_key);
   JsonMetadata metadata;
   JsonValue json_val;
   auto s = read(ns_key, &metadata, &json_val);
   if (!s.ok()) return s;
 
-  auto len_res = json_val.ObjLen(path, obj_lens);
-  if (!len_res) return rocksdb::Status::InvalidArgument(len_res.Msg());
-
+  auto obj_lens = json_val.ObjLen(path);
+  if (!obj_lens) return rocksdb::Status::InvalidArgument(obj_lens.Msg());
+  *results = std::move(*obj_lens);
   return rocksdb::Status::OK();
 }
 
diff --git a/src/types/redis_json.h b/src/types/redis_json.h
index bf5e21b5..e0e5ec31 100644
--- a/src/types/redis_json.h
+++ b/src/types/redis_json.h
@@ -38,34 +38,31 @@ class Json : public Database {
   rocksdb::Status Info(const std::string &user_key, JsonStorageFormat 
*storage_format);
   rocksdb::Status Type(const std::string &user_key, const std::string &path, 
std::vector<std::string> *results);
   rocksdb::Status ArrAppend(const std::string &user_key, const std::string 
&path,
-                            const std::vector<std::string> &values, 
std::vector<size_t> *result_count);
+                            const std::vector<std::string> &values, 
Optionals<size_t> *results);
   rocksdb::Status Merge(const std::string &user_key, const std::string &path, 
const std::string &value, bool &result);
   rocksdb::Status Clear(const std::string &user_key, const std::string &path, 
size_t *result);
-  rocksdb::Status ArrLen(const std::string &user_key, const std::string &path,
-                         std::vector<std::optional<uint64_t>> &arr_lens);
+  rocksdb::Status ArrLen(const std::string &user_key, const std::string &path, 
Optionals<uint64_t> *results);
   rocksdb::Status ArrInsert(const std::string &user_key, const std::string 
&path, const int64_t &index,
-                            const std::vector<std::string> &values, 
std::vector<std::optional<uint64_t>> *result_count);
-  rocksdb::Status Toggle(const std::string &user_key, const std::string &path,
-                         std::vector<std::optional<bool>> &result);
+                            const std::vector<std::string> &values, 
Optionals<uint64_t> *results);
+  rocksdb::Status Toggle(const std::string &user_key, const std::string &path, 
Optionals<bool> *results);
   rocksdb::Status ObjKeys(const std::string &user_key, const std::string &path,
-                          std::vector<std::optional<std::vector<std::string>>> 
&keys);
+                          Optionals<std::vector<std::string>> *keys);
   rocksdb::Status ArrPop(const std::string &user_key, const std::string &path, 
int64_t index,
                          std::vector<std::optional<JsonValue>> *results);
   rocksdb::Status ArrIndex(const std::string &user_key, const std::string 
&path, const std::string &needle,
-                           ssize_t start, ssize_t end, std::vector<ssize_t> 
*result);
+                           ssize_t start, ssize_t end, Optionals<ssize_t> 
*results);
   rocksdb::Status NumIncrBy(const std::string &user_key, const std::string 
&path, const std::string &value,
                             JsonValue *result);
   rocksdb::Status NumMultBy(const std::string &user_key, const std::string 
&path, const std::string &value,
                             JsonValue *result);
 
   rocksdb::Status ArrTrim(const std::string &user_key, const std::string 
&path, int64_t start, int64_t stop,
-                          std::vector<std::optional<uint64_t>> &results);
+                          Optionals<uint64_t> *results);
   rocksdb::Status Del(const std::string &user_key, const std::string &path, 
size_t *result);
   rocksdb::Status StrAppend(const std::string &user_key, const std::string 
&path, const std::string &value,
-                            std::vector<uint64_t> &append_cnt);
-  rocksdb::Status StrLen(const std::string &user_key, const std::string &path, 
std::vector<uint64_t> &lens);
-  rocksdb::Status ObjLen(const std::string &user_key, const std::string &path,
-                         std::vector<std::optional<uint64_t>> &obj_lens);
+                            Optionals<uint64_t> *results);
+  rocksdb::Status StrLen(const std::string &user_key, const std::string &path, 
Optionals<uint64_t> *results);
+  rocksdb::Status ObjLen(const std::string &user_key, const std::string &path, 
Optionals<uint64_t> *results);
 
  private:
   rocksdb::Status write(Slice ns_key, JsonMetadata *metadata, const JsonValue 
&json_val);
diff --git a/tests/cppunit/types/json_test.cc b/tests/cppunit/types/json_test.cc
index 196bff44..ee76a65c 100644
--- a/tests/cppunit/types/json_test.cc
+++ b/tests/cppunit/types/json_test.cc
@@ -36,7 +36,6 @@ class RedisJsonTest : public TestBase {
 
   std::unique_ptr<redis::Json> json_;
   JsonValue json_val_;
-  std::vector<uint64_t> append_cnt_;
 };
 
 using ::testing::MatchesRegex;
@@ -143,14 +142,14 @@ TEST_F(RedisJsonTest, Print) {
 }
 
 TEST_F(RedisJsonTest, ArrAppend) {
-  std::vector<size_t> res;
+  Optionals<size_t> res;
 
   ASSERT_FALSE(json_->ArrAppend(key_, "$", {"1"}, &res).ok());
 
   ASSERT_TRUE(json_->Set(key_, "$", R"({"x":1,"y":[]})").ok());
   ASSERT_TRUE(json_->ArrAppend(key_, "$.x", {"1"}, &res).ok());
   ASSERT_EQ(res.size(), 1);
-  ASSERT_EQ(res[0], 0);
+  ASSERT_EQ(res[0], std::nullopt);
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), R"({"x":1,"y":[]})");
   res.clear();
@@ -174,7 +173,7 @@ TEST_F(RedisJsonTest, ArrAppend) {
   ASSERT_TRUE(json_->ArrAppend(key_, "$..y", {"1", "2", "3"}, &res).ok());
   ASSERT_EQ(res.size(), 4);
   std::sort(res.begin(), res.end());
-  ASSERT_EQ(res[0], 0);
+  ASSERT_EQ(res[0], std::nullopt);
   ASSERT_EQ(res[1], 3);
   ASSERT_EQ(res[2], 4);
   ASSERT_EQ(res[3], 6);
@@ -269,52 +268,52 @@ TEST_F(RedisJsonTest, ArrLen) {
       json_->Set(key_, "$", 
R"({"a1":[1,2],"a2":[[1,5,7],[8],[9]],"i":1,"d":1.0,"s":"string","o":{"a3":[1,1,1]}})")
           .ok());
   // 1. simple array
-  std::vector<std::optional<uint64_t>> res;
-  ASSERT_TRUE(json_->ArrLen(key_, "$.a1", res).ok());
+  Optionals<uint64_t> res;
+  ASSERT_TRUE(json_->ArrLen(key_, "$.a1", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], 2);
   res.clear();
   // 2. nested array
-  ASSERT_TRUE(json_->ArrLen(key_, "$.a2", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.a2", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], 3);
   res.clear();
-  ASSERT_TRUE(json_->ArrLen(key_, "$.a2[0]", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.a2[0]", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], 3);
   res.clear();
   // 3.non-array type
-  ASSERT_TRUE(json_->ArrLen(key_, "$.i", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.i", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], std::nullopt);
   res.clear();
-  ASSERT_TRUE(json_->ArrLen(key_, "$.d", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.d", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], std::nullopt);
   res.clear();
-  ASSERT_TRUE(json_->ArrLen(key_, "$.s", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.s", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], std::nullopt);
   res.clear();
   // 4. object
-  ASSERT_TRUE(json_->ArrLen(key_, "$.o", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.o", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], std::nullopt);
   res.clear();
-  ASSERT_TRUE(json_->ArrLen(key_, "$.o.a3", res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.o.a3", &res).ok());
   ASSERT_EQ(res.size(), 1);
   ASSERT_EQ(res[0], 3);
   res.clear();
   // 5. key/path is not found
-  ASSERT_FALSE(json_->ArrLen("not_exists", "$.*", res).ok());
-  ASSERT_TRUE(json_->ArrLen(key_, "$.not_exists", res).ok());
+  ASSERT_FALSE(json_->ArrLen("not_exists", "$.*", &res).ok());
+  ASSERT_TRUE(json_->ArrLen(key_, "$.not_exists", &res).ok());
   ASSERT_TRUE(res.empty());
 }
 
 TEST_F(RedisJsonTest, Toggle) {
-  std::vector<std::optional<bool>> res;
+  Optionals<bool> res;
   ASSERT_TRUE(json_->Set(key_, "$", "true").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), "false");
   ASSERT_EQ(res.size(), 1);
@@ -322,7 +321,7 @@ TEST_F(RedisJsonTest, Toggle) {
   res.clear();
 
   ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true})").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$.bool", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$.bool", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false})");
   ASSERT_EQ(res.size(), 1);
@@ -330,7 +329,7 @@ TEST_F(RedisJsonTest, Toggle) {
   res.clear();
 
   ASSERT_TRUE(json_->Set(key_, "$", 
R"({"bool":true,"bools":{"bool":true}})").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$.bool", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$.bool", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), 
R"({"bool":false,"bools":{"bool":true}})");
   ASSERT_EQ(res.size(), 1);
@@ -338,7 +337,7 @@ TEST_F(RedisJsonTest, Toggle) {
   res.clear();
 
   ASSERT_TRUE(json_->Set(key_, "$", 
R"({"bool":true,"bools":{"bool":true}})").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$..bool", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$..bool", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), 
R"({"bool":false,"bools":{"bool":false}})");
   ASSERT_EQ(res.size(), 2);
@@ -346,31 +345,31 @@ TEST_F(RedisJsonTest, Toggle) {
   res.clear();
 
   ASSERT_TRUE(json_->Set(key_, "$", 
R"({"bool":false,"bools":{"bool":true}})").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$..bool", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$..bool", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), 
R"({"bool":true,"bools":{"bool":false}})");
   ASSERT_EQ(res.size(), 2);
-  ASSERT_THAT(res, testing::ElementsAre(false, true));
+  ASSERT_THAT(res, testing::ElementsAre(true, false));
   res.clear();
 
   ASSERT_TRUE(json_->Set(key_, "$", 
R"({"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}})").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$..bool", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$..bool", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), 
R"({"bool":true,"bools":{"bool":false},"incorrectbool":{"bool":88}})");
   ASSERT_EQ(res.size(), 3);
-  ASSERT_THAT(res, testing::ElementsAre(std::nullopt, false, true));
+  ASSERT_THAT(res, testing::ElementsAre(true, false, std::nullopt));
   res.clear();
 
   ASSERT_TRUE(json_->Set(key_, "$", "[true,true,99]").ok());
-  ASSERT_TRUE(json_->Toggle(key_, "$..*", res).ok());
+  ASSERT_TRUE(json_->Toggle(key_, "$..*", &res).ok());
   ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok());
   ASSERT_EQ(json_val_.Dump().GetValue(), "[false,false,99]");
   ASSERT_EQ(res.size(), 3);
-  ASSERT_THAT(res, testing::ElementsAre(std::nullopt, false, false));
+  ASSERT_THAT(res, testing::ElementsAre(false, false, std::nullopt));
 }
 
 TEST_F(RedisJsonTest, ArrPop) {
-  std::vector<std::optional<JsonValue>> res;
+  Optionals<JsonValue> res;
 
   // Array
   ASSERT_TRUE(json_->Set(key_, "$", R"([3,"str",2.1,{},[5,6]])").ok());
@@ -438,7 +437,7 @@ TEST_F(RedisJsonTest, ArrPop) {
 }
 
 TEST_F(RedisJsonTest, ArrIndex) {
-  std::vector<ssize_t> res;
+  Optionals<ssize_t> res;
   int max_end = std::numeric_limits<int>::max();
 
   ASSERT_TRUE(json_->Set(key_, "$", R"({"arr":[0, 1, 2, 3, 2, 1, 0]})").ok());
@@ -661,34 +660,35 @@ TEST_F(RedisJsonTest, NumMultBy) {
 }
 
 TEST_F(RedisJsonTest, StrAppend) {
+  Optionals<uint64_t> results;
   ASSERT_TRUE(json_->Set(key_, "$", R"({"a":"foo", "nested": {"a": "hello"}, 
"nested2": {"a": 31}})").ok());
-  ASSERT_TRUE(json_->StrAppend(key_, "$.a", "\"be\"", append_cnt_).ok());
-  ASSERT_EQ(append_cnt_.size(), 1);
-  ASSERT_EQ(append_cnt_[0], 5);
+  ASSERT_TRUE(json_->StrAppend(key_, "$.a", "\"be\"", &results).ok());
+  ASSERT_EQ(results.size(), 1);
+  ASSERT_EQ(results[0], 5);
 
-  append_cnt_.clear();
+  results.clear();
   ASSERT_TRUE(json_->Set(key_, "$", R"({"a":"foo", "nested": {"a": "hello"}, 
"nested2": {"a": 31}})").ok());
-  ASSERT_TRUE(json_->StrAppend(key_, "$..a", "\"be\"", append_cnt_).ok());
-  ASSERT_EQ(append_cnt_.size(), 3);
-  std::vector<int64_t> result1 = {5, 7, -1};
+  ASSERT_TRUE(json_->StrAppend(key_, "$..a", "\"be\"", &results).ok());
+  ASSERT_EQ(results.size(), 3);
+  Optionals<int64_t> result1 = {5, 7, std::nullopt};
   for (int i = 0; i < 3; ++i) {
-    ASSERT_EQ(append_cnt_[i], result1[i]);
+    ASSERT_EQ(results[i], result1[i]);
   }
 }
 
 TEST_F(RedisJsonTest, StrLen) {
-  append_cnt_.clear();
+  Optionals<uint64_t> results;
   ASSERT_TRUE(json_->Set(key_, "$", R"({"a":"foo", "nested": {"a": "hello"}, 
"nested2": {"a": 31}})").ok());
-  ASSERT_TRUE(json_->StrLen(key_, "$.a", append_cnt_).ok());
-  ASSERT_EQ(append_cnt_.size(), 1);
-  ASSERT_EQ(append_cnt_[0], 3);
+  ASSERT_TRUE(json_->StrLen(key_, "$.a", &results).ok());
+  ASSERT_EQ(results.size(), 1);
+  ASSERT_EQ(results[0], 3);
 
-  append_cnt_.clear();
+  results.clear();
   ASSERT_TRUE(json_->Set(key_, "$", R"({"a":"foo", "nested": {"a": "hello"}, 
"nested2": {"a": 31}})").ok());
-  ASSERT_TRUE(json_->StrLen(key_, "$..a", append_cnt_).ok());
-  ASSERT_EQ(append_cnt_.size(), 3);
-  std::vector<int64_t> result1 = {3, 5, -1};
+  ASSERT_TRUE(json_->StrLen(key_, "$..a", &results).ok());
+  ASSERT_EQ(results.size(), 3);
+  Optionals<int64_t> result1 = {3, 5, std::nullopt};
   for (int i = 0; i < 3; ++i) {
-    ASSERT_EQ(append_cnt_[i], result1[i]);
+    ASSERT_EQ(results[i], result1[i]);
   }
 }


Reply via email to