diff --git a/arangod/Aql/ShortestPathBlock.cpp b/arangod/Aql/ShortestPathBlock.cpp index dc6c352c57..e684ed10d6 100644 --- a/arangod/Aql/ShortestPathBlock.cpp +++ b/arangod/Aql/ShortestPathBlock.cpp @@ -41,157 +41,6 @@ typedef arangodb::graph::AttributeWeightShortestPathFinder ArangoDBPathFinder; using namespace arangodb::aql; -/// @brief Local class to expand edges. -/// Will be handed over to the path finder -namespace arangodb { -namespace aql { - -/// @brief Expander for weighted edges -struct EdgeWeightExpanderLocal { - private: - /// @brief reference to the Block - ShortestPathBlock const* _block; - - /// @brief Defines if this expander follows the edges in reverse - bool _reverse; - - public: - EdgeWeightExpanderLocal(ShortestPathBlock const* block, bool reverse) - : _block(block), _reverse(reverse) {} - - void inserter(std::unordered_map& candidates, - std::vector& result, - VPackSlice const& s, VPackSlice const& t, double currentWeight, - VPackSlice edge) { - auto cand = candidates.find(t); - if (cand == candidates.end()) { - // Add weight - auto step = - std::make_unique(t, s, currentWeight, edge); - result.emplace_back(step.release()); - candidates.emplace(t, result.size() - 1); - } else { - // Compare weight - auto old = result[cand->second]; - auto oldWeight = old->weight(); - if (currentWeight < oldWeight) { - old->setWeight(currentWeight); - old->_predecessor = s; - old->_edge = edge; - } - } - } - - void operator()(VPackSlice const& source, - std::vector& result) { - TRI_ASSERT(source.isString()); - std::string id = source.copyString(); - ManagedDocumentResult* mmdr = _block->_mmdr.get(); - std::unique_ptr edgeCursor; - std::unordered_map candidates; - for (auto const& edgeCollection : _block->_collectionInfos) { - TRI_ASSERT(edgeCollection != nullptr); - if (_reverse) { - edgeCursor = edgeCollection->getReverseEdges(id, mmdr); - } else { - edgeCursor = edgeCollection->getEdges(id, mmdr); - } - - candidates.clear(); - - LogicalCollection* collection = edgeCursor->collection(); - auto cb = [&](DocumentIdentifierToken const& element) { - if (collection->readDocument(_block->transaction(), element, *mmdr)) { - VPackSlice edge(mmdr->vpack()); - VPackSlice from = transaction::helpers::extractFromFromDocument(edge); - VPackSlice to = transaction::helpers::extractToFromDocument(edge); - double currentWeight = edgeCollection->weightEdge(edge); - if (from == source) { - inserter(candidates, result, from, to, currentWeight, edge); - } else { - inserter(candidates, result, to, from, currentWeight, edge); - } - } - }; - - while (edgeCursor->getMore(cb, 1000)) { - } - } - } -}; - -/// @brief Expander for weighted edges -struct EdgeWeightExpanderCluster { - private: - /// @brief reference to the Block - ShortestPathBlock* _block; - - /// @brief Defines if this expander follows the edges in reverse - bool _reverse; - - public: - EdgeWeightExpanderCluster(ShortestPathBlock* block, bool reverse) - : _block(block), _reverse(reverse) {} - - void operator()(VPackSlice const& source, - std::vector& result) { - int res = TRI_ERROR_NO_ERROR; - std::unordered_map candidates; - - for (auto const& edgeCollection : _block->_collectionInfos) { - TRI_ASSERT(edgeCollection != nullptr); - VPackBuilder edgesBuilder; - if (_reverse) { - res = edgeCollection->getReverseEdgesCoordinator(source, edgesBuilder); - } else { - res = edgeCollection->getEdgesCoordinator(source, edgesBuilder); - } - - if (res != TRI_ERROR_NO_ERROR) { - THROW_ARANGO_EXCEPTION(res); - } - - candidates.clear(); - - auto inserter = [&](VPackSlice const& s, VPackSlice const& t, - double currentWeight, VPackSlice const& edge) { - auto cand = candidates.find(t); - if (cand == candidates.end()) { - // Add weight - auto step = std::make_unique( - t, s, currentWeight, edge); - result.emplace_back(step.release()); - candidates.emplace(t, result.size() - 1); - } else { - // Compare weight - auto old = result[cand->second]; - auto oldWeight = old->weight(); - if (currentWeight < oldWeight) { - old->setWeight(currentWeight); - old->_predecessor = s; - old->_edge = edge; - } - } - }; - - VPackSlice edges = edgesBuilder.slice().get("edges"); - for (auto const& edge : VPackArrayIterator(edges)) { - VPackSlice from = transaction::helpers::extractFromFromDocument(edge); - VPackSlice to = transaction::helpers::extractToFromDocument(edge); - double currentWeight = edgeCollection->weightEdge(edge); - if (from == source) { - inserter(from, to, currentWeight, edge); - } else { - inserter(to, from, currentWeight, edge); - } - } - _block->_coordinatorCache.emplace_back(edgesBuilder.steal()); - } - } -}; -} -} - ShortestPathBlock::ShortestPathBlock(ExecutionEngine* engine, ShortestPathNode const* ep) : ExecutionBlock(engine, ep), @@ -252,21 +101,19 @@ ShortestPathBlock::ShortestPathBlock(ExecutionEngine* engine, if (arangodb::ServerState::instance()->isCoordinator()) { if (_opts->useWeight()) { - _finder.reset(new arangodb::graph::AttributeWeightShortestPathFinder( - EdgeWeightExpanderCluster(this, false), - EdgeWeightExpanderCluster(this, true), _opts->bidirectional)); + _finder.reset( + new arangodb::graph::AttributeWeightShortestPathFinder(_opts)); } else { _finder.reset( - new arangodb::graph::ConstantWeightShortestPathFinder(this)); + new arangodb::graph::ConstantWeightShortestPathFinder(_opts)); } } else { if (_opts->useWeight()) { - _finder.reset(new arangodb::graph::AttributeWeightShortestPathFinder( - EdgeWeightExpanderLocal(this, false), - EdgeWeightExpanderLocal(this, true), _opts->bidirectional)); + _finder.reset( + new arangodb::graph::AttributeWeightShortestPathFinder(_opts)); } else { _finder.reset( - new arangodb::graph::ConstantWeightShortestPathFinder(this)); + new arangodb::graph::ConstantWeightShortestPathFinder(_opts)); } } } @@ -457,18 +304,14 @@ AqlItemBlock* ShortestPathBlock::getSome(size_t, size_t atMost) { // only copy 1st row of registers inherited from previous frame(s) inheritRegisters(cur, res.get(), _pos); - // TODO: lease builder? - VPackBuilder resultBuilder; for (size_t j = 0; j < toSend; j++) { if (usesVertexOutput()) { - resultBuilder.clear(); - _path->vertexToVelocyPack(_trx, _mmdr.get(), _posInPath, resultBuilder); - res->setValue(j, _vertexReg, AqlValue(resultBuilder.slice())); + res->setValue(j, _vertexReg, + _path->vertexToAqlValue(_opts->cache(), _posInPath)); } if (usesEdgeOutput()) { - resultBuilder.clear(); - _path->edgeToVelocyPack(_trx, _mmdr.get(), _posInPath, resultBuilder); - res->setValue(j, _edgeReg, AqlValue(resultBuilder.slice())); + res->setValue(j, _edgeReg, + _path->edgeToAqlValue(_opts->cache(), _posInPath)); } if (j > 0) { // re-use already copied aqlvalues diff --git a/arangod/Graph/AttributeWeightShortestPathFinder.cpp b/arangod/Graph/AttributeWeightShortestPathFinder.cpp index fb9e39092f..e15469bd5b 100644 --- a/arangod/Graph/AttributeWeightShortestPathFinder.cpp +++ b/arangod/Graph/AttributeWeightShortestPathFinder.cpp @@ -25,12 +25,275 @@ #include "Basics/Exceptions.h" #include "Basics/StringRef.h" +#include "Graph/EdgeCursor.h" +#include "Graph/ShortestPathOptions.h" #include "Graph/ShortestPathResult.h" +#include "Transaction/Helpers.h" +#include "VocBase/ManagedDocumentResult.h" +#include "VocBase/TraverserCache.h" + #include +#include using namespace arangodb; using namespace arangodb::graph; +AttributeWeightShortestPathFinder::Searcher::Searcher( + AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo, + ThreadInfo& peerInfo, arangodb::StringRef const& start, + bool isBackward) + : _pathFinder(pathFinder), + _myInfo(myInfo), + _peerInfo(peerInfo), + _start(start), + _isBackward(isBackward) {} + +void AttributeWeightShortestPathFinder::Searcher::insertNeighbor( + Step* step, double newWeight) { + Step* s = _myInfo._pq.find(step->_vertex); + + // Not found, so insert it: + if (s == nullptr) { + step->setWeight(newWeight); + _myInfo._pq.insert(step->_vertex, step); + return; + } + if (!s->_done && s->weight() > newWeight) { + s->_predecessor = step->_predecessor; + s->_edge = step->_edge; + _myInfo._pq.lowerWeight(s->_vertex, newWeight); + } + delete step; +} + +void AttributeWeightShortestPathFinder::Searcher::lookupPeer( + arangodb::StringRef& vertex, double weight) { + Step* s = _peerInfo._pq.find(vertex); + + if (s == nullptr) { + // Not found, nothing more to do + return; + } + double total = s->weight() + weight; + + // Update the highscore: + if (!_pathFinder->_highscoreSet || total < _pathFinder->_highscore) { + _pathFinder->_highscoreSet = true; + _pathFinder->_highscore = total; + _pathFinder->_intermediate = vertex; + _pathFinder->_intermediateSet = true; + } + + // Now the highscore is set! + + // Did we find a solution together with the other thread? + if (s->_done) { + if (total <= _pathFinder->_highscore) { + _pathFinder->_intermediate = vertex; + _pathFinder->_intermediateSet = true; + } + // Hacki says: If the highscore was set, and even if + // it is better than total, then this observation here + // proves that it will never be better, so: BINGO. + _pathFinder->_bingo = true; + // We found a way, but somebody else found a better way, + // so this is not the shortest path + return; + } + + // Did we find a solution on our own? This is for the + // single thread case and for the case that the other + // thread is too slow to even finish its own start vertex! + if (s->weight() == 0) { + // We have found the target, we have finished all + // vertices with a smaller weight than this one (and did + // not succeed), so this must be a best solution: + _pathFinder->_intermediate = vertex; + _pathFinder->_intermediateSet = true; + _pathFinder->_bingo = true; + } +} + +bool AttributeWeightShortestPathFinder::Searcher::oneStep() { + arangodb::StringRef v; + Step* s = nullptr; + bool b = _myInfo._pq.popMinimal(v, s, true); + + if (_pathFinder->_bingo || !b) { + // We can leave this functino only under 2 conditions: + // 1) already bingo==true => bingo = true no effect + // 2) This queue is empty => if there would be a + // path we would have found it here + // => No path possible. Set bingo, intermediate is empty. + _pathFinder->_bingo = true; + return false; + } + + TRI_ASSERT(s != nullptr); + + std::vector neighbors; + _pathFinder->expandVertex(_isBackward, v, neighbors); + for (Step* neighbor : neighbors) { + insertNeighbor(neighbor, s->weight() + neighbor->weight()); + } + lookupPeer(v, s->weight()); + + Step* s2 = _myInfo._pq.find(v); + s2->_done = true; + return true; +} + +AttributeWeightShortestPathFinder::AttributeWeightShortestPathFinder( + ShortestPathOptions* options) + : _highscoreSet(false), + _highscore(0), + _bingo(false), + _resultCode(TRI_ERROR_NO_ERROR), + _intermediateSet(false), + _intermediate(), + _mmdr(new ManagedDocumentResult{}), + _options(options) {} + +AttributeWeightShortestPathFinder::~AttributeWeightShortestPathFinder(){}; + +bool AttributeWeightShortestPathFinder::shortestPath( + arangodb::velocypack::Slice const& st, + arangodb::velocypack::Slice const& ta, ShortestPathResult& result, + std::function const& callback) { + // For the result: + result.clear(); + _highscoreSet = false; + _highscore = 0; + _bingo = false; + _intermediateSet = false; + + StringRef start = _options->cache()->persistString(StringRef(st)); + StringRef target = _options->cache()->persistString(StringRef(ta)); + + // Forward with initialization: + arangodb::StringRef emptyVertex; + arangodb::StringRef emptyEdge; + ThreadInfo forward; + forward._pq.insert(start, new Step(start, emptyVertex, 0, emptyEdge)); + + // backward with initialization: + ThreadInfo backward; + backward._pq.insert(target, new Step(target, emptyVertex, 0, emptyEdge)); + + // Now the searcher threads: + Searcher forwardSearcher(this, forward, backward, start, false); + std::unique_ptr backwardSearcher; + if (_options->bidirectional) { + backwardSearcher.reset(new Searcher(this, backward, forward, target, true)); + } + + TRI_IF_FAILURE("TraversalOOMInitialize") { + THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); + } + + int counter = 0; + + while (!_bingo) { + if (!forwardSearcher.oneStep()) { + break; + } + if (_options->bidirectional && !backwardSearcher->oneStep()) { + break; + } + + if (++counter == 10) { + // check for abortion + callback(); + counter = 0; + } + } + + if (!_bingo || _intermediateSet == false) { + return false; + } + + Step* s = forward._pq.find(_intermediate); + result._vertices.emplace_back(_intermediate); + + // FORWARD Go path back from intermediate -> start. + // Insert all vertices and edges at front of vector + // Do NOT! insert the intermediate vertex + while (!s->_predecessor.empty()) { + // TODO FIXME + result._edges.push_front(StringRef(s->_edge)); + result._vertices.push_front(StringRef(s->_predecessor)); + s = forward._pq.find(s->_predecessor); + } + + // BACKWARD Go path back from intermediate -> target. + // Insert all vertices and edges at back of vector + // Also insert the intermediate vertex + s = backward._pq.find(_intermediate); + while (!s->_predecessor.empty()) { + result._edges.emplace_back(StringRef(s->_edge)); + result._vertices.emplace_back(StringRef(s->_predecessor)); + s = backward._pq.find(s->_predecessor); + } + + TRI_IF_FAILURE("TraversalOOMPath") { + THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); + } + + return true; +} + +void AttributeWeightShortestPathFinder::inserter( + std::unordered_map& candidates, + std::vector& result, StringRef const& s, + StringRef const& t, double currentWeight, StringRef edge) { + auto cand = candidates.find(t); + if (cand == candidates.end()) { + // Add weight + auto step = + std::make_unique(t, s, currentWeight, edge); + result.emplace_back(step.release()); + candidates.emplace(t, result.size() - 1); + } else { + // Compare weight + auto old = result[cand->second]; + auto oldWeight = old->weight(); + if (currentWeight < oldWeight) { + old->setWeight(currentWeight); + old->_predecessor = s; + old->_edge = edge; + } + } +} + +void AttributeWeightShortestPathFinder::expandVertex( + bool isBackward, + arangodb::StringRef const& vertex, + std::vector& result) { + std::unique_ptr edgeCursor; + if (isBackward) { + edgeCursor.reset(_options->nextReverseCursor(_mmdr.get(), vertex)); + } else { + edgeCursor.reset(_options->nextCursor(_mmdr.get(), vertex)); + } + + std::unordered_map candidates; + auto callback = [&] (arangodb::StringRef const& eid, VPackSlice edge, size_t cursorIdx) -> void { + StringRef fromTmp(transaction::helpers::extractFromFromDocument(edge)); + StringRef toTmp(transaction::helpers::extractToFromDocument(edge)); + StringRef from = _options->cache()->persistString(fromTmp); + StringRef to = _options->cache()->persistString(toTmp); + double currentWeight = _options->weightEdge(edge); + if (from == vertex) { + inserter(candidates, result, from, to, currentWeight, eid); + } else { + inserter(candidates, result, to, from, currentWeight, eid); + } + }; + + edgeCursor->readAll(callback); +} + +/* AttributeWeightShortestPathFinder::SearcherTwoThreads::SearcherTwoThreads( AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo, ThreadInfo& peerInfo, arangodb::velocypack::Slice const& start, @@ -167,211 +430,11 @@ void AttributeWeightShortestPathFinder::SearcherTwoThreads::start() { void AttributeWeightShortestPathFinder::SearcherTwoThreads::join() { _thread.join(); } +*/ -AttributeWeightShortestPathFinder::Searcher::Searcher( - AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo, - ThreadInfo& peerInfo, arangodb::velocypack::Slice const& start, - ExpanderFunction expander, std::string const& id) - : _pathFinder(pathFinder), - _myInfo(myInfo), - _peerInfo(peerInfo), - _start(start), - _expander(expander), - _id(id) {} -void AttributeWeightShortestPathFinder::Searcher::insertNeighbor( - Step* step, double newWeight) { - Step* s = _myInfo._pq.find(step->_vertex); - // Not found, so insert it: - if (s == nullptr) { - step->setWeight(newWeight); - _myInfo._pq.insert(step->_vertex, step); - return; - } - if (!s->_done && s->weight() > newWeight) { - s->_predecessor = step->_predecessor; - s->_edge = step->_edge; - _myInfo._pq.lowerWeight(s->_vertex, newWeight); - } - delete step; -} -void AttributeWeightShortestPathFinder::Searcher::lookupPeer( - arangodb::velocypack::Slice& vertex, double weight) { - Step* s = _peerInfo._pq.find(vertex); - - if (s == nullptr) { - // Not found, nothing more to do - return; - } - double total = s->weight() + weight; - - // Update the highscore: - if (!_pathFinder->_highscoreSet || total < _pathFinder->_highscore) { - _pathFinder->_highscoreSet = true; - _pathFinder->_highscore = total; - _pathFinder->_intermediate = vertex; - _pathFinder->_intermediateSet = true; - } - - // Now the highscore is set! - - // Did we find a solution together with the other thread? - if (s->_done) { - if (total <= _pathFinder->_highscore) { - _pathFinder->_intermediate = vertex; - _pathFinder->_intermediateSet = true; - } - // Hacki says: If the highscore was set, and even if - // it is better than total, then this observation here - // proves that it will never be better, so: BINGO. - _pathFinder->_bingo = true; - // We found a way, but somebody else found a better way, - // so this is not the shortest path - return; - } - - // Did we find a solution on our own? This is for the - // single thread case and for the case that the other - // thread is too slow to even finish its own start vertex! - if (s->weight() == 0) { - // We have found the target, we have finished all - // vertices with a smaller weight than this one (and did - // not succeed), so this must be a best solution: - _pathFinder->_intermediate = vertex; - _pathFinder->_intermediateSet = true; - _pathFinder->_bingo = true; - } -} - -bool AttributeWeightShortestPathFinder::Searcher::oneStep() { - arangodb::velocypack::Slice v; - Step* s = nullptr; - bool b = _myInfo._pq.popMinimal(v, s, true); - - if (_pathFinder->_bingo || !b) { - // We can leave this functino only under 2 conditions: - // 1) already bingo==true => bingo = true no effect - // 2) This queue is empty => if there would be a - // path we would have found it here - // => No path possible. Set bingo, intermediate is empty. - _pathFinder->_bingo = true; - return false; - } - - TRI_ASSERT(s != nullptr); - - std::vector neighbors; - _expander(v, neighbors); - for (Step* neighbor : neighbors) { - insertNeighbor(neighbor, s->weight() + neighbor->weight()); - } - lookupPeer(v, s->weight()); - - Step* s2 = _myInfo._pq.find(v); - s2->_done = true; - return true; -} - -AttributeWeightShortestPathFinder::AttributeWeightShortestPathFinder( - ExpanderFunction&& forwardExpander, ExpanderFunction&& backwardExpander, - bool bidirectional) - : _highscoreSet(false), - _highscore(0), - _bingo(false), - _resultCode(TRI_ERROR_NO_ERROR), - _intermediateSet(false), - _intermediate(), - _forwardExpander(forwardExpander), - _backwardExpander(backwardExpander), - _bidirectional(bidirectional){}; - -bool AttributeWeightShortestPathFinder::shortestPath( - arangodb::velocypack::Slice const& start, - arangodb::velocypack::Slice const& target, - ShortestPathResult& result, - std::function const& callback) { - // For the result: - result.clear(); - _highscoreSet = false; - _highscore = 0; - _bingo = false; - _intermediateSet = false; - - // Forward with initialization: - arangodb::velocypack::Slice emptyVertex; - arangodb::velocypack::Slice emptyEdge; - ThreadInfo forward; - forward._pq.insert(start, new Step(start, emptyVertex, 0, emptyEdge)); - - // backward with initialization: - ThreadInfo backward; - backward._pq.insert(target, new Step(target, emptyVertex, 0, emptyEdge)); - - // Now the searcher threads: - Searcher forwardSearcher(this, forward, backward, start, _forwardExpander, - "Forward"); - std::unique_ptr backwardSearcher; - if (_bidirectional) { - backwardSearcher.reset(new Searcher(this, backward, forward, target, - _backwardExpander, "Backward")); - } - - TRI_IF_FAILURE("TraversalOOMInitialize") { - THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); - } - - int counter = 0; - - while (!_bingo) { - if (!forwardSearcher.oneStep()) { - break; - } - if (_bidirectional && !backwardSearcher->oneStep()) { - break; - } - - if (++counter == 10) { - // check for abortion - callback(); - counter = 0; - } - } - - if (!_bingo || _intermediateSet == false) { - return false; - } - - Step* s = forward._pq.find(_intermediate); - result._vertices.emplace_back(_intermediate); - - // FORWARD Go path back from intermediate -> start. - // Insert all vertices and edges at front of vector - // Do NOT! insert the intermediate vertex - while (!s->_predecessor.isNone()) { - // TODO FIXME - result._edges.push_front(StringRef(s->_edge)); - result._vertices.push_front(StringRef(s->_predecessor)); - s = forward._pq.find(s->_predecessor); - } - - // BACKWARD Go path back from intermediate -> target. - // Insert all vertices and edges at back of vector - // Also insert the intermediate vertex - s = backward._pq.find(_intermediate); - while (!s->_predecessor.isNone()) { - result._edges.emplace_back(StringRef(s->_edge)); - result._vertices.emplace_back(StringRef(s->_predecessor)); - s = backward._pq.find(s->_predecessor); - } - - TRI_IF_FAILURE("TraversalOOMPath") { - THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); - } - - return true; -} /* Here is a proof for the correctness of this algorithm: * @@ -444,6 +507,8 @@ bool AttributeWeightShortestPathFinder::shortestPath( * algorithm is correct. */ +/* Unused code. Maybe reactivated + bool AttributeWeightShortestPathFinder::shortestPathTwoThreads( arangodb::velocypack::Slice& start, arangodb::velocypack::Slice& target, ShortestPathResult& result) { @@ -525,3 +590,4 @@ bool AttributeWeightShortestPathFinder::shortestPathTwoThreads( return true; } +*/ diff --git a/arangod/Graph/AttributeWeightShortestPathFinder.h b/arangod/Graph/AttributeWeightShortestPathFinder.h index 2e27a7de70..bc0d042105 100644 --- a/arangod/Graph/AttributeWeightShortestPathFinder.h +++ b/arangod/Graph/AttributeWeightShortestPathFinder.h @@ -26,16 +26,21 @@ #include "Basics/Mutex.h" #include "Basics/MutexLocker.h" +#include "Basics/StringRef.h" #include "Graph/ShortestPathFinder.h" #include "Graph/ShortestPathPriorityQueue.h" #include - namespace arangodb { + +class ManagedDocumentResult; + namespace graph { +class ShortestPathOptions; + class AttributeWeightShortestPathFinder : public ShortestPathFinder { public: ////////////////////////////////////////////////////////////////////////////// @@ -47,16 +52,16 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { double _weight; public: - arangodb::velocypack::Slice _vertex; - arangodb::velocypack::Slice _predecessor; - arangodb::velocypack::Slice _edge; + arangodb::StringRef _vertex; + arangodb::StringRef _predecessor; + arangodb::StringRef _edge; bool _done; Step() : _weight(0.0), _done(false) {} - Step(arangodb::velocypack::Slice const& vert, - arangodb::velocypack::Slice const& pred, double weig, - arangodb::velocypack::Slice const& edge) + Step(arangodb::StringRef const& vert, + arangodb::StringRef const& pred, double weig, + arangodb::StringRef const& edge) : _weight(weig), _vertex(vert), _predecessor(pred), @@ -67,7 +72,7 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { void setWeight(double w) { _weight = w; } - arangodb::velocypack::Slice const& getKey() const { return _vertex; } + arangodb::StringRef const& getKey() const { return _vertex; } }; ////////////////////////////////////////////////////////////////////////////// @@ -76,20 +81,12 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { typedef enum { FORWARD, BACKWARD } Direction; - ////////////////////////////////////////////////////////////////////////////// - /// @brief callback to find neighbors - ////////////////////////////////////////////////////////////////////////////// - - typedef std::function&)> - ExpanderFunction; - ////////////////////////////////////////////////////////////////////////////// /// @brief our specialization of the priority queue ////////////////////////////////////////////////////////////////////////////// typedef arangodb::graph::ShortestPathPriorityQueue< - arangodb::velocypack::Slice, Step, double> + arangodb::StringRef, Step, double> PQueue; ////////////////////////////////////////////////////////////////////////////// @@ -105,6 +102,7 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { /// @brief a Dijkstra searcher for the multi-threaded search ////////////////////////////////////////////////////////////////////////////// + /* class SearcherTwoThreads { AttributeWeightShortestPathFinder* _pathFinder; ThreadInfo& _myInfo; @@ -154,23 +152,24 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { private: std::thread _thread; }; + */ ////////////////////////////////////////////////////////////////////////////// /// @brief a Dijkstra searcher for the single-threaded search ////////////////////////////////////////////////////////////////////////////// class Searcher { - AttributeWeightShortestPathFinder* _pathFinder; - ThreadInfo& _myInfo; - ThreadInfo& _peerInfo; - arangodb::velocypack::Slice _start; - ExpanderFunction _expander; - std::string _id; - public: Searcher(AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo, - ThreadInfo& peerInfo, arangodb::velocypack::Slice const& start, - ExpanderFunction expander, std::string const& id); + ThreadInfo& peerInfo, arangodb::StringRef const& start, + bool isBackward); + + public: + ////////////////////////////////////////////////////////////////////////////// + /// @brief Do one step only. + ////////////////////////////////////////////////////////////////////////////// + + bool oneStep(); private: //////////////////////////////////////////////////////////////////////////////// @@ -183,20 +182,22 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { /// @brief Lookup our current vertex in the data of our peer. //////////////////////////////////////////////////////////////////////////////// - void lookupPeer(arangodb::velocypack::Slice& vertex, double weight); + void lookupPeer(arangodb::StringRef& vertex, double weight); - public: - //////////////////////////////////////////////////////////////////////////////// - /// @brief Do one step only. - //////////////////////////////////////////////////////////////////////////////// + private: + AttributeWeightShortestPathFinder* _pathFinder; + ThreadInfo& _myInfo; + ThreadInfo& _peerInfo; + arangodb::StringRef _start; + bool _isBackward; - bool oneStep(); }; // ----------------------------------------------------------------------------- AttributeWeightShortestPathFinder(AttributeWeightShortestPathFinder const&) = delete; + AttributeWeightShortestPathFinder& operator=( AttributeWeightShortestPathFinder const&) = delete; AttributeWeightShortestPathFinder() = delete; @@ -205,11 +206,9 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { /// @brief create the PathFinder ////////////////////////////////////////////////////////////////////////////// - AttributeWeightShortestPathFinder(ExpanderFunction&& forwardExpander, - ExpanderFunction&& backwardExpander, - bool bidirectional = true); + AttributeWeightShortestPathFinder(ShortestPathOptions* options); - ~AttributeWeightShortestPathFinder(){}; + ~AttributeWeightShortestPathFinder(); ////////////////////////////////////////////////////////////////////////////// /// @brief Find the shortest path between start and target. @@ -225,6 +224,14 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { arangodb::graph::ShortestPathResult& result, std::function const& callback) override; + void inserter(std::unordered_map& candidates, + std::vector& result, arangodb::StringRef const& s, + arangodb::StringRef const& t, double currentWeight, + arangodb::StringRef edge); + + void expandVertex(bool isBackward, arangodb::StringRef const& source, + std::vector& result); + ////////////////////////////////////////////////////////////////////////////// /// @brief return the shortest path between the start and target vertex, /// multi-threaded version using SearcherTwoThreads. @@ -234,9 +241,11 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { // If this returns true there is a path, if this returns false there is no // path + /* Unused for now maybe reactived bool shortestPathTwoThreads(arangodb::velocypack::Slice& start, arangodb::velocypack::Slice& target, arangodb::graph::ShortestPathResult& result); + */ ////////////////////////////////////////////////////////////////////////////// /// @brief lowest total weight for a complete path found @@ -276,12 +285,18 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder { ////////////////////////////////////////////////////////////////////////////// bool _intermediateSet; - arangodb::velocypack::Slice _intermediate; + arangodb::StringRef _intermediate; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief Reusable ManagedDocumentResult that temporarily takes + /// responsibility for one document. + ////////////////////////////////////////////////////////////////////////////// private: - ExpanderFunction _forwardExpander; - ExpanderFunction _backwardExpander; - bool _bidirectional; + + std::unique_ptr _mmdr; + + ShortestPathOptions* _options; }; } // namespace graph diff --git a/arangod/Graph/ConstantWeightShortestPathFinder.cpp b/arangod/Graph/ConstantWeightShortestPathFinder.cpp index db9898337b..5069d3bda3 100644 --- a/arangod/Graph/ConstantWeightShortestPathFinder.cpp +++ b/arangod/Graph/ConstantWeightShortestPathFinder.cpp @@ -42,9 +42,8 @@ using namespace arangodb; using namespace arangodb::graph; ConstantWeightShortestPathFinder::ConstantWeightShortestPathFinder( - arangodb::aql::ShortestPathBlock* block) - : _block(block), - _options(block->_opts), + ShortestPathOptions* options) + : _options(options), _mmdr(new ManagedDocumentResult{}) {} ConstantWeightShortestPathFinder::~ConstantWeightShortestPathFinder() { diff --git a/arangod/Graph/ConstantWeightShortestPathFinder.h b/arangod/Graph/ConstantWeightShortestPathFinder.h index 842f352479..ae8f558371 100644 --- a/arangod/Graph/ConstantWeightShortestPathFinder.h +++ b/arangod/Graph/ConstantWeightShortestPathFinder.h @@ -58,7 +58,7 @@ class ConstantWeightShortestPathFinder : public ShortestPathFinder { }; public: - ConstantWeightShortestPathFinder(arangodb::aql::ShortestPathBlock* block); + ConstantWeightShortestPathFinder(ShortestPathOptions* options); ~ConstantWeightShortestPathFinder(); @@ -81,9 +81,6 @@ class ConstantWeightShortestPathFinder : public ShortestPathFinder { std::unordered_map _rightFound; std::deque _rightClosure; - // TODO Remove Me! - arangodb::aql::ShortestPathBlock* _block; - ////////////////////////////////////////////////////////////////////////////// /// @brief The options to modify this shortest path computation ////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Graph/ShortestPathOptions.cpp b/arangod/Graph/ShortestPathOptions.cpp index 07f1b4b198..b2a5fdf35c 100644 --- a/arangod/Graph/ShortestPathOptions.cpp +++ b/arangod/Graph/ShortestPathOptions.cpp @@ -107,6 +107,12 @@ void ShortestPathOptions::addReverseLookupInfo( attributeName, condition); } +double ShortestPathOptions::weightEdge(VPackSlice edge) { + TRI_ASSERT(useWeight()); + return arangodb::basics::VelocyPackHelper::getNumericValue( + edge, weightAttribute.c_str(), defaultWeight); +} + EdgeCursor* ShortestPathOptions::nextCursor(ManagedDocumentResult* mmdr, StringRef vid) { if (_isCoordinator) { diff --git a/arangod/Graph/ShortestPathOptions.h b/arangod/Graph/ShortestPathOptions.h index dbe212237a..328115d090 100644 --- a/arangod/Graph/ShortestPathOptions.h +++ b/arangod/Graph/ShortestPathOptions.h @@ -86,6 +86,9 @@ struct ShortestPathOptions : public BaseOptions { std::string const& attributeName, aql::AstNode* condition); + // Compute the weight of the given edge + double weightEdge(arangodb::velocypack::Slice const); + EdgeCursor* nextCursor(ManagedDocumentResult*, StringRef vid); EdgeCursor* nextReverseCursor(ManagedDocumentResult*, StringRef vid); diff --git a/arangod/Graph/ShortestPathResult.cpp b/arangod/Graph/ShortestPathResult.cpp index 50fa54b495..abfba526c2 100644 --- a/arangod/Graph/ShortestPathResult.cpp +++ b/arangod/Graph/ShortestPathResult.cpp @@ -23,17 +23,21 @@ #include "Graph/ShortestPathResult.h" +#include "Aql/AqlValue.h" #include "Basics/StringRef.h" #include "Basics/VelocyPackHelper.h" #include "Transaction/Helpers.h" #include "Transaction/Methods.h" +#include "VocBase/TraverserCache.h" #include #include using namespace arangodb; +using namespace arangodb::aql; using namespace arangodb::graph; using namespace arangodb::transaction; +using namespace arangodb::traverser; ShortestPathResult::ShortestPathResult() : _readDocuments(0) {} @@ -45,39 +49,16 @@ void ShortestPathResult::clear() { _edges.clear(); } -void ShortestPathResult::edgeToVelocyPack(transaction::Methods*, - ManagedDocumentResult* mmdr, - size_t position, - VPackBuilder& builder) { - TRI_ASSERT(position < length()); +AqlValue ShortestPathResult::edgeToAqlValue(TraverserCache* cache, size_t position) const { if (position == 0) { - builder.add(basics::VelocyPackHelper::NullValue()); - } else { - TRI_ASSERT(position - 1 < _edges.size()); - // FIXME ADD CACHE! - std::string tmp = _edges[position - 1].toString(); - builder.add(VPackValue(tmp)); + // First Edge is defined as NULL + return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); } + TRI_ASSERT(position - 1 < _edges.size()); + return cache->fetchAqlResult(_edges[position - 1]); } -void ShortestPathResult::vertexToVelocyPack(transaction::Methods* trx, - ManagedDocumentResult* mmdr, - size_t position, - VPackBuilder& builder) { - TRI_ASSERT(position < length()); - StringRef v = _vertices[position]; - std::string collection = v.toString(); - size_t p = collection.find("/"); - TRI_ASSERT(p != std::string::npos); - - transaction::BuilderLeaser searchBuilder(trx); - searchBuilder->add(VPackValue(collection.substr(p + 1))); - collection = collection.substr(0, p); - - Result res = trx->documentFastPath(collection, mmdr, searchBuilder->slice(), - builder, true); - if (!res.ok()) { - builder.clear(); // Just in case... - builder.add(basics::VelocyPackHelper::NullValue()); - } +AqlValue ShortestPathResult::vertexToAqlValue(TraverserCache* cache, size_t position) const { + TRI_ASSERT(position < _vertices.size()); + return cache->fetchAqlResult(_vertices[position]); } diff --git a/arangod/Graph/ShortestPathResult.h b/arangod/Graph/ShortestPathResult.h index 17265f56cd..4d17d17fe3 100644 --- a/arangod/Graph/ShortestPathResult.h +++ b/arangod/Graph/ShortestPathResult.h @@ -31,10 +31,18 @@ namespace arangodb { class ManagedDocumentResult; class StringRef; +namespace aql { +class AqlValue; +} + namespace transaction { class Methods; } +namespace traverser { +class TraverserCache; +} + namespace velocypack { class Builder; } @@ -60,16 +68,18 @@ class ShortestPathResult { /// @brief Clears the path void clear(); + ////////////////////////////////////////////////////////////////////////////// /// @brief Builds only the last edge pointing to the vertex at position as - /// VelocyPack - - void edgeToVelocyPack(transaction::Methods*, ManagedDocumentResult*, size_t, arangodb::velocypack::Builder&); - - ////////////////////////////////////////////////////////////////////////////// - /// @brief Builds only the vertex at position as VelocyPack + /// AqlValue ////////////////////////////////////////////////////////////////////////////// - void vertexToVelocyPack(transaction::Methods*, ManagedDocumentResult*, size_t, arangodb::velocypack::Builder&); + aql::AqlValue edgeToAqlValue(traverser::TraverserCache* cache, size_t depth) const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief Builds only the vertex at position as AqlValue + ////////////////////////////////////////////////////////////////////////////// + + aql::AqlValue vertexToAqlValue(traverser::TraverserCache* cache, size_t depth) const; ////////////////////////////////////////////////////////////////////////////// /// @brief Gets the amount of read documents