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
This commit is contained in:
parent
9edc472eaf
commit
d6a885cce6
@ -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"
|
||||
|
||||
|
@ -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 };
|
||||
}
|
@ -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 << "]";
|
||||
}
|
||||
};
|
||||
}
|
@ -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_);
|
||||
}
|
||||
}
|
@ -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)...));
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user