diff --git a/cpp/.gitignore b/cpp/.gitignore index 4f3221962..29b5a5943 100644 --- a/cpp/.gitignore +++ b/cpp/.gitignore @@ -1,5 +1,6 @@ /build/ /examples/*/build/ +/cmake-build-debug apidoc/html apidoc/xml diff --git a/cpp/include/gar/graph_info.h b/cpp/include/gar/graph_info.h index d319e1075..1d6674562 100644 --- a/cpp/include/gar/graph_info.h +++ b/cpp/include/gar/graph_info.h @@ -33,16 +33,21 @@ class Property { std::string name; // property name std::shared_ptr type; // property data type bool is_primary; // primary key tag + bool is_nullable; // nullable tag for non-primary key explicit Property(const std::string& name, const std::shared_ptr& type = nullptr, - bool is_primary = false) - : name(name), type(type), is_primary(is_primary) {} + bool is_primary = false, bool is_nullable = true) + : name(name), + type(type), + is_primary(is_primary), + is_nullable(!is_primary && is_nullable) {} }; static bool operator==(const Property& lhs, const Property& rhs) { return (lhs.name == rhs.name) && (lhs.type == rhs.type) && - (lhs.is_primary == rhs.is_primary); + (lhs.is_primary == rhs.is_primary) && + (lhs.is_nullable == rhs.is_nullable); } /** @@ -271,6 +276,14 @@ class VertexInfo { */ bool IsPrimaryKey(const std::string& property_name) const; + /** + * Returns whether the specified property is a nullable key. + * + * @param property_name The name of the property. + * @return True if the property is a nullable key, False otherwise. + */ + bool IsNullableKey(const std::string& property_name) const; + /** * Returns whether the vertex info contains the specified property group. * @@ -596,11 +609,18 @@ class EdgeInfo { * Returns whether the specified property is a primary key. * * @param property_name The name of the property. - * @return A Result object containing a bool indicating whether the property - * is a primary key, or a KeyError Status object if the property is not found. + * @return True if the property is a primary key, False otherwise. */ bool IsPrimaryKey(const std::string& property_name) const; + /** + * Returns whether the specified property is a nullable key. + * + * @param property_name The name of the property. + * @return True if the property is a nullable key, False otherwise. + */ + bool IsNullableKey(const std::string& property_name) const; + /** * Saves the edge info to a YAML file. * diff --git a/cpp/src/graph_info.cc b/cpp/src/graph_info.cc index c001b969e..63d83cf83 100644 --- a/cpp/src/graph_info.cc +++ b/cpp/src/graph_info.cc @@ -184,6 +184,7 @@ class VertexInfo::Impl { for (const auto& p : pg->GetProperties()) { property_name_to_index_.emplace(p.name, i); property_name_to_primary_.emplace(p.name, p.is_primary); + property_name_to_nullable_.emplace(p.name, p.is_nullable); property_name_to_type_.emplace(p.name, p.type); } } @@ -220,6 +221,7 @@ class VertexInfo::Impl { std::shared_ptr version_; std::unordered_map property_name_to_index_; std::unordered_map property_name_to_primary_; + std::unordered_map property_name_to_nullable_; std::unordered_map> property_name_to_type_; }; @@ -291,6 +293,14 @@ bool VertexInfo::IsPrimaryKey(const std::string& property_name) const { return it->second; } +bool VertexInfo::IsNullableKey(const std::string& property_name) const { + auto it = impl_->property_name_to_nullable_.find(property_name); + if (it == impl_->property_name_to_nullable_.end()) { + return false; + } + return it->second; +} + bool VertexInfo::HasProperty(const std::string& property_name) const { return impl_->property_name_to_index_.find(property_name) != impl_->property_name_to_index_.end(); @@ -382,7 +392,10 @@ Result> VertexInfo::Load( auto property_type = DataType::TypeNameToDataType(p_node["data_type"].As()); bool is_primary = p_node["is_primary"].As(); - property_vec.emplace_back(property_name, property_type, is_primary); + bool is_nullable = + p_node["is_nullable"].IsNone() || p_node["is_nullable"].As(); + property_vec.emplace_back(property_name, property_type, is_primary, + is_nullable); } property_groups.push_back( std::make_shared(property_vec, file_type, pg_prefix)); @@ -416,6 +429,7 @@ Result VertexInfo::Dump() const noexcept { p_node["name"] = p.name; p_node["data_type"] = p.type->ToTypeName(); p_node["is_primary"] = p.is_primary ? "true" : "false"; + p_node["is_nullable"] = p.is_nullable ? "true" : "false"; pg_node["properties"].PushBack(); pg_node["properties"][pg_node["properties"].Size() - 1] = p_node; } @@ -469,6 +483,7 @@ class EdgeInfo::Impl { for (const auto& p : pg->GetProperties()) { property_name_to_index_.emplace(p.name, i); property_name_to_primary_.emplace(p.name, p.is_primary); + property_name_to_nullable_.emplace(p.name, p.is_nullable); property_name_to_type_.emplace(p.name, p.type); } } @@ -522,6 +537,7 @@ class EdgeInfo::Impl { std::unordered_map adjacent_list_type_to_index_; std::unordered_map property_name_to_index_; std::unordered_map property_name_to_primary_; + std::unordered_map property_name_to_nullable_; std::unordered_map> property_name_to_type_; std::shared_ptr version_; @@ -706,6 +722,14 @@ bool EdgeInfo::IsPrimaryKey(const std::string& property_name) const { return it->second; } +bool EdgeInfo::IsNullableKey(const std::string& property_name) const { + auto it = impl_->property_name_to_nullable_.find(property_name); + if (it == impl_->property_name_to_nullable_.end()) { + return false; + } + return it->second; +} + Result> EdgeInfo::AddAdjacentList( std::shared_ptr adj_list) const { if (adj_list == nullptr) { @@ -817,7 +841,10 @@ Result> EdgeInfo::Load(std::shared_ptr yaml) { auto property_type = DataType::TypeNameToDataType(p_node["data_type"].As()); bool is_primary = p_node["is_primary"].As(); - property_vec.emplace_back(property_name, property_type, is_primary); + bool is_nullable = + p_node["is_nullable"].IsNone() || p_node["is_nullable"].As(); + property_vec.emplace_back(property_name, property_type, is_primary, + is_nullable); } property_groups.push_back( std::make_shared(property_vec, file_type, pg_prefix)); @@ -869,6 +896,7 @@ Result EdgeInfo::Dump() const noexcept { p_node["name"] = p.name; p_node["data_type"] = p.type->ToTypeName(); p_node["is_primary"] = p.is_primary ? "true" : "false"; + p_node["is_nullable"] = p.is_nullable ? "true" : "false"; pg_node["properties"].PushBack(); pg_node["properties"][pg_node["properties"].Size() - 1] = p_node; } diff --git a/cpp/test/test_info.cc b/cpp/test/test_info.cc index a67619fcd..252bfb345 100644 --- a/cpp/test/test_info.cc +++ b/cpp/test/test_info.cc @@ -71,7 +71,9 @@ TEST_CASE("Property") { REQUIRE(p0.name == "p0"); REQUIRE(p0.type->ToTypeName() == int32()->ToTypeName()); REQUIRE(p0.is_primary == true); + REQUIRE(p0.is_nullable == false); REQUIRE(p1.is_primary == false); + REQUIRE(p1.is_nullable == true); } TEST_CASE("PropertyGroup") { @@ -95,6 +97,7 @@ TEST_CASE("PropertyGroup") { REQUIRE(p.name == "p0"); REQUIRE(p.type->ToTypeName() == int32()->ToTypeName()); REQUIRE(p.is_primary == true); + REQUIRE(p.is_nullable == false); } SECTION("FileType") { @@ -199,6 +202,8 @@ TEST_CASE("VertexInfo") { int32()->ToTypeName()); REQUIRE(vertex_info->IsPrimaryKey("p0") == true); REQUIRE(vertex_info->IsPrimaryKey("p1") == false); + REQUIRE(vertex_info->IsNullableKey("p0") == false); + REQUIRE(vertex_info->IsNullableKey("p1") == true); REQUIRE(vertex_info->HasProperty("not_exist") == false); REQUIRE(vertex_info->IsPrimaryKey("not_exist") == false); REQUIRE(vertex_info->HasPropertyGroup(nullptr) == false); @@ -242,9 +247,11 @@ prefix: test_vertex prefix: p0_p1/ properties: - data_type: int32 + is_nullable: false is_primary: true name: p0 - data_type: string + is_nullable: true is_primary: false name: p1 version: gar/v1 @@ -269,6 +276,7 @@ version: gar/v1 REQUIRE(extend_info->GetPropertyType("p2").value()->ToTypeName() == int32()->ToTypeName()); REQUIRE(extend_info->IsPrimaryKey("p2") == false); + REQUIRE(extend_info->IsNullableKey("p2") == true); auto extend_info2 = extend_info->AddPropertyGroup(pg2); REQUIRE(!extend_info2.status().ok()); } @@ -327,8 +335,11 @@ TEST_CASE("EdgeInfo") { int32()->ToTypeName()); REQUIRE(edge_info->IsPrimaryKey("p0") == true); REQUIRE(edge_info->IsPrimaryKey("p1") == false); + REQUIRE(edge_info->IsNullableKey("p0") == false); + REQUIRE(edge_info->IsNullableKey("p1") == true); REQUIRE(edge_info->HasProperty("not_exist") == false); REQUIRE(edge_info->IsPrimaryKey("not_exist") == false); + REQUIRE(edge_info->IsNullableKey("not_exist") == false); REQUIRE(edge_info->HasPropertyGroup(nullptr) == false); } @@ -410,9 +421,11 @@ prefix: test_edge/ prefix: p0_p1/ properties: - data_type: int32 + is_nullable: false is_primary: true name: p0 - data_type: string + is_nullable: true is_primary: false name: p1 src_chunk_size: 100 @@ -601,17 +614,21 @@ prefix: vertex/person/ - name: id data_type: int64 is_primary: true + is_nullable: false file_type: parquet - properties: - name: firstName data_type: string is_primary: false + is_nullable: true - name: lastName data_type: string is_primary: false + is_nullable: true - name: gender data_type: string is_primary: false + is_nullable: true file_type: parquet version: gar/v1 )"; @@ -639,6 +656,7 @@ prefix: edge/person_knows_person/ - name: creationDate data_type: string is_primary: false + is_nullable: true version: gar/v1 )"; std::string graph_info_yaml = R"(name: ldbc_sample diff --git a/docs/file-format.rst b/docs/file-format.rst index f8d384df1..2b581ae78 100644 --- a/docs/file-format.rst +++ b/docs/file-format.rst @@ -124,7 +124,7 @@ A vertex information file which named "