From d6a885cce6ae53e29ae4c7c937952e9aadf0959d Mon Sep 17 00:00:00 2001 From: florijan <florijan@memgraph.io> Date: Tue, 19 Sep 2017 09:54:52 +0200 Subject: [PATCH] Traversal API removed Summary: Removed the traversal API, been waiting for named path to land because that data structure has replaced traversal's Path. I left the wiki page of the API, just put a warning that it's not used. Reviewers: buda, teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D809 --- src/query/typed_value.hpp | 1 - src/traversal/enums.hpp | 68 ---- src/traversal/path.hpp | 218 ----------- src/traversal/templates.hpp | 727 ------------------------------------ src/traversal/traversal.hpp | 60 --- tests/unit/traversal.cpp | 691 ---------------------------------- 6 files changed, 1765 deletions(-) delete mode 100644 src/traversal/enums.hpp delete mode 100644 src/traversal/path.hpp delete mode 100644 src/traversal/templates.hpp delete mode 100644 src/traversal/traversal.hpp delete mode 100644 tests/unit/traversal.cpp diff --git a/src/query/typed_value.hpp b/src/query/typed_value.hpp index de38a2249..fae0f793a 100644 --- a/src/query/typed_value.hpp +++ b/src/query/typed_value.hpp @@ -12,7 +12,6 @@ #include "storage/edge_accessor.hpp" #include "storage/property_value.hpp" #include "storage/vertex_accessor.hpp" -#include "traversal/path.hpp" #include "utils/exceptions.hpp" #include "utils/total_ordering.hpp" diff --git a/src/traversal/enums.hpp b/src/traversal/enums.hpp deleted file mode 100644 index e6ff48921..000000000 --- a/src/traversal/enums.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright 2017 Memgraph -// Created by Florijan Stamenkovic on 23.02.17. -// - -#pragma once - -namespace traversal_template { - -/** - * Indicates the type of uniqueness used when matching data to a pattern. - * "Uniqueness" means that a single Vertex or Edge in the result path - * may not map to a single element in the pattern. - * - * This is equivalent to saying that a single Vertex or Edge may not occur - * more then once in the resulting path. - * - * Often the "-morphism terminology is used, where Homomorphism means - * there are not uniqueness constraints, "Cyphermorphism" (Neo4j's current - * default) means Edge uniqueness, and Isomorphism means Vertex uniqueness - * (which also implies Edge uniqueness). - * - * TODO: Look into the slides on uniqueness from OpenCypher implementor meeting, - * it is mentioned that Vertex uniqueness can result in exponential performance - * degradation. Figure out why and how. - */ -enum class Uniqueness { None, Vertex, Edge }; - -/** - * Indicates how a path should be expanded using the traversal API. For the - * given path (we ignore relationship directionality in the example because it - * does not affect it): - * - * p = (node_N)-[]-(node_N+1)-...-(node_M) - * - * the Expansion::Front enum means that the path is expanded from (node_N) - * backwards, so the resulting path would be: - * - * q = (node_N-1)-[]-(node_N)-[]-(node_N+1)-...-(node_M) - * - * The Expansion::Back enum has the opposite meaning, path p would get expanded - * from node_M. - * - * Note that this implies that a Path has direction (start and finish). - */ -enum class Expansion { Front, Back }; - -/** - * Indicates which relationships from the expansion vertex should be used to - * expand - * the path. Direction::In means that incoming relationships are used for - * expansion. - * - * For example, for the given graph data: - * - * (a)-[]->(b)<-[]-(c) - * - * And the given current path - * - * p = (b) - * - * Expansion (let's assume Expansion::Back) in the Direction::In would result - * in: - * - * q = (b)<-[]-(a) - */ -enum class Direction { In, Out, Both }; -} diff --git a/src/traversal/path.hpp b/src/traversal/path.hpp deleted file mode 100644 index 45966bc9d..000000000 --- a/src/traversal/path.hpp +++ /dev/null @@ -1,218 +0,0 @@ -// -// Copyright 2017 Memgraph -// Created by Florijan Stamenkovic on 23.02.17. -// - -#pragma once - -#include <algorithm> -#include <functional> -#include <list> - -#include "enums.hpp" -#include "utils/assert.hpp" - -/** - * For a documentation of this namespace and it's - * intended usage (namespace specialization) take - * a look at the docs in "templates.hpp". - */ -namespace traversal_template { - -/** - * A path containing zero or more vertices. Between - * each two vertices is an edge. - * - * @tparam TVertex - * @tparam TEdge - */ -template <typename TVertex, typename TEdge> -class Path { - public: - Path() {} - - size_t Size() const { return vertices_.size(); } - - friend std::ostream &operator<<(std::ostream &stream, const Path &path) { - auto vertices_it = path.vertices_.begin(); - auto vertices_end = path.vertices_.end(); - auto edges_it = path.edges_.begin(); - - if (vertices_it != vertices_end) stream << *vertices_it++; - - while (vertices_it != vertices_end) - - // current vertex has incoming if it is - if (edges_it->to() == *vertices_it) - stream << "-" << *edges_it++ << "->" << *vertices_it++; - else - stream << "<-" << *edges_it++ << "-" << *vertices_it++; - - return stream; - } - - bool operator==(const Path &other) const { - return vertices_ == other.vertices_ && edges_ == other.edges_; - } - - bool operator!=(const Path &other) const { return !(*this == other); } - - /** - * Starts the path. At this moment the Path must be empty. - * - * @return A reference to this same path. - */ - Path &Start(const TVertex &v) { - debug_assert(vertices_.size() == 0, - "Can only start iteration on empty path"); - vertices_.push_back(v); - return *this; - } - - /** - * Checks if the given vertex is contained in this path. - */ - bool Contains(const TVertex &vertex) const { - for (const auto &v : vertices_) - if (v == vertex) return true; - - return false; - } - - /** - * Checks if the given edge is contained in this path. - */ - bool Contains(const TEdge &edge) const { - for (const auto &e : edges_) - if (e == edge) return true; - - return false; - } - - /** - * Gets the last Vertex of this path. Fails if the path contains no elements. - */ - const TVertex &Back() const { - debug_assert(vertices_.size() > 0, - "Can only get a Vertex on non-empty path"); - return vertices_.back(); - } - - /** - * Appends a traversal to the end of this path. - * - * @param edge The edge along with the traversal happens. - * @param vertex The new vertex to append to the path. - * @param direction The direction along which the traversal happens - * (relative to the currently last vertex in path). - * @return A reference to this same path. - */ - Path &Append(const TEdge &edge, const TVertex &vertex) { - edges_.emplace_back(edge); - vertices_.emplace_back(vertex); - return *this; - } - - /** - * Removes the last element from the path. Fails if the path contains no - * elements. - */ - void PopBack() { - debug_assert(vertices_.size() > 0, - "Can only remove a vertex from a non-empty path"); - vertices_.pop_back(); - - if (vertices_.size() > 0) edges_.pop_back(); - } - - /** - * Gets the first Vertex of this path. Fails if the path contains no elements. - */ - const TVertex &Front() const { - debug_assert(vertices_.size() > 0, - "Can only get a vertex from a non-empty path"); - return vertices_.front(); - } - - /** - * Prepends a traversal to the beginning of this path. - * - * @param edge The edge along with the traversal happens. - * @param vertex The new vertex to prepend to the path. - * @return A reference to this same path. - */ - Path &Prepend(const TEdge &edge, const TVertex &vertex) { - edges_.emplace_front(edge); - vertices_.emplace_front(vertex); - return *this; - } - - /** - * Removes the first element from the path. Fails if the path contains no - * elements. - */ - void PopFront() { - debug_assert(vertices_.size() > 0, - "Can only remove a vertex from a non-empty path"); - vertices_.pop_front(); - - if (vertices_.size() > 0) edges_.pop_front(); - } - - /** - * Removes all the elements from the path. - */ - void Clear() { - edges_.clear(); - vertices_.clear(); - } - - /** - * Returns all the vertices in this path. - */ - const auto &Vertices() const { return vertices_; } - - /** - * Returns all the edges in this path. - */ - const auto &Edges() const { return edges_; } - - private: - std::list<TVertex> vertices_; - std::list<TEdge> edges_; -}; - -/** - * An ordered sequence of Path reference wrappers. Used when - * visiting cartesian products of traversals. - */ -// TODO review: do we like std::list inheritance (and why)? -// alternatively we could do: -// A. using Paths = std::list<std::reference_wrapper<Path>>; -// B. encapsulation -template <typename TVertex, typename TEdge> -class Paths : public std::list<std::reference_wrapper<Path<TVertex, TEdge>>> { - using Path = Path<TVertex, TEdge>; - - public: - bool operator==(const Paths<TVertex, TEdge> &other) const { - return std::equal(this->begin(), this->end(), other.begin(), - [](const std::reference_wrapper<Path> &p1, - const std::reference_wrapper<Path> &p2) { - return p1.get() == p2.get(); - }); - } - - bool operator!=(const Paths &other) const { return !(*this == other); } - - friend std::ostream &operator<<(std::ostream &stream, const Paths &paths) { - stream << "["; - auto it = paths.begin(); - auto end = paths.end(); - - if (it != end) stream << *it++; - while (it != end) stream << ", " << *it++; - return stream << "]"; - } -}; -} diff --git a/src/traversal/templates.hpp b/src/traversal/templates.hpp deleted file mode 100644 index 697bd3f23..000000000 --- a/src/traversal/templates.hpp +++ /dev/null @@ -1,727 +0,0 @@ -// -// Copyright 2017 Memgraph -// Created by Florijan Stamenkovic on 20.02.17. -// - -#pragma once - -#include <functional> -#include <list> - -#include "enums.hpp" -#include "path.hpp" -#include "utils/assert.hpp" - -/** - * This namespace contains traversal class templates that must - * be parameterized with Vertex and Edge classes. This abstraction - * is made so that the traversal API can easily be used with real - * MemgraphDB Vertex and Edge classes, as well as mock classes used - * for testing independently of the DB engine. - * - * For an overview of the traversal API-s architecture (and a how-to-use) - * consult the Memgraph Wiki. - * - * The classes have the following templates: - * TVertex - vertex type. Provides functions for getting - * incoming and outgoing relationship TEdge objects. - * TEdge - edge type. Provides functions for getting - * origin and destination TVertex objects. - * TIterable - a class given to Begin. Can be iterated over - * (iteration provides Vertex references). - * TVisitable - a class that provides the Visit function - * that accepts a single std::function<void(Path &p)> - * argument. The path traversal classes here all conform - * to this definition, and can therefore be chained. - */ -namespace traversal_template { - -/** - * An object that enables Vertex and Edge uniqueness checks - * across multiple paths (multiple interdependent UniquenessGroups). - * - * It is intended that only the BeginType keeps a UniquenessGroup - * object and all of it's expansions only provide access to it. - * - * @tparam TVertex - * @tparam TEdge - */ -template <typename TVertex, typename TEdge> -class UniquenessGroup { - public: - UniquenessGroup(const Path<TVertex, TEdge> &path) : current_path_(path) {} - - /** - * Checks if this group or any of it's - * subgroups contains the given vertex. - * - * @param vertex - * @return - */ - bool Contains(const TVertex &vertex) const { - if (current_path_.Contains(vertex)) return true; - - for (const auto &group : subgroups_) - if (group.get().Contains(vertex)) return true; - - return false; - } - - /** - * Checks if this group or any of it's - * subgroups contains the given edge. - * - * @param vertex - * @return - */ - bool Contains(const TEdge &edge) const { - if (current_path_.Contains(edge)) return true; - - for (const auto &group : subgroups_) - if (group.get().Contains(edge)) return true; - - return false; - } - - /** - * Adds the given UniquenessGroup to this groups collection - * of subgroups. - * - * @param subgroup - */ - void Add(const UniquenessGroup<TVertex, TEdge> &subgroup) { - subgroups_.emplace_back(subgroup); - } - - private: - // the currently traversed path of this uniqueness group - // set by the BeginType - const Path<TVertex, TEdge> ¤t_path_; - - std::vector<std::reference_wrapper<const UniquenessGroup<TVertex, TEdge>>> - subgroups_; -}; - -/** - * Base class for path expansion. Provides functionalities common - * to both one-relationship expansions and variable-number expansions. - * - * Template parameters are documented in the namespace documentation, - * as they are the same for many classes. - * - * @tparam TVisitable - * @tparam TVertex - * @tparam TEdge - */ -template <typename TVisitable, typename TVertex, typename TEdge> -class ExpandBaseType { - using TPath = Path<TVertex, TEdge>; - using VertexFilter = std::function<bool(const TVertex &)>; - using EdgeFilter = std::function<bool(const TEdge &)>; - - public: - /** - * @return This expander's visitable's uniqueness group. - */ - UniquenessGroup<TVertex, TEdge> &UniquenessGroup() { - return visitable_.UniquenessGroup(); - } - - protected: - // tracking last appended path elements during traversal - TVertex const *current_vertex_ = nullptr; - TEdge const *current_edge_ = nullptr; - - // for docs on all member variables consult the only constructor - TVisitable &visitable_; - const Expansion expansion_; - const Direction direction_; - const VertexFilter vertex_filter_; - const EdgeFilter edge_filter_; - const Uniqueness uniqueness_; - - /** - * @param visitable The visitable that provides paths to expand from. - * @param expansion Indicates how the path should be expanded. Consult - * the enum documentation for an explanation. - * @param direction Indicates which relationships are used for expansion - * from a vertex. - * @param vertex_filter A function that accepts or rejects a Vertex that - * would get added to the path in this expansion. A path is only - * generated and visited if this function returns true (or is not - * provided). - * @param edge_filter A function that accepts or rejects an Edge that - * would get added to the path in this expansion. A path is only - * generated and visited if this function returns true (or is not - * provided). - * @param uniqueness Which kind of uniqueness should be applied. - */ - ExpandBaseType(TVisitable &&visitable, Expansion expansion, - Direction direction, VertexFilter vertex_filter, - EdgeFilter edge_filter, Uniqueness uniqueness) - : visitable_(std::forward<TVisitable>(visitable)), - expansion_(expansion), - direction_(direction), - vertex_filter_(vertex_filter), - edge_filter_(edge_filter), - uniqueness_(uniqueness) {} - - /** - * Visits the given visitor with every expansion of the given path - * that is in-line with this object's expansion-control members - * (expansion type and direction). - * - * @param visitor - * @param p - */ - void VisitExpansions(std::function<void(TPath &)> visitor, TPath &p) { - // the start or end point of the vertex - const auto &origin_vertex = - expansion_ == Expansion::Back ? p.Back() : p.Front(); - - if (direction_ == Direction::In || direction_ == Direction::Both) - VisitExpansions(origin_vertex.in(), p, visitor, Direction::In); - if (direction_ == Direction::Out || direction_ == Direction::Both) - VisitExpansions(origin_vertex.out(), p, visitor, Direction::Out); - } - - private: - /** - * Helper method that handles path expansion and visiting w.r.t. - * expansion params. - * - * NOTE: This function accepts the Direction as a param (it does - * NOT use the direction_ member) so that - * Direction::Both can be handled with two calls to this function - * (for ::In and ::Out). - * - * @tparam TVertices - * @param vertices - * @param p - * @param visitor - * @param direction - */ - template <typename Edges> - void VisitExpansions(Edges &edges, TPath &p, - std::function<void(TPath &)> visitor, - Direction direction) { - for (const TEdge &e : edges) { - // edge filtering and uniqueness - if (edge_filter_ && !edge_filter_(e)) continue; - if (uniqueness_ == Uniqueness::Edge && UniquenessGroup().Contains(e)) - continue; - - // vertex filtering and uniqueness - const TVertex &v = (direction == Direction::In) ? e.from() : e.to(); - if (vertex_filter_ && !vertex_filter_(v)) continue; - if (uniqueness_ == Uniqueness::Vertex && UniquenessGroup().Contains(v)) - continue; - - current_edge_ = &e; - current_vertex_ = &v; - - switch (expansion_) { - case Expansion::Front: - p.Prepend(e, v); - visitor(p); - p.PopFront(); - break; - case Expansion::Back: - p.Append(e, v); - visitor(p); - p.PopBack(); - break; - } - - current_edge_ = nullptr; - current_vertex_ = nullptr; - } - } -}; - -/** - * Expansion along a variable number of traversals. - * - * Template parameters are documented in the namespace documentation, - * as they are the same for many classes. - * - * @tparam TVisitable - * @tparam TVertex - * @tparam TEdge - */ -template <typename TVisitable, typename TVertex, typename TEdge> -class ExpandVariableType : public ExpandBaseType<TVisitable, TVertex, TEdge> { - using TPath = Path<TVertex, TEdge>; - using VertexFilter = std::function<bool(const TVertex &)>; - using EdgeFilter = std::function<bool(const TEdge &)>; - - public: - /** - * For most params see the ExpandBaseType::ExpandBaseType documentation. - * - * @param min_length Minimum number of vertices in a path for it to be - * visited. Inclusive. - * @param max_length Maximum number of vertices in a path for it to be - * visited. Exclusive. - */ - ExpandVariableType(TVisitable &&visitable, Expansion expansion, - Direction direction, VertexFilter vertex_filter, - EdgeFilter edge_filter, int min_length, int max_length, - Uniqueness uniqueness) - : ExpandBaseType<TVisitable, TVertex, TEdge>( - std::forward<TVisitable>(visitable), expansion, direction, {}, - edge_filter, uniqueness), - min_length_(min_length), - max_length_(max_length), - current_vertex_filter_(vertex_filter) {} - - /** - * Calls the given visitor function once for every path this traversal - * generates. - * - * @param visitor - */ - void Visit(std::function<void(TPath &)> visitor) { - this->visitable_.Visit( - [this, &visitor](TPath &p) { VisitRecursive(visitor, p, p.Size()); }); - } - - /** - * Expands from this expansion along one traversal. - * Arguments are simply passed to the expansion constructor. - * - * @return An expansion that generates paths one traversal longer. - */ - auto Expand(Expansion expansion, Direction direction, - VertexFilter vertex_filter = {}, EdgeFilter edge_filter = {}); - - /** - * Expands from this expansion along a variable number traversal. - * Arguments are simply passed to the variable expansion constructor. - * - * @return An expansion that generates paths variable length longer. - */ - auto ExpandVariable(Expansion expansion, Direction direction, - VertexFilter vertex_filter = {}, - EdgeFilter edge_filter = {}, int min_length = 0, - int max_length = 1000); - - /** - * Returns a reference to the vertex currently being traversed - * by this part of the traversal chain. ONLY VALID DURING - * TRAVERSAL!!! - */ - const TVertex &CurrentVertex() const { - debug_assert(this->current_vertex_ != nullptr, - "Current vertex not set, function most likely called outside " - "of traversal"); - return *this->current_vertex_; - } - - /** - * Returns a reference to the edge currently being traversed - * by this part of the traversal chain. ONLY VALID DURING - * TRAVERSAL!!! - */ - const std::list<TEdge> &CurrentEdges() const { - debug_assert(this->current_edge_ != nullptr, - "Current edge not set, function most likely called outside of " - "traversal"); - return current_edges_; - } - - private: - // see constructor documentation for member var explanation - const int min_length_; - const int max_length_; - - // the expand variable has another vertex filter used only on the last path - // element - // because traversal is done recursively using superclass functionality, - // so we give an empty filter to superclass so it does not end traversal, and - // use - // this filter to see if we actually need to visit the path or not - const VertexFilter current_vertex_filter_; - - // variable length traversal can return all the traversed edges as a list - // store them here during traversal - std::list<TEdge> current_edges_; - - /** - * Helper function for recursive path visiting. - * TODO: Try to make a stack implementation. It would be safer. - * - * @param visitor - * @param p - * @param p_start_size The size of the path before variable-length expansion. - * It's necessary to keep track of it because min and max length are - * evaluated against how much this traversal generated, not against the - * actual path length (there could have been plan expansions before this - * variable length). - */ - void VisitRecursive(std::function<void(TPath &)> visitor, TPath &p, - const size_t p_start_size) { - debug_assert(p.Size() >= p_start_size, - "Current path must be greater then start size"); - - size_t recursion_size = p.Size() - p_start_size; - - // only append to current_edges once the first traversal happened - if (recursion_size > 0) current_edges_.emplace_back(*this->current_edge_); - - if (recursion_size >= min_length_ && - (!current_vertex_filter_ || - current_vertex_filter_(*this->current_vertex_))) - visitor(p); - - if (recursion_size >= max_length_ - 1) return; - - // a lambda we'll inject to ExpandVisit, that calls this function - // with the expanded path - auto recursive_visitor = [this, &visitor, p_start_size](TPath &path) { - VisitRecursive(visitor, path, p_start_size); - }; - - this->VisitExpansions(recursive_visitor, p); - - if (recursion_size > 0) current_edges_.pop_back(); - } -}; - -/** - * Expansion along a single traversal. - * - * Template parameters are documented in the namespace documentation, - * as they are the same for many classes. - * - * @tparam TVisitable - * @tparam TVertex - * @tparam TEdge - */ -template <typename TVisitable, typename TVertex, typename TEdge> -class ExpandType : public ExpandBaseType<TVisitable, TVertex, TEdge> { - using TPath = Path<TVertex, TEdge>; - using VertexFilter = std::function<bool(const TVertex &)>; - using EdgeFilter = std::function<bool(const TEdge &)>; - - public: - /** - * For all params see the ExpandBaseType::ExpandBaseType documentation. - */ - ExpandType(TVisitable &&visitable, Expansion expansion, Direction direction, - VertexFilter vertex_filter, EdgeFilter edge_filter, - Uniqueness uniqueness) - : ExpandBaseType<TVisitable, TVertex, TEdge>( - std::forward<TVisitable>(visitable), expansion, direction, - vertex_filter, edge_filter, uniqueness) {} - - /** - * Calls the given visitor function once for every path this traversal - * generates. - * - * @param visitor - */ - void Visit(std::function<void(TPath &)> visitor) { - this->visitable_.Visit( - [this, &visitor](TPath &p) { this->VisitExpansions(visitor, p); }); - } - - /** - * Returns a reference to the vertex currently being traversed - * by this part of the traversal chain. ONLY VALID DURING - * TRAVERSAL!!! - */ - const TVertex &CurrentVertex() const { - debug_assert(this->current_vertex_ != nullptr, - "Current vertex not set, function most likely called outside " - "of traversal"); - return *this->current_vertex_; - } - - /** - * Returns a reference to the edge currently being traversed - * by this part of the traversal chain. ONLY VALID DURING - * TRAVERSAL!!! - */ - const TEdge &CurrentEdge() const { - debug_assert(this->current_edge_ != nullptr, - "Current edge not set, function most likely called outside of " - "traversal"); - return *this->current_edge_; - } - - /** - * Expands from this expansion along one traversal. - * Arguments are simply passed to the expansion constructor. - * - * @return An expansion that generates paths one traversal longer. - */ - auto Expand(Expansion expansion, Direction direction, - VertexFilter vertex_filter = {}, EdgeFilter edge_filter = {}) { - return ExpandType<ExpandType<TVisitable, TVertex, TEdge> &, TVertex, TEdge>( - *this, expansion, direction, vertex_filter, edge_filter, - this->uniqueness_); - } - - /** - * Expands from this expansion along a variable number traversal. - * Arguments are simply passed to the variable expansion constructor. - * - * @return An expansion that generates paths variable length longer. - */ - auto ExpandVariable(Expansion expansion, Direction direction, - VertexFilter vertex_filter = {}, - EdgeFilter edge_filter = {}, int min_length = 0, - int max_length = 1000) { - return ExpandVariableType<const ExpandType<TVisitable, TVertex, TEdge> &, - TVertex, TEdge>( - *this, expansion, direction, vertex_filter, edge_filter, min_length, - max_length, this->uniqueness_); - } -}; - -/** - * Start traversals from an iterable over Vertices. - * - * @tparam TIterable - * @tparam TVertex - * @tparam TEdge - */ -template <typename TIterable, typename TVertex, typename TEdge> -class BeginType { - using TPath = Path<TVertex, TEdge>; - using VertexFilter = std::function<bool(const TVertex &)>; - using EdgeFilter = std::function<bool(const TEdge &)>; - - public: - BeginType(const TIterable &vertices, VertexFilter vertex_filter) - : vertices_(vertices), - vertex_filter_(vertex_filter), - uniqueness_group_(path) {} - - /** - * Calls the visitor with a path containing a single vertex - * yielded by the given collection of vertices and accepted - * by the filter. - * - * @param visitor - */ - void Visit(std::function<void(TPath &)> visitor) { - for (const TVertex &v : vertices_) { - if (vertex_filter_ && !vertex_filter_(v)) continue; - - path.Start(v); - visitor(path); - path.PopFront(); - } - } - - /** - * The UniquenessGroup of this BeginType (the only one that - * exists for all the expansions from this Begin). - */ - UniquenessGroup<TVertex, TEdge> &UniquenessGroup() { - return uniqueness_group_; - } - - /** - * Returns a reference to the vertex currently being traversed - * by this part of the traversal chain. ONLY VALID DURING - * TRAVERSAL!!! - */ - const TVertex &CurrentVertex() const { - debug_assert(path.Size() > 0, - "Current path is empty, function most likely called outside " - "of traversal"); - return path.Front(); - } - - /** - * Expands from this expansion along one traversal. - * Arguments are simply passed to the expansion constructor. - * - * @return An expansion that generates paths one traversal longer. - */ - auto Expand(Expansion expansion, Direction direction, - VertexFilter vertex_filter = {}, EdgeFilter edge_filter = {}, - Uniqueness uniqueness = Uniqueness::Edge) { - return ExpandType<BeginType<TIterable, TVertex, TEdge> &, TVertex, TEdge>( - *this, expansion, direction, vertex_filter, edge_filter, uniqueness); - } - - /** - * Expands from this expansion along a variable number traversal. - * Arguments are simply passed to the variable expansion constructor. - * - * @return An expansion that generates paths variable length longer. - */ - auto ExpandVariable(Expansion expansion, Direction direction, - VertexFilter vertex_filter = {}, - EdgeFilter edge_filter = {}, int min_length = 1, - int max_length = 1000, - Uniqueness uniqueness = Uniqueness::Edge) { - return ExpandVariableType<BeginType<TIterable, TVertex, TEdge> &, TVertex, - TEdge>(*this, expansion, direction, vertex_filter, - edge_filter, min_length, max_length, - uniqueness); - } - - private: - const TIterable &vertices_; - // the BeingType has only one path that gets appended to and emitted to - // visitors - TPath path; - const VertexFilter vertex_filter_; - // TODO review: why do I have to have namespace:: here??? - traversal_template::UniquenessGroup<TVertex, TEdge> uniqueness_group_; -}; - -/** - * Creates a BeginType. - * - * Template arguments are explained in namespace documentation. - * - * Function arguments are simply forwarded to the BeginType constructor. - * - * @return A BeginType. - */ -template <typename TIterable, typename TVertex, typename TEdge> -auto Begin(const TIterable &vertices, - std::function<bool(const TVertex &)> vertex_filter = {}) { - return BeginType<TIterable, TVertex, TEdge>(vertices, vertex_filter); -} - -/** - * Creates a start point for a recursion of Cartesian wrappers. - */ -template <typename TVisitable, typename TVertex, typename TEdge> -class CartesianUnaryType { - using TPath = Path<TVertex, TEdge>; - using TPaths = Paths<TVertex, TEdge>; - - public: - CartesianUnaryType(TVisitable &&visitable) - : visitable_(std::forward<TVisitable>(visitable)) {} - - void Visit(std::function<void(TPaths &)> visitor) const { - TPaths paths; - visitable_.Visit([&visitor, &paths](TPath &p) { - paths.emplace_back(p); - visitor(paths); - paths.pop_back(); - }); - } - - private: - const TVisitable visitable_; -}; - -/** - * Provides means to visit a cartesian product of traversals. - * - * @tparam TVisitableFirst A visitable whose visitor accepts a list of path - * reference (recursion down). - * @tparam TVisitableOthers Visitables whose visitor accepts a single path - * reference. - */ -template <typename TVisitableFirst, typename TVisitableOthers, typename TVertex, - typename TEdge> -class CartesianBinaryType { - using TPath = Path<TVertex, TEdge>; - using TPaths = Paths<TVertex, TEdge>; - - public: - /** - * @tparam visitable_first A visitable whose visitor accepts a list of path - * reference (recursion down). - * @tparam visitable_others Visitable whose visitor accepts a single path - * reference. - */ - CartesianBinaryType(TVisitableFirst &&visitable_first, - TVisitableOthers &&visitable_others) - : visitable_first_(std::forward<TVisitableFirst>(visitable_first)), - visitable_others_(std::forward<TVisitableOthers>(visitable_others)) {} - - /** - * Calls the given visitor with a list of reference wrappers to Paths - * for every combination in this cartesian. - * - * @param visitor - */ - void Visit(std::function<void(TPaths &)> visitor) const { - // TODO currently cartesian product does NOT check for uniqueness - // for example between edges in the emitted path combinations - - visitable_first_.Visit([this, &visitor](TPath &path) { - visitable_others_.Visit([&path, &visitor](TPaths &paths) { - paths.emplace_front(path); - visitor(paths); - paths.pop_front(); - }); - }); - } - - private: - const TVisitableFirst visitable_first_; - const TVisitableOthers visitable_others_; -}; - -/** - * Creates an object that can be visited with a function that accepts a list - * of path reference wrappers. That function will be called one for every - * element - * of a cartesian product of the given visitables (that emit paths). - * - * @tparam TVisitableFirst A visitable that emits paths. - * @tparam TVisitableOthers An arbitrary number of visitables that emit paths. - * @param first A visitable that emits paths. - * @param others An arbitrary number of visitables that emit paths. - * @return See above. - */ -template <typename TVisitable, typename TVertex, typename TEdge> -auto Cartesian(TVisitable &&visitable) { - return CartesianUnaryType<TVisitable, TVertex, TEdge>( - std::forward<TVisitable>(visitable)); -} - -/** - * Creates an object that can be visited with a function that accepts a list - * of path reference wrappers. That function will be called one for every - * element - * of a cartesian product of the given visitables (that emit paths). - * - * @tparam TVisitableFirst A visitable that emits paths. - * @tparam TVisitableOthers An arbitrary number of visitables that emit paths. - * @param first A visitable that emits paths. - * @param others An arbitrary number of visitables that emit paths. - * @return See above. - */ -template <typename TVisitableFirst, typename Vertex, typename Edge, - typename... TVisitableOthers> -auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) { - return CartesianBinaryType< - TVisitableFirst, - decltype(Cartesian(std::forward<TVisitableOthers>(others)...)), Vertex, - Edge>(std::forward<TVisitableFirst>(first), - Cartesian(std::forward<TVisitableOthers>(others)...)); -} - -template <typename TVisitable, typename TVertex, typename TEdge> -auto ExpandVariableType<TVisitable, TVertex, TEdge>::Expand( - Expansion expansion, Direction direction, VertexFilter vertex_filter, - EdgeFilter edge_filter) { - return ExpandType<ExpandVariableType<TVisitable, TVertex, TEdge> &, TVertex, - TEdge>(*this, expansion, direction, vertex_filter, - edge_filter, this->uniqueness_); -} - -template <typename TVisitable, typename TVertex, typename TEdge> -auto ExpandVariableType<TVisitable, TVertex, TEdge>::ExpandVariable( - Expansion expansion, Direction direction, VertexFilter vertex_filter, - EdgeFilter edge_filter, int min_length, int max_length) { - return ExpandVariableType<ExpandVariableType<TVisitable, TVertex, TEdge> &, - TVertex, TEdge>( - *this, expansion, direction, vertex_filter, edge_filter, min_length, - max_length, this->uniqueness_); -} -} diff --git a/src/traversal/traversal.hpp b/src/traversal/traversal.hpp deleted file mode 100644 index 429471761..000000000 --- a/src/traversal/traversal.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright 2017 Memgraph -// Created by Florijan Stamenkovic on 02.03.17. -// - -#pragma once - -#include "enums.hpp" -#include "path.hpp" -#include "templates.hpp" - -#include "storage/edge_accessor.hpp" -#include "storage/vertex_accessor.hpp" - -/** - * A specialization of the "traversal" namespace that uses - * real DB VertexAccessor and EdgeAccessor classes. - */ -namespace traversal { - -// expose Path and Paths as class template instantiations -using Path = traversal_template::Path<VertexAccessor, EdgeAccessor>; -using Paths = traversal_template::Paths<VertexAccessor, EdgeAccessor>; - -/** - * Specialization of the traversal_template::Begin function. - */ -template <typename TCollection> -auto Begin(const TCollection &vertices, - std::function<bool(const VertexAccessor &)> vertex_filter = {}) { - return traversal_template::Begin<TCollection, VertexAccessor, EdgeAccessor>( - vertices, vertex_filter); -} - -/** - * Specialization of the traversal_template::Cartesian function that accepts - * a single argument. - */ -template <typename TVisitable> -auto Cartesian(TVisitable &&visitable) { - return traversal_template::Cartesian<TVisitable, VertexAccessor, - EdgeAccessor>( - std::forward<TVisitable>(visitable)); -} - -/** - * Specialization of the traversal_template::Cartesian function that accepts - * multiple arguments. - */ -template <typename TVisitableFirst, typename... TVisitableOthers> -auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) { - return traversal_template::CartesianBinaryType< - TVisitableFirst, - decltype(Cartesian(std::forward<TVisitableOthers>(others)...)), - VertexAccessor, EdgeAccessor>( - std::forward<TVisitableFirst>(first), - Cartesian(std::forward<TVisitableOthers>(others)...)); -} -} - diff --git a/tests/unit/traversal.cpp b/tests/unit/traversal.cpp deleted file mode 100644 index b7d32ccb3..000000000 --- a/tests/unit/traversal.cpp +++ /dev/null @@ -1,691 +0,0 @@ -// -// Copyright 2017 Memgraph -// Created by Florijan Stamenkovic on 24.01.17.. -// - -#include <iostream> -#include <list> -#include <list> -#include <memory> -#include <unordered_set> -#include <vector> - -#include "gtest/gtest.h" - -#include "traversal/enums.hpp" -#include "traversal/path.hpp" -#include "traversal/templates.hpp" - -/** - * A specialization of the "traversal" namespace that uses - * test Vertex and Edge classes (as opposed to the real database - * ones). - */ -namespace traversal_test { - -// add the standard enums to this namespace -using Direction = traversal_template::Direction; -using Expansion = traversal_template::Expansion; -using Uniqueness = traversal_template::Uniqueness; - -// forward declaring Edge because Vertex holds references to it -class Edge; - -/** - * A Vertex class for traversal testing. Has only the - * traversal capabilities and an exposed id_ member. - */ -class Vertex { - friend class Edge; - - public: - // unique identifier (uniqueness guaranteed by this class) - const int id_; - - // vertex set by the user, for testing purposes - const int label_; - - Vertex(int label = 0) : id_(counter_++), label_(label) {} - - const auto &in() const { return *in_; } - const auto &out() const { return *out_; } - - friend std::ostream &operator<<(std::ostream &stream, const Vertex &v) { - return stream << "(" << v.id_ << ")"; - } - - bool operator==(const Vertex &v) const { return this->id_ == v.id_; } - bool operator!=(const Vertex &v) const { return !(*this == v); } - - private: - // shared pointers to outgoing and incoming edges are used so that when - // a Vertex is copied it still points to the same connectivity storage - std::shared_ptr<std::vector<Edge>> in_ = - std::make_shared<std::vector<Edge>>(); - std::shared_ptr<std::vector<Edge>> out_ = - std::make_shared<std::vector<Edge>>(); - - // Vertex counter, used for generating IDs - static int counter_; -}; - -int Vertex::counter_ = 0; - -/** - * An Edge class for traversal testing. Has only traversal capabilities - * and an exposed id_ member. - */ -class Edge { - public: - // unique identifier (uniqueness guaranteed by this class) - const int id_; - - // vertex set by the user, for testing purposes - const int type_; - - Edge(const Vertex &from, const Vertex &to, int type = 0) - : id_(counter_++), type_(type), from_(from), to_(to) { - from.out_->emplace_back(*this); - to.in_->emplace_back(*this); - } - - const Vertex &from() const { return from_; } - const Vertex &to() const { return to_; } - - bool operator==(const Edge &e) const { return id_ == e.id_; } - bool operator!=(const Edge &e) const { return !(*this == e); } - - friend std::ostream &operator<<(std::ostream &stream, const Edge &e) { - return stream << "[" << e.id_ << "]"; - } - - private: - const Vertex &from_; - const Vertex &to_; - - // Edge counter, used for generating IDs - static int counter_; -}; - -int Edge::counter_ = 0; - -// expose Path and Paths as class template instantiations -using Path = traversal_template::Path<Vertex, Edge>; -using Paths = traversal_template::Paths<Vertex, Edge>; - -/** - * Specialization of the traversal_template::Begin function. - */ -template <typename TCollection> -auto Begin(const TCollection &vertices, - std::function<bool(const Vertex &)> vertex_filter = {}) { - return traversal_template::Begin<TCollection, Vertex, Edge>(vertices, - vertex_filter); -} - -/** - * Specialization of the traversal_template::Cartesian function that accepts - * a single argument. - */ -template <typename TVisitable> -auto Cartesian(TVisitable &&visitable) { - return traversal_template::Cartesian<TVisitable, Vertex, Edge>( - std::forward<TVisitable>(visitable)); -} - -/** - * Specialization of the traversal_template::Cartesian function that accepts - * multiple arguments. - */ -template <typename TVisitableFirst, typename... TVisitableOthers> -auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) { - return traversal_template::CartesianBinaryType< - TVisitableFirst, - decltype(Cartesian(std::forward<TVisitableOthers>(others)...)), Vertex, - Edge>(std::forward<TVisitableFirst>(first), - Cartesian(std::forward<TVisitableOthers>(others)...)); -} -} - -/** - * Hash calculations for text vertex, edge, Path and Paths. - * Only for testing purposes. - */ -namespace std { - -template <> -struct hash<traversal_test::Vertex> { - public: - size_t operator()(const traversal_test::Vertex &vertex) const { - return (size_t)vertex.id_; - } -}; - -template <> -struct hash<traversal_test::Edge> { - public: - size_t operator()(const traversal_test::Edge &edge) const { - return (size_t)edge.id_; - } -}; - -template <> -struct hash<traversal_test::Path> { - public: - std::size_t operator()(const traversal_test::Path &path) const { - std::size_t r_val = 0; - - for (const auto &vertex : path.Vertices()) - r_val ^= std::hash<traversal_test::Vertex>{}(vertex); - - return r_val; - } -}; - -template <> -struct hash<traversal_test::Paths> { - public: - std::size_t operator()(const traversal_test::Paths &paths) const { - std::size_t r_val = 0; - - for (const auto &path : paths) - r_val ^= std::hash<traversal_test::Path>{}(path); - - return r_val; - } -}; -} - -/** - * Hash for a list of edges, used in tests. - */ -namespace std { -template <> -struct hash<std::list<traversal_test::Edge>> { - public: - std::size_t operator()(const std::list<traversal_test::Edge> &edges) const { - std::size_t r_val = 0; - - for (const auto &edge : edges) - r_val ^= std::hash<traversal_test::Edge>{}(edge); - - return r_val; - } -}; -} - -using namespace traversal_test; - -template <typename TElement, typename TVisitable> -std::unordered_set<TElement> accumulate_visited(const TVisitable &visitable) { - // we accept const here so we can receive r-values - // we cast the const-ness away because the Visit function is not const - std::unordered_set<TElement> results; - const_cast<TVisitable &>(visitable).Visit( - [&results](auto e) { results.insert(e); }); - return results; -}; - -TEST(Traversal, VertexCreationAndEquality) { - Vertex v0; - Vertex v1; - EXPECT_EQ(v0, v0); - EXPECT_NE(v0, v1); -} - -TEST(Traversal, EdgeCreationAndEquality) { - Vertex v0; - Vertex v1; - - Edge e0(v0, v1); - Edge e1(v0, v1); - EXPECT_EQ(e0, e0); - EXPECT_NE(e0, e1); -} - -TEST(Traversal, PathCreationAndEquality) { - Vertex v0; - Vertex v1; - Vertex v2; - Edge e0(v0, v1); - Edge e1(v0, v2); - - // first test single-vertex-path equality - Path ref_path; - ref_path.Start(v0); - EXPECT_EQ(ref_path, Path().Start(v0)); - EXPECT_NE(ref_path, Path().Start(v1)); - - // now test two-vertex-path equality - // reference path is (v0)-[e0]->(v1) - ref_path.Append(e0, v1); - EXPECT_EQ(ref_path, Path().Start(v0).Append(e0, v1)); - - // test against one-vertex paths - EXPECT_NE(ref_path, Path().Start(v0)); - EXPECT_NE(ref_path, Path().Start(v1)); - - // test on different vertex - EXPECT_NE(ref_path, Path().Start(v0).Append(e0, v2)); - // test on different edge - EXPECT_NE(ref_path, Path().Start(v0).Append(e1, v1)); - // test on different expansion direction - EXPECT_NE(ref_path, Path().Start(v0).Prepend(e0, v1)); -} - -TEST(Traversal, Begin) { - std::vector<Vertex> vertices(3); - - std::unordered_set<Path> expected; - for (const auto &vertex : vertices) expected.insert(Path().Start(vertex)); - EXPECT_EQ(expected.size(), 3); - - auto result = accumulate_visited<Path>(Begin(vertices)); - EXPECT_EQ(result, expected); -} - -TEST(Traversal, BeginExpand) { - Vertex v0, v1, v2, v3; - Edge e0(v0, v1); - Edge e1(v0, v2); - Edge e2(v2, v3); - - // test traversal from (v0) - auto expected = std::unordered_set<Path>{Path().Start(v0).Append(e0, v1), - Path().Start(v0).Append(e1, v2)}; - auto result = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}).Expand(Expansion::Back, Direction::Out)); - EXPECT_EQ(expected, result); -} - -TEST(Traversal, BeginExpandVariable) { - Vertex v0, v1, v2, v3, v4; - Edge e0(v0, v1); - Edge e1(v0, v2); - Edge e2(v2, v3); - // disconnected from the above graph, just to have something else - Vertex v5, v6; - Edge e3(v5, v6); - - // test traversal from (v0) - auto expected = std::unordered_set<Path>{ - Path().Start(v0).Append(e0, v1), Path().Start(v0).Append(e1, v2), - Path().Start(v0).Append(e1, v2).Append(e2, v3)}; - auto result = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out)); - EXPECT_EQ(expected, result); -} - -TEST(Traversal, BeginFilter) { - Vertex v0(1), v1(1), v2(2); - - std::unordered_set<Path> expected{Path().Start(v0), Path().Start(v1)}; - - auto result = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0, v1, v2}, - [](const Vertex &v) { return v.label_ == 1; })); - EXPECT_EQ(expected, result); -} - -TEST(Traversal, BeginCurrent) { - Vertex v0, v1, v2, v3, v4; - - std::unordered_set<Vertex> expected{v0, v1, v2}; - std::vector<Vertex> begin_input{v0, v1, v2}; - auto b = Begin(begin_input); - - b.Visit([&b, &expected](Path &path) { - EXPECT_EQ(1, expected.erase(b.CurrentVertex())); - }); - - EXPECT_EQ(0, expected.size()); -} - -TEST(Traversal, ExpandExpand) { - Vertex v0, v1, v2, v3, v4, v5; - Edge e0(v0, v1); - Edge e1(v0, v2); - Edge e2(v1, v3); - Edge e3(v2, v4); - Edge e4(v2, v5); - - // not part of the results - Vertex v6, v7; - Edge e5(v4, v6); - Edge e6(v7, v1); - - // test traversal from (v0) - auto expected = - std::unordered_set<Path>{Path().Start(v0).Append(e0, v1).Append(e2, v3), - Path().Start(v0).Append(e1, v2).Append(e3, v4), - Path().Start(v0).Append(e1, v2).Append(e4, v5)}; - auto result = - accumulate_visited<Path>(Begin(std::vector<Vertex>{v0}) - .Expand(Expansion::Back, Direction::Out) - .Expand(Expansion::Back, Direction::Out)); - EXPECT_EQ(expected, result); -} - -TEST(Traversal, ExpandFilter) { - // make a one-level deep tree and 4 children that have {1,2} vertex labels and - // {3,4} edge types - Vertex root; - Vertex v1(1); - Edge e1(root, v1, 3); - Vertex v2(1); - Edge e2(root, v2, 4); - Vertex v3(2); - Edge e3(root, v3, 3); - Vertex v4(2); - Edge e4(root, v4, 4); - - // filter to accept only label 1 and edge type 3, expect exactly one path - auto result = accumulate_visited<Path>( - Begin(std::vector<Vertex>{root}) - .Expand(Expansion::Back, Direction::Out, - [](const Vertex &v) { return v.label_ == 1; }, - [](const Edge &e) { return e.type_ == 3; })); - - auto expected = std::unordered_set<Path>{Path().Start(root).Append(e1, v1)}; - EXPECT_EQ(expected, result); -} - -TEST(Traversal, ExpandCurrent) { - // make a complete binary tree two levels deep (7 nodes) - Vertex v0; - Vertex v1, v2; - Edge e1(v0, v1), e2(v0, v2); - Vertex v3, v4, v5, v6; - Edge e3(v1, v3), e4(v1, v4), e5(v2, v5), e6(v2, v6); - - // traverse two levels deep and accumulate results - std::vector<Vertex> begin_input{v0}; - auto begin = Begin(begin_input); - auto expand0 = begin.Expand(Expansion::Back, Direction::Out); - auto expand1 = expand0.Expand(Expansion::Back, Direction::Out); - - // expected results - std::unordered_set<Vertex> expected_expand0_vertices{v1, v2}; - std::unordered_set<Edge> expected_expand0_edges{e1, e2}; - std::unordered_set<Vertex> expected_expand1_vertices{v3, v4, v5, v6}; - std::unordered_set<Edge> expected_expand1_edges{e3, e4, e5, e6}; - - expand1.Visit([&](Path &path) { - // we can't check that erasure on the first level because it only gets - // erased the first time a path containing first level stuff gets visited - expected_expand0_vertices.erase(expand0.CurrentVertex()); - expected_expand0_edges.erase(expand0.CurrentEdge()); - EXPECT_EQ(1, expected_expand1_vertices.erase(expand1.CurrentVertex())); - EXPECT_EQ(1, expected_expand1_edges.erase(expand1.CurrentEdge())); - }); - - EXPECT_EQ(0, expected_expand0_vertices.size()); - EXPECT_EQ(0, expected_expand0_edges.size()); - EXPECT_EQ(0, expected_expand1_vertices.size()); - EXPECT_EQ(0, expected_expand1_edges.size()); -} - -TEST(Traversal, ExpandVariableLimits) { - // make a chain - Vertex v0; - Vertex v1; - Edge e1(v0, v1); - Vertex v2; - Edge e2(v1, v2); - Vertex v3; - Edge e3(v2, v3); - Vertex v4; - Edge e4(v3, v4); - - // all possible results - std::vector<Path> all_possible{Path().Start(v0)}; - all_possible.emplace_back(Path(all_possible.back()).Append(e1, v1)); - all_possible.emplace_back(Path(all_possible.back()).Append(e2, v2)); - all_possible.emplace_back(Path(all_possible.back()).Append(e3, v3)); - all_possible.emplace_back(Path(all_possible.back()).Append(e4, v4)); - - // default is one or more traversals - auto result0 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out)); - EXPECT_EQ(result0, std::unordered_set<Path>(all_possible.begin() + 1, - all_possible.end())); - - // zero or more traversals - auto result1 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out, {}, {}, 0)); - EXPECT_EQ(result1, - std::unordered_set<Path>(all_possible.begin(), all_possible.end())); - - // an exact number of traversals [2, 4) - auto result2 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out, {}, {}, 2, 4)); - EXPECT_EQ(result2, std::unordered_set<Path>(all_possible.begin() + 2, - all_possible.begin() + 4)); -} - -TEST(Traversal, ExpandVariableFilter) { - // make a chain with vertex labels and edge types with these values - // (0)-[1]->(1)-[2]->(2)-[3]->(3) - Vertex v0(0); - Vertex v1(1); - Edge e1(v0, v1, 1); - Vertex v2(2); - Edge e2(v1, v2, 2); - Vertex v3(3); - Edge e3(v2, v3, 3); - - // all possible paths - std::vector<Path> all_possible{Path().Start(v0)}; - all_possible.emplace_back(Path(all_possible.back()).Append(e1, v1)); - all_possible.emplace_back(Path(all_possible.back()).Append(e2, v2)); - all_possible.emplace_back(Path(all_possible.back()).Append(e3, v3)); - - // filter on edge type < 3 - auto result0 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out, {}, - [](const Edge &e) { return e.type_ < 3; })); - EXPECT_EQ(result0, std::unordered_set<Path>(all_possible.begin() + 1, - all_possible.begin() + 3)); - - // filter on edge type > 1, expect nothing because the first edge is 1 - auto result1 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out, {}, - [](const Edge &e) { return e.type_ > 1; })); - EXPECT_EQ(result1.size(), 0); - - // filter on vertex label > 1, expect last 2 results - auto result2 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .ExpandVariable(Expansion::Back, Direction::Out, - [](const Vertex &v) { return v.label_ > 1; }, {})); - EXPECT_EQ(result2, std::unordered_set<Path>(all_possible.end() - 2, - all_possible.end())); -} - -TEST(Traversal, ExpandVariableCurrent) { - // a chain - Vertex v0; - Vertex v1; - Edge e1(v0, v1); - Vertex v2; - Edge e2(v1, v2); - Vertex v3; - Edge e3(v2, v3); - - // ensure that expanded vertices and all edge sets get visited as current - std::unordered_set<Vertex> expected_vertices{v1, v2, v3}; - std::unordered_set<std::list<Edge>> expected_edge_lists{ - std::list<Edge>{e1}, std::list<Edge>{e1, e2}, - std::list<Edge>{e1, e2, e3}}; - - std::vector<Vertex> begin_input{v0}; - auto begin = Begin(begin_input); - auto expand = begin.ExpandVariable(Expansion::Back, Direction::Out); - - expand.Visit([&](Path &path) { - EXPECT_EQ(1, expected_vertices.erase(expand.CurrentVertex())); - EXPECT_EQ(1, expected_edge_lists.erase(expand.CurrentEdges())); - }); -} - -TEST(Traversal, EnumExpansion) { - // (v0)-[]->(v1) - Vertex v0, v1; - Edge e0(v0, v1); - - // Expansion::Back - auto result0 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}).Expand(Expansion::Back, Direction::Out)); - auto expected0 = std::unordered_set<Path>{Path().Start(v0).Append(e0, v1)}; - EXPECT_EQ(result0, expected0); - - // Expansion::Front - auto result1 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}).Expand(Expansion::Front, Direction::Out)); - auto expected1 = std::unordered_set<Path>{Path().Start(v0).Prepend(e0, v1)}; - EXPECT_EQ(result1, expected1); -} - -TEST(Traversal, EnumDirection) { - // (v0)-[]->(v1)-[]->(v2) - Vertex v0, v1, v2; - Edge e0(v0, v1), e1(v1, v2); - - // Direction::Out - auto result0 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v1}).Expand(Expansion::Back, Direction::Out)); - auto expected0 = std::unordered_set<Path>{Path().Start(v1).Append(e1, v2)}; - EXPECT_EQ(result0, expected0); - - // Direction::In - auto result1 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v1}).Expand(Expansion::Back, Direction::In)); - auto expected1 = std::unordered_set<Path>{Path().Start(v1).Append(e0, v0)}; - EXPECT_EQ(result1, expected1); - - // Direction::Both - auto result2 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v1}).Expand(Expansion::Back, Direction::Both)); - auto expected2 = std::unordered_set<Path>{Path().Start(v1).Append(e0, v0), - Path().Start(v1).Append(e1, v2)}; - EXPECT_EQ(result2, expected2); -} - -TEST(Traversal, EnumUniqueness) { - // make a (v0)-[]->(v1), where (v0) has a recursive relationship to self - Vertex v0, v1; - Edge e0(v0, v0), e1(v0, v1); - - // Uniqueness::Edge - auto result0 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Edge) - .Expand(Expansion::Back, Direction::Out)); - auto expected0 = - std::unordered_set<Path>{Path().Start(v0).Append(e0, v0).Append(e1, v1)}; - EXPECT_EQ(result0, expected0); - - // Uniqueness::Vertex - auto result1 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::Vertex) - .Expand(Expansion::Back, Direction::Out)); - EXPECT_EQ(result1.size(), 0); - - // Uniqueness::None - auto result2 = accumulate_visited<Path>( - Begin(std::vector<Vertex>{v0}) - .Expand(Expansion::Back, Direction::Out, {}, {}, Uniqueness::None) - .Expand(Expansion::Back, Direction::Out)); - auto expected2 = - std::unordered_set<Path>{Path().Start(v0).Append(e0, v0).Append(e1, v1), - Path().Start(v0).Append(e0, v0).Append(e0, v0)}; - EXPECT_EQ(result2, expected2); -} - -TEST(Traversal, Cartesian) { - // first make two simple trees - Vertex v0, v1, v2; - Edge e1(v0, v1), e2(v0, v1); - Vertex v3, v4, v5; - Edge e3(v3, v4), e4(v3, v5); - - // now accumulate all paths from three starting points - std::vector<Vertex> begin0{v0}, begin1{v3}; - auto paths0 = Begin(begin0).Expand(Expansion::Back, Direction::Out); - auto paths1 = Begin(begin1).Expand(Expansion::Back, Direction::Out); - auto paths0_set = accumulate_visited<Path>(paths0); - auto paths1_set = accumulate_visited<Path>(paths1); - EXPECT_EQ(2, paths0_set.size()); - EXPECT_EQ(2, paths1_set.size()); - - // now make a cartesian product of (0, 1, 0) and remember the results - std::unordered_set<Paths> expected; - for (auto &path1 : paths0_set) - for (auto &path2 : paths1_set) - for (auto &path3 : paths0_set) { - Paths paths; - paths.emplace_back(std::ref(const_cast<Path &>(path1))); - paths.emplace_back(std::ref(const_cast<Path &>(path2))); - paths.emplace_back(std::ref(const_cast<Path &>(path3))); - expected.insert(paths); - } - EXPECT_EQ(8, expected.size()); - - // now use the Cartesian API - auto paths2 = Begin(begin0).Expand(Expansion::Back, Direction::Out); - Cartesian(paths0, paths1, paths2).Visit([&expected](Paths &p) { - EXPECT_EQ(1, expected.erase(p)); - }); - - EXPECT_EQ(0, expected.size()); -} - -TEST(Traversal, UniquenessSubgroups) { - // graph is (v1)<-[]-(v0)-[]->(v2) - Vertex v0, v1, v2; - Edge e1(v0, v1), e2(v0, v1); - std::vector<Vertex> begin_v{v0}; - - // counts the number of visits to the visitable - auto visit_counter = [](const auto &visitable) { - auto r_val = 0; - visitable.Visit([&r_val](const auto &x) { r_val++; }); - return r_val; - }; - - // make a few experiments with different uniqueness sharing - - // no uniqueness sharing - { - auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out); - auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out); - EXPECT_EQ(4, visit_counter(Cartesian(paths0, paths1))); - } - - // edge uniqueness sharing - { - auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, - Uniqueness::Edge); - auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, - Uniqueness::Edge); - paths1.UniquenessGroup().Add(paths0.UniquenessGroup()); - EXPECT_EQ(2, visit_counter(Cartesian(paths0, paths1))); - } - - // vertex uniqueness sharing - { - auto paths0 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, - Uniqueness::Vertex); - auto paths1 = Begin(begin_v).Expand(Expansion::Back, Direction::Out, {}, {}, - Uniqueness::Vertex); - paths1.UniquenessGroup().Add(paths0.UniquenessGroup()); - EXPECT_EQ(0, visit_counter(Cartesian(paths0, paths1))); - } -}