query/procedure: Implement graph related API
Reviewers: mferencevic, llugovic, ipaljak Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2522
This commit is contained in:
parent
bd998dc618
commit
043ae87a83
@ -702,13 +702,74 @@ const mgp_map_item *mgp_map_items_iterator_get(
|
||||
|
||||
const mgp_map_item *mgp_map_items_iterator_next(mgp_map_items_iterator *it) {
|
||||
if (it->current_it == it->map->items.end()) return nullptr;
|
||||
++it->current_it;
|
||||
if (it->current_it == it->map->items.end()) return nullptr;
|
||||
if (++it->current_it == it->map->items.end()) return nullptr;
|
||||
it->current.key = it->current_it->first.c_str();
|
||||
it->current.value = &it->current_it->second;
|
||||
return &it->current;
|
||||
}
|
||||
|
||||
mgp_path *mgp_path_make_with_start(const mgp_vertex *vertex,
|
||||
mgp_memory *memory) {
|
||||
auto *path = new_mgp_object<mgp_path>(memory);
|
||||
if (!path) return nullptr;
|
||||
try {
|
||||
path->vertices.push_back(*vertex);
|
||||
} catch (...) {
|
||||
delete_mgp_object(path);
|
||||
return nullptr;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void mgp_path_destroy(mgp_path *path) { delete_mgp_object(path); }
|
||||
|
||||
int mgp_path_expand(mgp_path *path, const mgp_edge *edge) {
|
||||
CHECK(mgp_path_size(path) == path->vertices.size() - 1) << "Invalid mgp_path";
|
||||
// Check that the both the last vertex on path and dst_vertex are endpoints of
|
||||
// the given edge.
|
||||
const auto *src_vertex = &path->vertices.back();
|
||||
const mgp_vertex *dst_vertex = nullptr;
|
||||
if (mgp_vertex_equal(mgp_edge_get_to(edge), src_vertex)) {
|
||||
dst_vertex = mgp_edge_get_from(edge);
|
||||
} else if (mgp_vertex_equal(mgp_edge_get_from(edge), src_vertex)) {
|
||||
dst_vertex = mgp_edge_get_to(edge);
|
||||
} else {
|
||||
// edge is not a continuation on src_vertex
|
||||
return 0;
|
||||
}
|
||||
// Try appending edge and dst_vertex to path, preserving the original mgp_path
|
||||
// instance if anything fails.
|
||||
try {
|
||||
path->edges.push_back(*edge);
|
||||
} catch (...) {
|
||||
CHECK(mgp_path_size(path) == path->vertices.size() - 1);
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
path->vertices.push_back(*dst_vertex);
|
||||
} catch (...) {
|
||||
path->edges.pop_back();
|
||||
CHECK(mgp_path_size(path) == path->vertices.size() - 1);
|
||||
return 0;
|
||||
}
|
||||
CHECK(mgp_path_size(path) == path->vertices.size() - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t mgp_path_size(const mgp_path *path) { return path->edges.size(); }
|
||||
|
||||
const mgp_vertex *mgp_path_vertex_at(const mgp_path *path, size_t i) {
|
||||
CHECK(mgp_path_size(path) == path->vertices.size() - 1);
|
||||
if (i > mgp_path_size(path)) return nullptr;
|
||||
return &path->vertices[i];
|
||||
}
|
||||
|
||||
const mgp_edge *mgp_path_edge_at(const mgp_path *path, size_t i) {
|
||||
CHECK(mgp_path_size(path) == path->vertices.size() - 1);
|
||||
if (i >= mgp_path_size(path)) return nullptr;
|
||||
return &path->edges[i];
|
||||
}
|
||||
|
||||
/// Plugin Result
|
||||
|
||||
int mgp_result_set_error_msg(mgp_result *res, const char *msg) {
|
||||
@ -744,3 +805,402 @@ int mgp_result_record_insert(mgp_result_record *record, const char *field_name,
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Graph Constructs
|
||||
|
||||
void mgp_properties_iterator_destroy(mgp_properties_iterator *it) {
|
||||
delete_mgp_object(it);
|
||||
}
|
||||
|
||||
const mgp_property *mgp_properties_iterator_get(
|
||||
const mgp_properties_iterator *it) {
|
||||
if (it->current) return &it->property;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const mgp_property *mgp_properties_iterator_next(mgp_properties_iterator *it) {
|
||||
// Incrementing the iterator either for on-disk or in-memory
|
||||
// storage, so perhaps the underlying thing can throw.
|
||||
// Both copying TypedValue and/or string from PropertyName may fail to
|
||||
// allocate. Also, dereferencing `it->current_it` could also throw, so
|
||||
// either way return nullptr and leave `it` in undefined state.
|
||||
// Hopefully iterator comparison doesn't throw, but wrap the whole thing in
|
||||
// try ... catch just to be sure.
|
||||
try {
|
||||
if (it->current_it == it->pvs.end()) {
|
||||
CHECK(!it->current) << "Iteration is already done, so it->current should "
|
||||
"have been set to std::nullopt";
|
||||
return nullptr;
|
||||
}
|
||||
if (++it->current_it == it->pvs.end()) {
|
||||
it->current = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
it->current.emplace(
|
||||
utils::pmr::string(
|
||||
it->graph->impl->PropertyToName(it->current_it->first),
|
||||
it->GetMemoryResource()),
|
||||
mgp_value(it->current_it->second, it->GetMemoryResource()));
|
||||
it->property.name = it->current->first.c_str();
|
||||
it->property.value = &it->current->second;
|
||||
return &it->property;
|
||||
} catch (...) {
|
||||
it->current = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
mgp_vertex *mgp_vertex_copy(const mgp_vertex *v, mgp_memory *memory) {
|
||||
return new_mgp_object<mgp_vertex>(memory, *v);
|
||||
}
|
||||
|
||||
void mgp_vertex_destroy(mgp_vertex *v) { delete_mgp_object(v); }
|
||||
|
||||
int mgp_vertex_equal(const mgp_vertex *a, const mgp_vertex *b) {
|
||||
return a->impl == b->impl ? 1 : 0;
|
||||
}
|
||||
|
||||
size_t mgp_vertex_labels_count(const mgp_vertex *v) {
|
||||
auto maybe_labels = v->impl.Labels(v->graph->view);
|
||||
if (maybe_labels.HasError()) {
|
||||
switch (maybe_labels.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no labels.
|
||||
return 0;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting vertex labels.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return maybe_labels->size();
|
||||
}
|
||||
|
||||
mgp_label mgp_vertex_label_at(const mgp_vertex *v, size_t i) {
|
||||
// TODO: Maybe it's worth caching this in mgp_vertex.
|
||||
auto maybe_labels = v->impl.Labels(v->graph->view);
|
||||
if (maybe_labels.HasError()) {
|
||||
switch (maybe_labels.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
return mgp_label{nullptr};
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting vertex labels.";
|
||||
return mgp_label{nullptr};
|
||||
}
|
||||
}
|
||||
if (i >= maybe_labels->size()) return mgp_label{nullptr};
|
||||
const auto &label = (*maybe_labels)[i];
|
||||
static_assert(
|
||||
std::is_lvalue_reference_v<decltype(v->graph->impl->LabelToName(label))>,
|
||||
"Expected LabelToName to return a pointer or reference, so we "
|
||||
"don't have to take a copy and manage memory.");
|
||||
const auto &name = v->graph->impl->LabelToName(label);
|
||||
return mgp_label{name.c_str()};
|
||||
}
|
||||
|
||||
int mgp_vertex_has_label_named(const mgp_vertex *v, const char *name) {
|
||||
storage::Label label;
|
||||
try {
|
||||
// This will allocate a std::string from `name`, which may throw
|
||||
// std::bad_alloc or std::length_error. This could be avoided with a
|
||||
// std::string_view. Although storage API may be updated with
|
||||
// std::string_view, NameToLabel itself may still throw std::bad_alloc when
|
||||
// creating a new LabelId mapping and we need to handle that.
|
||||
label = v->graph->impl->NameToLabel(name);
|
||||
} catch (...) {
|
||||
LOG(ERROR) << "Unable to allocate a LabelId mapping";
|
||||
// If we need to allocate a new mapping, then the vertex does not have such
|
||||
// a label, so return 0.
|
||||
return 0;
|
||||
}
|
||||
auto maybe_has_label = v->impl.HasLabel(v->graph->view, label);
|
||||
if (maybe_has_label.HasError()) {
|
||||
switch (maybe_has_label.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
return 0;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when checking vertex has label.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return *maybe_has_label;
|
||||
}
|
||||
|
||||
int mgp_vertex_has_label(const mgp_vertex *v, mgp_label label) {
|
||||
return mgp_vertex_has_label_named(v, label.name);
|
||||
}
|
||||
|
||||
mgp_value *mgp_vertex_get_property(const mgp_vertex *v, const char *name,
|
||||
mgp_memory *memory) {
|
||||
try {
|
||||
const auto &key = v->graph->impl->NameToProperty(name);
|
||||
auto maybe_prop = v->impl.GetProperty(v->graph->view, key);
|
||||
if (maybe_prop.HasError()) {
|
||||
switch (maybe_prop.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no properties.
|
||||
return new_mgp_object<mgp_value>(memory);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting vertex property";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return new_mgp_object<mgp_value>(memory, std::move(*maybe_prop));
|
||||
} catch (...) {
|
||||
// In case NameToProperty or GetProperty throw an exception, most likely
|
||||
// std::bad_alloc.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
mgp_properties_iterator *mgp_vertex_iter_properties(const mgp_vertex *v,
|
||||
mgp_memory *memory) {
|
||||
// NOTE: This copies the whole properties into the iterator.
|
||||
// TODO: Think of a good way to avoid the copy which doesn't just rely on some
|
||||
// assumption that storage may return a pointer to the property store. This
|
||||
// will probably require a different API in storage.
|
||||
try {
|
||||
auto maybe_props = v->impl.Properties(v->graph->view);
|
||||
if (maybe_props.HasError()) {
|
||||
switch (maybe_props.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no properties.
|
||||
return new_mgp_object<mgp_properties_iterator>(memory, v->graph);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting vertex properties";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return new_mgp_object<mgp_properties_iterator>(memory, v->graph,
|
||||
std::move(*maybe_props));
|
||||
} catch (...) {
|
||||
// Since we are copying stuff, we may get std::bad_alloc. Hopefully, no
|
||||
// other exceptions are possible, but catch them all just in case.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void mgp_edges_iterator_destroy(mgp_edges_iterator *it) {
|
||||
delete_mgp_object(it);
|
||||
}
|
||||
|
||||
mgp_edges_iterator *mgp_vertex_iter_in_edges(const mgp_vertex *v,
|
||||
mgp_memory *memory) {
|
||||
auto *it = new_mgp_object<mgp_edges_iterator>(memory, *v);
|
||||
if (!it) return nullptr;
|
||||
try {
|
||||
auto maybe_edges = v->impl.InEdges(v->graph->view);
|
||||
if (maybe_edges.HasError()) {
|
||||
switch (maybe_edges.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no edges.
|
||||
return it;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting in edges";
|
||||
mgp_edges_iterator_destroy(it);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
it->in.emplace(std::move(*maybe_edges));
|
||||
it->in_it.emplace(it->in->begin());
|
||||
if (*it->in_it != it->in->end()) {
|
||||
it->current_e.emplace(**it->in_it, v->graph, it->GetMemoryResource());
|
||||
}
|
||||
} catch (...) {
|
||||
// We are probably copying edges, and that may throw std::bad_alloc.
|
||||
mgp_edges_iterator_destroy(it);
|
||||
return nullptr;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
mgp_edges_iterator *mgp_vertex_iter_out_edges(const mgp_vertex *v,
|
||||
mgp_memory *memory) {
|
||||
auto *it = new_mgp_object<mgp_edges_iterator>(memory, *v);
|
||||
if (!it) return nullptr;
|
||||
try {
|
||||
auto maybe_edges = v->impl.OutEdges(v->graph->view);
|
||||
if (maybe_edges.HasError()) {
|
||||
switch (maybe_edges.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted vertex as having no edges.
|
||||
return it;
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting out edges";
|
||||
mgp_edges_iterator_destroy(it);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
it->out.emplace(std::move(*maybe_edges));
|
||||
it->out_it.emplace(it->out->begin());
|
||||
if (*it->out_it != it->out->end()) {
|
||||
it->current_e.emplace(**it->out_it, v->graph, it->GetMemoryResource());
|
||||
}
|
||||
} catch (...) {
|
||||
// We are probably copying edges, and that may throw std::bad_alloc.
|
||||
mgp_edges_iterator_destroy(it);
|
||||
return nullptr;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
const mgp_edge *mgp_edges_iterator_get(const mgp_edges_iterator *it) {
|
||||
if (it->current_e) return &*it->current_e;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const mgp_edge *mgp_edges_iterator_next(mgp_edges_iterator *it) {
|
||||
if (!it->in && !it->out) return nullptr;
|
||||
auto next = [&](auto *impl_it, const auto &end) -> const mgp_edge * {
|
||||
if (*impl_it == end) {
|
||||
CHECK(!it->current_e) << "Iteration is already done, so it->current_e "
|
||||
"should have been set to std::nullopt";
|
||||
return nullptr;
|
||||
}
|
||||
if (++(*impl_it) == end) {
|
||||
it->current_e = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
it->current_e.emplace(**impl_it, it->source_vertex.graph,
|
||||
it->GetMemoryResource());
|
||||
return &*it->current_e;
|
||||
};
|
||||
try {
|
||||
if (it->in_it) {
|
||||
return next(&*it->in_it, it->in->end());
|
||||
} else {
|
||||
return next(&*it->out_it, it->out->end());
|
||||
}
|
||||
} catch (...) {
|
||||
// Just to be sure that operator++ or anything else has thrown something.
|
||||
it->current_e = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
mgp_edge *mgp_edge_copy(const mgp_edge *v, mgp_memory *memory) {
|
||||
return new_mgp_object<mgp_edge>(memory, v->impl, v->from.graph);
|
||||
}
|
||||
|
||||
void mgp_edge_destroy(mgp_edge *e) { delete_mgp_object(e); }
|
||||
|
||||
mgp_edge_type mgp_edge_get_type(const mgp_edge *e) {
|
||||
const auto &name = e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType());
|
||||
static_assert(
|
||||
std::is_lvalue_reference_v<decltype(
|
||||
e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType()))>,
|
||||
"Expected EdgeTypeToName to return a pointer or reference, so we "
|
||||
"don't have to take a copy and manage memory.");
|
||||
return mgp_edge_type{name.c_str()};
|
||||
}
|
||||
|
||||
const mgp_vertex *mgp_edge_get_from(const mgp_edge *e) { return &e->from; }
|
||||
|
||||
const mgp_vertex *mgp_edge_get_to(const mgp_edge *e) { return &e->to; }
|
||||
|
||||
mgp_value *mgp_edge_get_property(const mgp_edge *e, const char *name,
|
||||
mgp_memory *memory) {
|
||||
try {
|
||||
const auto &key = e->from.graph->impl->NameToProperty(name);
|
||||
auto view = e->from.graph->view;
|
||||
auto maybe_prop = e->impl.GetProperty(view, key);
|
||||
if (maybe_prop.HasError()) {
|
||||
switch (maybe_prop.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted edge as having no properties.
|
||||
return new_mgp_object<mgp_value>(memory);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting edge property";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return new_mgp_object<mgp_value>(memory, std::move(*maybe_prop));
|
||||
} catch (...) {
|
||||
// In case NameToProperty or GetProperty throw an exception, most likely
|
||||
// std::bad_alloc.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
mgp_properties_iterator *mgp_edge_iter_properties(const mgp_edge *e,
|
||||
mgp_memory *memory) {
|
||||
// NOTE: This copies the whole properties into iterator.
|
||||
// TODO: Think of a good way to avoid the copy which doesn't just rely on some
|
||||
// assumption that storage may return a pointer to the property store. This
|
||||
// will probably require a different API in storage.
|
||||
try {
|
||||
auto view = e->from.graph->view;
|
||||
auto maybe_props = e->impl.Properties(view);
|
||||
if (maybe_props.HasError()) {
|
||||
switch (maybe_props.GetError()) {
|
||||
case storage::Error::DELETED_OBJECT:
|
||||
// Treat deleted edge as having no properties.
|
||||
return new_mgp_object<mgp_properties_iterator>(memory, e->from.graph);
|
||||
case storage::Error::PROPERTIES_DISABLED:
|
||||
case storage::Error::VERTEX_HAS_EDGES:
|
||||
case storage::Error::SERIALIZATION_ERROR:
|
||||
LOG(ERROR) << "Unexpected error when getting edge properties";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return new_mgp_object<mgp_properties_iterator>(memory, e->from.graph,
|
||||
std::move(*maybe_props));
|
||||
} catch (...) {
|
||||
// Since we are copying stuff, we may get std::bad_alloc. Hopefully, no
|
||||
// other exceptions are possible, but catch them all just in case.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void mgp_vertices_iterator_destroy(mgp_vertices_iterator *it) {
|
||||
delete_mgp_object(it);
|
||||
}
|
||||
|
||||
mgp_vertices_iterator *mgp_graph_iter_vertices(const mgp_graph *graph,
|
||||
mgp_memory *memory) {
|
||||
try {
|
||||
return new_mgp_object<mgp_vertices_iterator>(memory, graph);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const mgp_vertex *mgp_vertices_iterator_get(const mgp_vertices_iterator *it) {
|
||||
if (it->current_v) return &*it->current_v;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const mgp_vertex *mgp_vertices_iterator_next(mgp_vertices_iterator *it) {
|
||||
try {
|
||||
if (it->current_it == it->vertices.end()) {
|
||||
CHECK(!it->current_v) << "Iteration is already done, so it->current_v "
|
||||
"should have been set to std::nullopt";
|
||||
return nullptr;
|
||||
}
|
||||
if (++it->current_it == it->vertices.end()) {
|
||||
it->current_v = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
it->current_v.emplace(*it->current_it, it->graph, it->GetMemoryResource());
|
||||
return &*it->current_v;
|
||||
} catch (...) {
|
||||
// VerticesIterable::Iterator::operator++ may throw
|
||||
it->current_v = std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -339,3 +339,119 @@ struct mgp_graph {
|
||||
query::DbAccessor *impl;
|
||||
storage::View view;
|
||||
};
|
||||
|
||||
struct mgp_properties_iterator {
|
||||
using allocator_type = utils::Allocator<mgp_properties_iterator>;
|
||||
|
||||
// Define members at the start because we use decltype a lot here, so members
|
||||
// need to be visible in method definitions.
|
||||
|
||||
utils::MemoryResource *memory;
|
||||
const mgp_graph *graph;
|
||||
std::remove_reference_t<decltype(
|
||||
*std::declval<query::VertexAccessor>().Properties(graph->view))>
|
||||
pvs;
|
||||
decltype(pvs.begin()) current_it;
|
||||
std::optional<std::pair<utils::pmr::string, mgp_value>> current;
|
||||
mgp_property property{nullptr, nullptr};
|
||||
|
||||
// Construct with no properties.
|
||||
explicit mgp_properties_iterator(const mgp_graph *graph,
|
||||
utils::MemoryResource *memory)
|
||||
: memory(memory), graph(graph), current_it(pvs.begin()) {}
|
||||
|
||||
// May throw who the #$@! knows what because PropertyValueStore doesn't
|
||||
// document what it throws, and it may surely throw some piece of !@#$
|
||||
// exception because it's built on top of STL and other libraries.
|
||||
mgp_properties_iterator(const mgp_graph *graph, decltype(pvs) pvs,
|
||||
utils::MemoryResource *memory)
|
||||
: memory(memory),
|
||||
graph(graph),
|
||||
pvs(std::move(pvs)),
|
||||
current_it(this->pvs.begin()) {
|
||||
if (current_it != this->pvs.end()) {
|
||||
current.emplace(
|
||||
utils::pmr::string(graph->impl->PropertyToName(current_it->first),
|
||||
memory),
|
||||
mgp_value(current_it->second, memory));
|
||||
property.name = current->first.c_str();
|
||||
property.value = ¤t->second;
|
||||
}
|
||||
}
|
||||
|
||||
mgp_properties_iterator(const mgp_properties_iterator &) = delete;
|
||||
mgp_properties_iterator(mgp_properties_iterator &&) = delete;
|
||||
|
||||
mgp_properties_iterator &operator=(const mgp_properties_iterator &) = delete;
|
||||
mgp_properties_iterator &operator=(mgp_properties_iterator &&) = delete;
|
||||
|
||||
~mgp_properties_iterator() = default;
|
||||
|
||||
utils::MemoryResource *GetMemoryResource() const { return memory; }
|
||||
};
|
||||
|
||||
struct mgp_edges_iterator {
|
||||
using allocator_type = utils::Allocator<mgp_edges_iterator>;
|
||||
|
||||
// Hopefully mgp_vertex copy constructor remains noexcept, so that we can
|
||||
// have everything noexcept here.
|
||||
static_assert(std::is_nothrow_constructible_v<mgp_vertex, const mgp_vertex &,
|
||||
utils::MemoryResource *>);
|
||||
|
||||
mgp_edges_iterator(const mgp_vertex &v,
|
||||
utils::MemoryResource *memory) noexcept
|
||||
: memory(memory), source_vertex(v, memory) {}
|
||||
|
||||
mgp_edges_iterator(mgp_edges_iterator &&other) noexcept
|
||||
: memory(other.memory),
|
||||
source_vertex(std::move(other.source_vertex)),
|
||||
in(std::move(other.in)),
|
||||
in_it(std::move(other.in_it)),
|
||||
out(std::move(other.out)),
|
||||
out_it(std::move(other.out_it)),
|
||||
current_e(std::move(other.current_e)) {}
|
||||
|
||||
mgp_edges_iterator(const mgp_edges_iterator &) = delete;
|
||||
mgp_edges_iterator &operator=(const mgp_edges_iterator &) = delete;
|
||||
mgp_edges_iterator &operator=(mgp_edges_iterator &&) = delete;
|
||||
|
||||
~mgp_edges_iterator() = default;
|
||||
|
||||
utils::MemoryResource *GetMemoryResource() const { return memory; }
|
||||
|
||||
utils::MemoryResource *memory;
|
||||
mgp_vertex source_vertex;
|
||||
std::optional<std::remove_reference_t<decltype(
|
||||
*source_vertex.impl.InEdges(source_vertex.graph->view))>>
|
||||
in;
|
||||
std::optional<decltype(in->begin())> in_it;
|
||||
std::optional<std::remove_reference_t<decltype(
|
||||
*source_vertex.impl.OutEdges(source_vertex.graph->view))>>
|
||||
out;
|
||||
std::optional<decltype(out->begin())> out_it;
|
||||
std::optional<mgp_edge> current_e;
|
||||
};
|
||||
|
||||
struct mgp_vertices_iterator {
|
||||
using allocator_type = utils::Allocator<mgp_vertices_iterator>;
|
||||
|
||||
/// @throw anything VerticesIterable may throw
|
||||
mgp_vertices_iterator(const mgp_graph *graph, utils::MemoryResource *memory)
|
||||
: memory(memory),
|
||||
graph(graph),
|
||||
vertices(graph->impl->Vertices(graph->view)),
|
||||
current_it(vertices.begin()) {
|
||||
if (current_it != vertices.end()) {
|
||||
current_v.emplace(*current_it, graph, memory);
|
||||
}
|
||||
}
|
||||
|
||||
utils::MemoryResource *GetMemoryResource() const { return memory; }
|
||||
|
||||
utils::MemoryResource *memory;
|
||||
const mgp_graph *graph;
|
||||
decltype(graph->impl->Vertices(graph->view)) vertices;
|
||||
decltype(vertices.begin()) current_it;
|
||||
std::optional<mgp_vertex> current_v;
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user