From b1f95e363157cf5ba3931267791fade22e381d63 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Fri, 3 Nov 2023 04:38:08 +0000 Subject: [PATCH 01/13] support for the JSON.TOGGLE command --- src/commands/cmd_json.cc | 19 ++ src/types/json.h | 19 ++ src/types/redis_json.cc | 16 + src/types/redis_json.h | 1 + tests/cppunit/disk_test.cc | 494 +++++++++++++++---------------- tests/cppunit/types/json_test.cc | 39 +++ 6 files changed, 341 insertions(+), 247 deletions(-) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 6c2d40cd53f..2bb69396cd5 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -171,6 +171,24 @@ class CommandJsonClear : public Commander { } }; +class CommandJsonToggle : public Commander { + public: + Status Execute(Server *svr, Connection *conn, std::string *output) override { + redis::Json json(svr->storage, conn->GetNamespace()); + + std::string path = (args_.size() > 2) ? args_[2] : "$"; + auto s = json.Toggle(args_[1], path); + + if (s.IsNotFound()) { + return Status::OK(); + } + + if (!s.ok()) return {Status::RedisExecErr, s.ToString()}; + + return Status::OK(); + } +}; + class CommandJsonArrLen : public Commander { public: Status Execute(Server *svr, Connection *conn, std::string *output) override { @@ -209,6 +227,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr("json.set", 4, "write", 1, 1 MakeCmdAttr("json.type", -2, "read-only", 1, 1, 1), MakeCmdAttr("json.arrappend", -4, "write", 1, 1, 1), MakeCmdAttr("json.clear", -2, "write", 1, 1, 1), + MakeCmdAttr("json.toggle", -2, "write", 1, 1, 1), MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), ); } // namespace redis diff --git a/src/types/json.h b/src/types/json.h index cea9bcc9810..d340284d494 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -173,6 +173,25 @@ struct JsonValue { return types; } + Status Toggle(std::string_view path) { + try { + jsoncons::jsonpath::json_replace(value, path, [&](const std::string & /*path*/, jsoncons::json &val) { + bool is_boolean = val.is_bool() && !val.empty(); + // bool is_array = val.is_array() && !val.empty(); + + // if (is_array) + // val = jsoncons::json::array(); + if (is_boolean) + val = !val.as_bool(); + else + return; + }); + } catch (const jsoncons::jsonpath::jsonpath_error &e) { + return {Status::NotOK, e.what()}; + } + return Status::OK(); + } + StatusOr Clear(std::string_view path) { size_t count = 0; try { diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc index 6096003b64b..bb837314022 100644 --- a/src/types/redis_json.cc +++ b/src/types/redis_json.cc @@ -201,4 +201,20 @@ rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string &pat return rocksdb::Status::OK(); } + +rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &path) { + auto ns_key = AppendNamespacePrefix(user_key); + + LockGuard guard(storage_->GetLockManager(), ns_key); + + JsonMetadata metadata; + JsonValue origin; + auto s = read(ns_key, &metadata, &origin); + if (!s.ok()) return s; + + auto toggle_res = origin.Toggle(path); + if (!toggle_res) return rocksdb::Status::InvalidArgument(toggle_res.Msg()); + + return write(ns_key, &metadata, origin); +} } // namespace redis diff --git a/src/types/redis_json.h b/src/types/redis_json.h index 8eeb2571144..feb80edef69 100644 --- a/src/types/redis_json.h +++ b/src/types/redis_json.h @@ -41,6 +41,7 @@ class Json : public Database { 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> &arr_lens); + rocksdb::Status Toggle(const std::string &user_key, const std::string &path); private: rocksdb::Status write(Slice ns_key, JsonMetadata *metadata, const JsonValue &json_val); diff --git a/tests/cppunit/disk_test.cc b/tests/cppunit/disk_test.cc index 124e1f816b1..6ade3b5cffe 100644 --- a/tests/cppunit/disk_test.cc +++ b/tests/cppunit/disk_test.cc @@ -1,247 +1,247 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include - -#include -#include -#include -#include - -#include "stats/disk_stats.h" -#include "storage/redis_metadata.h" -#include "test_base.h" -#include "types/redis_bitmap.h" -#include "types/redis_list.h" -#include "types/redis_set.h" -#include "types/redis_sortedint.h" -#include "types/redis_stream.h" -#include "types/redis_string.h" -#include "types/redis_zset.h" - -class RedisDiskTest : public TestBase { - protected: - explicit RedisDiskTest() = default; - ~RedisDiskTest() override = default; - - double estimation_factor_ = 0.1; -}; - -TEST_F(RedisDiskTest, StringDisk) { - key_ = "stringdisk_key"; - std::unique_ptr string = std::make_unique(storage_, "disk_ns_string"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_string"); - std::vector value_size{1024 * 1024}; - EXPECT_TRUE(string->Set(key_, std::string(value_size[0], 'a')).ok()); - uint64_t result = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisString, &result).ok()); - EXPECT_GE(result, value_size[0] * estimation_factor_); - EXPECT_LE(result, value_size[0] / estimation_factor_); - auto s = string->Del(key_); -} - -TEST_F(RedisDiskTest, HashDisk) { - std::unique_ptr hash = std::make_unique(storage_, "disk_ns_hash"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_hash"); - key_ = "hashdisk_key"; - fields_ = {"hashdisk_kkey1", "hashdisk_kkey2", "hashdisk_kkey3", "hashdisk_kkey4", "hashdisk_kkey5"}; - values_.resize(5); - uint64_t approximate_size = 0; - uint64_t ret = 0; - std::vector value_size{1024, 1024, 1024, 1024, 1024}; - std::vector values(value_size.size()); - for (int i = 0; i < int(fields_.size()); i++) { - values[i] = std::string(value_size[i], static_cast('a' + i)); - values_[i] = values[i]; - approximate_size += key_.size() + 8 + fields_[i].size() + values_[i].size(); - rocksdb::Status s = hash->Set(key_, fields_[i], values_[i], &ret); - EXPECT_TRUE(s.ok() && ret == 1); - } - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisHash, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - auto s = hash->Del(key_); -} - -TEST_F(RedisDiskTest, SetDisk) { - std::unique_ptr set = std::make_unique(storage_, "disk_ns_set"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_set"); - key_ = "setdisk_key"; - values_.resize(5); - uint64_t approximate_size = 0; - uint64_t ret = 0; - std::vector value_size{1024, 1024, 1024, 1024, 1024}; - std::vector values(value_size.size()); - for (int i = 0; i < int(values_.size()); i++) { - values[i] = std::string(value_size[i], static_cast(i + 'a')); - values_[i] = values[i]; - approximate_size += key_.size() + values_[i].size() + 8; - } - rocksdb::Status s = set->Add(key_, values_, &ret); - EXPECT_TRUE(s.ok() && ret == 5); - - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisSet, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - - s = set->Del(key_); -} - -TEST_F(RedisDiskTest, ListDisk) { - std::unique_ptr list = std::make_unique(storage_, "disk_ns_list"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_list"); - key_ = "listdisk_key"; - values_.resize(5); - std::vector value_size{1024, 1024, 1024, 1024, 1024}; - std::vector values(value_size.size()); - uint64_t approximate_size = 0; - for (int i = 0; i < int(values_.size()); i++) { - values[i] = std::string(value_size[i], static_cast('a' + i)); - values_[i] = values[i]; - approximate_size += key_.size() + values_[i].size() + 8 + 8; - } - uint64_t ret = 0; - rocksdb::Status s = list->Push(key_, values_, false, &ret); - EXPECT_TRUE(s.ok() && ret == 5); - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisList, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - s = list->Del(key_); -} - -TEST_F(RedisDiskTest, ZsetDisk) { - std::unique_ptr zset = std::make_unique(storage_, "disk_ns_zet"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_zet"); - key_ = "zsetdisk_key"; - uint64_t ret = 0; - uint64_t approximate_size = 0; - std::vector mscores(5); - std::vector value_size{1024, 1024, 1024, 1024, 1024}; - for (int i = 0; i < int(value_size.size()); i++) { - mscores[i].member = std::string(value_size[i], static_cast('a' + i)); - mscores[i].score = 1.0; - approximate_size += (key_.size() + 8 + mscores[i].member.size() + 8) * 2; - } - rocksdb::Status s = zset->Add(key_, ZAddFlags::Default(), &mscores, &ret); - EXPECT_TRUE(s.ok() && ret == 5); - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisZSet, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - s = zset->Del(key_); -} - -TEST_F(RedisDiskTest, BitmapDisk) { - std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap"); - key_ = "bitmapdisk_key"; - bool bit = false; - uint64_t approximate_size = 0; - for (int i = 0; i < 1024 * 8 * 100000; i += 1024 * 8) { - EXPECT_TRUE(bitmap->SetBit(key_, i, true, &bit).ok()); - approximate_size += key_.size() + 8 + std::to_string(i / 1024 / 8).size(); - } - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisBitmap, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - auto s = bitmap->Del(key_); -} - -TEST_F(RedisDiskTest, BitmapDisk2) { - const int64_t kGroupSize = 8192; - for (size_t num_bits : {8192, 16384}) { - for (bool set_op : {false, true}) { - std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap2"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap2"); - key_ = "bitmapdisk_key2"; - auto s = bitmap->Del(key_); - bool bit = false; - - for (size_t i = 0; i < num_bits; i += kGroupSize) { - // Set all first bit of group to `!set_op` - EXPECT_TRUE(bitmap->SetBit(key_, i, !set_op, &bit).ok()); - // Set all last bit of group to `set_op`. - EXPECT_TRUE(bitmap->SetBit(key_, i + kGroupSize - 1, set_op, &bit).ok()); - } - - auto bit_not_dest_key = "bit_op_not_dest_key"; - - int64_t len = 0; - EXPECT_TRUE(bitmap->BitOp(BitOpFlags::kBitOpNot, "NOT", bit_not_dest_key, {key_}, &len).ok()); - - for (size_t i = 0; i < num_bits; i += kGroupSize) { - bool result = false; - // Check all first bit of group is `set_op` - EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i, &result).ok()); - EXPECT_EQ(set_op, result); - // Check all last bit of group is `!set_op` - EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i + kGroupSize - 1, &result).ok()); - EXPECT_EQ(!set_op, result); - // Check bit in group between (first, last) is "1". - for (size_t j = i + 1; j < i + kGroupSize - 1; ++j) { - EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, j, &result).ok()); - EXPECT_TRUE(result) << j << " is not true"; - } - } - } - } -} - -TEST_F(RedisDiskTest, SortedintDisk) { - std::unique_ptr sortedint = std::make_unique(storage_, "disk_ns_sortedint"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_sortedint"); - key_ = "sortedintdisk_key"; - uint64_t ret = 0; - uint64_t approximate_size = 0; - for (int i = 0; i < 100000; i++) { - EXPECT_TRUE(sortedint->Add(key_, std::vector{uint64_t(i)}, &ret).ok() && ret == 1); - approximate_size += key_.size() + 8 + 8; - } - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisSortedint, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - auto s = sortedint->Del(key_); -} - -TEST_F(RedisDiskTest, StreamDisk) { - std::unique_ptr stream = std::make_unique(storage_, "disk_ns_stream"); - std::unique_ptr disk = std::make_unique(storage_, "disk_ns_stream"); - key_ = "streamdisk_key"; - redis::StreamAddOptions options; - options.next_id_strategy = *redis::ParseNextStreamEntryIDStrategy("*"); - redis::StreamEntryID id; - uint64_t approximate_size = 0; - for (int i = 0; i < 100000; i++) { - std::vector values = {"key" + std::to_string(i), "val" + std::to_string(i)}; - auto s = stream->Add(key_, options, values, &id); - EXPECT_TRUE(s.ok()); - approximate_size += key_.size() + 8 + 8 + values[0].size() + values[1].size(); - } - uint64_t key_size = 0; - EXPECT_TRUE(disk->GetKeySize(key_, kRedisStream, &key_size).ok()); - EXPECT_GE(key_size, approximate_size * estimation_factor_); - EXPECT_LE(key_size, approximate_size / estimation_factor_); - auto s = stream->Del(key_); -} +// /* +// * Licensed to the Apache Software Foundation (ASF) under one +// * or more contributor license agreements. See the NOTICE file +// * distributed with this work for additional information +// * regarding copyright ownership. The ASF licenses this file +// * to you under the Apache License, Version 2.0 (the +// * "License"); you may not use this file except in compliance +// * with the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, +// * software distributed under the License is distributed on an +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// * KIND, either express or implied. See the License for the +// * specific language governing permissions and limitations +// * under the License. +// * +// */ + +// #include + +// #include +// #include +// #include +// #include + +// #include "stats/disk_stats.h" +// #include "storage/redis_metadata.h" +// #include "test_base.h" +// #include "types/redis_bitmap.h" +// #include "types/redis_list.h" +// #include "types/redis_set.h" +// #include "types/redis_sortedint.h" +// #include "types/redis_stream.h" +// #include "types/redis_string.h" +// #include "types/redis_zset.h" + +// class RedisDiskTest : public TestBase { +// protected: +// explicit RedisDiskTest() = default; +// ~RedisDiskTest() override = default; + +// double estimation_factor_ = 0.1; +// }; + +// TEST_F(RedisDiskTest, StringDisk) { +// key_ = "stringdisk_key"; +// std::unique_ptr string = std::make_unique(storage_, "disk_ns_string"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_string"); +// std::vector value_size{1024 * 1024}; +// EXPECT_TRUE(string->Set(key_, std::string(value_size[0], 'a')).ok()); +// uint64_t result = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisString, &result).ok()); +// EXPECT_GE(result, value_size[0] * estimation_factor_); +// EXPECT_LE(result, value_size[0] / estimation_factor_); +// auto s = string->Del(key_); +// } + +// TEST_F(RedisDiskTest, HashDisk) { +// std::unique_ptr hash = std::make_unique(storage_, "disk_ns_hash"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_hash"); +// key_ = "hashdisk_key"; +// fields_ = {"hashdisk_kkey1", "hashdisk_kkey2", "hashdisk_kkey3", "hashdisk_kkey4", "hashdisk_kkey5"}; +// values_.resize(5); +// uint64_t approximate_size = 0; +// uint64_t ret = 0; +// std::vector value_size{1024, 1024, 1024, 1024, 1024}; +// std::vector values(value_size.size()); +// for (int i = 0; i < int(fields_.size()); i++) { +// values[i] = std::string(value_size[i], static_cast('a' + i)); +// values_[i] = values[i]; +// approximate_size += key_.size() + 8 + fields_[i].size() + values_[i].size(); +// rocksdb::Status s = hash->Set(key_, fields_[i], values_[i], &ret); +// EXPECT_TRUE(s.ok() && ret == 1); +// } +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisHash, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); +// auto s = hash->Del(key_); +// } + +// TEST_F(RedisDiskTest, SetDisk) { +// std::unique_ptr set = std::make_unique(storage_, "disk_ns_set"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_set"); +// key_ = "setdisk_key"; +// values_.resize(5); +// uint64_t approximate_size = 0; +// uint64_t ret = 0; +// std::vector value_size{1024, 1024, 1024, 1024, 1024}; +// std::vector values(value_size.size()); +// for (int i = 0; i < int(values_.size()); i++) { +// values[i] = std::string(value_size[i], static_cast(i + 'a')); +// values_[i] = values[i]; +// approximate_size += key_.size() + values_[i].size() + 8; +// } +// rocksdb::Status s = set->Add(key_, values_, &ret); +// EXPECT_TRUE(s.ok() && ret == 5); + +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisSet, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); + +// s = set->Del(key_); +// } + +// TEST_F(RedisDiskTest, ListDisk) { +// std::unique_ptr list = std::make_unique(storage_, "disk_ns_list"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_list"); +// key_ = "listdisk_key"; +// values_.resize(5); +// std::vector value_size{1024, 1024, 1024, 1024, 1024}; +// std::vector values(value_size.size()); +// uint64_t approximate_size = 0; +// for (int i = 0; i < int(values_.size()); i++) { +// values[i] = std::string(value_size[i], static_cast('a' + i)); +// values_[i] = values[i]; +// approximate_size += key_.size() + values_[i].size() + 8 + 8; +// } +// uint64_t ret = 0; +// rocksdb::Status s = list->Push(key_, values_, false, &ret); +// EXPECT_TRUE(s.ok() && ret == 5); +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisList, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); +// s = list->Del(key_); +// } + +// TEST_F(RedisDiskTest, ZsetDisk) { +// std::unique_ptr zset = std::make_unique(storage_, "disk_ns_zet"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_zet"); +// key_ = "zsetdisk_key"; +// uint64_t ret = 0; +// uint64_t approximate_size = 0; +// std::vector mscores(5); +// std::vector value_size{1024, 1024, 1024, 1024, 1024}; +// for (int i = 0; i < int(value_size.size()); i++) { +// mscores[i].member = std::string(value_size[i], static_cast('a' + i)); +// mscores[i].score = 1.0; +// approximate_size += (key_.size() + 8 + mscores[i].member.size() + 8) * 2; +// } +// rocksdb::Status s = zset->Add(key_, ZAddFlags::Default(), &mscores, &ret); +// EXPECT_TRUE(s.ok() && ret == 5); +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisZSet, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); +// s = zset->Del(key_); +// } + +// TEST_F(RedisDiskTest, BitmapDisk) { +// std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap"); +// key_ = "bitmapdisk_key"; +// bool bit = false; +// uint64_t approximate_size = 0; +// for (int i = 0; i < 1024 * 8 * 100000; i += 1024 * 8) { +// EXPECT_TRUE(bitmap->SetBit(key_, i, true, &bit).ok()); +// approximate_size += key_.size() + 8 + std::to_string(i / 1024 / 8).size(); +// } +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisBitmap, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); +// auto s = bitmap->Del(key_); +// } + +// TEST_F(RedisDiskTest, BitmapDisk2) { +// const int64_t kGroupSize = 8192; +// for (size_t num_bits : {8192, 16384}) { +// for (bool set_op : {false, true}) { +// std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap2"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap2"); +// key_ = "bitmapdisk_key2"; +// auto s = bitmap->Del(key_); +// bool bit = false; + +// for (size_t i = 0; i < num_bits; i += kGroupSize) { +// // Set all first bit of group to `!set_op` +// EXPECT_TRUE(bitmap->SetBit(key_, i, !set_op, &bit).ok()); +// // Set all last bit of group to `set_op`. +// EXPECT_TRUE(bitmap->SetBit(key_, i + kGroupSize - 1, set_op, &bit).ok()); +// } + +// auto bit_not_dest_key = "bit_op_not_dest_key"; + +// int64_t len = 0; +// EXPECT_TRUE(bitmap->BitOp(BitOpFlags::kBitOpNot, "NOT", bit_not_dest_key, {key_}, &len).ok()); + +// for (size_t i = 0; i < num_bits; i += kGroupSize) { +// bool result = false; +// // Check all first bit of group is `set_op` +// EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i, &result).ok()); +// EXPECT_EQ(set_op, result); +// // Check all last bit of group is `!set_op` +// EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i + kGroupSize - 1, &result).ok()); +// EXPECT_EQ(!set_op, result); +// // Check bit in group between (first, last) is "1". +// for (size_t j = i + 1; j < i + kGroupSize - 1; ++j) { +// EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, j, &result).ok()); +// EXPECT_TRUE(result) << j << " is not true"; +// } +// } +// } +// } +// } + +// TEST_F(RedisDiskTest, SortedintDisk) { +// std::unique_ptr sortedint = std::make_unique(storage_, "disk_ns_sortedint"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_sortedint"); +// key_ = "sortedintdisk_key"; +// uint64_t ret = 0; +// uint64_t approximate_size = 0; +// for (int i = 0; i < 100000; i++) { +// EXPECT_TRUE(sortedint->Add(key_, std::vector{uint64_t(i)}, &ret).ok() && ret == 1); +// approximate_size += key_.size() + 8 + 8; +// } +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisSortedint, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); +// auto s = sortedint->Del(key_); +// } + +// TEST_F(RedisDiskTest, StreamDisk) { +// std::unique_ptr stream = std::make_unique(storage_, "disk_ns_stream"); +// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_stream"); +// key_ = "streamdisk_key"; +// redis::StreamAddOptions options; +// options.next_id_strategy = *redis::ParseNextStreamEntryIDStrategy("*"); +// redis::StreamEntryID id; +// uint64_t approximate_size = 0; +// for (int i = 0; i < 100000; i++) { +// std::vector values = {"key" + std::to_string(i), "val" + std::to_string(i)}; +// auto s = stream->Add(key_, options, values, &id); +// EXPECT_TRUE(s.ok()); +// approximate_size += key_.size() + 8 + 8 + values[0].size() + values[1].size(); +// } +// uint64_t key_size = 0; +// EXPECT_TRUE(disk->GetKeySize(key_, kRedisStream, &key_size).ok()); +// EXPECT_GE(key_size, approximate_size * estimation_factor_); +// EXPECT_LE(key_size, approximate_size / estimation_factor_); +// auto s = stream->Del(key_); +// } diff --git a/tests/cppunit/types/json_test.cc b/tests/cppunit/types/json_test.cc index efd646b980e..e0270791776 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -272,3 +272,42 @@ TEST_F(RedisJsonTest, ArrLen) { ASSERT_TRUE(json_->ArrLen(key_, "$.not_exists", res).ok()); ASSERT_TRUE(res.empty()); } + +TEST_F(RedisJsonTest, Toggle) { + + ASSERT_TRUE(json_->Set(key_, "$", "true").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$", &json_val_).ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), "false"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$.bool", &json_val_).ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$.bool", &json_val_).ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false,"bools":{"bool":true}})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool", &json_val_).ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false,"bools":{"bool":false}})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool", &json_val_).ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":true,"bools":{"bool":false}})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool", &json_val_).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_TRUE(json_->Set(key_, "$", "[true,99,false]").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..*", &json_val_).ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), "[false,99,true]"); + +} \ No newline at end of file From 20a043bffad3af195872a7be1b81d86ed8d18418 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Fri, 3 Nov 2023 04:40:16 +0000 Subject: [PATCH 02/13] uncomment disk_test.cc --- tests/cppunit/disk_test.cc | 494 ++++++++++++++++++------------------- 1 file changed, 247 insertions(+), 247 deletions(-) diff --git a/tests/cppunit/disk_test.cc b/tests/cppunit/disk_test.cc index 6ade3b5cffe..124e1f816b1 100644 --- a/tests/cppunit/disk_test.cc +++ b/tests/cppunit/disk_test.cc @@ -1,247 +1,247 @@ -// /* -// * Licensed to the Apache Software Foundation (ASF) under one -// * or more contributor license agreements. See the NOTICE file -// * distributed with this work for additional information -// * regarding copyright ownership. The ASF licenses this file -// * to you under the Apache License, Version 2.0 (the -// * "License"); you may not use this file except in compliance -// * with the License. You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, -// * software distributed under the License is distributed on an -// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// * KIND, either express or implied. See the License for the -// * specific language governing permissions and limitations -// * under the License. -// * -// */ - -// #include - -// #include -// #include -// #include -// #include - -// #include "stats/disk_stats.h" -// #include "storage/redis_metadata.h" -// #include "test_base.h" -// #include "types/redis_bitmap.h" -// #include "types/redis_list.h" -// #include "types/redis_set.h" -// #include "types/redis_sortedint.h" -// #include "types/redis_stream.h" -// #include "types/redis_string.h" -// #include "types/redis_zset.h" - -// class RedisDiskTest : public TestBase { -// protected: -// explicit RedisDiskTest() = default; -// ~RedisDiskTest() override = default; - -// double estimation_factor_ = 0.1; -// }; - -// TEST_F(RedisDiskTest, StringDisk) { -// key_ = "stringdisk_key"; -// std::unique_ptr string = std::make_unique(storage_, "disk_ns_string"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_string"); -// std::vector value_size{1024 * 1024}; -// EXPECT_TRUE(string->Set(key_, std::string(value_size[0], 'a')).ok()); -// uint64_t result = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisString, &result).ok()); -// EXPECT_GE(result, value_size[0] * estimation_factor_); -// EXPECT_LE(result, value_size[0] / estimation_factor_); -// auto s = string->Del(key_); -// } - -// TEST_F(RedisDiskTest, HashDisk) { -// std::unique_ptr hash = std::make_unique(storage_, "disk_ns_hash"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_hash"); -// key_ = "hashdisk_key"; -// fields_ = {"hashdisk_kkey1", "hashdisk_kkey2", "hashdisk_kkey3", "hashdisk_kkey4", "hashdisk_kkey5"}; -// values_.resize(5); -// uint64_t approximate_size = 0; -// uint64_t ret = 0; -// std::vector value_size{1024, 1024, 1024, 1024, 1024}; -// std::vector values(value_size.size()); -// for (int i = 0; i < int(fields_.size()); i++) { -// values[i] = std::string(value_size[i], static_cast('a' + i)); -// values_[i] = values[i]; -// approximate_size += key_.size() + 8 + fields_[i].size() + values_[i].size(); -// rocksdb::Status s = hash->Set(key_, fields_[i], values_[i], &ret); -// EXPECT_TRUE(s.ok() && ret == 1); -// } -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisHash, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); -// auto s = hash->Del(key_); -// } - -// TEST_F(RedisDiskTest, SetDisk) { -// std::unique_ptr set = std::make_unique(storage_, "disk_ns_set"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_set"); -// key_ = "setdisk_key"; -// values_.resize(5); -// uint64_t approximate_size = 0; -// uint64_t ret = 0; -// std::vector value_size{1024, 1024, 1024, 1024, 1024}; -// std::vector values(value_size.size()); -// for (int i = 0; i < int(values_.size()); i++) { -// values[i] = std::string(value_size[i], static_cast(i + 'a')); -// values_[i] = values[i]; -// approximate_size += key_.size() + values_[i].size() + 8; -// } -// rocksdb::Status s = set->Add(key_, values_, &ret); -// EXPECT_TRUE(s.ok() && ret == 5); - -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisSet, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); - -// s = set->Del(key_); -// } - -// TEST_F(RedisDiskTest, ListDisk) { -// std::unique_ptr list = std::make_unique(storage_, "disk_ns_list"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_list"); -// key_ = "listdisk_key"; -// values_.resize(5); -// std::vector value_size{1024, 1024, 1024, 1024, 1024}; -// std::vector values(value_size.size()); -// uint64_t approximate_size = 0; -// for (int i = 0; i < int(values_.size()); i++) { -// values[i] = std::string(value_size[i], static_cast('a' + i)); -// values_[i] = values[i]; -// approximate_size += key_.size() + values_[i].size() + 8 + 8; -// } -// uint64_t ret = 0; -// rocksdb::Status s = list->Push(key_, values_, false, &ret); -// EXPECT_TRUE(s.ok() && ret == 5); -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisList, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); -// s = list->Del(key_); -// } - -// TEST_F(RedisDiskTest, ZsetDisk) { -// std::unique_ptr zset = std::make_unique(storage_, "disk_ns_zet"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_zet"); -// key_ = "zsetdisk_key"; -// uint64_t ret = 0; -// uint64_t approximate_size = 0; -// std::vector mscores(5); -// std::vector value_size{1024, 1024, 1024, 1024, 1024}; -// for (int i = 0; i < int(value_size.size()); i++) { -// mscores[i].member = std::string(value_size[i], static_cast('a' + i)); -// mscores[i].score = 1.0; -// approximate_size += (key_.size() + 8 + mscores[i].member.size() + 8) * 2; -// } -// rocksdb::Status s = zset->Add(key_, ZAddFlags::Default(), &mscores, &ret); -// EXPECT_TRUE(s.ok() && ret == 5); -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisZSet, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); -// s = zset->Del(key_); -// } - -// TEST_F(RedisDiskTest, BitmapDisk) { -// std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap"); -// key_ = "bitmapdisk_key"; -// bool bit = false; -// uint64_t approximate_size = 0; -// for (int i = 0; i < 1024 * 8 * 100000; i += 1024 * 8) { -// EXPECT_TRUE(bitmap->SetBit(key_, i, true, &bit).ok()); -// approximate_size += key_.size() + 8 + std::to_string(i / 1024 / 8).size(); -// } -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisBitmap, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); -// auto s = bitmap->Del(key_); -// } - -// TEST_F(RedisDiskTest, BitmapDisk2) { -// const int64_t kGroupSize = 8192; -// for (size_t num_bits : {8192, 16384}) { -// for (bool set_op : {false, true}) { -// std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap2"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap2"); -// key_ = "bitmapdisk_key2"; -// auto s = bitmap->Del(key_); -// bool bit = false; - -// for (size_t i = 0; i < num_bits; i += kGroupSize) { -// // Set all first bit of group to `!set_op` -// EXPECT_TRUE(bitmap->SetBit(key_, i, !set_op, &bit).ok()); -// // Set all last bit of group to `set_op`. -// EXPECT_TRUE(bitmap->SetBit(key_, i + kGroupSize - 1, set_op, &bit).ok()); -// } - -// auto bit_not_dest_key = "bit_op_not_dest_key"; - -// int64_t len = 0; -// EXPECT_TRUE(bitmap->BitOp(BitOpFlags::kBitOpNot, "NOT", bit_not_dest_key, {key_}, &len).ok()); - -// for (size_t i = 0; i < num_bits; i += kGroupSize) { -// bool result = false; -// // Check all first bit of group is `set_op` -// EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i, &result).ok()); -// EXPECT_EQ(set_op, result); -// // Check all last bit of group is `!set_op` -// EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i + kGroupSize - 1, &result).ok()); -// EXPECT_EQ(!set_op, result); -// // Check bit in group between (first, last) is "1". -// for (size_t j = i + 1; j < i + kGroupSize - 1; ++j) { -// EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, j, &result).ok()); -// EXPECT_TRUE(result) << j << " is not true"; -// } -// } -// } -// } -// } - -// TEST_F(RedisDiskTest, SortedintDisk) { -// std::unique_ptr sortedint = std::make_unique(storage_, "disk_ns_sortedint"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_sortedint"); -// key_ = "sortedintdisk_key"; -// uint64_t ret = 0; -// uint64_t approximate_size = 0; -// for (int i = 0; i < 100000; i++) { -// EXPECT_TRUE(sortedint->Add(key_, std::vector{uint64_t(i)}, &ret).ok() && ret == 1); -// approximate_size += key_.size() + 8 + 8; -// } -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisSortedint, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); -// auto s = sortedint->Del(key_); -// } - -// TEST_F(RedisDiskTest, StreamDisk) { -// std::unique_ptr stream = std::make_unique(storage_, "disk_ns_stream"); -// std::unique_ptr disk = std::make_unique(storage_, "disk_ns_stream"); -// key_ = "streamdisk_key"; -// redis::StreamAddOptions options; -// options.next_id_strategy = *redis::ParseNextStreamEntryIDStrategy("*"); -// redis::StreamEntryID id; -// uint64_t approximate_size = 0; -// for (int i = 0; i < 100000; i++) { -// std::vector values = {"key" + std::to_string(i), "val" + std::to_string(i)}; -// auto s = stream->Add(key_, options, values, &id); -// EXPECT_TRUE(s.ok()); -// approximate_size += key_.size() + 8 + 8 + values[0].size() + values[1].size(); -// } -// uint64_t key_size = 0; -// EXPECT_TRUE(disk->GetKeySize(key_, kRedisStream, &key_size).ok()); -// EXPECT_GE(key_size, approximate_size * estimation_factor_); -// EXPECT_LE(key_size, approximate_size / estimation_factor_); -// auto s = stream->Del(key_); -// } +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include + +#include +#include +#include +#include + +#include "stats/disk_stats.h" +#include "storage/redis_metadata.h" +#include "test_base.h" +#include "types/redis_bitmap.h" +#include "types/redis_list.h" +#include "types/redis_set.h" +#include "types/redis_sortedint.h" +#include "types/redis_stream.h" +#include "types/redis_string.h" +#include "types/redis_zset.h" + +class RedisDiskTest : public TestBase { + protected: + explicit RedisDiskTest() = default; + ~RedisDiskTest() override = default; + + double estimation_factor_ = 0.1; +}; + +TEST_F(RedisDiskTest, StringDisk) { + key_ = "stringdisk_key"; + std::unique_ptr string = std::make_unique(storage_, "disk_ns_string"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_string"); + std::vector value_size{1024 * 1024}; + EXPECT_TRUE(string->Set(key_, std::string(value_size[0], 'a')).ok()); + uint64_t result = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisString, &result).ok()); + EXPECT_GE(result, value_size[0] * estimation_factor_); + EXPECT_LE(result, value_size[0] / estimation_factor_); + auto s = string->Del(key_); +} + +TEST_F(RedisDiskTest, HashDisk) { + std::unique_ptr hash = std::make_unique(storage_, "disk_ns_hash"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_hash"); + key_ = "hashdisk_key"; + fields_ = {"hashdisk_kkey1", "hashdisk_kkey2", "hashdisk_kkey3", "hashdisk_kkey4", "hashdisk_kkey5"}; + values_.resize(5); + uint64_t approximate_size = 0; + uint64_t ret = 0; + std::vector value_size{1024, 1024, 1024, 1024, 1024}; + std::vector values(value_size.size()); + for (int i = 0; i < int(fields_.size()); i++) { + values[i] = std::string(value_size[i], static_cast('a' + i)); + values_[i] = values[i]; + approximate_size += key_.size() + 8 + fields_[i].size() + values_[i].size(); + rocksdb::Status s = hash->Set(key_, fields_[i], values_[i], &ret); + EXPECT_TRUE(s.ok() && ret == 1); + } + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisHash, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + auto s = hash->Del(key_); +} + +TEST_F(RedisDiskTest, SetDisk) { + std::unique_ptr set = std::make_unique(storage_, "disk_ns_set"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_set"); + key_ = "setdisk_key"; + values_.resize(5); + uint64_t approximate_size = 0; + uint64_t ret = 0; + std::vector value_size{1024, 1024, 1024, 1024, 1024}; + std::vector values(value_size.size()); + for (int i = 0; i < int(values_.size()); i++) { + values[i] = std::string(value_size[i], static_cast(i + 'a')); + values_[i] = values[i]; + approximate_size += key_.size() + values_[i].size() + 8; + } + rocksdb::Status s = set->Add(key_, values_, &ret); + EXPECT_TRUE(s.ok() && ret == 5); + + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisSet, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + + s = set->Del(key_); +} + +TEST_F(RedisDiskTest, ListDisk) { + std::unique_ptr list = std::make_unique(storage_, "disk_ns_list"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_list"); + key_ = "listdisk_key"; + values_.resize(5); + std::vector value_size{1024, 1024, 1024, 1024, 1024}; + std::vector values(value_size.size()); + uint64_t approximate_size = 0; + for (int i = 0; i < int(values_.size()); i++) { + values[i] = std::string(value_size[i], static_cast('a' + i)); + values_[i] = values[i]; + approximate_size += key_.size() + values_[i].size() + 8 + 8; + } + uint64_t ret = 0; + rocksdb::Status s = list->Push(key_, values_, false, &ret); + EXPECT_TRUE(s.ok() && ret == 5); + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisList, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + s = list->Del(key_); +} + +TEST_F(RedisDiskTest, ZsetDisk) { + std::unique_ptr zset = std::make_unique(storage_, "disk_ns_zet"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_zet"); + key_ = "zsetdisk_key"; + uint64_t ret = 0; + uint64_t approximate_size = 0; + std::vector mscores(5); + std::vector value_size{1024, 1024, 1024, 1024, 1024}; + for (int i = 0; i < int(value_size.size()); i++) { + mscores[i].member = std::string(value_size[i], static_cast('a' + i)); + mscores[i].score = 1.0; + approximate_size += (key_.size() + 8 + mscores[i].member.size() + 8) * 2; + } + rocksdb::Status s = zset->Add(key_, ZAddFlags::Default(), &mscores, &ret); + EXPECT_TRUE(s.ok() && ret == 5); + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisZSet, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + s = zset->Del(key_); +} + +TEST_F(RedisDiskTest, BitmapDisk) { + std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap"); + key_ = "bitmapdisk_key"; + bool bit = false; + uint64_t approximate_size = 0; + for (int i = 0; i < 1024 * 8 * 100000; i += 1024 * 8) { + EXPECT_TRUE(bitmap->SetBit(key_, i, true, &bit).ok()); + approximate_size += key_.size() + 8 + std::to_string(i / 1024 / 8).size(); + } + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisBitmap, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + auto s = bitmap->Del(key_); +} + +TEST_F(RedisDiskTest, BitmapDisk2) { + const int64_t kGroupSize = 8192; + for (size_t num_bits : {8192, 16384}) { + for (bool set_op : {false, true}) { + std::unique_ptr bitmap = std::make_unique(storage_, "disk_ns_bitmap2"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_bitmap2"); + key_ = "bitmapdisk_key2"; + auto s = bitmap->Del(key_); + bool bit = false; + + for (size_t i = 0; i < num_bits; i += kGroupSize) { + // Set all first bit of group to `!set_op` + EXPECT_TRUE(bitmap->SetBit(key_, i, !set_op, &bit).ok()); + // Set all last bit of group to `set_op`. + EXPECT_TRUE(bitmap->SetBit(key_, i + kGroupSize - 1, set_op, &bit).ok()); + } + + auto bit_not_dest_key = "bit_op_not_dest_key"; + + int64_t len = 0; + EXPECT_TRUE(bitmap->BitOp(BitOpFlags::kBitOpNot, "NOT", bit_not_dest_key, {key_}, &len).ok()); + + for (size_t i = 0; i < num_bits; i += kGroupSize) { + bool result = false; + // Check all first bit of group is `set_op` + EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i, &result).ok()); + EXPECT_EQ(set_op, result); + // Check all last bit of group is `!set_op` + EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, i + kGroupSize - 1, &result).ok()); + EXPECT_EQ(!set_op, result); + // Check bit in group between (first, last) is "1". + for (size_t j = i + 1; j < i + kGroupSize - 1; ++j) { + EXPECT_TRUE(bitmap->GetBit(bit_not_dest_key, j, &result).ok()); + EXPECT_TRUE(result) << j << " is not true"; + } + } + } + } +} + +TEST_F(RedisDiskTest, SortedintDisk) { + std::unique_ptr sortedint = std::make_unique(storage_, "disk_ns_sortedint"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_sortedint"); + key_ = "sortedintdisk_key"; + uint64_t ret = 0; + uint64_t approximate_size = 0; + for (int i = 0; i < 100000; i++) { + EXPECT_TRUE(sortedint->Add(key_, std::vector{uint64_t(i)}, &ret).ok() && ret == 1); + approximate_size += key_.size() + 8 + 8; + } + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisSortedint, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + auto s = sortedint->Del(key_); +} + +TEST_F(RedisDiskTest, StreamDisk) { + std::unique_ptr stream = std::make_unique(storage_, "disk_ns_stream"); + std::unique_ptr disk = std::make_unique(storage_, "disk_ns_stream"); + key_ = "streamdisk_key"; + redis::StreamAddOptions options; + options.next_id_strategy = *redis::ParseNextStreamEntryIDStrategy("*"); + redis::StreamEntryID id; + uint64_t approximate_size = 0; + for (int i = 0; i < 100000; i++) { + std::vector values = {"key" + std::to_string(i), "val" + std::to_string(i)}; + auto s = stream->Add(key_, options, values, &id); + EXPECT_TRUE(s.ok()); + approximate_size += key_.size() + 8 + 8 + values[0].size() + values[1].size(); + } + uint64_t key_size = 0; + EXPECT_TRUE(disk->GetKeySize(key_, kRedisStream, &key_size).ok()); + EXPECT_GE(key_size, approximate_size * estimation_factor_); + EXPECT_LE(key_size, approximate_size / estimation_factor_); + auto s = stream->Del(key_); +} From 82170cbb03ff22abffc5da7d55a67c249c3ab6c2 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Fri, 3 Nov 2023 04:49:29 +0000 Subject: [PATCH 03/13] Fix for too many arguments --- tests/cppunit/types/json_test.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/cppunit/types/json_test.cc b/tests/cppunit/types/json_test.cc index e0270791776..a41ce76c29a 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -276,37 +276,37 @@ TEST_F(RedisJsonTest, ArrLen) { TEST_F(RedisJsonTest, Toggle) { ASSERT_TRUE(json_->Set(key_, "$", "true").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), "false"); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$.bool", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$.bool").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false})"); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$.bool", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$.bool").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false,"bools":{"bool":true}})"); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..bool", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false,"bools":{"bool":false}})"); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..bool", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":true,"bools":{"bool":false}})"); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..bool", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool").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_TRUE(json_->Set(key_, "$", "[true,99,false]").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..*", &json_val_).ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..*").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), "[false,99,true]"); From 6dc799240f74998bdb5c9ef0eeccafc155842971 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Fri, 3 Nov 2023 04:38:08 +0000 Subject: [PATCH 04/13] support for the JSON.TOGGLE command support for the JSON.TOGGLE command --- src/commands/cmd_json.cc | 19 ++++++++++++++++ src/types/json.h | 19 ++++++++++++++++ src/types/redis_json.cc | 16 +++++++++++++ src/types/redis_json.h | 1 + tests/cppunit/types/json_test.cc | 39 ++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 6c2d40cd53f..2bb69396cd5 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -171,6 +171,24 @@ class CommandJsonClear : public Commander { } }; +class CommandJsonToggle : public Commander { + public: + Status Execute(Server *svr, Connection *conn, std::string *output) override { + redis::Json json(svr->storage, conn->GetNamespace()); + + std::string path = (args_.size() > 2) ? args_[2] : "$"; + auto s = json.Toggle(args_[1], path); + + if (s.IsNotFound()) { + return Status::OK(); + } + + if (!s.ok()) return {Status::RedisExecErr, s.ToString()}; + + return Status::OK(); + } +}; + class CommandJsonArrLen : public Commander { public: Status Execute(Server *svr, Connection *conn, std::string *output) override { @@ -209,6 +227,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr("json.set", 4, "write", 1, 1 MakeCmdAttr("json.type", -2, "read-only", 1, 1, 1), MakeCmdAttr("json.arrappend", -4, "write", 1, 1, 1), MakeCmdAttr("json.clear", -2, "write", 1, 1, 1), + MakeCmdAttr("json.toggle", -2, "write", 1, 1, 1), MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), ); } // namespace redis diff --git a/src/types/json.h b/src/types/json.h index cea9bcc9810..d340284d494 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -173,6 +173,25 @@ struct JsonValue { return types; } + Status Toggle(std::string_view path) { + try { + jsoncons::jsonpath::json_replace(value, path, [&](const std::string & /*path*/, jsoncons::json &val) { + bool is_boolean = val.is_bool() && !val.empty(); + // bool is_array = val.is_array() && !val.empty(); + + // if (is_array) + // val = jsoncons::json::array(); + if (is_boolean) + val = !val.as_bool(); + else + return; + }); + } catch (const jsoncons::jsonpath::jsonpath_error &e) { + return {Status::NotOK, e.what()}; + } + return Status::OK(); + } + StatusOr Clear(std::string_view path) { size_t count = 0; try { diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc index 6096003b64b..bb837314022 100644 --- a/src/types/redis_json.cc +++ b/src/types/redis_json.cc @@ -201,4 +201,20 @@ rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string &pat return rocksdb::Status::OK(); } + +rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &path) { + auto ns_key = AppendNamespacePrefix(user_key); + + LockGuard guard(storage_->GetLockManager(), ns_key); + + JsonMetadata metadata; + JsonValue origin; + auto s = read(ns_key, &metadata, &origin); + if (!s.ok()) return s; + + auto toggle_res = origin.Toggle(path); + if (!toggle_res) return rocksdb::Status::InvalidArgument(toggle_res.Msg()); + + return write(ns_key, &metadata, origin); +} } // namespace redis diff --git a/src/types/redis_json.h b/src/types/redis_json.h index 8eeb2571144..feb80edef69 100644 --- a/src/types/redis_json.h +++ b/src/types/redis_json.h @@ -41,6 +41,7 @@ class Json : public Database { 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> &arr_lens); + rocksdb::Status Toggle(const std::string &user_key, const std::string &path); 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 efd646b980e..a41ce76c29a 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -272,3 +272,42 @@ TEST_F(RedisJsonTest, ArrLen) { ASSERT_TRUE(json_->ArrLen(key_, "$.not_exists", res).ok()); ASSERT_TRUE(res.empty()); } + +TEST_F(RedisJsonTest, Toggle) { + + ASSERT_TRUE(json_->Set(key_, "$", "true").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$").ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), "false"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$.bool").ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$.bool").ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false,"bools":{"bool":true}})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool").ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false,"bools":{"bool":false}})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool").ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":true,"bools":{"bool":false}})"); + + ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}})").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..bool").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_TRUE(json_->Set(key_, "$", "[true,99,false]").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..*").ok()); + ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); + ASSERT_EQ(json_val_.Dump().GetValue(), "[false,99,true]"); + +} \ No newline at end of file From a5b5d58b067e214f75ca601a084d9690b572b416 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Fri, 3 Nov 2023 05:09:20 +0000 Subject: [PATCH 05/13] support for the JSON.TOGGLE command support for the JSON.TOGGLE command --- src/types/json.h | 3 --- tests/cppunit/types/json_test.cc | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/types/json.h b/src/types/json.h index d340284d494..58233a3ff27 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -177,10 +177,7 @@ struct JsonValue { try { jsoncons::jsonpath::json_replace(value, path, [&](const std::string & /*path*/, jsoncons::json &val) { bool is_boolean = val.is_bool() && !val.empty(); - // bool is_array = val.is_array() && !val.empty(); - // if (is_array) - // val = jsoncons::json::array(); if (is_boolean) val = !val.as_bool(); else diff --git a/tests/cppunit/types/json_test.cc b/tests/cppunit/types/json_test.cc index a41ce76c29a..81a9919e55e 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -274,7 +274,6 @@ TEST_F(RedisJsonTest, ArrLen) { } TEST_F(RedisJsonTest, Toggle) { - ASSERT_TRUE(json_->Set(key_, "$", "true").ok()); ASSERT_TRUE(json_->Toggle(key_, "$").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); @@ -309,5 +308,4 @@ TEST_F(RedisJsonTest, Toggle) { ASSERT_TRUE(json_->Toggle(key_, "$..*").ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), "[false,99,true]"); - -} \ No newline at end of file +} From cea7b9af691baa1b18a13008825ca7919f543932 Mon Sep 17 00:00:00 2001 From: HashTagInclude <58337761+MaheshMadushan@users.noreply.github.com> Date: Fri, 3 Nov 2023 21:50:57 +0530 Subject: [PATCH 06/13] Review comments addressing --- src/types/json.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/json.h b/src/types/json.h index 58233a3ff27..aad7ab15436 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -176,7 +176,7 @@ struct JsonValue { Status Toggle(std::string_view path) { try { jsoncons::jsonpath::json_replace(value, path, [&](const std::string & /*path*/, jsoncons::json &val) { - bool is_boolean = val.is_bool() && !val.empty(); + bool is_boolean = val.is_bool(); if (is_boolean) val = !val.as_bool(); From 57a6db1748887c067ea018774e21c7efb6b113c9 Mon Sep 17 00:00:00 2001 From: HashTagInclude <58337761+MaheshMadushan@users.noreply.github.com> Date: Sat, 4 Nov 2023 08:35:03 +0530 Subject: [PATCH 07/13] Update src/types/json.h Co-authored-by: Twice --- src/types/json.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/types/json.h b/src/types/json.h index aad7ab15436..0a08e2d1c83 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -176,12 +176,7 @@ struct JsonValue { Status Toggle(std::string_view path) { try { jsoncons::jsonpath::json_replace(value, path, [&](const std::string & /*path*/, jsoncons::json &val) { - bool is_boolean = val.is_bool(); - - if (is_boolean) - val = !val.as_bool(); - else - return; + if (val.is_bool()) val = !val.as_bool(); }); } catch (const jsoncons::jsonpath::jsonpath_error &e) { return {Status::NotOK, e.what()}; From 4940a9fc98de47c99dd00794166cd3bd382e6654 Mon Sep 17 00:00:00 2001 From: HashTagInclude <58337761+MaheshMadushan@users.noreply.github.com> Date: Sat, 4 Nov 2023 10:27:58 +0530 Subject: [PATCH 08/13] JSON.TOGGLE returns an array of integer replies for each path, the new value (0 if false or 1 if true), or nil for JSON values matching the path that are not Boolean. JSON TOGGLE ouput fix JSON>TOGGLE output fixed Update cmd_json.cc --- src/commands/cmd_json.cc | 14 +++++++++-- src/types/json.h | 14 +++++++---- src/types/redis_json.cc | 4 +++- src/types/redis_json.h | 2 +- tests/cppunit/types/json_test.cc | 40 +++++++++++++++++++++++++------- 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 1839233fb1d..2d403569538 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -195,12 +195,22 @@ class CommandJsonToggle : public Commander { redis::Json json(svr->storage, conn->GetNamespace()); std::string path = (args_.size() > 2) ? args_[2] : "$"; - auto s = json.Toggle(args_[1], path); + std::vector> results; + auto s = json.Toggle(args_[1], path, results); if (s.IsNotFound()) { + *output = redis::NilString(); return Status::OK(); } + 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()}; return Status::OK(); @@ -246,7 +256,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr("json.set", 4, "write", 1, 1 MakeCmdAttr("json.type", -2, "read-only", 1, 1, 1), MakeCmdAttr("json.arrappend", -4, "write", 1, 1, 1), MakeCmdAttr("json.clear", -2, "write", 1, 1, 1), - MakeCmdAttr("json.toggle", -2, "write", 1, 1, 1), + MakeCmdAttr("json.toggle", -2, "write", 1, 1, 1), MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), ); } // namespace redis diff --git a/src/types/json.h b/src/types/json.h index 6fa49f0c948..beec9fb9bda 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -211,15 +211,21 @@ struct JsonValue { return types; } - Status Toggle(std::string_view path) { + StatusOr>> Toggle(std::string_view path) { + std::vector> result; try { - jsoncons::jsonpath::json_replace(value, path, [&](const std::string & /*path*/, jsoncons::json &val) { - if (val.is_bool()) val = !val.as_bool(); + jsoncons::jsonpath::json_replace(value, path, [&result](const std::string & /*path*/, jsoncons::json &val) { + if (val.is_bool()) { + val = !val.as_bool(); + result.emplace_back(val == true ? 1 : 0); + } else { + result.emplace_back(std::nullopt); + } }); } catch (const jsoncons::jsonpath::jsonpath_error &e) { return {Status::NotOK, e.what()}; } - return Status::OK(); + return result; } StatusOr Clear(std::string_view path) { diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc index 743c600dc54..54d4a8d2ce1 100644 --- a/src/types/redis_json.cc +++ b/src/types/redis_json.cc @@ -231,7 +231,8 @@ rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string &pat return rocksdb::Status::OK(); } -rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &path) { +rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &path, + std::vector> &result) { auto ns_key = AppendNamespacePrefix(user_key); LockGuard guard(storage_->GetLockManager(), ns_key); @@ -243,6 +244,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; return write(ns_key, &metadata, origin); } diff --git a/src/types/redis_json.h b/src/types/redis_json.h index 33a3d127b71..1b9520ebb3f 100644 --- a/src/types/redis_json.h +++ b/src/types/redis_json.h @@ -42,7 +42,7 @@ class Json : public Database { 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> &arr_lens); - rocksdb::Status Toggle(const std::string &user_key, const std::string &path); + rocksdb::Status Toggle(const std::string &user_key, const std::string &path, std::vector> &result); 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 81a9919e55e..fd076ac7d5d 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -274,38 +274,60 @@ TEST_F(RedisJsonTest, ArrLen) { } TEST_F(RedisJsonTest, Toggle) { + std::vector> res; ASSERT_TRUE(json_->Set(key_, "$", "true").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$").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); + ASSERT_THAT(res, testing::ElementsAre(0)); + res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$.bool").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); + ASSERT_THAT(res, testing::ElementsAre(0)); + res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$.bool").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); + ASSERT_THAT(res, testing::ElementsAre(0)); + res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..bool").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); + ASSERT_THAT(res, testing::ElementsAre(0, 0)); + res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..bool").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(0, 1)); + res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}})").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..bool").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, 0, 1)); + res.clear(); - ASSERT_TRUE(json_->Set(key_, "$", "[true,99,false]").ok()); - ASSERT_TRUE(json_->Toggle(key_, "$..*").ok()); + ASSERT_TRUE(json_->Set(key_, "$", "[true,true,99]").ok()); + ASSERT_TRUE(json_->Toggle(key_, "$..*", res).ok()); ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); - ASSERT_EQ(json_val_.Dump().GetValue(), "[false,99,true]"); + ASSERT_EQ(json_val_.Dump().GetValue(), "[false,false,99]"); + ASSERT_EQ(res.size(), 3); + ASSERT_THAT(res, testing::ElementsAre(std::nullopt, 0, 0)); + res.clear(); } From 008b3c86d1da46964dba16e12923a5b1b796b19b Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Sun, 5 Nov 2023 11:43:23 +0000 Subject: [PATCH 09/13] using bool instead of int --- src/commands/cmd_json.cc | 2 +- src/types/json.h | 6 +++--- src/types/redis_json.cc | 2 +- src/types/redis_json.h | 2 +- tests/cppunit/types/json_test.cc | 16 ++++++++-------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 2d403569538..e3363682b1f 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -195,7 +195,7 @@ class CommandJsonToggle : public Commander { redis::Json json(svr->storage, conn->GetNamespace()); std::string path = (args_.size() > 2) ? args_[2] : "$"; - std::vector> results; + std::vector> results; auto s = json.Toggle(args_[1], path, results); if (s.IsNotFound()) { diff --git a/src/types/json.h b/src/types/json.h index beec9fb9bda..afa3adab99e 100644 --- a/src/types/json.h +++ b/src/types/json.h @@ -211,13 +211,13 @@ struct JsonValue { return types; } - StatusOr>> Toggle(std::string_view path) { - std::vector> result; + StatusOr>> Toggle(std::string_view path) { + std::vector> result; try { jsoncons::jsonpath::json_replace(value, path, [&result](const std::string & /*path*/, jsoncons::json &val) { if (val.is_bool()) { val = !val.as_bool(); - result.emplace_back(val == true ? 1 : 0); + result.emplace_back(val.as_bool()); } else { result.emplace_back(std::nullopt); } diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc index 54d4a8d2ce1..f23a3c5b8dd 100644 --- a/src/types/redis_json.cc +++ b/src/types/redis_json.cc @@ -232,7 +232,7 @@ rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string &pat } rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &path, - std::vector> &result) { + std::vector> &result) { auto ns_key = AppendNamespacePrefix(user_key); LockGuard guard(storage_->GetLockManager(), ns_key); diff --git a/src/types/redis_json.h b/src/types/redis_json.h index 1b9520ebb3f..3fedc2491d5 100644 --- a/src/types/redis_json.h +++ b/src/types/redis_json.h @@ -42,7 +42,7 @@ class Json : public Database { 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> &arr_lens); - rocksdb::Status Toggle(const std::string &user_key, const std::string &path, std::vector> &result); + rocksdb::Status Toggle(const std::string &user_key, const std::string &path, std::vector> &result); 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 fd076ac7d5d..db73135d600 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -274,13 +274,13 @@ TEST_F(RedisJsonTest, ArrLen) { } TEST_F(RedisJsonTest, Toggle) { - std::vector> res; + std::vector> res; ASSERT_TRUE(json_->Set(key_, "$", "true").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); - ASSERT_THAT(res, testing::ElementsAre(0)); + ASSERT_THAT(res, testing::ElementsAre(false)); res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true})").ok()); @@ -288,7 +288,7 @@ TEST_F(RedisJsonTest, Toggle) { ASSERT_TRUE(json_->Get(key_, {}, &json_val_).ok()); ASSERT_EQ(json_val_.Dump().GetValue(), R"({"bool":false})"); ASSERT_EQ(res.size(), 1); - ASSERT_THAT(res, testing::ElementsAre(0)); + ASSERT_THAT(res, testing::ElementsAre(false)); res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); @@ -296,7 +296,7 @@ TEST_F(RedisJsonTest, Toggle) { 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); - ASSERT_THAT(res, testing::ElementsAre(0)); + ASSERT_THAT(res, testing::ElementsAre(false)); res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":true,"bools":{"bool":true}})").ok()); @@ -304,7 +304,7 @@ TEST_F(RedisJsonTest, Toggle) { 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); - ASSERT_THAT(res, testing::ElementsAre(0, 0)); + ASSERT_THAT(res, testing::ElementsAre(false, false)); res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true}})").ok()); @@ -312,7 +312,7 @@ TEST_F(RedisJsonTest, Toggle) { 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(0, 1)); + ASSERT_THAT(res, testing::ElementsAre(false, true)); res.clear(); ASSERT_TRUE(json_->Set(key_, "$", R"({"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}})").ok()); @@ -320,7 +320,7 @@ TEST_F(RedisJsonTest, Toggle) { 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, 0, 1)); + ASSERT_THAT(res, testing::ElementsAre(std::nullopt, false, true)); res.clear(); ASSERT_TRUE(json_->Set(key_, "$", "[true,true,99]").ok()); @@ -328,6 +328,6 @@ TEST_F(RedisJsonTest, Toggle) { 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, 0, 0)); + ASSERT_THAT(res, testing::ElementsAre(std::nullopt, false, false)); res.clear(); } From d94911e2eda2866b3b813c954ce3a99cba2f77c7 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Sun, 5 Nov 2023 11:57:27 +0000 Subject: [PATCH 10/13] Resolving conflicts --- src/commands/cmd_json.cc | 2 +- src/types/redis_json.cc | 3 +-- src/types/redis_json.h | 3 ++- tests/cppunit/types/json_test.cc | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 2532dc94743..12b7b945226 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -297,7 +297,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr("json.set", 4, "write", 1, 1 MakeCmdAttr("json.arrappend", -4, "write", 1, 1, 1), MakeCmdAttr("json.clear", -2, "write", 1, 1, 1), MakeCmdAttr("json.toggle", -2, "write", 1, 1, 1), - MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), ); + MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), MakeCmdAttr("json.arrpop", -2, "write", 1, 1, 1), ); } // namespace redis diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc index 309b6a1596c..edfe18ba092 100644 --- a/src/types/redis_json.cc +++ b/src/types/redis_json.cc @@ -231,7 +231,6 @@ rocksdb::Status Json::ArrLen(const std::string &user_key, const std::string &pat return rocksdb::Status::OK(); } - rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &path, std::vector> &result) { auto ns_key = AppendNamespacePrefix(user_key); @@ -249,7 +248,7 @@ rocksdb::Status Json::Toggle(const std::string &user_key, const std::string &pat return write(ns_key, &metadata, origin); } - + rocksdb::Status Json::ArrPop(const std::string &user_key, const std::string &path, int64_t index, std::vector> *results) { auto ns_key = AppendNamespacePrefix(user_key); diff --git a/src/types/redis_json.h b/src/types/redis_json.h index e97cf021ee4..3a5c2d964e0 100644 --- a/src/types/redis_json.h +++ b/src/types/redis_json.h @@ -42,7 +42,8 @@ class Json : public Database { 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> &arr_lens); - rocksdb::Status Toggle(const std::string &user_key, const std::string &path, std::vector> &result); + rocksdb::Status Toggle(const std::string &user_key, const std::string &path, + std::vector> &result); rocksdb::Status ArrPop(const std::string &user_key, const std::string &path, int64_t index, std::vector> *results); diff --git a/tests/cppunit/types/json_test.cc b/tests/cppunit/types/json_test.cc index 6585bf76950..c1c10e8dc2b 100644 --- a/tests/cppunit/types/json_test.cc +++ b/tests/cppunit/types/json_test.cc @@ -329,6 +329,7 @@ TEST_F(RedisJsonTest, Toggle) { ASSERT_EQ(json_val_.Dump().GetValue(), "[false,false,99]"); ASSERT_EQ(res.size(), 3); ASSERT_THAT(res, testing::ElementsAre(std::nullopt, false, false)); +} TEST_F(RedisJsonTest, ArrPop) { std::vector> res; From 400921c6b2caf99d44a9e6a4d5e90681898ff02b Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Sun, 5 Nov 2023 12:06:53 +0000 Subject: [PATCH 11/13] rsolve conflicts --- src/commands/cmd_json.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 12b7b945226..ff6298d6a01 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -298,6 +298,5 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr("json.set", 4, "write", 1, 1 MakeCmdAttr("json.clear", -2, "write", 1, 1, 1), MakeCmdAttr("json.toggle", -2, "write", 1, 1, 1), MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), - MakeCmdAttr("json.arrlen", -2, "read-only", 1, 1, 1), MakeCmdAttr("json.arrpop", -2, "write", 1, 1, 1), ); } // namespace redis From 3506f1bbc7484c90c8f433fe5920e75fd2bd4c0d Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Mon, 6 Nov 2023 16:48:19 +0000 Subject: [PATCH 12/13] add go test cases for JSON.TOGGLE cmd --- tests/gocase/unit/type/json/json_test.go | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/gocase/unit/type/json/json_test.go b/tests/gocase/unit/type/json/json_test.go index 61c4276c2f0..8ad617dfa66 100644 --- a/tests/gocase/unit/type/json/json_test.go +++ b/tests/gocase/unit/type/json/json_test.go @@ -220,4 +220,34 @@ func TestJson(t *testing.T) { require.ErrorContains(t, rdb.Do(ctx, "JSON.ARRPOP", "a", "$", "0", "1").Err(), "wrong number of arguments") }) + t.Run("JSON.TOGGLE basics", func(t *testing.T) { + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `true`).Err()) + require.EqualValues(t, []interface{}{bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `false`) + + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":true}`).Err()) + require.EqualValues(t, []interface{}{bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$.bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":false}`) + + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":true,"bools":{"bool":true}}`).Err()) + require.EqualValues(t, []interface{}{bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$.bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":false,"bools":{"bool":true}}`) + + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":true,"bools":{"bool":true}}`).Err()) + require.EqualValues(t, []interface{}{bool(false), bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":false,"bools":{"bool":false}}`) + + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":false,"bools":{"bool":true}}`).Err()) + require.EqualValues(t, []interface{}{bool(false), bool(true)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":true,"bools":{"bool":false}}`) + + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}}`).Err()) + require.EqualValues(t, []interface{}{nil, bool(false), bool(true)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":true,"bools":{"bool":false},"incorrectbool":{"bool":88}}`) + + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `[true,true,99]`).Err()) + require.EqualValues(t, []interface{}{nil, bool(false), bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..*").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `[false,false,99]`) + }) + } From 47d100ec29058087fe3a8aaca31e2f6dc4d66ab0 Mon Sep 17 00:00:00 2001 From: MaheshMadushan Date: Wed, 8 Nov 2023 08:34:00 +0000 Subject: [PATCH 13/13] review comment addresed issue fix and go test cases fixes issue fix and go test cases fixes --- src/commands/cmd_json.cc | 5 ++-- tests/gocase/unit/type/json/json_test.go | 32 ++++++++++++------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc index 7f2d27b7e19..08bcf7abe85 100644 --- a/src/commands/cmd_json.cc +++ b/src/commands/cmd_json.cc @@ -233,9 +233,10 @@ class CommandJsonToggle : public Commander { 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()); + if (it->has_value()) { + *output += redis::Integer(it->value()); } else { *output += redis::NilString(); } diff --git a/tests/gocase/unit/type/json/json_test.go b/tests/gocase/unit/type/json/json_test.go index 2173d395ac7..0d5f3fd9517 100644 --- a/tests/gocase/unit/type/json/json_test.go +++ b/tests/gocase/unit/type/json/json_test.go @@ -247,32 +247,32 @@ func TestJson(t *testing.T) { t.Run("JSON.TOGGLE basics", func(t *testing.T) { require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `true`).Err()) - require.EqualValues(t, []interface{}{bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `false`) + require.EqualValues(t, []interface{}{int64(0)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `false`) require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":true}`).Err()) - require.EqualValues(t, []interface{}{bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$.bool").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":false}`) + require.EqualValues(t, []interface{}{int64(0)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$.bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `{"bool":false}`) require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":true,"bools":{"bool":true}}`).Err()) - require.EqualValues(t, []interface{}{bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$.bool").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":false,"bools":{"bool":true}}`) + require.EqualValues(t, []interface{}{int64(0)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$.bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `{"bool":false,"bools":{"bool":true}}`) require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":true,"bools":{"bool":true}}`).Err()) - require.EqualValues(t, []interface{}{bool(false), bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":false,"bools":{"bool":false}}`) + require.EqualValues(t, []interface{}{int64(0), int64(0)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `{"bool":false,"bools":{"bool":false}}`) require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":false,"bools":{"bool":true}}`).Err()) - require.EqualValues(t, []interface{}{bool(false), bool(true)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":true,"bools":{"bool":false}}`) + require.EqualValues(t, []interface{}{int64(1), int64(0)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `{"bool":true,"bools":{"bool":false}}`) - require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"bool":false,"bools":{"bool":true},"incorrectbool":{"bool":88}}`).Err()) - require.EqualValues(t, []interface{}{nil, bool(false), bool(true)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `{"bool":true,"bools":{"bool":false},"incorrectbool":{"bool":88}}`) + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"incorrectbool":99,"bools":{"bool":true},"bool":{"bool":false}}`).Err()) + require.EqualValues(t, []interface{}{nil, int64(1), int64(0)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..bool").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `{"bool":{"bool":true},"bools":{"bool":false},"incorrectbool":99}`) - require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `[true,true,99]`).Err()) - require.EqualValues(t, []interface{}{nil, bool(false), bool(false)}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..*").Val()) - require.Equal(t, rdb.Do(ctx, "JSON.GET", "a", "$").Val(), `[false,false,99]`) + require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `[99,true,99]`).Err()) + require.EqualValues(t, []interface{}{nil, int64(0), nil}, rdb.Do(ctx, "JSON.TOGGLE", "a", "$..*").Val()) + require.Equal(t, rdb.Do(ctx, "JSON.GET", "a").Val(), `[99,false,99]`) }) }