diff --git a/arangod/Aql/TraversalBlock.cpp b/arangod/Aql/TraversalBlock.cpp index 7c73c39071..8feabf853b 100644 --- a/arangod/Aql/TraversalBlock.cpp +++ b/arangod/Aql/TraversalBlock.cpp @@ -92,6 +92,12 @@ TraversalBlock::TraversalBlock(ExecutionEngine* engine, TraversalNode const* ep) _traverser.reset( new arangodb::traverser::SingleServerTraverser(_opts, _trx)); } + if (!ep->usesEdgeOutVariable() && !ep->usesPathOutVariable() && + _opts->useBreadthFirst && + _opts->uniqueVertices == + traverser::TraverserOptions::UniquenessLevel::GLOBAL) { + _traverser->allowOptimizedNeighbors(); + } if (!ep->usesInVariable()) { _vertexId = ep->getStartVertex(); } else { diff --git a/arangod/VocBase/PathEnumerator.cpp b/arangod/VocBase/PathEnumerator.cpp index df0d37fd21..6af16e68d2 100644 --- a/arangod/VocBase/PathEnumerator.cpp +++ b/arangod/VocBase/PathEnumerator.cpp @@ -26,6 +26,7 @@ using DepthFirstEnumerator = arangodb::traverser::DepthFirstEnumerator; using BreadthFirstEnumerator = arangodb::traverser::BreadthFirstEnumerator; +using NeighborsEnumerator = arangodb::traverser::NeighborsEnumerator; using Traverser = arangodb::traverser::Traverser; using TraverserOptions = arangodb::traverser::TraverserOptions; @@ -388,3 +389,77 @@ void BreadthFirstEnumerator::computeEnumeratedPath(size_t index) { _enumeratedPath.vertices[0] = current->vertex; } +NeighborsEnumerator::NeighborsEnumerator(Traverser* traverser, + VPackSlice startVertex, + TraverserOptions const* opts) + : PathEnumerator(traverser, startVertex, opts), + _searchDepth(0) { + _allFound.insert(startVertex); + _currentDepth.insert(startVertex); + _iterator = _currentDepth.begin(); +} + +bool NeighborsEnumerator::next() { + if (_isFirst) { + _isFirst = false; + if (_opts->minDepth == 0) { + return true; + } + } + + if (_iterator == _currentDepth.end() || ++_iterator == _currentDepth.end()) { + do { + // This depth is done. Get next + if (_opts->maxDepth == _searchDepth) { + // We are finished. + return false; + } + + _lastDepth.swap(_currentDepth); + _currentDepth.clear(); + size_t cursorIdx; + for (auto const& nextVertex : _lastDepth) { + cursorIdx = 0; + std::unique_ptr cursor(_opts->nextCursor(nextVertex, _searchDepth)); + while (cursor->readAll(_tmpEdges, cursorIdx)) { + if (!_tmpEdges.empty()) { + _traverser->_readDocuments += _tmpEdges.size(); + VPackSlice v; + for (auto const& e : _tmpEdges) { + if (_traverser->getSingleVertex(e, nextVertex, _searchDepth, v)) { + if (_allFound.find(v) == _allFound.end()) { + _currentDepth.emplace(v); + _allFound.emplace(v); + } + } + } + _tmpEdges.clear(); + } + } + } + if (_currentDepth.empty()) { + // Nothing found. Cannot do anything more. + return false; + } + ++_searchDepth; + } while (_searchDepth < _opts->minDepth); + _iterator = _currentDepth.begin(); + } + TRI_ASSERT(_iterator != _currentDepth.end()); + return true; +} + +arangodb::aql::AqlValue NeighborsEnumerator::lastVertexToAqlValue() { + TRI_ASSERT(_iterator != _currentDepth.end()); + return _traverser->fetchVertexData(*_iterator); +} + +arangodb::aql::AqlValue NeighborsEnumerator::lastEdgeToAqlValue() { + // TODO should return Optimizer failed + THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); +} + +arangodb::aql::AqlValue NeighborsEnumerator::pathToAqlValue(arangodb::velocypack::Builder& result) { + // TODO should return Optimizer failed + THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED); +} diff --git a/arangod/VocBase/PathEnumerator.h b/arangod/VocBase/PathEnumerator.h index fd345d1163..0a108d5084 100644 --- a/arangod/VocBase/PathEnumerator.h +++ b/arangod/VocBase/PathEnumerator.h @@ -248,11 +248,6 @@ class BreadthFirstEnumerator final : public PathEnumerator { bool next() override; - ////////////////////////////////////////////////////////////////////////////// - /// @brief Prunes the current path prefix, the next function should not return - /// any path having this prefix anymore. - ////////////////////////////////////////////////////////////////////////////// - aql::AqlValue lastVertexToAqlValue() override; aql::AqlValue lastEdgeToAqlValue() override; @@ -278,6 +273,56 @@ class BreadthFirstEnumerator final : public PathEnumerator { void computeEnumeratedPath(size_t index); }; +// @brief Enumerator optimized for neighbors. Does not allow edge access + +class NeighborsEnumerator final : public PathEnumerator { + std::unordered_set + _allFound; + + std::unordered_set + _currentDepth; + + std::unordered_set + _lastDepth; + + std::unordered_set::iterator _iterator; + size_t _searchDepth; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief Vector storing the position at current search depth + ////////////////////////////////////////////////////////////////////////////// + + std::unordered_set _tmpEdges; + + + public: + NeighborsEnumerator(Traverser* traverser, + arangodb::velocypack::Slice startVertex, + TraverserOptions const* opts); + + ~NeighborsEnumerator() { + } + + ////////////////////////////////////////////////////////////////////////////// + /// @brief Get the next Path element from the traversal. + ////////////////////////////////////////////////////////////////////////////// + + bool next() override; + + aql::AqlValue lastVertexToAqlValue() override; + + aql::AqlValue lastEdgeToAqlValue() override; + + aql::AqlValue pathToAqlValue(arangodb::velocypack::Builder& result) override; + +}; + } // namespace traverser } // namespace arangodb diff --git a/arangod/VocBase/SingleServerTraverser.cpp b/arangod/VocBase/SingleServerTraverser.cpp index 9c09fd4f3e..67d200f968 100644 --- a/arangod/VocBase/SingleServerTraverser.cpp +++ b/arangod/VocBase/SingleServerTraverser.cpp @@ -225,7 +225,11 @@ void SingleServerTraverser::setStartVertex(std::string const& v) { _vertexGetter->reset(idSlice); if (_opts->useBreadthFirst) { - _enumerator.reset(new BreadthFirstEnumerator(this, idSlice, _opts)); + if (_canUseOptimizedNeighbors) { + _enumerator.reset(new NeighborsEnumerator(this, idSlice, _opts)); + } else { + _enumerator.reset(new BreadthFirstEnumerator(this, idSlice, _opts)); + } } else { _enumerator.reset(new DepthFirstEnumerator(this, idSlice, _opts)); } diff --git a/arangod/VocBase/SingleServerTraverser.h b/arangod/VocBase/SingleServerTraverser.h index 76735c2cb6..7a94fce045 100644 --- a/arangod/VocBase/SingleServerTraverser.h +++ b/arangod/VocBase/SingleServerTraverser.h @@ -82,7 +82,6 @@ class SingleServerTraverser final : public Traverser { void setStartVertex(std::string const& v) override; - protected: /// @brief Function to load the other sides vertex of an edge /// Returns true if the vertex passes filtering conditions diff --git a/arangod/VocBase/Traverser.cpp b/arangod/VocBase/Traverser.cpp index 15cdd7aec1..002f1471c8 100644 --- a/arangod/VocBase/Traverser.cpp +++ b/arangod/VocBase/Traverser.cpp @@ -340,7 +340,8 @@ Traverser::Traverser(arangodb::traverser::TraverserOptions* opts, arangodb::Tran _readDocuments(0), _filteredPaths(0), _done(true), - _opts(opts) { + _opts(opts), + _canUseOptimizedNeighbors(false) { if (opts->uniqueVertices == TraverserOptions::UniquenessLevel::GLOBAL) { _vertexGetter = std::make_unique(this); } else { @@ -392,3 +393,7 @@ arangodb::aql::AqlValue arangodb::traverser::Traverser::pathToAqlValue( VPackBuilder& builder) { return _enumerator->pathToAqlValue(builder); } + +void arangodb::traverser::Traverser::allowOptimizedNeighbors() { + _canUseOptimizedNeighbors = true; +} diff --git a/arangod/VocBase/Traverser.h b/arangod/VocBase/Traverser.h index 62193ea1f9..aecfc17532 100644 --- a/arangod/VocBase/Traverser.h +++ b/arangod/VocBase/Traverser.h @@ -211,6 +211,7 @@ class TraversalPath { class Traverser { friend class BreadthFirstEnumerator; friend class DepthFirstEnumerator; + friend class NeighborsEnumerator; protected: @@ -376,6 +377,8 @@ class Traverser { bool vertexMatchesConditions(arangodb::velocypack::Slice, size_t); + void allowOptimizedNeighbors(); + protected: /// @brief Outer top level transaction @@ -405,6 +408,8 @@ class Traverser { /// @brief options for traversal TraverserOptions* _opts; + bool _canUseOptimizedNeighbors; + /// @brief Function to fetch the real data of a vertex into an AQLValue virtual aql::AqlValue fetchVertexData(arangodb::velocypack::Slice) = 0; @@ -418,6 +423,7 @@ class Traverser { /// @brief Function to add the real data of an edge into a velocypack builder virtual void addEdgeToVelocyPack(arangodb::velocypack::Slice, arangodb::velocypack::Builder&) = 0; + }; } // traverser } // arangodb