blob: 9959f194d076e78560e2e02b4f8cbb43c74b0399 [file] [log] [blame]
/*
* 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 <unordered_set>
#include <utility>
#include "graphar/status.h"
#include "mini-yaml/yaml/Yaml.hpp"
#include "graphar/filesystem.h"
#include "graphar/graph_info.h"
#include "graphar/result.h"
#include "graphar/types.h"
#include "graphar/version_parser.h"
#include "graphar/yaml.h"
namespace graphar {
#define CHECK_HAS_ADJ_LIST_TYPE(adj_list_type) \
do { \
if (!HasAdjacentListType(adj_list_type)) { \
return Status::KeyError( \
"Adjacency list type: ", AdjListTypeToString(adj_list_type), \
" is not found in edge info."); \
} \
} while (false)
namespace {
std::string ConcatEdgeTriple(const std::string& src_type,
const std::string& edge_type,
const std::string& dst_type) {
return src_type + REGULAR_SEPARATOR + edge_type + REGULAR_SEPARATOR +
dst_type;
}
template <int NotFoundValue = -1>
int LookupKeyIndex(const std::unordered_map<std::string, int>& key_to_index,
const std::string& type) {
auto it = key_to_index.find(type);
if (it == key_to_index.end()) {
return NotFoundValue;
}
return it->second;
}
template <typename T>
std::vector<T> AddVectorElement(const std::vector<T>& values, T new_element) {
std::vector<T> out;
out.reserve(values.size() + 1);
for (size_t i = 0; i < values.size(); ++i) {
out.push_back(values[i]);
}
out.emplace_back(std::move(new_element));
return out;
}
std::string BuildPath(const std::vector<std::string>& paths) {
std::string path;
for (const auto& p : paths) {
if (p.back() == '/') {
path += p;
} else {
path += p + "/";
}
}
return path;
}
} // namespace
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_nullable == rhs.is_nullable) &&
(lhs.cardinality == rhs.cardinality);
}
PropertyGroup::PropertyGroup(const std::vector<Property>& properties,
FileType file_type, const std::string& prefix)
: properties_(properties), file_type_(file_type), prefix_(prefix) {
if (prefix_.empty() && !properties_.empty()) {
for (const auto& p : properties_) {
prefix_ += p.name + REGULAR_SEPARATOR;
}
prefix_.back() = '/';
}
}
const std::vector<Property>& PropertyGroup::GetProperties() const {
return properties_;
}
bool PropertyGroup::HasProperty(const std::string& property_name) const {
for (const auto& p : properties_) {
if (p.name == property_name) {
return true;
}
}
return false;
}
bool PropertyGroup::IsValidated() const {
if (prefix_.empty() ||
(file_type_ != FileType::CSV && file_type_ != FileType::PARQUET &&
file_type_ != FileType::ORC)) {
return false;
}
if (properties_.empty()) {
return false;
}
std::unordered_set<std::string> check_property_unique_set;
for (const auto& p : properties_) {
if (p.name.empty() || p.type == nullptr) {
return false;
}
if (check_property_unique_set.find(p.name) !=
check_property_unique_set.end()) {
return false;
} else {
check_property_unique_set.insert(p.name);
}
// TODO(@acezen): support list type in csv file
if (p.type->id() == Type::LIST && file_type_ == FileType::CSV) {
// list type is not supported in csv file
return false;
}
// TODO(@yangxk): support cardinality in csv file
if (p.cardinality != Cardinality::SINGLE && file_type_ == FileType::CSV) {
// list cardinality is not supported in csv file
return false;
}
}
return true;
}
std::shared_ptr<PropertyGroup> CreatePropertyGroup(
const std::vector<Property>& properties, FileType file_type,
const std::string& prefix) {
if (properties.empty()) {
// empty property group is not allowed
return nullptr;
}
return std::make_shared<PropertyGroup>(properties, file_type, prefix);
}
bool operator==(const PropertyGroup& lhs, const PropertyGroup& rhs) {
return (lhs.GetPrefix() == rhs.GetPrefix()) &&
(lhs.GetFileType() == rhs.GetFileType()) &&
(lhs.GetProperties() == rhs.GetProperties());
}
AdjacentList::AdjacentList(AdjListType type, FileType file_type,
const std::string& prefix)
: type_(type), file_type_(file_type), prefix_(prefix) {
if (prefix_.empty()) {
prefix_ = std::string(AdjListTypeToString(type_)) + "/";
}
}
bool AdjacentList::IsValidated() const {
if (type_ != AdjListType::unordered_by_source &&
type_ != AdjListType::ordered_by_source &&
type_ != AdjListType::unordered_by_dest &&
type_ != AdjListType::ordered_by_dest) {
return false;
}
if (prefix_.empty() ||
(file_type_ != FileType::CSV && file_type_ != FileType::PARQUET &&
file_type_ != FileType::ORC)) {
return false;
}
return true;
}
std::shared_ptr<AdjacentList> CreateAdjacentList(AdjListType type,
FileType file_type,
const std::string& prefix) {
return std::make_shared<AdjacentList>(type, file_type, prefix);
}
class VertexInfo::Impl {
public:
Impl(const std::string& type, IdType chunk_size, const std::string& prefix,
const PropertyGroupVector& property_groups,
const std::vector<std::string>& labels,
std::shared_ptr<const InfoVersion> version)
: type_(type),
chunk_size_(chunk_size),
property_groups_(std::move(property_groups)),
labels_(labels),
prefix_(prefix),
version_(std::move(version)) {
if (prefix_.empty()) {
prefix_ = type_ + "/"; // default prefix
}
for (size_t i = 0; i < property_groups_.size(); i++) {
const auto& pg = property_groups_[i];
if (!pg) {
continue;
}
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);
property_name_to_cardinality_.emplace(p.name, p.cardinality);
}
}
}
bool is_validated() const noexcept {
if (type_.empty() || chunk_size_ <= 0 || prefix_.empty()) {
return false;
}
std::unordered_set<std::string> check_property_unique_set;
for (const auto& pg : property_groups_) {
// check if property group is validated
if (!pg || !pg->IsValidated()) {
return false;
}
// check if property name is unique in all property groups
for (const auto& p : pg->GetProperties()) {
if (check_property_unique_set.find(p.name) !=
check_property_unique_set.end()) {
return false;
} else {
check_property_unique_set.insert(p.name);
}
}
}
return true;
}
std::string type_;
IdType chunk_size_;
PropertyGroupVector property_groups_;
std::vector<std::string> labels_;
std::string prefix_;
std::shared_ptr<const InfoVersion> version_;
std::unordered_map<std::string, int> property_name_to_index_;
std::unordered_map<std::string, bool> property_name_to_primary_;
std::unordered_map<std::string, bool> property_name_to_nullable_;
std::unordered_map<std::string, std::shared_ptr<DataType>>
property_name_to_type_;
std::unordered_map<std::string, Cardinality> property_name_to_cardinality_;
};
VertexInfo::VertexInfo(const std::string& type, IdType chunk_size,
const PropertyGroupVector& property_groups,
const std::vector<std::string>& labels,
const std::string& prefix,
std::shared_ptr<const InfoVersion> version)
: impl_(new Impl(type, chunk_size, prefix, property_groups, labels,
version)) {}
VertexInfo::~VertexInfo() = default;
const std::string& VertexInfo::GetType() const { return impl_->type_; }
IdType VertexInfo::GetChunkSize() const { return impl_->chunk_size_; }
const std::string& VertexInfo::GetPrefix() const { return impl_->prefix_; }
const std::vector<std::string>& VertexInfo::GetLabels() const {
return impl_->labels_;
}
const std::shared_ptr<const InfoVersion>& VertexInfo::version() const {
return impl_->version_;
}
Result<std::string> VertexInfo::GetFilePath(
std::shared_ptr<PropertyGroup> property_group, IdType chunk_index) const {
if (property_group == nullptr) {
return Status::Invalid("property group is nullptr");
}
return BuildPath({impl_->prefix_, property_group->GetPrefix()}) + "chunk" +
std::to_string(chunk_index);
}
Result<std::string> VertexInfo::GetPathPrefix(
std::shared_ptr<PropertyGroup> property_group) const {
if (property_group == nullptr) {
return Status::Invalid("property group is nullptr");
}
return BuildPath({impl_->prefix_, property_group->GetPrefix()});
}
Result<std::string> VertexInfo::GetVerticesNumFilePath() const {
return BuildPath({impl_->prefix_}) + "vertex_count";
}
int VertexInfo::PropertyGroupNum() const {
return static_cast<int>(impl_->property_groups_.size());
}
std::shared_ptr<PropertyGroup> VertexInfo::GetPropertyGroup(
const std::string& property_name) const {
int i = LookupKeyIndex(impl_->property_name_to_index_, property_name);
return i == -1 ? nullptr : impl_->property_groups_[i];
}
std::shared_ptr<PropertyGroup> VertexInfo::GetPropertyGroupByIndex(
int index) const {
if (index < 0 || index >= static_cast<int>(impl_->property_groups_.size())) {
return nullptr;
}
return impl_->property_groups_[index];
}
const PropertyGroupVector& VertexInfo::GetPropertyGroups() const {
return impl_->property_groups_;
}
bool VertexInfo::IsPrimaryKey(const std::string& property_name) const {
auto it = impl_->property_name_to_primary_.find(property_name);
if (it == impl_->property_name_to_primary_.end()) {
return false;
}
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();
}
bool VertexInfo::HasPropertyGroup(
const std::shared_ptr<PropertyGroup>& property_group) const {
if (property_group == nullptr) {
return false;
}
for (const auto& pg : impl_->property_groups_) {
if (*pg == *property_group) {
return true;
}
}
return false;
}
Result<std::shared_ptr<DataType>> VertexInfo::GetPropertyType(
const std::string& property_name) const {
auto it = impl_->property_name_to_type_.find(property_name);
if (it == impl_->property_name_to_type_.end()) {
return Status::Invalid("property name not found: ", property_name);
}
return it->second;
}
Result<Cardinality> VertexInfo::GetPropertyCardinality(
const std::string& property_name) const {
auto it = impl_->property_name_to_cardinality_.find(property_name);
if (it == impl_->property_name_to_cardinality_.end()) {
return Status::Invalid("property name not found: ", property_name);
}
return it->second;
}
Result<std::shared_ptr<VertexInfo>> VertexInfo::AddPropertyGroup(
std::shared_ptr<PropertyGroup> property_group) const {
if (property_group == nullptr) {
return Status::Invalid("property group is nullptr");
}
for (const auto& property : property_group->GetProperties()) {
if (HasProperty(property.name)) {
return Status::Invalid("property in the property group already exists: ",
property.name);
}
}
return std::make_shared<VertexInfo>(
impl_->type_, impl_->chunk_size_,
AddVectorElement(impl_->property_groups_, property_group), impl_->labels_,
impl_->prefix_, impl_->version_);
}
bool VertexInfo::IsValidated() const { return impl_->is_validated(); }
std::shared_ptr<VertexInfo> CreateVertexInfo(
const std::string& type, IdType chunk_size,
const PropertyGroupVector& property_groups,
const std::vector<std::string>& labels, const std::string& prefix,
std::shared_ptr<const InfoVersion> version) {
if (type.empty() || chunk_size <= 0) {
return nullptr;
}
return std::make_shared<VertexInfo>(type, chunk_size, property_groups, labels,
prefix, version);
}
Result<std::shared_ptr<VertexInfo>> VertexInfo::Load(
std::shared_ptr<Yaml> yaml) {
if (yaml == nullptr) {
return Status::Invalid("yaml shared pointer is nullptr");
}
std::string type = yaml->operator[]("type").As<std::string>();
IdType chunk_size =
static_cast<IdType>(yaml->operator[]("chunk_size").As<int64_t>());
std::string prefix;
if (!yaml->operator[]("prefix").IsNone()) {
prefix = yaml->operator[]("prefix").As<std::string>();
}
std::vector<std::string> labels;
const auto& labels_node = yaml->operator[]("labels");
if (labels_node.IsSequence()) {
for (auto it = labels_node.Begin(); it != labels_node.End(); it++) {
labels.push_back((*it).second.As<std::string>());
}
}
std::shared_ptr<const InfoVersion> version = nullptr;
if (!yaml->operator[]("version").IsNone()) {
GAR_ASSIGN_OR_RAISE(
version,
InfoVersion::Parse(yaml->operator[]("version").As<std::string>()));
}
PropertyGroupVector property_groups;
auto property_groups_node = yaml->operator[]("property_groups");
if (!property_groups_node.IsNone()) { // property_groups exist
for (auto it = property_groups_node.Begin();
it != property_groups_node.End(); it++) {
std::string pg_prefix;
auto& node = (*it).second;
if (!node["prefix"].IsNone()) {
pg_prefix = node["prefix"].As<std::string>();
}
auto file_type = StringToFileType(node["file_type"].As<std::string>());
std::vector<Property> property_vec;
auto& properties = node["properties"];
for (auto iit = properties.Begin(); iit != properties.End(); iit++) {
auto& p_node = (*iit).second;
auto property_name = p_node["name"].As<std::string>();
auto property_type =
DataType::TypeNameToDataType(p_node["data_type"].As<std::string>());
bool is_primary = p_node["is_primary"].As<bool>();
bool is_nullable =
p_node["is_nullable"].IsNone() || p_node["is_nullable"].As<bool>();
Cardinality cardinality = Cardinality::SINGLE;
if (!p_node["cardinality"].IsNone()) {
cardinality =
StringToCardinality(p_node["cardinality"].As<std::string>());
}
property_vec.emplace_back(property_name, property_type, is_primary,
is_nullable, cardinality);
}
property_groups.push_back(
std::make_shared<PropertyGroup>(property_vec, file_type, pg_prefix));
}
}
return std::make_shared<VertexInfo>(type, chunk_size, property_groups, labels,
prefix, version);
}
Result<std::shared_ptr<VertexInfo>> VertexInfo::Load(const std::string& input) {
GAR_ASSIGN_OR_RAISE(auto yaml, Yaml::Load(input));
return VertexInfo::Load(yaml);
}
Result<std::string> VertexInfo::Dump() const noexcept {
if (!IsValidated()) {
return Status::Invalid("The vertex info is not validated");
}
std::string dump_string;
::Yaml::Node node;
try {
node["type"] = impl_->type_;
node["chunk_size"] = std::to_string(impl_->chunk_size_);
node["prefix"] = impl_->prefix_;
if (impl_->labels_.size() > 0) {
node["labels"];
for (const auto& label : impl_->labels_) {
node["labels"].PushBack();
node["labels"][node["labels"].Size() - 1] = label;
}
}
for (const auto& pg : impl_->property_groups_) {
::Yaml::Node pg_node;
if (!pg->GetPrefix().empty()) {
pg_node["prefix"] = pg->GetPrefix();
}
pg_node["file_type"] = FileTypeToString(pg->GetFileType());
for (const auto& p : pg->GetProperties()) {
::Yaml::Node p_node;
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";
if (p.cardinality != Cardinality::SINGLE) {
p_node["cardinality"] = CardinalityToString(p.cardinality);
}
pg_node["properties"].PushBack();
pg_node["properties"][pg_node["properties"].Size() - 1] = p_node;
}
node["property_groups"].PushBack();
node["property_groups"][node["property_groups"].Size() - 1] = pg_node;
}
if (impl_->version_ != nullptr) {
node["version"] = impl_->version_->ToString();
}
::Yaml::Serialize(node, dump_string);
} catch (const std::exception& e) {
return Status::Invalid("Failed to dump vertex info: ", e.what());
}
return dump_string;
}
Status VertexInfo::Save(const std::string& path) const {
std::string no_url_path;
GAR_ASSIGN_OR_RAISE(auto fs, FileSystemFromUriOrPath(path, &no_url_path));
GAR_ASSIGN_OR_RAISE(auto yaml_content, this->Dump());
return fs->WriteValueToFile(yaml_content, no_url_path);
}
class EdgeInfo::Impl {
public:
Impl(const std::string& src_type, const std::string& edge_type,
const std::string& dst_type, IdType chunk_size, IdType src_chunk_size,
IdType dst_chunk_size, bool directed, const std::string& prefix,
const AdjacentListVector& adjacent_lists,
const PropertyGroupVector& property_groups,
std::shared_ptr<const InfoVersion> version)
: src_type_(src_type),
edge_type_(edge_type),
dst_type_(dst_type),
chunk_size_(chunk_size),
src_chunk_size_(src_chunk_size),
dst_chunk_size_(dst_chunk_size),
directed_(directed),
prefix_(prefix),
adjacent_lists_(std::move(adjacent_lists)),
property_groups_(std::move(property_groups)),
version_(std::move(version)) {
if (prefix_.empty()) {
prefix_ = src_type_ + REGULAR_SEPARATOR + edge_type_ + REGULAR_SEPARATOR +
dst_type_ + "/"; // default prefix
}
for (size_t i = 0; i < adjacent_lists_.size(); i++) {
if (!adjacent_lists_[i]) {
continue;
}
auto adj_list_type = adjacent_lists_[i]->GetType();
adjacent_list_type_to_index_[adj_list_type] = i;
}
for (size_t i = 0; i < property_groups_.size(); i++) {
const auto& pg = property_groups_[i];
if (!pg) {
continue;
}
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);
}
}
}
bool is_validated() const noexcept {
if (src_type_.empty() || edge_type_.empty() || dst_type_.empty() ||
chunk_size_ <= 0 || src_chunk_size_ <= 0 || dst_chunk_size_ <= 0 ||
prefix_.empty() || adjacent_lists_.empty()) {
return false;
}
for (const auto& al : adjacent_lists_) {
if (!al || !al->IsValidated()) {
return false;
}
}
std::unordered_set<std::string> check_property_unique_set;
for (const auto& pg : property_groups_) {
// check if property group is validated
if (!pg || !pg->IsValidated()) {
return false;
}
// check if property name is unique in all property groups
for (const auto& p : pg->GetProperties()) {
if (p.cardinality != Cardinality::SINGLE) {
// edge property only supports single cardinality
std::cout
<< "Edge property only supports single cardinality, but got: "
<< CardinalityToString(p.cardinality) << std::endl;
return false;
}
if (check_property_unique_set.find(p.name) !=
check_property_unique_set.end()) {
return false;
} else {
check_property_unique_set.insert(p.name);
}
}
}
if (adjacent_lists_.size() != adjacent_list_type_to_index_.size()) {
return false;
}
return true;
}
std::string src_type_;
std::string edge_type_;
std::string dst_type_;
IdType chunk_size_;
IdType src_chunk_size_;
IdType dst_chunk_size_;
bool directed_;
std::string prefix_;
AdjacentListVector adjacent_lists_;
PropertyGroupVector property_groups_;
std::unordered_map<AdjListType, int> adjacent_list_type_to_index_;
std::unordered_map<std::string, int> property_name_to_index_;
std::unordered_map<std::string, bool> property_name_to_primary_;
std::unordered_map<std::string, bool> property_name_to_nullable_;
std::unordered_map<std::string, std::shared_ptr<DataType>>
property_name_to_type_;
std::shared_ptr<const InfoVersion> version_;
};
EdgeInfo::EdgeInfo(const std::string& src_type, const std::string& edge_type,
const std::string& dst_type, IdType chunk_size,
IdType src_chunk_size, IdType dst_chunk_size, bool directed,
const AdjacentListVector& adjacent_lists,
const PropertyGroupVector& property_groups,
const std::string& prefix,
std::shared_ptr<const InfoVersion> version)
: impl_(new Impl(src_type, edge_type, dst_type, chunk_size, src_chunk_size,
dst_chunk_size, directed, prefix, adjacent_lists,
property_groups, version)) {}
EdgeInfo::~EdgeInfo() = default;
const std::string& EdgeInfo::GetSrcType() const { return impl_->src_type_; }
const std::string& EdgeInfo::GetEdgeType() const { return impl_->edge_type_; }
const std::string& EdgeInfo::GetDstType() const { return impl_->dst_type_; }
IdType EdgeInfo::GetChunkSize() const { return impl_->chunk_size_; }
IdType EdgeInfo::GetSrcChunkSize() const { return impl_->src_chunk_size_; }
IdType EdgeInfo::GetDstChunkSize() const { return impl_->dst_chunk_size_; }
const std::string& EdgeInfo::GetPrefix() const { return impl_->prefix_; }
bool EdgeInfo::IsDirected() const { return impl_->directed_; }
const std::shared_ptr<const InfoVersion>& EdgeInfo::version() const {
return impl_->version_;
}
bool EdgeInfo::HasAdjacentListType(AdjListType adj_list_type) const {
return impl_->adjacent_list_type_to_index_.find(adj_list_type) !=
impl_->adjacent_list_type_to_index_.end();
}
bool EdgeInfo::HasProperty(const std::string& property_name) const {
return impl_->property_name_to_index_.find(property_name) !=
impl_->property_name_to_index_.end();
}
bool EdgeInfo::HasPropertyGroup(
const std::shared_ptr<PropertyGroup>& property_group) const {
if (property_group == nullptr) {
return false;
}
for (const auto& pg : impl_->property_groups_) {
if (*pg == *property_group) {
return true;
}
}
return false;
}
std::shared_ptr<AdjacentList> EdgeInfo::GetAdjacentList(
AdjListType adj_list_type) const {
auto it = impl_->adjacent_list_type_to_index_.find(adj_list_type);
if (it == impl_->adjacent_list_type_to_index_.end()) {
return nullptr;
}
return impl_->adjacent_lists_[it->second];
}
int EdgeInfo::PropertyGroupNum() const {
return static_cast<int>(impl_->property_groups_.size());
}
const PropertyGroupVector& EdgeInfo::GetPropertyGroups() const {
return impl_->property_groups_;
}
std::shared_ptr<PropertyGroup> EdgeInfo::GetPropertyGroup(
const std::string& property_name) const {
int i = LookupKeyIndex(impl_->property_name_to_index_, property_name);
return i == -1 ? nullptr : impl_->property_groups_[i];
}
std::shared_ptr<PropertyGroup> EdgeInfo::GetPropertyGroupByIndex(
int index) const {
if (index < 0 || index >= static_cast<int>(impl_->property_groups_.size())) {
return nullptr;
}
return impl_->property_groups_[index];
}
Result<std::string> EdgeInfo::GetVerticesNumFilePath(
AdjListType adj_list_type) const {
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix()}) +
"vertex_count";
}
Result<std::string> EdgeInfo::GetEdgesNumFilePath(
IdType vertex_chunk_index, AdjListType adj_list_type) const {
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix()}) +
"edge_count" + std::to_string(vertex_chunk_index);
}
Result<std::string> EdgeInfo::GetAdjListFilePath(
IdType vertex_chunk_index, IdType edge_chunk_index,
AdjListType adj_list_type) const {
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix()}) +
"adj_list/part" + std::to_string(vertex_chunk_index) + "/chunk" +
std::to_string(edge_chunk_index);
}
Result<std::string> EdgeInfo::GetAdjListPathPrefix(
AdjListType adj_list_type) const {
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix()}) +
"adj_list/";
}
Result<std::string> EdgeInfo::GetAdjListOffsetFilePath(
IdType vertex_chunk_index, AdjListType adj_list_type) const {
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix()}) +
"offset/chunk" + std::to_string(vertex_chunk_index);
}
Result<std::string> EdgeInfo::GetOffsetPathPrefix(
AdjListType adj_list_type) const {
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix()}) +
"offset/";
}
Result<std::string> EdgeInfo::GetPropertyFilePath(
const std::shared_ptr<PropertyGroup>& property_group,
AdjListType adj_list_type, IdType vertex_chunk_index,
IdType edge_chunk_index) const {
if (property_group == nullptr) {
return Status::Invalid("property group is nullptr");
}
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix(),
property_group->GetPrefix()}) +
"part" + std::to_string(vertex_chunk_index) + "/chunk" +
std::to_string(edge_chunk_index);
}
Result<std::string> EdgeInfo::GetPropertyGroupPathPrefix(
const std::shared_ptr<PropertyGroup>& property_group,
AdjListType adj_list_type) const {
if (property_group == nullptr) {
return Status::Invalid("property group is nullptr");
}
CHECK_HAS_ADJ_LIST_TYPE(adj_list_type);
int i = impl_->adjacent_list_type_to_index_.at(adj_list_type);
return BuildPath({impl_->prefix_, impl_->adjacent_lists_[i]->GetPrefix(),
property_group->GetPrefix()});
}
Result<std::shared_ptr<DataType>> EdgeInfo::GetPropertyType(
const std::string& property_name) const {
auto it = impl_->property_name_to_type_.find(property_name);
if (it == impl_->property_name_to_type_.end()) {
return Status::Invalid("property name not found: ", property_name);
}
return it->second;
}
bool EdgeInfo::IsPrimaryKey(const std::string& property_name) const {
auto it = impl_->property_name_to_primary_.find(property_name);
if (it == impl_->property_name_to_primary_.end()) {
return false;
}
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<std::shared_ptr<EdgeInfo>> EdgeInfo::AddAdjacentList(
std::shared_ptr<AdjacentList> adj_list) const {
if (adj_list == nullptr) {
return Status::Invalid("adj list is nullptr");
}
if (HasAdjacentListType(adj_list->GetType())) {
return Status::Invalid("adj list type already exists: ",
AdjListTypeToString(adj_list->GetType()));
}
return std::make_shared<EdgeInfo>(
impl_->src_type_, impl_->edge_type_, impl_->dst_type_, impl_->chunk_size_,
impl_->src_chunk_size_, impl_->dst_chunk_size_, impl_->directed_,
AddVectorElement(impl_->adjacent_lists_, adj_list),
impl_->property_groups_, impl_->prefix_, impl_->version_);
}
Result<std::shared_ptr<EdgeInfo>> EdgeInfo::AddPropertyGroup(
std::shared_ptr<PropertyGroup> property_group) const {
if (property_group == nullptr) {
return Status::Invalid("property group is nullptr");
}
for (const auto& property : property_group->GetProperties()) {
if (HasProperty(property.name)) {
return Status::Invalid("property in property group already exists: ",
property.name);
}
}
return std::make_shared<EdgeInfo>(
impl_->src_type_, impl_->edge_type_, impl_->dst_type_, impl_->chunk_size_,
impl_->src_chunk_size_, impl_->dst_chunk_size_, impl_->directed_,
impl_->adjacent_lists_,
AddVectorElement(impl_->property_groups_, property_group), impl_->prefix_,
impl_->version_);
}
bool EdgeInfo::IsValidated() const { return impl_->is_validated(); }
std::shared_ptr<EdgeInfo> CreateEdgeInfo(
const std::string& src_type, const std::string& edge_type,
const std::string& dst_type, IdType chunk_size, IdType src_chunk_size,
IdType dst_chunk_size, bool directed,
const AdjacentListVector& adjacent_lists,
const PropertyGroupVector& property_groups, const std::string& prefix,
std::shared_ptr<const InfoVersion> version) {
if (src_type.empty() || edge_type.empty() || dst_type.empty() ||
chunk_size <= 0 || src_chunk_size <= 0 || dst_chunk_size <= 0 ||
adjacent_lists.empty()) {
return nullptr;
}
return std::make_shared<EdgeInfo>(
src_type, edge_type, dst_type, chunk_size, src_chunk_size, dst_chunk_size,
directed, adjacent_lists, property_groups, prefix, version);
}
Result<std::shared_ptr<EdgeInfo>> EdgeInfo::Load(std::shared_ptr<Yaml> yaml) {
if (yaml == nullptr) {
return Status::Invalid("yaml shared pointer is nullptr.");
}
std::string src_type = yaml->operator[]("src_type").As<std::string>();
std::string edge_type = yaml->operator[]("edge_type").As<std::string>();
std::string dst_type = yaml->operator[]("dst_type").As<std::string>();
IdType chunk_size =
static_cast<IdType>(yaml->operator[]("chunk_size").As<int64_t>());
IdType src_chunk_size =
static_cast<IdType>(yaml->operator[]("src_chunk_size").As<int64_t>());
IdType dst_chunk_size =
static_cast<IdType>(yaml->operator[]("dst_chunk_size").As<int64_t>());
bool directed = yaml->operator[]("directed").As<bool>();
std::string prefix;
if (!yaml->operator[]("prefix").IsNone()) {
prefix = yaml->operator[]("prefix").As<std::string>();
}
std::shared_ptr<const InfoVersion> version = nullptr;
if (!yaml->operator[]("version").IsNone()) {
GAR_ASSIGN_OR_RAISE(
version,
InfoVersion::Parse(yaml->operator[]("version").As<std::string>()));
}
AdjacentListVector adjacent_lists;
PropertyGroupVector property_groups;
auto adj_lists_node = yaml->operator[]("adj_lists");
if (adj_lists_node.IsSequence()) {
for (auto it = adj_lists_node.Begin(); it != adj_lists_node.End(); it++) {
auto& node = (*it).second;
auto ordered = node["ordered"].As<bool>();
auto aligned = node["aligned_by"].As<std::string>();
auto adj_list_type = OrderedAlignedToAdjListType(ordered, aligned);
auto file_type = StringToFileType(node["file_type"].As<std::string>());
std::string adj_list_prefix;
if (!node["prefix"].IsNone()) {
adj_list_prefix = node["prefix"].As<std::string>();
}
adjacent_lists.push_back(std::make_shared<AdjacentList>(
adj_list_type, file_type, adj_list_prefix));
}
}
auto property_groups_node = yaml->operator[]("property_groups");
if (!property_groups_node.IsNone()) { // property_groups exist
for (auto pg_it = property_groups_node.Begin();
pg_it != property_groups_node.End(); pg_it++) {
auto& pg_node = (*pg_it).second;
std::string pg_prefix;
if (!pg_node["prefix"].IsNone()) {
pg_prefix = pg_node["prefix"].As<std::string>();
}
auto file_type = StringToFileType(pg_node["file_type"].As<std::string>());
auto properties = pg_node["properties"];
std::vector<Property> property_vec;
for (auto p_it = properties.Begin(); p_it != properties.End(); p_it++) {
auto& p_node = (*p_it).second;
auto property_name = p_node["name"].As<std::string>();
auto property_type =
DataType::TypeNameToDataType(p_node["data_type"].As<std::string>());
if (!p_node["cardinality"].IsNone() &&
StringToCardinality(p_node["cardinality"].As<std::string>()) !=
Cardinality::SINGLE) {
return Status::YamlError(
"Unsupported set cardinality for edge property.");
}
bool is_primary = p_node["is_primary"].As<bool>();
bool is_nullable =
p_node["is_nullable"].IsNone() || p_node["is_nullable"].As<bool>();
property_vec.emplace_back(property_name, property_type, is_primary,
is_nullable);
}
property_groups.push_back(
std::make_shared<PropertyGroup>(property_vec, file_type, pg_prefix));
}
}
return std::make_shared<EdgeInfo>(
src_type, edge_type, dst_type, chunk_size, src_chunk_size, dst_chunk_size,
directed, adjacent_lists, property_groups, prefix, version);
}
Result<std::shared_ptr<EdgeInfo>> EdgeInfo::Load(const std::string& input) {
GAR_ASSIGN_OR_RAISE(auto yaml, Yaml::Load(input));
return EdgeInfo::Load(yaml);
}
Result<std::string> EdgeInfo::Dump() const noexcept {
if (!IsValidated()) {
return Status::Invalid("The edge info is not validated.");
}
std::string dump_string;
::Yaml::Node node;
try {
node["src_type"] = impl_->src_type_;
node["edge_type"] = impl_->edge_type_;
node["dst_type"] = impl_->dst_type_;
node["chunk_size"] = std::to_string(impl_->chunk_size_);
node["src_chunk_size"] = std::to_string(impl_->src_chunk_size_);
node["dst_chunk_size"] = std::to_string(impl_->dst_chunk_size_);
node["prefix"] = impl_->prefix_;
node["directed"] = impl_->directed_ ? "true" : "false";
for (const auto& adjacent_list : impl_->adjacent_lists_) {
::Yaml::Node adj_list_node;
auto adj_list_type = adjacent_list->GetType();
auto pair = AdjListTypeToOrderedAligned(adj_list_type);
adj_list_node["ordered"] = pair.first ? "true" : "false";
adj_list_node["aligned_by"] = pair.second;
adj_list_node["prefix"] = adjacent_list->GetPrefix();
adj_list_node["file_type"] =
FileTypeToString(adjacent_list->GetFileType());
node["adj_lists"].PushBack();
node["adj_lists"][node["adj_lists"].Size() - 1] = adj_list_node;
}
for (const auto& pg : impl_->property_groups_) {
::Yaml::Node pg_node;
if (!pg->GetPrefix().empty()) {
pg_node["prefix"] = pg->GetPrefix();
}
pg_node["file_type"] = FileTypeToString(pg->GetFileType());
for (const auto& p : pg->GetProperties()) {
::Yaml::Node p_node;
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;
}
node["property_groups"].PushBack();
node["property_groups"][node["property_groups"].Size() - 1] = pg_node;
}
if (impl_->version_ != nullptr) {
node["version"] = impl_->version_->ToString();
}
::Yaml::Serialize(node, dump_string);
} catch (const std::exception& e) {
return Status::Invalid("Failed to dump edge info: ", e.what());
}
return dump_string;
}
Status EdgeInfo::Save(const std::string& path) const {
std::string no_url_path;
GAR_ASSIGN_OR_RAISE(auto fs, FileSystemFromUriOrPath(path, &no_url_path));
GAR_ASSIGN_OR_RAISE(auto yaml_content, this->Dump());
return fs->WriteValueToFile(yaml_content, no_url_path);
}
namespace {
static std::string PathToDirectory(const std::string& path) {
if (path.rfind("s3://", 0) == 0) {
int t = path.find_last_of('?');
std::string prefix = path.substr(0, t);
std::string suffix = path.substr(t);
const size_t last_slash_idx = prefix.rfind('/');
if (std::string::npos != last_slash_idx) {
return prefix.substr(0, last_slash_idx + 1) + suffix;
}
} else {
const size_t last_slash_idx = path.rfind('/');
if (std::string::npos != last_slash_idx) {
return path.substr(0, last_slash_idx + 1); // +1 to include the slash
}
}
return path;
}
static Result<std::shared_ptr<GraphInfo>> ConstructGraphInfo(
std::shared_ptr<Yaml> graph_meta, const std::string& default_name,
const std::string& default_prefix, const std::shared_ptr<FileSystem> fs,
const std::string& no_url_path) {
std::string name = default_name;
std::string prefix = default_prefix;
if (!graph_meta->operator[]("name").IsNone()) {
name = graph_meta->operator[]("name").As<std::string>();
}
if (!graph_meta->operator[]("prefix").IsNone()) {
prefix = graph_meta->operator[]("prefix").As<std::string>();
}
std::shared_ptr<const InfoVersion> version = nullptr;
if (!graph_meta->operator[]("version").IsNone()) {
GAR_ASSIGN_OR_RAISE(
version, InfoVersion::Parse(
graph_meta->operator[]("version").As<std::string>()));
}
std::unordered_map<std::string, std::string> extra_info;
if (!graph_meta->operator[]("extra_info").IsNone()) {
auto& extra_info_node = graph_meta->operator[]("extra_info");
for (auto it = extra_info_node.Begin(); it != extra_info_node.End(); it++) {
auto node = (*it).second;
auto key = node["key"].As<std::string>();
auto value = node["value"].As<std::string>();
extra_info.emplace(key, value);
}
}
VertexInfoVector vertex_infos;
EdgeInfoVector edge_infos;
const auto& vertices = graph_meta->operator[]("vertices");
if (vertices.IsSequence()) {
for (auto it = vertices.Begin(); it != vertices.End(); it++) {
std::string vertex_meta_file =
no_url_path + (*it).second.As<std::string>();
GAR_ASSIGN_OR_RAISE(auto input,
fs->ReadFileToValue<std::string>(vertex_meta_file));
GAR_ASSIGN_OR_RAISE(auto vertex_meta, Yaml::Load(input));
GAR_ASSIGN_OR_RAISE(auto vertex_info, VertexInfo::Load(vertex_meta));
vertex_infos.push_back(vertex_info);
}
}
const auto& edges = graph_meta->operator[]("edges");
if (edges.IsSequence()) {
for (auto it = edges.Begin(); it != edges.End(); it++) {
std::string edge_meta_file = no_url_path + (*it).second.As<std::string>();
GAR_ASSIGN_OR_RAISE(auto input,
fs->ReadFileToValue<std::string>(edge_meta_file));
GAR_ASSIGN_OR_RAISE(auto edge_meta, Yaml::Load(input));
GAR_ASSIGN_OR_RAISE(auto edge_info, EdgeInfo::Load(edge_meta));
edge_infos.push_back(edge_info);
}
}
std::vector<std::string> labels;
if (!graph_meta->operator[]("labels").IsNone()) {
const auto& labels_node = graph_meta->operator[]("labels");
if (labels_node.IsSequence()) {
for (auto it = labels_node.Begin(); it != labels_node.End(); it++) {
labels.push_back((*it).second.As<std::string>());
}
}
}
return std::make_shared<GraphInfo>(name, vertex_infos, edge_infos, labels,
prefix, version, extra_info);
}
} // namespace
class GraphInfo::Impl {
public:
Impl(const std::string& graph_name, VertexInfoVector vertex_infos,
EdgeInfoVector edge_infos, const std::vector<std::string>& labels,
const std::string& prefix, std::shared_ptr<const InfoVersion> version,
const std::unordered_map<std::string, std::string>& extra_info)
: name_(graph_name),
vertex_infos_(std::move(vertex_infos)),
edge_infos_(std::move(edge_infos)),
labels_(labels),
prefix_(prefix),
version_(std::move(version)),
extra_info_(extra_info) {
for (size_t i = 0; i < vertex_infos_.size(); i++) {
if (vertex_infos_[i] != nullptr) {
vtype_to_index_[vertex_infos_[i]->GetType()] = i;
}
}
for (size_t i = 0; i < edge_infos_.size(); i++) {
if (edge_infos_[i] != nullptr) {
std::string edge_key = ConcatEdgeTriple(edge_infos_[i]->GetSrcType(),
edge_infos_[i]->GetEdgeType(),
edge_infos_[i]->GetDstType());
etype_to_index_[edge_key] = i;
}
}
}
bool is_validated() const noexcept {
if (name_.empty() || prefix_.empty()) {
return false;
}
for (const auto& v : vertex_infos_) {
if (!v || !v->IsValidated()) {
return false;
}
}
for (const auto& e : edge_infos_) {
if (!e || !e->IsValidated()) {
return false;
}
}
if (vertex_infos_.size() != vtype_to_index_.size() ||
edge_infos_.size() != etype_to_index_.size()) {
return false;
}
return true;
}
std::string name_;
VertexInfoVector vertex_infos_;
EdgeInfoVector edge_infos_;
std::vector<std::string> labels_;
std::string prefix_;
std::shared_ptr<const InfoVersion> version_;
std::unordered_map<std::string, std::string> extra_info_;
std::unordered_map<std::string, int> vtype_to_index_;
std::unordered_map<std::string, int> etype_to_index_;
};
GraphInfo::GraphInfo(
const std::string& graph_name, VertexInfoVector vertex_infos,
EdgeInfoVector edge_infos, const std::vector<std::string>& labels,
const std::string& prefix, std::shared_ptr<const InfoVersion> version,
const std::unordered_map<std::string, std::string>& extra_info)
: impl_(new Impl(graph_name, std::move(vertex_infos), std::move(edge_infos),
labels, prefix, version, extra_info)) {}
GraphInfo::~GraphInfo() = default;
const std::string& GraphInfo::GetName() const { return impl_->name_; }
const std::vector<std::string>& GraphInfo::GetLabels() const {
return impl_->labels_;
}
const std::string& GraphInfo::GetPrefix() const { return impl_->prefix_; }
const std::shared_ptr<const InfoVersion>& GraphInfo::version() const {
return impl_->version_;
}
const std::unordered_map<std::string, std::string>& GraphInfo::GetExtraInfo()
const {
return impl_->extra_info_;
}
std::shared_ptr<VertexInfo> GraphInfo::GetVertexInfo(
const std::string& type) const {
int i = GetVertexInfoIndex(type);
return i == -1 ? nullptr : impl_->vertex_infos_[i];
}
int GraphInfo::GetVertexInfoIndex(const std::string& type) const {
return LookupKeyIndex(impl_->vtype_to_index_, type);
}
std::shared_ptr<EdgeInfo> GraphInfo::GetEdgeInfo(
const std::string& src_type, const std::string& edge_type,
const std::string& dst_type) const {
int i = GetEdgeInfoIndex(src_type, edge_type, dst_type);
return i == -1 ? nullptr : impl_->edge_infos_[i];
}
int GraphInfo::GetEdgeInfoIndex(const std::string& src_type,
const std::string& edge_type,
const std::string& dst_type) const {
std::string edge_key = ConcatEdgeTriple(src_type, edge_type, dst_type);
return LookupKeyIndex(impl_->etype_to_index_, edge_key);
}
int GraphInfo::VertexInfoNum() const {
return static_cast<int>(impl_->vertex_infos_.size());
}
int GraphInfo::EdgeInfoNum() const {
return static_cast<int>(impl_->edge_infos_.size());
}
const std::shared_ptr<VertexInfo> GraphInfo::GetVertexInfoByIndex(
int index) const {
if (index < 0 || index >= static_cast<int>(impl_->vertex_infos_.size())) {
return nullptr;
}
return impl_->vertex_infos_[index];
}
const std::shared_ptr<EdgeInfo> GraphInfo::GetEdgeInfoByIndex(int index) const {
if (index < 0 || index >= static_cast<int>(impl_->edge_infos_.size())) {
return nullptr;
}
return impl_->edge_infos_[index];
}
const VertexInfoVector& GraphInfo::GetVertexInfos() const {
return impl_->vertex_infos_;
}
const EdgeInfoVector& GraphInfo::GetEdgeInfos() const {
return impl_->edge_infos_;
}
bool GraphInfo::IsValidated() const { return impl_->is_validated(); }
Result<std::shared_ptr<GraphInfo>> GraphInfo::AddVertex(
std::shared_ptr<VertexInfo> vertex_info) const {
if (vertex_info == nullptr) {
return Status::Invalid("vertex info is nullptr");
}
if (GetVertexInfoIndex(vertex_info->GetType()) != -1) {
return Status::Invalid("vertex info already exists");
}
return std::make_shared<GraphInfo>(
impl_->name_, AddVectorElement(impl_->vertex_infos_, vertex_info),
impl_->edge_infos_, impl_->labels_, impl_->prefix_, impl_->version_);
}
Result<std::shared_ptr<GraphInfo>> GraphInfo::AddEdge(
std::shared_ptr<EdgeInfo> edge_info) const {
if (edge_info == nullptr) {
return Status::Invalid("edge info is nullptr");
}
if (GetEdgeInfoIndex(edge_info->GetSrcType(), edge_info->GetEdgeType(),
edge_info->GetDstType()) != -1) {
return Status::Invalid("edge info already exists");
}
return std::make_shared<GraphInfo>(
impl_->name_, impl_->vertex_infos_,
AddVectorElement(impl_->edge_infos_, edge_info), impl_->labels_,
impl_->prefix_, impl_->version_);
}
std::shared_ptr<GraphInfo> CreateGraphInfo(
const std::string& name, const VertexInfoVector& vertex_infos,
const EdgeInfoVector& edge_infos, const std::vector<std::string>& labels,
const std::string& prefix, std::shared_ptr<const InfoVersion> version,
const std::unordered_map<std::string, std::string>& extra_info) {
if (name.empty()) {
return nullptr;
}
return std::make_shared<GraphInfo>(name, vertex_infos, edge_infos, labels,
prefix, version, extra_info);
}
Result<std::shared_ptr<GraphInfo>> GraphInfo::Load(const std::string& path) {
std::string no_url_path;
GAR_ASSIGN_OR_RAISE(auto fs, FileSystemFromUriOrPath(path, &no_url_path));
GAR_ASSIGN_OR_RAISE(auto yaml_content,
fs->ReadFileToValue<std::string>(no_url_path));
GAR_ASSIGN_OR_RAISE(auto graph_meta, Yaml::Load(yaml_content));
std::string default_name = "graph";
std::string default_prefix = PathToDirectory(path);
no_url_path = PathToDirectory(no_url_path);
return ConstructGraphInfo(graph_meta, default_name, default_prefix, fs,
no_url_path);
}
Result<std::shared_ptr<GraphInfo>> GraphInfo::Load(
const std::string& input, const std::string& relative_location) {
GAR_ASSIGN_OR_RAISE(auto graph_meta, Yaml::Load(input));
std::string default_name = "graph";
std::string default_prefix =
relative_location; // default chunk file prefix is relative location
std::string no_url_path;
GAR_ASSIGN_OR_RAISE(auto fs,
FileSystemFromUriOrPath(relative_location, &no_url_path));
return ConstructGraphInfo(graph_meta, default_name, default_prefix, fs,
no_url_path);
}
Result<std::string> GraphInfo::Dump() const {
if (!IsValidated()) {
return Status::Invalid("The graph info is not validated.");
}
::Yaml::Node node;
std::string dump_string;
try {
node["name"] = impl_->name_;
node["prefix"] = impl_->prefix_;
node["vertices"];
node["edges"];
for (const auto& vertex : GetVertexInfos()) {
node["vertices"].PushBack();
node["vertices"][node["vertices"].Size() - 1] =
vertex->GetType() + ".vertex.yaml";
}
for (const auto& edge : GetEdgeInfos()) {
node["edges"].PushBack();
node["edges"][node["edges"].Size() - 1] =
ConcatEdgeTriple(edge->GetSrcType(), edge->GetEdgeType(),
edge->GetDstType()) +
".edge.yaml";
}
if (impl_->labels_.size() > 0) {
node["labels"];
for (const auto& label : impl_->labels_) {
node["labels"].PushBack();
node["labels"][node["labels"].Size() - 1] = label;
}
}
if (impl_->version_ != nullptr) {
node["version"] = impl_->version_->ToString();
}
if (impl_->extra_info_.size() > 0) {
node["extra_info"];
for (const auto& pair : impl_->extra_info_) {
::Yaml::Node extra_info_node;
extra_info_node["key"] = pair.first;
extra_info_node["value"] = pair.second;
node["extra_info"].PushBack();
node["extra_info"][node["extra_info"].Size() - 1] = extra_info_node;
}
}
::Yaml::Serialize(node, dump_string);
} catch (const std::exception& e) {
return Status::Invalid("Failed to dump graph info: ", e.what());
}
return dump_string;
}
Status GraphInfo::Save(const std::string& path) const {
std::string no_url_path;
GAR_ASSIGN_OR_RAISE(auto fs, FileSystemFromUriOrPath(path, &no_url_path));
GAR_ASSIGN_OR_RAISE(auto yaml_content, this->Dump());
return fs->WriteValueToFile(yaml_content, no_url_path);
}
} // namespace graphar