1
0
Fork 0

Fixed AttribtueWeighted ShortestPath computation.

This commit is contained in:
Michael Hackstein 2017-04-12 17:27:45 +02:00
parent 034b38f0cc
commit 5f33a95a82
9 changed files with 373 additions and 453 deletions

View File

@ -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<VPackSlice, size_t>& candidates,
std::vector<ArangoDBPathFinder::Step*>& 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<ArangoDBPathFinder::Step>(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<ArangoDBPathFinder::Step*>& result) {
TRI_ASSERT(source.isString());
std::string id = source.copyString();
ManagedDocumentResult* mmdr = _block->_mmdr.get();
std::unique_ptr<arangodb::OperationCursor> edgeCursor;
std::unordered_map<VPackSlice, size_t> 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<ArangoDBPathFinder::Step*>& result) {
int res = TRI_ERROR_NO_ERROR;
std::unordered_map<VPackSlice, size_t> 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<ArangoDBPathFinder::Step>(
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

View File

@ -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 <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>
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<Step*> 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<void()> 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<Searcher> 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<StringRef, size_t>& candidates,
std::vector<Step*>& 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<Step>(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<Step*>& result) {
std::unique_ptr<EdgeCursor> edgeCursor;
if (isBackward) {
edgeCursor.reset(_options->nextReverseCursor(_mmdr.get(), vertex));
} else {
edgeCursor.reset(_options->nextCursor(_mmdr.get(), vertex));
}
std::unordered_map<StringRef, size_t> 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<Step*> 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<void()> 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<Searcher> 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;
}
*/

View File

@ -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 <thread>
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<void(arangodb::velocypack::Slice const&,
std::vector<Step*>&)>
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<void()> const& callback) override;
void inserter(std::unordered_map<arangodb::StringRef, size_t>& candidates,
std::vector<Step*>& result, arangodb::StringRef const& s,
arangodb::StringRef const& t, double currentWeight,
arangodb::StringRef edge);
void expandVertex(bool isBackward, arangodb::StringRef const& source,
std::vector<Step*>& 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<ManagedDocumentResult> _mmdr;
ShortestPathOptions* _options;
};
} // namespace graph

View File

@ -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() {

View File

@ -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<arangodb::StringRef, PathSnippet*> _rightFound;
std::deque<arangodb::StringRef> _rightClosure;
// TODO Remove Me!
arangodb::aql::ShortestPathBlock* _block;
//////////////////////////////////////////////////////////////////////////////
/// @brief The options to modify this shortest path computation
//////////////////////////////////////////////////////////////////////////////

View File

@ -107,6 +107,12 @@ void ShortestPathOptions::addReverseLookupInfo(
attributeName, condition);
}
double ShortestPathOptions::weightEdge(VPackSlice edge) {
TRI_ASSERT(useWeight());
return arangodb::basics::VelocyPackHelper::getNumericValue<double>(
edge, weightAttribute.c_str(), defaultWeight);
}
EdgeCursor* ShortestPathOptions::nextCursor(ManagedDocumentResult* mmdr,
StringRef vid) {
if (_isCoordinator) {

View File

@ -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);

View File

@ -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 <velocypack/Builder.h>
#include <velocypack/velocypack-aliases.h>
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]);
}

View File

@ -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