diff --git a/CMakeLists.txt b/CMakeLists.txt index 54b14a1..974f190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,10 +117,12 @@ endif(BUILD_SHARED_LIBS) # Must be included before CMAKE_INSTALL_INCLUDEDIR is used. include(GNUInstallDirs) -add_library(leveldb "") +add_library(leveldb) target_sources(leveldb PRIVATE "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" + "db/fields.cpp" + "db/fields.h" "db/builder.cc" "db/builder.h" "db/c.cc" @@ -521,11 +523,19 @@ endif(LEVELDB_INSTALL) add_executable(db_test2 "${PROJECT_SOURCE_DIR}/test/db_test2.cc" + test/value_field_test.cc ) target_link_libraries(db_test2 PRIVATE leveldb) add_executable(ttl_test "${PROJECT_SOURCE_DIR}/test/ttl_test.cc" + test/value_field_test.cc ) -target_link_libraries(ttl_test PRIVATE leveldb gtest) \ No newline at end of file +target_link_libraries(ttl_test PRIVATE leveldb gtest) + +add_executable(value_field_test + "${PROJECT_SOURCE_DIR}/test/value_field_test.cc" + test/value_field_test.cc +) +target_link_libraries(value_field_test PRIVATE leveldb gtest) \ No newline at end of file diff --git a/README.md b/README.md index 6f68032..9d801ab 100755 --- a/README.md +++ b/README.md @@ -72,30 +72,55 @@ + `class Fields` ```c++ - class Fields { - public: - // 从 FieldArray 构造 - explicit Fields(FieldArray field_array); - // 从字符串解码构造 - explicit Fields(const std::string& value_str); + class Fields { + private: + FieldArray fields_; + + public: + /* 从 FieldArray 构造 */ + explicit Fields(const FieldArray& fields); + /* 从单个 Field 构造 */ + explicit Fields(const Field& field); + /* 只传参 field_name 数组的构造 */ + explicit Fields(const std::vector& field_names); + + Fields() = default; + ~Fields() = default; - ~Fields(); + /* 根据 field_name 从小到大进行排序,减少通过 field_name 遍历 Fields 的耗时 */ + void SortFields(); + + /* 更新/插入单个字段值,插入后会进行 Fields 排序,减少通过 field_name 遍历 Fields 的耗时 */ + void UpdateField(const std::string& field_name, const std::string& field_value); + void UpdateField(const Field& field); + /* 更新/插入多个字段值 */ + void UpdateFields(const std::vector& field_names, const std::vector& field_values); + void UpdateFields(const FieldArray& fields); - // 更新字段值 - void update_field(const std::string& name, const std::string& value); - // 获取字段 - Field get_field(const std::string& name) const; - // 检查字段是否存在 - bool has_field(const std::string& name) const; - // 序列化字段数组为字符串 - std::string Serialize() const; - // 重载运算符 [] 用于访问字段值 - std::string operator[](const std::string& name) const; - // 重载运算符 [] 用于修改字段值 - std::string& operator[](const std::string& name); - // 重载运算符 == 用于比较两个 Fields 是否相等 - bool operator==(const Fields& other) const; - }; + /* 删除单个字段 */ + void DeleteField(const std::string& field_name); + /* 删除多个字段 */ + void DeleteFields(const std::vector& field_names); + + /* 序列化 Field 或 FieldArray 为 value 字符串 */ + /* static 修饰的函数序列化/反序列化无需访问一个 Fields 对象的 fields_ */ + static std::string SerializeValue(const FieldArray& fields); + static std::string SerializeValue(const Field& field); + std::string SerializeValue() const; + + /* 反序列化 value 字符串为 Fields */ + static Fields ParseValue(const std::string& value_str); + + /* 获取字段 */ + Field GetField(const std::string& field_name) const; + /* 检查字段是否存在 */ + bool HasField(const std::string& field_name) const; + + /* 重载运算符 [] 用于访问字段值 */ + std::string operator[](const std::string& field_name) const; + /* 重载运算符 [] 用于修改字段值 */ + std::string& operator[](const std::string& field_name); + }; ``` ### 4.2 KV 分离 diff --git a/db/db_impl.cc b/db/db_impl.cc index f96d245..3a5c225 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -12,6 +12,7 @@ #include #include +#include "fields.h" #include "db/builder.h" #include "db/db_iter.h" #include "db/dbformat.h" @@ -1193,6 +1194,22 @@ void DBImpl::ReleaseSnapshot(const Snapshot* snapshot) { snapshots_.Delete(static_cast(snapshot)); } +/*** DBImpl 类关于 Fields 类的 Put、Get 接口 ***/ +Status DBImpl::PutFields(const WriteOptions& o, const Slice& key, const Fields& fields) { + return DBImpl::Put(o, key, Slice(fields.SerializeValue())); +} + +Status DBImpl::GetFields(const ReadOptions& o, const Slice& key, Fields& fields) { + std::string value_str; + + Status s = DBImpl::Get(o, key, &value_str); + if (!s.ok()) return s; + + fields = Fields::ParseValue(value_str); + return Status::OK(); +} +/**************************************************/ + // Convenience methods Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { return DB::Put(o, key, val); diff --git a/db/db_impl.h b/db/db_impl.h index c7b0172..e00559e 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -25,6 +25,7 @@ class TableCache; class Version; class VersionEdit; class VersionSet; +class Fields; class DBImpl : public DB { public: @@ -36,6 +37,9 @@ class DBImpl : public DB { ~DBImpl() override; // Implementations of the DB interface + Status PutFields(const WriteOptions&, const Slice& key, const Fields& fields); + Status GetFields(const ReadOptions& options, const Slice& key, Fields& fields); + Status Put(const WriteOptions&, const Slice& key, const Slice& value) override; Status Delete(const WriteOptions&, const Slice& key) override; diff --git a/db/fields.cpp b/db/fields.cpp new file mode 100644 index 0000000..cdd902f --- /dev/null +++ b/db/fields.cpp @@ -0,0 +1,165 @@ +#include "fields.h" +#include "util/coding.h" + +#include +#include + + +namespace leveldb { + +/* 构造函数 */ +Fields::Fields(FieldArray fields) + : fields_(std::move(fields)) { + SortFields(); +} + +Fields::Fields(const Field& field) + : fields_({field}) {} + +Fields::Fields(const std::vector& field_names) { + for (const auto& name : field_names) { + fields_.emplace_back(name, ""); + } + SortFields(); +} + +/* 根据 field_name 从小到大进行排序,减少通过 field_name 遍历 Fields 的耗时 */ +void Fields::SortFields() { + std::sort(fields_.begin(), fields_.end(), [](const Field& a, const Field& b) { + return a.first < b.first; + }); +} + +/* 更新/插入单个字段值 */ +void Fields::UpdateField(const std::string& field_name, const std::string& field_value) { + for (auto iter = fields_.begin(); iter != fields_.end(); iter++) { + if ((*iter).first > field_name) { + fields_.insert(iter, {field_name, field_value}); + return; + } + if ((*iter).first == field_name) { + (*iter).second = field_value; + return; + } + } + fields_.emplace_back(field_name, field_value); +} + +void Fields::UpdateField(const Field& field) { + UpdateField(field.first, field.second); +} + +/* 更新/插入多个字段值 */ +void Fields::UpdateFields(const std::vector& field_names, const std::vector& field_values) { + if (field_names.size() != field_values.size()) { + std::cerr << "UpdateFields Failed: field_name and field_values must have the same size." << std::endl; + return; + } + for (size_t i = 0; i < field_names.size(); ++i) { + UpdateField(field_names[i], field_values[i]); + } +} + +void Fields::UpdateFields(const FieldArray& fields) { + for (const auto& field : fields) { + UpdateField(field); + } +} + +/* 删除单个字段 */ +void Fields::DeleteField(const std::string& field_name) { + for (auto iter = fields_.begin(); iter != fields_.end(); iter++) { + if ((*iter).first > field_name) return; + if ((*iter).first == field_name) { + fields_.erase(iter); + return; + } + } +} + +/* 删除多个字段 */ +void Fields::DeleteFields(const std::vector& field_names) { + for (auto &name : field_names) { + if (fields_.empty()) return; + DeleteField(name); + } +} + +/* 序列化 Field 或 FieldArray 为 value 字符串 */ +std::string Fields::SerializeValue(const FieldArray& fields) { + std::string value_str; + for (const auto& field : fields) { + std::string field_str = SerializeValue(field); + value_str += field_str; + } + return value_str; +} + +std::string Fields::SerializeValue(const Field& field) { + std::string value_str; + PutLengthPrefixedSlice(&value_str, Slice(field.first)); + PutLengthPrefixedSlice(&value_str, Slice(field.second)); + return value_str; +} + +std::string Fields::SerializeValue() const { + return SerializeValue(fields_); +} + +/* 反序列化 value 字符串为 Fields */ +Fields Fields::ParseValue(const std::string& value_str) { + Fields fields; + Slice value_slice(value_str); + while (!value_slice.empty()) { + Slice field_name; + Slice field_value; + if (!GetLengthPrefixedSlice(&value_slice, &field_name)) break; + if (!GetLengthPrefixedSlice(&value_slice, &field_value)) break; + fields.UpdateField(field_name.ToString(), field_value.ToString()); + } + return fields; +} + +/* 获取字段 */ +Field Fields::GetField(const std::string& field_name) const { + for (auto iter = fields_.begin(); iter != fields_.end(); iter++) { + if ((*iter).first == field_name) return *iter; + if ((*iter).first > field_name || iter == fields_.end() - 1) { + std::cerr << "GetField Failed: field name [" + field_name + "] doesn't exist, return {}." << std::endl; + return {}; + } + } +} + +/* 检查字段是否存在 */ +bool Fields::HasField(const std::string& field_name) const { + for (auto iter = fields_.begin(); iter != fields_.end(); iter++) { + if ((*iter).first == field_name) return true; + if ((*iter).first > field_name || iter == fields_.end() - 1) return false; + } +} + +/* 重载运算符 [] 用于访问字段值 */ +std::string Fields::operator[](const std::string& field_name) const { + for (auto iter = fields_.begin(); iter != fields_.end(); iter++) { + if ((*iter).first == field_name) return (*iter).second; + if ((*iter).first > field_name || iter == fields_.end() - 1) { + static const std::string empty_str; + std::cerr << "GetField Failed: field name [" + field_name + "] doesn't exist." << std::endl; + return empty_str; + } + } +} + +std::string& Fields::operator[](const std::string& field_name) { + for (auto iter = fields_.begin(); iter != fields_.end(); iter++) { + if ((*iter).first == field_name) return (*iter).second; + if ((*iter).first > field_name) { + return fields_.insert(iter, {field_name, ""})->second; + } + } + fields_.emplace_back(field_name, ""); + return fields_.back().second; +} + +} // namespace leveldb diff --git a/db/fields.h b/db/fields.h new file mode 100644 index 0000000..2290ea0 --- /dev/null +++ b/db/fields.h @@ -0,0 +1,63 @@ +#ifndef LEVELDB_FIELDS_H +#define LEVELDB_FIELDS_H + +#include "vector" +#include "leveldb/db.h" + +namespace leveldb { + +using Field = std::pair; +using FieldArray = std::vector; + +class Fields { + private: + FieldArray fields_; + + public: + /* 从 FieldArray 构造 */ + explicit Fields(FieldArray fields); + /* 从单个 Field 构造 */ + explicit Fields(const Field& field); + /* 只传参 field_name 数组的构造 */ + explicit Fields(const std::vector& field_names); + + Fields() = default; + ~Fields() = default; + + /* 根据 field_name 从小到大进行排序,减少通过 field_name 遍历 Fields 的耗时 */ + void SortFields(); + + /* 更新/插入单个字段值,插入后会进行 field_name 的排序 */ + void UpdateField(const std::string& field_name, const std::string& field_value); + void UpdateField(const Field& field); + /* 更新/插入多个字段值 */ + void UpdateFields(const std::vector& field_names, const std::vector& field_values); + void UpdateFields(const FieldArray& fields); + + /* 删除单个字段 */ + void DeleteField(const std::string& field_name); + /* 删除多个字段 */ + void DeleteFields(const std::vector& field_names); + + /* 序列化 Field 或 FieldArray 为 value 字符串 */ + static std::string SerializeValue(const FieldArray& fields); + static std::string SerializeValue(const Field& field); + std::string SerializeValue() const; + + /* 反序列化 value 字符串为 Fields */ + static Fields ParseValue(const std::string& value_str); + + /* 获取字段 */ + Field GetField(const std::string& field_name) const; + /* 检查字段是否存在 */ + bool HasField(const std::string& field_name) const; + + /* 重载运算符 [] 用于访问字段值 */ + std::string operator[](const std::string& field_name) const; + /* 重载运算符 [] 用于修改字段值 */ + std::string& operator[](const std::string& field_name); +}; + +} // namespace leveldb + +#endif // LEVELDB_FIELDS_H diff --git a/include/leveldb/db.h b/include/leveldb/db.h index bf4eec5..0ee62f4 100644 --- a/include/leveldb/db.h +++ b/include/leveldb/db.h @@ -148,8 +148,8 @@ class LEVELDB_EXPORT DB { // ----------------------------For TTL----------------------------- // 为当前key设置ttl,过期后自动失效 - virtual Status Put(const WriteOptions& options, const Slice& key, - const Slice& value, uint64_t ttl) = 0; +// virtual Status Put(const WriteOptions& options, const Slice& key, +// const Slice& value, uint64_t ttl) = 0; }; // Destroy the contents of the specified database. diff --git a/test/value_field_test.cc b/test/value_field_test.cc new file mode 100644 index 0000000..0945325 --- /dev/null +++ b/test/value_field_test.cc @@ -0,0 +1,55 @@ +#include "gtest/gtest.h" +#include "leveldb/db.h" +#include "db/fields.h" + + +using namespace leveldb; + + +constexpr int value_size = 2048; +constexpr int data_size = 128 << 20; + + +Status OpenDB(std::string dbName, DB **db) { + Options options; + options.create_if_missing = true; + return DB::Open(options, dbName, db); +} + + +TEST(TestValueField, ReadValueField) { + DB *db; + + if(OpenDB("testdb", &db).ok() == false) { + std::cerr << "open db failed" << std::endl; + abort(); + } + + std::string key = "k_1"; + + FieldArray fields = { + {"name", "Customer#000000001"}, + {"address", "IVhzIApeRb"}, + {"phone", "25-989-741-2988"} + }; + + // 序列化并插入 + std::string value = SerializeValue(fields); + db->Put(WriteOptions(), key, value); + + // 读取并反序列化 + std::string value_ret; + db->Get(ReadOptions(), key, &value_ret); + auto fields_ret = ParseValue(value_ret); + + + + +} + + +int main(int argc, char** argv) { + // All tests currently run with the same read-only file limits. + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}