[master < T1110] Add merge optimization to expand dynamically during runtime (#1110)

This commit is contained in:
Josipmrden 2023-09-08 17:12:25 +02:00 committed by GitHub
parent bd1852f407
commit 07dea328d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2164 additions and 1905 deletions

View File

@ -129,10 +129,10 @@ query::Graph *SubgraphDbAccessor::getGraph() { return graph_; }
VertexAccessor SubgraphVertexAccessor::GetVertexAccessor() const { return impl_; }
auto SubgraphVertexAccessor::OutEdges(storage::View view) const -> decltype(impl_.OutEdges(view)) {
storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::OutEdges(storage::View view) const {
auto maybe_edges = impl_.impl_.OutEdges(view, {});
if (maybe_edges.HasError()) return maybe_edges.GetError();
auto edges = std::move(*maybe_edges);
auto edges = std::move(maybe_edges->edges);
const auto &graph_edges = graph_->edges();
std::vector<storage::EdgeAccessor> filteredOutEdges;
@ -143,13 +143,18 @@ auto SubgraphVertexAccessor::OutEdges(storage::View view) const -> decltype(impl
}
}
return iter::imap(VertexAccessor::MakeEdgeAccessor, std::move(filteredOutEdges));
std::vector<EdgeAccessor> resulting_edges;
resulting_edges.reserve(filteredOutEdges.size());
std::ranges::transform(filteredOutEdges, std::back_inserter(resulting_edges),
[](auto const &edge) { return VertexAccessor::MakeEdgeAccessor(edge); });
return EdgeVertexAccessorResult{.edges = std::move(resulting_edges), .expanded_count = maybe_edges->expanded_count};
}
auto SubgraphVertexAccessor::InEdges(storage::View view) const -> decltype(impl_.InEdges(view)) {
storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::InEdges(storage::View view) const {
auto maybe_edges = impl_.impl_.InEdges(view, {});
if (maybe_edges.HasError()) return maybe_edges.GetError();
auto edges = std::move(*maybe_edges);
auto edges = std::move(maybe_edges->edges);
const auto &graph_edges = graph_->edges();
std::vector<storage::EdgeAccessor> filteredOutEdges;
@ -160,7 +165,12 @@ auto SubgraphVertexAccessor::InEdges(storage::View view) const -> decltype(impl_
}
}
return iter::imap(VertexAccessor::MakeEdgeAccessor, std::move(filteredOutEdges));
std::vector<EdgeAccessor> resulting_edges;
resulting_edges.reserve(filteredOutEdges.size());
std::ranges::transform(filteredOutEdges, std::back_inserter(resulting_edges),
[](auto const &edge) { return VertexAccessor::MakeEdgeAccessor(edge); });
return EdgeVertexAccessorResult{.edges = std::move(resulting_edges), .expanded_count = maybe_edges->expanded_count};
}
} // namespace memgraph::query

View File

@ -105,6 +105,11 @@ class EdgeAccessor final {
bool operator!=(const EdgeAccessor &e) const noexcept { return !(*this == e); }
};
struct EdgeVertexAccessorResult {
std::vector<EdgeAccessor> edges;
int64_t expanded_count;
};
class VertexAccessor final {
public:
storage::VertexAccessor impl_;
@ -153,37 +158,62 @@ class VertexAccessor final {
return impl_.ClearProperties();
}
auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types) const
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
auto maybe_edges = impl_.InEdges(view, edge_types);
if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
storage::Result<EdgeVertexAccessorResult> InEdges(storage::View view,
const std::vector<storage::EdgeTypeId> &edge_types) const {
auto maybe_result = impl_.InEdges(view, edge_types);
if (maybe_result.HasError()) return maybe_result.GetError();
std::vector<EdgeAccessor> edges;
edges.reserve((*maybe_result).edges.size());
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
[](auto const &edge) { return EdgeAccessor(edge); });
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
}
auto InEdges(storage::View view) const { return InEdges(view, {}); }
storage::Result<EdgeVertexAccessorResult> InEdges(storage::View view) const { return InEdges(view, {}); }
auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types, const VertexAccessor &dest) const
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
auto maybe_edges = impl_.InEdges(view, edge_types, &dest.impl_);
if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
storage::Result<EdgeVertexAccessorResult> InEdges(storage::View view,
const std::vector<storage::EdgeTypeId> &edge_types,
const VertexAccessor &dest) const {
auto maybe_result = impl_.InEdges(view, edge_types, &dest.impl_);
if (maybe_result.HasError()) return maybe_result.GetError();
std::vector<EdgeAccessor> edges;
edges.reserve((*maybe_result).edges.size());
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
[](auto const &edge) { return EdgeAccessor(edge); });
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
}
auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types) const
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
auto maybe_edges = impl_.OutEdges(view, edge_types);
if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
storage::Result<EdgeVertexAccessorResult> OutEdges(storage::View view,
const std::vector<storage::EdgeTypeId> &edge_types) const {
auto maybe_result = impl_.OutEdges(view, edge_types);
if (maybe_result.HasError()) return maybe_result.GetError();
std::vector<EdgeAccessor> edges;
edges.reserve((*maybe_result).edges.size());
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
[](auto const &edge) { return EdgeAccessor(edge); });
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
}
auto OutEdges(storage::View view) const { return OutEdges(view, {}); }
storage::Result<EdgeVertexAccessorResult> OutEdges(storage::View view) const { return OutEdges(view, {}); }
auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types,
const VertexAccessor &dest) const
-> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
auto maybe_edges = impl_.OutEdges(view, edge_types, &dest.impl_);
if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
storage::Result<EdgeVertexAccessorResult> OutEdges(storage::View view,
const std::vector<storage::EdgeTypeId> &edge_types,
const VertexAccessor &dest) const {
auto maybe_result = impl_.OutEdges(view, edge_types, &dest.impl_);
if (maybe_result.HasError()) return maybe_result.GetError();
std::vector<EdgeAccessor> edges;
edges.reserve((*maybe_result).edges.size());
std::ranges::transform((*maybe_result).edges, std::back_inserter(edges),
[](auto const &edge) { return EdgeAccessor(edge); });
return EdgeVertexAccessorResult{.edges = edges, .expanded_count = (*maybe_result).expanded_count};
}
storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); }
@ -260,7 +290,6 @@ class SubgraphVertexAccessor final {
} // namespace memgraph::query
namespace std {
template <>
struct hash<memgraph::query::VertexAccessor> {
size_t operator()(const memgraph::query::VertexAccessor &v) const { return std::hash<decltype(v.impl_)>{}(v.impl_); }

View File

@ -487,8 +487,8 @@ PullPlanDump::PullChunk PullPlanDump::CreateEdgePullChunk() {
}
auto &maybe_edges = *maybe_edge_iterable;
MG_ASSERT(maybe_edges.HasValue(), "Invalid database state!");
auto current_edge_iter = maybe_current_edge_iter ? *maybe_current_edge_iter : maybe_edges->begin();
for (; current_edge_iter != maybe_edges->end() && (!n || local_counter < *n); ++current_edge_iter) {
auto current_edge_iter = maybe_current_edge_iter ? *maybe_current_edge_iter : maybe_edges->edges.begin();
for (; current_edge_iter != maybe_edges->edges.end() && (!n || local_counter < *n); ++current_edge_iter) {
std::ostringstream os;
DumpEdge(&os, dba_, *current_edge_iter);
stream->Result({TypedValue(os.str())});
@ -496,7 +496,7 @@ PullPlanDump::PullChunk PullPlanDump::CreateEdgePullChunk() {
++local_counter;
}
if (current_edge_iter != maybe_edges->end()) {
if (current_edge_iter != maybe_edges->edges.end()) {
maybe_current_edge_iter.emplace(current_edge_iter);
return std::nullopt;
}

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -38,7 +38,7 @@ struct PullPlanDump {
using VertexAccessorIterableIterator = decltype(std::declval<VertexAccessorIterable>().begin());
using EdgeAccessorIterable = decltype(std::declval<VertexAccessor>().OutEdges(storage::View::OLD));
using EdgeAccessorIterableIterator = decltype(std::declval<EdgeAccessorIterable>().GetValue().begin());
using EdgeAccessorIterableIterator = decltype(std::declval<EdgeAccessorIterable>().GetValue().edges.begin());
VertexAccessorIterable vertices_iterable_;
bool internal_index_created_ = false;

View File

@ -680,8 +680,8 @@ bool CheckExistingNode(const VertexAccessor &new_node, const Symbol &existing_no
return existing_node.ValueVertex() == new_node;
}
template <class TEdges>
auto UnwrapEdgesResult(storage::Result<TEdges> &&result) {
template <class TEdgesResult>
auto UnwrapEdgesResult(storage::Result<TEdgesResult> &&result) {
if (result.HasError()) {
switch (result.GetError()) {
case storage::Error::DELETED_OBJECT:
@ -725,6 +725,13 @@ std::vector<Symbol> Expand::ModifiedSymbols(const SymbolTable &table) const {
Expand::ExpandCursor::ExpandCursor(const Expand &self, utils::MemoryResource *mem)
: self_(self), input_cursor_(self.input_->MakeCursor(mem)) {}
Expand::ExpandCursor::ExpandCursor(const Expand &self, int64_t input_degree, int64_t existing_node_degree,
utils::MemoryResource *mem)
: self_(self),
input_cursor_(self.input_->MakeCursor(mem)),
prev_input_degree_(input_degree),
prev_existing_degree_(existing_node_degree) {}
bool Expand::ExpandCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("Expand");
@ -800,58 +807,131 @@ void Expand::ExpandCursor::Reset() {
out_edges_it_ = std::nullopt;
}
ExpansionInfo Expand::ExpandCursor::GetExpansionInfo(Frame &frame) {
TypedValue &vertex_value = frame[self_.input_symbol_];
if (vertex_value.IsNull()) {
return ExpansionInfo{};
}
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &vertex = vertex_value.ValueVertex();
auto direction = self_.common_.direction;
if (!self_.common_.existing_node) {
return ExpansionInfo{.input_node = vertex, .direction = direction};
}
TypedValue &existing_node = frame[self_.common_.node_symbol];
if (existing_node.IsNull()) {
return ExpansionInfo{.input_node = vertex, .direction = direction};
}
ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
auto &existing_vertex = existing_node.ValueVertex();
// -1 and -1 -> normal expansion
// -1 and expanded -> can't happen
// expanded and -1 -> reverse
// expanded and expanded -> see if can reverse
if ((prev_input_degree_ == -1 && prev_existing_degree_ == -1) || prev_input_degree_ < prev_existing_degree_) {
return ExpansionInfo{.input_node = vertex, .direction = direction, .existing_node = existing_vertex};
}
auto new_direction = direction;
switch (new_direction) {
case EdgeAtom::Direction::IN:
new_direction = EdgeAtom::Direction::OUT;
break;
case EdgeAtom::Direction::OUT:
new_direction = EdgeAtom::Direction::IN;
break;
default:
new_direction = EdgeAtom::Direction::BOTH;
break;
}
return ExpansionInfo{
.input_node = existing_vertex, .direction = new_direction, .existing_node = vertex, .reversed = true};
}
bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
// Input Vertex could be null if it is created by a failed optional match. In
// those cases we skip that input pull and continue with the next.
while (true) {
if (!input_cursor_->Pull(frame, context)) return false;
TypedValue &vertex_value = frame[self_.input_symbol_];
// Null check due to possible failed optional match.
if (vertex_value.IsNull()) continue;
expansion_info_ = GetExpansionInfo(frame);
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &vertex = vertex_value.ValueVertex();
if (!expansion_info_.input_node) {
continue;
}
auto direction = self_.common_.direction;
auto vertex = *expansion_info_.input_node;
auto direction = expansion_info_.direction;
int64_t num_expanded_first = -1;
if (direction == EdgeAtom::Direction::IN || direction == EdgeAtom::Direction::BOTH) {
if (self_.common_.existing_node) {
TypedValue &existing_node = frame[self_.common_.node_symbol];
// old_node_value may be Null when using optional matching
if (!existing_node.IsNull()) {
ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
if (expansion_info_.existing_node) {
auto existing_node = *expansion_info_.existing_node;
context.db_accessor->PrefetchInEdges(vertex);
in_edges_.emplace(
UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types, existing_node.ValueVertex())));
auto edges_result = UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types, existing_node));
in_edges_.emplace(edges_result.edges);
num_expanded_first = edges_result.expanded_count;
}
} else {
context.db_accessor->PrefetchInEdges(vertex);
in_edges_.emplace(UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types)));
auto edges_result = UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types));
in_edges_.emplace(edges_result.edges);
num_expanded_first = edges_result.expanded_count;
}
if (in_edges_) {
in_edges_it_.emplace(in_edges_->begin());
}
}
int64_t num_expanded_second = -1;
if (direction == EdgeAtom::Direction::OUT || direction == EdgeAtom::Direction::BOTH) {
if (self_.common_.existing_node) {
TypedValue &existing_node = frame[self_.common_.node_symbol];
// old_node_value may be Null when using optional matching
if (!existing_node.IsNull()) {
ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
if (expansion_info_.existing_node) {
auto existing_node = *expansion_info_.existing_node;
context.db_accessor->PrefetchOutEdges(vertex);
out_edges_.emplace(
UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types, existing_node.ValueVertex())));
auto edges_result = UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types, existing_node));
out_edges_.emplace(edges_result.edges);
num_expanded_second = edges_result.expanded_count;
}
} else {
context.db_accessor->PrefetchOutEdges(vertex);
out_edges_.emplace(UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types)));
auto edges_result = UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types));
out_edges_.emplace(edges_result.edges);
num_expanded_second = edges_result.expanded_count;
}
if (out_edges_) {
out_edges_it_.emplace(out_edges_->begin());
}
}
if (!expansion_info_.existing_node) {
return true;
}
num_expanded_first = num_expanded_first == -1 ? 0 : num_expanded_first;
num_expanded_second = num_expanded_second == -1 ? 0 : num_expanded_second;
int64_t total_expanded_edges = num_expanded_first + num_expanded_second;
if (!expansion_info_.reversed) {
prev_input_degree_ = total_expanded_edges;
} else {
prev_existing_degree_ = total_expanded_edges;
}
return true;
}
}
@ -911,11 +991,12 @@ auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction directio
};
storage::View view = storage::View::OLD;
utils::pmr::vector<decltype(wrapper(direction, *vertex.InEdges(view, edge_types)))> chain_elements(memory);
utils::pmr::vector<decltype(wrapper(direction, vertex.InEdges(view, edge_types).GetValue().edges))> chain_elements(
memory);
if (direction != EdgeAtom::Direction::OUT) {
db_accessor->PrefetchInEdges(vertex);
auto edges = UnwrapEdgesResult(vertex.InEdges(view, edge_types));
auto edges = UnwrapEdgesResult(vertex.InEdges(view, edge_types)).edges;
if (edges.begin() != edges.end()) {
chain_elements.emplace_back(wrapper(EdgeAtom::Direction::IN, std::move(edges)));
}
@ -923,7 +1004,7 @@ auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction directio
if (direction != EdgeAtom::Direction::IN) {
db_accessor->PrefetchOutEdges(vertex);
auto edges = UnwrapEdgesResult(vertex.OutEdges(view, edge_types));
auto edges = UnwrapEdgesResult(vertex.OutEdges(view, edge_types)).edges;
if (edges.begin() != edges.end()) {
chain_elements.emplace_back(wrapper(EdgeAtom::Direction::OUT, std::move(edges)));
}
@ -1283,7 +1364,7 @@ class STShortestPathCursor : public query::plan::Cursor {
for (const auto &vertex : source_frontier) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
context.db_accessor->PrefetchOutEdges(vertex);
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) {
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
@ -1310,7 +1391,7 @@ class STShortestPathCursor : public query::plan::Cursor {
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
dba.PrefetchInEdges(vertex);
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) {
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
@ -1351,7 +1432,7 @@ class STShortestPathCursor : public query::plan::Cursor {
for (const auto &vertex : sink_frontier) {
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
context.db_accessor->PrefetchOutEdges(vertex);
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) {
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
@ -1377,7 +1458,7 @@ class STShortestPathCursor : public query::plan::Cursor {
}
if (self_.common_.direction != EdgeAtom::Direction::IN) {
dba.PrefetchInEdges(vertex);
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) {
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
@ -1469,12 +1550,12 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
auto expand_from_vertex = [this, &expand_pair, &context](const auto &vertex) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
context.db_accessor->PrefetchOutEdges(vertex);
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) expand_pair(edge, edge.To());
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
context.db_accessor->PrefetchInEdges(vertex);
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) expand_pair(edge, edge.From());
}
};
@ -1672,14 +1753,14 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
int64_t depth) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
context.db_accessor->PrefetchOutEdges(vertex);
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) {
expand_pair(edge, edge.To(), weight, depth);
}
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
context.db_accessor->PrefetchInEdges(vertex);
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) {
expand_pair(edge, edge.From(), weight, depth);
}
@ -1938,7 +2019,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
int64_t depth) {
if (self_.common_.direction != EdgeAtom::Direction::IN) {
context.db_accessor->PrefetchOutEdges(vertex);
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : out_edges) {
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&
@ -1953,7 +2034,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
}
if (self_.common_.direction != EdgeAtom::Direction::OUT) {
context.db_accessor->PrefetchInEdges(vertex);
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types)).edges;
for (const auto &edge : in_edges) {
#ifdef MG_ENTERPRISE
if (license::global_license_checker.IsEnterpriseValidFast() && context.auth_checker &&

View File

@ -764,6 +764,13 @@ struct ExpandCommon {
bool existing_node;
};
struct ExpansionInfo {
std::optional<VertexAccessor> input_node;
EdgeAtom::Direction direction;
std::optional<VertexAccessor> existing_node;
bool reversed{false};
};
/// Expansion operator. For a node existing in the frame it
/// expands one edge and one node and places them on the frame.
///
@ -806,14 +813,16 @@ class Expand : public memgraph::query::plan::LogicalOperator {
class ExpandCursor : public Cursor {
public:
ExpandCursor(const Expand &, utils::MemoryResource *);
ExpandCursor(const Expand &, int64_t input_degree, int64_t existing_node_degree, utils::MemoryResource *);
bool Pull(Frame &, ExecutionContext &) override;
void Shutdown() override;
void Reset() override;
ExpansionInfo GetExpansionInfo(Frame &);
private:
using InEdgeT = std::remove_reference_t<decltype(*std::declval<VertexAccessor>().InEdges(storage::View::OLD))>;
using InEdgeT = std::vector<EdgeAccessor>;
using InEdgeIteratorT = decltype(std::declval<InEdgeT>().begin());
using OutEdgeT = std::remove_reference_t<decltype(*std::declval<VertexAccessor>().OutEdges(storage::View::OLD))>;
using OutEdgeT = std::vector<EdgeAccessor>;
using OutEdgeIteratorT = decltype(std::declval<OutEdgeT>().begin());
const Expand &self_;
@ -826,6 +835,9 @@ class Expand : public memgraph::query::plan::LogicalOperator {
std::optional<InEdgeIteratorT> in_edges_it_;
std::optional<OutEdgeT> out_edges_;
std::optional<OutEdgeIteratorT> out_edges_it_;
ExpansionInfo expansion_info_;
int64_t prev_input_degree_{-1};
int64_t prev_existing_degree_{-1};
bool InitEdges(Frame &, ExecutionContext &);
};

View File

@ -2120,7 +2120,7 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
LOG_FATAL("Unexpected error when getting the inbound edges of a vertex.");
}
}
it->in.emplace(std::move(*maybe_edges));
it->in.emplace(std::move(maybe_edges->edges));
it->in_it.emplace(it->in->begin());
#ifdef MG_ENTERPRISE
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
@ -2180,7 +2180,7 @@ mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges
}
}
it->out.emplace(std::move(*maybe_edges));
it->out.emplace(std::move(maybe_edges->edges));
it->out_it.emplace(it->out->begin());
#ifdef MG_ENTERPRISE

View File

@ -687,12 +687,14 @@ struct mgp_edges_iterator {
memgraph::utils::MemoryResource *memory;
mgp_vertex source_vertex;
std::optional<std::remove_reference_t<
decltype(*std::get<memgraph::query::VertexAccessor>(source_vertex.impl).InEdges(source_vertex.graph->view))>>
std::optional<std::remove_reference_t<decltype(std::get<memgraph::query::VertexAccessor>(source_vertex.impl)
.InEdges(source_vertex.graph->view)
->edges)>>
in;
std::optional<decltype(in->begin())> in_it;
std::optional<std::remove_reference_t<
decltype(*std::get<memgraph::query::VertexAccessor>(source_vertex.impl).OutEdges(source_vertex.graph->view))>>
std::optional<std::remove_reference_t<decltype(std::get<memgraph::query::VertexAccessor>(source_vertex.impl)
.OutEdges(source_vertex.graph->view)
->edges)>>
out;
std::optional<decltype(out->begin())> out_it;
std::optional<mgp_edge> current_e;

View File

@ -306,7 +306,7 @@ void TriggerContext::AdaptForAccessor(DbAccessor *accessor) {
auto maybe_out_edges = maybe_from_vertex->OutEdges(storage::View::OLD);
MG_ASSERT(maybe_out_edges.HasValue());
const auto edge_gid = created_edge.object.Gid();
for (const auto &edge : *maybe_out_edges) {
for (const auto &edge : maybe_out_edges->edges) {
if (edge.Gid() == edge_gid) {
*it = detail::CreatedObject{edge};
++it;
@ -327,7 +327,7 @@ void TriggerContext::AdaptForAccessor(DbAccessor *accessor) {
accessor->PrefetchOutEdges(*maybe_vertex);
auto maybe_out_edges = maybe_vertex->OutEdges(storage::View::OLD);
MG_ASSERT(maybe_out_edges.HasValue());
for (const auto &edge : *maybe_out_edges) {
for (const auto &edge : maybe_out_edges->edges) {
if (edge.Gid() == value.object.Gid()) {
*it = std::move(value);
it->object = edge;

View File

@ -1178,10 +1178,10 @@ bool DiskStorage::DiskAccessor::PrefetchEdgeFilter(const std::string_view disk_e
}
MG_ASSERT(edges_res.HasValue());
auto edges = edges_res.GetValue();
bool isEdgeAlreadyInMemory = std::any_of(edges.begin(), edges.end(), [edge_gid](const auto &edge_acc) {
return utils::SerializeIdType(edge_acc.Gid()) == edge_gid;
});
auto edges_result = edges_res.GetValue();
bool isEdgeAlreadyInMemory =
std::any_of(edges_result.edges.begin(), edges_result.edges.end(),
[edge_gid](const auto &edge_acc) { return utils::SerializeIdType(edge_acc.Gid()) == edge_gid; });
return !isEdgeAlreadyInMemory;
}

View File

@ -1533,14 +1533,14 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
write_mapping(item.first);
snapshot.WritePropertyValue(item.second);
}
const auto &in_edges = maybe_in_edges.GetValue();
const auto &in_edges = maybe_in_edges.GetValue().edges;
snapshot.WriteUint(in_edges.size());
for (const auto &item : in_edges) {
snapshot.WriteUint(item.Gid().AsUint());
snapshot.WriteUint(item.FromVertex().Gid().AsUint());
write_mapping(item.EdgeType());
}
const auto &out_edges = maybe_out_edges.GetValue();
const auto &out_edges = maybe_out_edges.GetValue().edges;
snapshot.WriteUint(out_edges.size());
for (const auto &item : out_edges) {
snapshot.WriteUint(item.Gid().AsUint());

View File

@ -388,8 +388,8 @@ uint64_t InMemoryReplicationServer::ReadAndApplyDelta(InMemoryStorage *storage,
auto edges = from_vertex->OutEdges(View::NEW, {transaction->NameToEdgeType(delta.edge_create_delete.edge_type)},
&*to_vertex);
if (edges.HasError()) throw utils::BasicException("Invalid transaction!");
if (edges->size() != 1) throw utils::BasicException("Invalid transaction!");
auto &edge = (*edges)[0];
if (edges->edges.size() != 1) throw utils::BasicException("Invalid transaction!");
auto &edge = (*edges).edges[0];
auto ret = transaction->DeleteEdge(&edge);
if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
break;

View File

@ -423,7 +423,7 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view
return std::move(properties);
}
Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::vector<EdgeTypeId> &edge_types,
Result<EdgesVertexAccessorResult> VertexAccessor::InEdges(View view, const std::vector<EdgeTypeId> &edge_types,
const VertexAccessor *destination) const {
MG_ASSERT(!destination || destination->transaction_ == transaction_, "Invalid accessor!");
@ -445,9 +445,11 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
bool deleted = false;
auto in_edges = edge_store{};
Delta *delta = nullptr;
int64_t expanded_count = 0;
{
auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted;
expanded_count = static_cast<int64_t>(vertex_->in_edges.size());
// TODO: a better filter copy
if (edge_types.empty() && !destination) {
in_edges = vertex_->in_edges;
@ -472,7 +474,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
auto const &cache = transaction_->manyDeltasCache;
if (auto resError = HasError(view, cache, vertex_, for_deleted_); resError) return *resError;
if (auto resInEdges = cache.GetInEdges(view, vertex_, destination_vertex, edge_types); resInEdges)
return {build_result(*resInEdges)};
return EdgesVertexAccessorResult{.edges = build_result(*resInEdges), .expanded_count = expanded_count};
}
auto const n_processed = ApplyDeltasForRead(
@ -497,10 +499,11 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
if (!exists) return Error::NONEXISTENT_OBJECT;
if (deleted) return Error::DELETED_OBJECT;
return build_result(in_edges);
return EdgesVertexAccessorResult{.edges = build_result(in_edges), .expanded_count = expanded_count};
}
Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std::vector<EdgeTypeId> &edge_types,
Result<EdgesVertexAccessorResult> VertexAccessor::OutEdges(View view, const std::vector<EdgeTypeId> &edge_types,
const VertexAccessor *destination) const {
MG_ASSERT(!destination || destination->transaction_ == transaction_, "Invalid accessor!");
@ -521,9 +524,11 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
bool deleted = false;
auto out_edges = edge_store{};
Delta *delta = nullptr;
int64_t expanded_count = 0;
{
auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted;
expanded_count = static_cast<int64_t>(vertex_->out_edges.size());
if (edge_types.empty() && !destination) {
out_edges = vertex_->out_edges;
} else {
@ -547,7 +552,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
auto const &cache = transaction_->manyDeltasCache;
if (auto resError = HasError(view, cache, vertex_, for_deleted_); resError) return *resError;
if (auto resOutEdges = cache.GetOutEdges(view, vertex_, dst_vertex, edge_types); resOutEdges)
return {build_result(*resOutEdges)};
return EdgesVertexAccessorResult{.edges = build_result(*resOutEdges), .expanded_count = expanded_count};
}
auto const n_processed = ApplyDeltasForRead(
@ -571,7 +576,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
if (!exists) return Error::NONEXISTENT_OBJECT;
if (deleted) return Error::DELETED_OBJECT;
return build_result(out_edges);
return EdgesVertexAccessorResult{.edges = build_result(out_edges), .expanded_count = expanded_count};
}
Result<size_t> VertexAccessor::InDegree(View view) const {

View File

@ -26,6 +26,7 @@ class EdgeAccessor;
class Storage;
struct Constraints;
struct Indices;
struct EdgesVertexAccessorResult;
class VertexAccessor final {
private:
@ -89,13 +90,13 @@ class VertexAccessor final {
/// @throw std::bad_alloc
/// @throw std::length_error if the resulting vector exceeds
/// std::vector::max_size().
Result<std::vector<EdgeAccessor>> InEdges(View view, const std::vector<EdgeTypeId> &edge_types = {},
Result<EdgesVertexAccessorResult> InEdges(View view, const std::vector<EdgeTypeId> &edge_types = {},
const VertexAccessor *destination = nullptr) const;
/// @throw std::bad_alloc
/// @throw std::length_error if the resulting vector exceeds
/// std::vector::max_size().
Result<std::vector<EdgeAccessor>> OutEdges(View view, const std::vector<EdgeTypeId> &edge_types = {},
Result<EdgesVertexAccessorResult> OutEdges(View view, const std::vector<EdgeTypeId> &edge_types = {},
const VertexAccessor *destination = nullptr) const;
Result<size_t> InDegree(View view) const;
@ -125,6 +126,11 @@ class VertexAccessor final {
bool for_deleted_{false};
};
struct EdgesVertexAccessorResult {
std::vector<EdgeAccessor> edges;
int64_t expanded_count;
};
} // namespace memgraph::storage
namespace std {

View File

@ -71,7 +71,7 @@ RC_GTEST_PROP(RandomGraph, RandomGraph, (std::vector<std::string> vertex_labels,
vertices_num_check++;
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
RC_ASSERT(maybe_edges.HasValue());
for (auto &edge : *maybe_edges) {
for (auto &edge : maybe_edges->edges) {
const auto &type = edge_type_map.at(edge);
RC_ASSERT(dba->EdgeTypeToName(edge.EdgeType()) == type);
edges_num_check++;

View File

@ -131,7 +131,7 @@ TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithEdgeTimestampUpdate) {
acc2->PrefetchOutEdges(from_vertex);
auto ret = from_vertex.OutEdges(memgraph::storage::View::NEW);
auto fetched_edge = ret.GetValue()[0];
auto fetched_edge = ret.GetValue().edges[0];
/// This is the same property as in the first transaction, we just want to test
/// the number of entries inside RocksDB when the timestamp changes
@ -171,7 +171,7 @@ TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithEdgeValueUpdate) {
acc2->PrefetchOutEdges(from_vertex);
auto ret = from_vertex.OutEdges(memgraph::storage::View::NEW);
auto fetched_edge = ret.GetValue()[0];
auto fetched_edge = ret.GetValue().edges[0];
auto property2 = acc2->NameToProperty("DiskProperty");
ASSERT_TRUE(fetched_edge.SetProperty(property2, memgraph::storage::PropertyValue(15)).HasValue());

View File

@ -158,7 +158,7 @@ DatabaseState GetState(memgraph::storage::Storage *db) {
for (const auto &vertex : dba->Vertices(memgraph::storage::View::NEW)) {
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
MG_ASSERT(maybe_edges.HasValue());
for (const auto &edge : *maybe_edges) {
for (const auto &edge : maybe_edges->edges) {
const auto &edge_type_name = dba->EdgeTypeToName(edge.EdgeType());
std::map<std::string, memgraph::storage::PropertyValue> props;
auto maybe_properties = edge.Properties(memgraph::storage::View::NEW);

View File

@ -224,7 +224,7 @@ inline uint64_t CountEdges(memgraph::query::DbAccessor *dba, memgraph::storage::
dba->PrefetchOutEdges(vertex);
auto maybe_edges = vertex.OutEdges(view);
MG_ASSERT(maybe_edges.HasValue());
count += CountIterable(*maybe_edges);
count += CountIterable(maybe_edges->edges);
}
return count;
}

View File

@ -304,7 +304,7 @@ TYPED_TEST(QueryPlanTest, CreateExpand) {
dba.PrefetchOutEdges(vertex);
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
MG_ASSERT(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
for (auto edge : maybe_edges->edges) {
EXPECT_EQ(edge.EdgeType(), edge_type);
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, property.second)->ValueInt(), 3);
}
@ -1129,7 +1129,7 @@ TYPED_TEST(QueryPlanTest, SetProperty) {
dba.PrefetchOutEdges(vertex);
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
for (auto edge : maybe_edges->edges) {
ASSERT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
memgraph::storage::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop1)->ValueInt(), 42);
@ -1184,7 +1184,7 @@ TYPED_TEST(QueryPlanTest, SetProperties) {
dba.PrefetchOutEdges(vertex);
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
for (auto edge : maybe_edges->edges) {
auto from = edge.From();
EXPECT_EQ(from.Properties(memgraph::storage::View::OLD)->size(), update ? 2 : 1);
if (update) {
@ -1365,7 +1365,7 @@ TYPED_TEST(QueryPlanTest, RemoveProperty) {
dba.PrefetchOutEdges(vertex);
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
for (auto edge : maybe_edges->edges) {
EXPECT_EQ(edge.GetProperty(memgraph::storage::View::OLD, prop1)->type(),
memgraph::storage::PropertyValue::Type::Null);
auto from = edge.From();
@ -2258,7 +2258,7 @@ TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
for (auto vertex : this->dba.Vertices(memgraph::storage::View::NEW)) {
if (vertex.OutEdges(memgraph::storage::View::NEW).HasValue()) {
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
for (auto edge : *maybe_edges) {
for (auto edge : maybe_edges->edges) {
EXPECT_EQ(edge.EdgeType(), edge_type_id);
auto maybe_properties = edge.Properties(memgraph::storage::View::NEW);
ASSERT_TRUE(maybe_properties.HasValue());
@ -2276,7 +2276,7 @@ TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
for (auto vertex : this->dba.Vertices(memgraph::storage::View::NEW)) {
if (vertex.OutEdges(memgraph::storage::View::NEW).HasValue()) {
auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
for (auto edge : *maybe_edges) {
for (auto edge : maybe_edges->edges) {
EXPECT_EQ(edge.EdgeType(), edge_type_id);
auto maybe_properties = edge.Properties(memgraph::storage::View::NEW);
ASSERT_TRUE(maybe_properties.HasValue());
@ -2495,3 +2495,112 @@ TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
}
}
#endif
template <typename StorageType>
class DynamicExpandFixture : public testing::Test {
protected:
const std::string testSuite = "query_plan_create_set_remove_delete_dynamic_expand";
memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
memgraph::query::DbAccessor dba{storage_dba.get()};
SymbolTable symbol_table;
AstStorage storage;
// make 2 nodes connected to the third node
memgraph::query::VertexAccessor v1{dba.InsertVertex()};
memgraph::query::VertexAccessor v2{dba.InsertVertex()};
memgraph::query::VertexAccessor v3{dba.InsertVertex()};
memgraph::query::VertexAccessor v4{dba.InsertVertex()};
memgraph::query::VertexAccessor v5{dba.InsertVertex()};
memgraph::storage::EdgeTypeId edge_type{db->NameToEdgeType("Edge")};
memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&v1, &v5, edge_type)};
memgraph::query::EdgeAccessor r2{*dba.InsertEdge(&v2, &v5, edge_type)};
memgraph::query::EdgeAccessor r3{*dba.InsertEdge(&v3, &v5, edge_type)};
memgraph::query::EdgeAccessor r4{*dba.InsertEdge(&v4, &v5, edge_type)};
memgraph::storage::LabelId node_label{dba.NameToLabel("Node")};
memgraph::storage::LabelId supernode_label{dba.NameToLabel("Supernode")};
void SetUp() override {
ASSERT_TRUE(v1.AddLabel(node_label).HasValue());
ASSERT_TRUE(v2.AddLabel(node_label).HasValue());
ASSERT_TRUE(v3.AddLabel(node_label).HasValue());
ASSERT_TRUE(v4.AddLabel(node_label).HasValue());
ASSERT_TRUE(v5.AddLabel(supernode_label).HasValue());
memgraph::license::global_license_checker.EnableTesting();
dba.AdvanceCommand();
}
void TearDown() override {
if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
disk_test_utils::RemoveRocksDbDirs(testSuite);
}
}
};
using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
TYPED_TEST_CASE(DynamicExpandFixture, StorageTypes);
TYPED_TEST(DynamicExpandFixture, Expand) {
using ExpandCursor = memgraph::query::plan::Expand::ExpandCursor;
auto scan_node_by_label = MakeScanAllByLabel(this->storage, this->symbol_table, "n", this->node_label);
auto scan_supernode_by_label =
MakeScanAllByLabel(this->storage, this->symbol_table, "s", this->supernode_label, scan_node_by_label.op_);
auto once = std::make_shared<Once>();
auto edge_sym = this->symbol_table.CreateSymbol("r", true);
auto my_expand = std::make_shared<Expand>(
scan_supernode_by_label.op_, scan_supernode_by_label.sym_, scan_node_by_label.sym_, edge_sym,
EdgeAtom::Direction::OUT, std::vector<memgraph::storage::EdgeTypeId>{}, true, memgraph::storage::View::OLD);
auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
Frame frame{context.symbol_table.max_position()};
frame[scan_supernode_by_label.sym_] = this->v4;
frame[scan_node_by_label.sym_] = this->v1;
auto *mem = memgraph::utils::NewDeleteResource();
auto initial_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, -1, -1, mem);
auto *initial_cursor = dynamic_cast<ExpandCursor *>(initial_cursor_ptr.get());
auto expansion_info = initial_cursor->GetExpansionInfo(frame);
ASSERT_EQ(expansion_info.input_node.value(), this->v4);
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::OUT);
ASSERT_EQ(expansion_info.existing_node.value(), this->v1);
auto expanded_first_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 1, -1, mem);
auto *expanded_first_cursor = dynamic_cast<ExpandCursor *>(expanded_first_cursor_ptr.get());
expansion_info = expanded_first_cursor->GetExpansionInfo(frame);
ASSERT_EQ(expansion_info.input_node.value(), this->v1);
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::IN);
ASSERT_EQ(expansion_info.existing_node.value(), this->v4);
auto expanded_both_take_first_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 1, 100, mem);
auto *expanded_both_take_first = dynamic_cast<ExpandCursor *>(expanded_both_take_first_cursor_ptr.get());
expansion_info = expanded_both_take_first->GetExpansionInfo(frame);
ASSERT_EQ(expansion_info.input_node.value(), this->v4);
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::OUT);
ASSERT_EQ(expansion_info.existing_node.value(), this->v1);
auto expanded_both_take_second_cursor_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 100, 1, mem);
auto *expanded_both_take_second = dynamic_cast<ExpandCursor *>(expanded_both_take_second_cursor_ptr.get());
expansion_info = expanded_both_take_second->GetExpansionInfo(frame);
ASSERT_EQ(expansion_info.input_node.value(), this->v1);
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::IN);
ASSERT_EQ(expansion_info.existing_node.value(), this->v4);
auto expanded_equal_take_second_cursror_ptr = MakeUniqueCursorPtr<ExpandCursor>(mem, *my_expand, 5, 5, mem);
auto *expanded_equal_take_second = dynamic_cast<ExpandCursor *>(expanded_equal_take_second_cursror_ptr.get());
expansion_info = expanded_equal_take_second->GetExpansionInfo(frame);
ASSERT_EQ(expansion_info.input_node.value(), this->v1);
ASSERT_EQ(expansion_info.direction, EdgeAtom::Direction::IN);
ASSERT_EQ(expansion_info.existing_node.value(), this->v4);
}

View File

@ -1231,9 +1231,9 @@ TYPED_TEST(QueryPlanExpandVariable, NamedPath) {
for (const auto &v : this->dba.Vertices(memgraph::storage::View::OLD)) {
if (!*v.HasLabel(memgraph::storage::View::OLD, this->labels[0])) continue;
auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
for (const auto &e1 : *maybe_edges1) {
for (const auto &e1 : maybe_edges1->edges) {
auto maybe_edges2 = e1.To().OutEdges(memgraph::storage::View::OLD);
for (const auto &e2 : *maybe_edges2) {
for (const auto &e2 : maybe_edges2->edges) {
expected_paths.emplace_back(v, e1, e1.To(), e2, e2.To());
}
}
@ -1341,7 +1341,7 @@ TYPED_TEST(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
if (!*v.HasLabel(memgraph::storage::View::OLD, this->labels[0])) continue;
expected_paths.emplace_back(v);
auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
for (const auto &e1 : *maybe_edges1) {
for (const auto &e1 : maybe_edges1->edges) {
expected_paths.emplace_back(v, e1, e1.To());
}
}
@ -1378,7 +1378,7 @@ TYPED_TEST(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
if (!*v.HasLabel(memgraph::storage::View::OLD, this->labels[0])) continue;
expected_paths.emplace_back(v);
auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
for (const auto &e1 : *maybe_edges1) {
for (const auto &e1 : maybe_edges1->edges) {
expected_paths.emplace_back(v, e1, e1.To());
}
}

View File

@ -66,8 +66,8 @@ TYPED_TEST(QueryPlan, CreateNodeWithAttributes) {
EXPECT_EQ(v.GetProperty(memgraph::storage::View::NEW, property)->ValueInt(), 42);
dba->PrefetchInEdges(v.impl_);
dba->PrefetchOutEdges(v.impl_);
EXPECT_EQ(CountIterable(*v.InEdges(memgraph::storage::View::NEW)), 0);
EXPECT_EQ(CountIterable(*v.OutEdges(memgraph::storage::View::NEW)), 0);
EXPECT_EQ(CountIterable(v.InEdges(memgraph::storage::View::NEW)->edges), 0);
EXPECT_EQ(CountIterable(v.OutEdges(memgraph::storage::View::NEW)->edges), 0);
// Invokes LOG(FATAL) instead of erroring out.
// EXPECT_TRUE(v.HasLabel(label, memgraph::storage::View::OLD).IsError());
}

View File

@ -83,13 +83,13 @@ using MgpVertexPtr = std::unique_ptr<mgp_vertex, MgpVertexDeleter>;
using MgpVerticesIteratorPtr = std::unique_ptr<mgp_vertices_iterator, MgpVerticesIteratorDeleter>;
using MgpValuePtr = std::unique_ptr<mgp_value, MgpValueDeleter>;
template <typename TMaybeIterable>
size_t CountMaybeIterables(TMaybeIterable &&maybe_iterable) {
template <typename TMaybeIterable, typename TIterableAccessor>
size_t CountMaybeIterables(TMaybeIterable &&maybe_iterable, TIterableAccessor func) {
if (maybe_iterable.HasError()) {
ADD_FAILURE() << static_cast<std::underlying_type_t<typename TMaybeIterable::ErrorType>>(maybe_iterable.GetError());
return 0;
}
auto &iterable = maybe_iterable.GetValue();
auto iterable = func(maybe_iterable.GetValue());
return std::distance(iterable.begin(), iterable.end());
}
@ -109,16 +109,20 @@ void CheckEdgeCountBetween(const MgpVertexPtr &from, const MgpVertexPtr &to, con
}
EXPECT_EQ(
CountMaybeIterables(std::visit([](auto impl) { return impl.InEdges(memgraph::storage::View::NEW); }, from->impl)),
CountMaybeIterables(std::visit([](auto impl) { return impl.InEdges(memgraph::storage::View::NEW); }, from->impl),
[](const auto &edge_result) { return edge_result.edges; }),
0);
EXPECT_EQ(CountMaybeIterables(
std::visit([](auto impl) { return impl.OutEdges(memgraph::storage::View::NEW); }, from->impl)),
number_of_edges_between);
EXPECT_EQ(
CountMaybeIterables(std::visit([](auto impl) { return impl.InEdges(memgraph::storage::View::NEW); }, to->impl)),
CountMaybeIterables(std::visit([](auto impl) { return impl.OutEdges(memgraph::storage::View::NEW); }, from->impl),
[](const auto &edge_result) { return edge_result.edges; }),
number_of_edges_between);
EXPECT_EQ(
CountMaybeIterables(std::visit([](auto impl) { return impl.OutEdges(memgraph::storage::View::NEW); }, to->impl)),
CountMaybeIterables(std::visit([](auto impl) { return impl.InEdges(memgraph::storage::View::NEW); }, to->impl),
[](const auto &edge_result) { return edge_result.edges; }),
number_of_edges_between);
EXPECT_EQ(
CountMaybeIterables(std::visit([](auto impl) { return impl.OutEdges(memgraph::storage::View::NEW); }, to->impl),
[](const auto &edge_result) { return edge_result.edges; }),
0);
}
} // namespace
@ -624,7 +628,7 @@ TYPED_TEST(MgpGraphTest, EdgeSetProperty) {
memgraph::storage::Gid from_vertex_id{};
auto get_edge = [&from_vertex_id](memgraph::storage::Storage::Accessor *accessor) -> memgraph::storage::EdgeAccessor {
auto from = accessor->FindVertex(from_vertex_id, memgraph::storage::View::NEW);
return std::move(from->OutEdges(memgraph::storage::View::NEW).GetValue().front());
return std::move(from->OutEdges(memgraph::storage::View::NEW).GetValue().edges.front());
};
{
const auto vertex_ids = this->CreateEdge();

View File

@ -208,7 +208,7 @@ TYPED_TEST(TriggerContextTest, ValidObjectsTest) {
auto out_edges = vertex.OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
for (auto edge : *out_edges) {
for (auto edge : out_edges->edges) {
trigger_context_collector.RegisterSetObjectProperty(edge, dba.NameToProperty("PROPERTY1"),
memgraph::query::TypedValue("Value"),
memgraph::query::TypedValue("ValueNew"));

View File

@ -2278,8 +2278,8 @@ TYPED_TEST(StorageV2Test, VertexNonexistentLabelPropertyEdgeAPI) {
ASSERT_EQ(*vertex.HasLabel(label, memgraph::storage::View::NEW), false);
ASSERT_EQ(vertex.Properties(memgraph::storage::View::NEW)->size(), 0);
ASSERT_EQ(*vertex.GetProperty(property, memgraph::storage::View::NEW), memgraph::storage::PropertyValue());
ASSERT_EQ(vertex.InEdges(memgraph::storage::View::NEW)->size(), 0);
ASSERT_EQ(vertex.OutEdges(memgraph::storage::View::NEW)->size(), 0);
ASSERT_EQ(vertex.InEdges(memgraph::storage::View::NEW)->edges.size(), 0);
ASSERT_EQ(vertex.OutEdges(memgraph::storage::View::NEW)->edges.size(), 0);
ASSERT_EQ(*vertex.InDegree(memgraph::storage::View::NEW), 0);
ASSERT_EQ(*vertex.OutDegree(memgraph::storage::View::NEW), 0);
@ -2305,8 +2305,8 @@ TYPED_TEST(StorageV2Test, VertexNonexistentLabelPropertyEdgeAPI) {
ASSERT_EQ(*vertex.HasLabel(label, memgraph::storage::View::NEW), true);
ASSERT_EQ(vertex.Properties(memgraph::storage::View::NEW)->size(), 1);
ASSERT_EQ(*vertex.GetProperty(property, memgraph::storage::View::NEW), memgraph::storage::PropertyValue("value"));
ASSERT_EQ(vertex.InEdges(memgraph::storage::View::NEW)->size(), 1);
ASSERT_EQ(vertex.OutEdges(memgraph::storage::View::NEW)->size(), 1);
ASSERT_EQ(vertex.InEdges(memgraph::storage::View::NEW)->edges.size(), 1);
ASSERT_EQ(vertex.OutEdges(memgraph::storage::View::NEW)->edges.size(), 1);
ASSERT_EQ(*vertex.InDegree(memgraph::storage::View::NEW), 1);
ASSERT_EQ(*vertex.OutDegree(memgraph::storage::View::NEW), 1);

View File

@ -322,7 +322,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
ASSERT_TRUE(vertex1);
auto out_edges = vertex1->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
auto edge1 = find_edge(*out_edges);
auto edge1 = find_edge(out_edges->edges);
ASSERT_TRUE(edge1);
if (i < kNumBaseEdges / 2) {
ASSERT_EQ(edge1->EdgeType(), et1);
@ -344,7 +344,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
ASSERT_TRUE(vertex2);
auto in_edges = vertex2->InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(in_edges.HasValue());
auto edge2 = find_edge(*in_edges);
auto edge2 = find_edge(in_edges->edges);
ASSERT_TRUE(edge2);
if (i < kNumBaseEdges / 2) {
ASSERT_EQ(edge2->EdgeType(), et1);
@ -457,7 +457,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
ASSERT_TRUE(vertex1);
auto out_edges = vertex1->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
auto edge1 = find_edge(*out_edges);
auto edge1 = find_edge(out_edges->edges);
ASSERT_TRUE(edge1);
if (i < kNumExtendedEdges / 4) {
ASSERT_EQ(edge1->EdgeType(), et3);
@ -475,7 +475,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
ASSERT_TRUE(vertex2);
auto in_edges = vertex2->InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(in_edges.HasValue());
auto edge2 = find_edge(*in_edges);
auto edge2 = find_edge(in_edges->edges);
ASSERT_TRUE(edge2);
if (i < kNumExtendedEdges / 4) {
ASSERT_EQ(edge2->EdgeType(), et3);
@ -1128,7 +1128,7 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutProp
for (auto vertex : acc->Vertices(memgraph::storage::View::OLD)) {
auto in_edges = vertex.InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(in_edges.HasValue());
for (auto &edge : *in_edges) {
for (auto &edge : in_edges->edges) {
// TODO (mferencevic): Replace with `ClearProperties()`
auto props = edge.Properties(memgraph::storage::View::NEW);
ASSERT_TRUE(props.HasValue());
@ -1138,7 +1138,7 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutProp
}
auto out_edges = vertex.InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
for (auto &edge : *out_edges) {
for (auto &edge : out_edges->edges) {
// TODO (mferencevic): Replace with `ClearProperties()`
auto props = edge.Properties(memgraph::storage::View::NEW);
ASSERT_TRUE(props.HasValue());
@ -1377,11 +1377,11 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
ASSERT_EQ(props->size(), 0);
auto in_edges = v1->InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(in_edges.HasValue());
ASSERT_EQ(in_edges->size(), 0);
ASSERT_EQ(in_edges->edges.size(), 0);
auto out_edges = v1->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
ASSERT_EQ(out_edges->size(), 1);
const auto &edge = (*out_edges)[0];
ASSERT_EQ(out_edges->edges.size(), 1);
const auto &edge = out_edges->edges[0];
ASSERT_EQ(edge.Gid(), gid_e1);
auto edge_props = edge.Properties(memgraph::storage::View::OLD);
ASSERT_TRUE(edge_props.HasValue());
@ -1404,8 +1404,8 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
memgraph::storage::PropertyValue("world"))));
auto in_edges = v2->InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(in_edges.HasValue());
ASSERT_EQ(in_edges->size(), 1);
const auto &edge = (*in_edges)[0];
ASSERT_EQ(in_edges->edges.size(), 1);
const auto &edge = in_edges->edges[0];
ASSERT_EQ(edge.Gid(), gid_e1);
auto edge_props = edge.Properties(memgraph::storage::View::OLD);
ASSERT_TRUE(edge_props.HasValue());
@ -1417,7 +1417,7 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
}
auto out_edges = v2->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
ASSERT_EQ(out_edges->size(), 0);
ASSERT_EQ(out_edges->edges.size(), 0);
}
{
auto v3 = acc->FindVertex(gid_v3, memgraph::storage::View::OLD);
@ -1431,10 +1431,10 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
std::make_pair(store->NameToProperty("v3"), memgraph::storage::PropertyValue(42))));
auto in_edges = v3->InEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(in_edges.HasValue());
ASSERT_EQ(in_edges->size(), 0);
ASSERT_EQ(in_edges->edges.size(), 0);
auto out_edges = v3->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
ASSERT_EQ(out_edges->size(), 0);
ASSERT_EQ(out_edges->edges.size(), 0);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -134,22 +134,22 @@ TEST(StorageV2Gc, Sanity) {
auto vertex = acc->FindVertex(vertices[i], memgraph::storage::View::NEW);
EXPECT_EQ(vertex.has_value(), i % 5 != 0 && i % 3 != 0);
if (vertex.has_value()) {
auto out_edges = vertex->OutEdges(memgraph::storage::View::NEW);
auto out_edges = vertex->OutEdges(memgraph::storage::View::NEW)->edges;
if (i % 5 != 4 && i % 3 != 2) {
EXPECT_EQ(out_edges.GetValue().size(), 1);
EXPECT_EQ(out_edges.size(), 1);
EXPECT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
EXPECT_EQ(out_edges.GetValue().at(0).EdgeType().AsUint(), i);
EXPECT_EQ(out_edges.at(0).EdgeType().AsUint(), i);
} else {
EXPECT_TRUE(out_edges->empty());
EXPECT_TRUE(out_edges.empty());
}
auto in_edges = vertex->InEdges(memgraph::storage::View::NEW);
auto in_edges = vertex->InEdges(memgraph::storage::View::NEW)->edges;
if (i % 5 != 1 && i % 3 != 1) {
EXPECT_EQ(in_edges.GetValue().size(), 1);
EXPECT_EQ(in_edges.size(), 1);
EXPECT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
EXPECT_EQ(in_edges.GetValue().at(0).EdgeType().AsUint(), (i + 999) % 1000);
EXPECT_EQ(in_edges.at(0).EdgeType().AsUint(), (i + 999) % 1000);
} else {
EXPECT_TRUE(in_edges->empty());
EXPECT_TRUE(in_edges.empty());
}
}
}

View File

@ -180,7 +180,7 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
ASSERT_TRUE(v);
const auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
const auto edge = find_edge(*out_edges, *edge_gid);
const auto edge = find_edge(out_edges->edges, *edge_gid);
ASSERT_EQ(edge->EdgeType(), replica_store->NameToEdgeType(edge_type));
const auto properties = edge->Properties(memgraph::storage::View::OLD);
ASSERT_TRUE(properties.HasValue());
@ -197,7 +197,7 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
ASSERT_TRUE(v);
auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
auto edge = find_edge(*out_edges, *edge_gid);
auto edge = find_edge(out_edges->edges, *edge_gid);
ASSERT_TRUE(edge);
ASSERT_TRUE(acc->DeleteEdge(&*edge).HasValue());
ASSERT_FALSE(acc->Commit().HasError());
@ -209,7 +209,7 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
ASSERT_TRUE(v);
const auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
ASSERT_TRUE(out_edges.HasValue());
ASSERT_FALSE(find_edge(*out_edges, *edge_gid));
ASSERT_FALSE(find_edge(out_edges->edges, *edge_gid));
ASSERT_FALSE(acc->Commit().HasError());
}