1
0
Fork 0

Simplified AqlTraverser Setup code by moving shared logic into the TraverserOptions.

This commit is contained in:
Michael Hackstein 2017-04-11 13:47:06 +02:00
parent b09dfaa323
commit 212de2b0d1
3 changed files with 80 additions and 148 deletions

View File

@ -900,7 +900,6 @@ void TraversalNode::prepareOptions() {
_options->_tmpVar = _tmpObjVariable;
size_t numEdgeColls = _edgeColls.size();
bool res = false;
TraversalEdgeConditionBuilder globalEdgeConditionBuilder(this);
for (auto& it : _globalEdgeConditions) {
@ -908,78 +907,30 @@ void TraversalNode::prepareOptions() {
}
Ast* ast = _plan->getAst();
auto trx = ast->query()->trx();
_options->_baseLookupInfos.reserve(numEdgeColls);
// Compute Edge Indexes. First default indexes:
for (size_t i = 0; i < numEdgeColls; ++i) {
std::string usedField;
auto dir = _directions[i];
// TODO we can optimize here. indexCondition and Expression could be
// made non-overlapping.
traverser::TraverserOptions::LookupInfo info;
switch (dir) {
case TRI_EDGE_IN:
usedField = StaticStrings::ToString;
info.indexCondition =
globalEdgeConditionBuilder.getInboundCondition()->clone(ast);
_options->addLookupInfo(
ast, _edgeColls[i]->getName(), StaticStrings::ToString,
globalEdgeConditionBuilder.getInboundCondition()->clone(ast));
break;
case TRI_EDGE_OUT:
usedField = StaticStrings::FromString;
info.indexCondition =
globalEdgeConditionBuilder.getOutboundCondition()->clone(ast);
_options->addLookupInfo(
ast, _edgeColls[i]->getName(), StaticStrings::FromString,
globalEdgeConditionBuilder.getOutboundCondition()->clone(ast));
break;
case TRI_EDGE_ANY:
TRI_ASSERT(false);
break;
}
info.expression = new Expression(ast, info.indexCondition->clone(ast));
res = trx->getBestIndexHandleForFilterCondition(
_edgeColls[i]->getName(), info.indexCondition, _tmpObjVariable, 1000,
info.idxHandles[0]);
TRI_ASSERT(res); // Right now we have an enforced edge index which will
// always fit.
if (!res) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "expected edge index not found");
}
// We now have to check if we need _from / _to inside the index lookup and which position
// it is used in. Such that the traverser can update the respective string value
// in-place
std::pair<Variable const*, std::vector<basics::AttributeName>> pathCmp;
for (size_t i = 0; i < info.indexCondition->numMembers(); ++i) {
// We search through the nary-and and look for EQ - _from/_to
auto eq = info.indexCondition->getMemberUnchecked(i);
if (eq->type != NODE_TYPE_OPERATOR_BINARY_EQ) {
// No equality. Skip
continue;
}
TRI_ASSERT(eq->numMembers() == 2);
// It is sufficient to only check member one.
// We build the condition this way.
auto mem = eq->getMemberUnchecked(0);
if (mem->isAttributeAccessForVariable(pathCmp)) {
if (pathCmp.first != _tmpObjVariable) {
continue;
}
if (pathCmp.second.size() == 1 && pathCmp.second[0].name == usedField) {
info.conditionNeedUpdate = true;
info.conditionMemberToUpdate = i;
break;
}
continue;
}
}
_options->_baseLookupInfos.emplace_back(std::move(info));
}
for (auto& it : _edgeConditions) {
auto ins = _options->_depthLookupInfo.emplace(
it.first, std::vector<traverser::TraverserOptions::LookupInfo>());
uint64_t depth = it.first;
// We probably have to adopt minDepth. We cannot fulfill a condition of larger depth anyway
TRI_ASSERT(ins.second);
auto& infos = ins.first->second;
infos.reserve(numEdgeColls);
auto& builder = it.second;
for (auto& it : _globalEdgeConditions) {
@ -987,64 +938,24 @@ void TraversalNode::prepareOptions() {
}
for (size_t i = 0; i < numEdgeColls; ++i) {
std::string usedField;
auto dir = _directions[i];
// TODO we can optimize here. indexCondition and Expression could be
// made non-overlapping.
traverser::TraverserOptions::LookupInfo info;
switch (dir) {
case TRI_EDGE_IN:
usedField = StaticStrings::ToString;
info.indexCondition = builder->getInboundCondition()->clone(ast);
_options->addDepthLookupInfo(
ast, _edgeColls[i]->getName(), StaticStrings::ToString,
builder->getInboundCondition()->clone(ast), depth);
break;
case TRI_EDGE_OUT:
usedField = StaticStrings::FromString;
info.indexCondition = builder->getOutboundCondition()->clone(ast);
_options->addDepthLookupInfo(
ast, _edgeColls[i]->getName(), StaticStrings::FromString,
builder->getOutboundCondition()->clone(ast), depth);
break;
case TRI_EDGE_ANY:
TRI_ASSERT(false);
break;
}
info.expression = new Expression(ast, info.indexCondition->clone(ast));
res = trx->getBestIndexHandleForFilterCondition(
_edgeColls[i]->getName(), info.indexCondition, _tmpObjVariable, 1000,
info.idxHandles[0]);
TRI_ASSERT(res); // Right now we have an enforced edge index which will
// always fit.
if (!res) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "expected edge index not found");
}
// We now have to check if we need _from / _to inside the index lookup and which position
// it is used in. Such that the traverser can update the respective string value
// in-place
std::pair<Variable const*, std::vector<basics::AttributeName>> pathCmp;
for (size_t i = 0; i < info.indexCondition->numMembers(); ++i) {
// We search through the nary-and and look for EQ - _from/_to
auto eq = info.indexCondition->getMemberUnchecked(i);
if (eq->type != NODE_TYPE_OPERATOR_BINARY_EQ) {
// No equality. Skip
continue;
}
TRI_ASSERT(eq->numMembers() == 2);
// It is sufficient to only check member one.
// We build the condition this way.
auto mem = eq->getMemberUnchecked(0);
if (mem->isAttributeAccessForVariable(pathCmp)) {
if (pathCmp.first != _tmpObjVariable) {
continue;
}
if (pathCmp.second.size() == 1 && pathCmp.second[0].name == usedField) {
info.conditionNeedUpdate = true;
info.conditionMemberToUpdate = i;
break;
}
continue;
}
}
infos.emplace_back(std::move(info));
}
}

View File

@ -43,18 +43,18 @@ using namespace arangodb::traverser;
using VPackHelper = arangodb::basics::VelocyPackHelper;
TraverserOptions::TraverserOptions(transaction::Methods* trx)
: BaseOptions(trx),
_baseVertexExpression(nullptr),
_traverser(nullptr),
minDepth(1),
maxDepth(1),
useBreadthFirst(false),
uniqueVertices(UniquenessLevel::NONE),
uniqueEdges(UniquenessLevel::PATH) {}
: BaseOptions(trx),
_baseVertexExpression(nullptr),
_traverser(nullptr),
minDepth(1),
maxDepth(1),
useBreadthFirst(false),
uniqueVertices(UniquenessLevel::NONE),
uniqueEdges(UniquenessLevel::PATH) {}
TraverserOptions::TraverserOptions(
transaction::Methods* trx, VPackSlice const& slice)
: BaseOptions(trx),
TraverserOptions::TraverserOptions(transaction::Methods* trx,
VPackSlice const& slice)
: BaseOptions(trx),
_baseVertexExpression(nullptr),
_traverser(nullptr),
minDepth(1),
@ -89,16 +89,14 @@ TraverserOptions::TraverserOptions(
tmp = VPackHelper::getStringValue(obj, "uniqueEdges", "");
if (tmp == "none") {
uniqueEdges =
arangodb::traverser::TraverserOptions::UniquenessLevel::NONE;
uniqueEdges = arangodb::traverser::TraverserOptions::UniquenessLevel::NONE;
} else if (tmp == "global") {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
"uniqueEdges: 'global' is not supported, "
"due to unpredictable results. Use 'path' "
"or 'none' instead");
} else {
uniqueEdges =
arangodb::traverser::TraverserOptions::UniquenessLevel::PATH;
uniqueEdges = arangodb::traverser::TraverserOptions::UniquenessLevel::PATH;
}
}
@ -112,7 +110,7 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
useBreadthFirst(false),
uniqueVertices(UniquenessLevel::NONE),
uniqueEdges(UniquenessLevel::PATH) {
// NOTE collections is an array of arrays of strings
// NOTE collections is an array of arrays of strings
VPackSlice read = info.get("minDepth");
if (!read.isInteger()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
@ -233,8 +231,8 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
d, new aql::Expression(query->ast(), info.value));
TRI_ASSERT(it.second);
#else
_vertexExpressions.emplace(
d, new aql::Expression(query->ast(), info.value));
_vertexExpressions.emplace(d,
new aql::Expression(query->ast(), info.value));
#endif
}
}
@ -259,7 +257,7 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
arangodb::traverser::TraverserOptions::TraverserOptions(
TraverserOptions const& other)
: BaseOptions(other.trx()),
: BaseOptions(other.trx()),
_baseVertexExpression(nullptr),
_traverser(nullptr),
minDepth(other.minDepth),
@ -289,7 +287,8 @@ arangodb::traverser::TraverserOptions::~TraverserOptions() {
delete _baseVertexExpression;
}
void arangodb::traverser::TraverserOptions::toVelocyPack(VPackBuilder& builder) const {
void arangodb::traverser::TraverserOptions::toVelocyPack(
VPackBuilder& builder) const {
VPackObjectBuilder guard(&builder);
builder.add("minDepth", VPackValue(minDepth));
@ -321,7 +320,8 @@ void arangodb::traverser::TraverserOptions::toVelocyPack(VPackBuilder& builder)
}
}
void arangodb::traverser::TraverserOptions::toVelocyPackIndexes(VPackBuilder& builder) const {
void arangodb::traverser::TraverserOptions::toVelocyPackIndexes(
VPackBuilder& builder) const {
VPackObjectBuilder guard(&builder);
// base indexes
@ -334,7 +334,7 @@ void arangodb::traverser::TraverserOptions::toVelocyPackIndexes(VPackBuilder& bu
}
}
builder.close();
// depth lookup indexes
builder.add("levels", VPackValue(VPackValueType::Object));
for (auto const& it : _depthLookupInfo) {
@ -352,7 +352,8 @@ void arangodb::traverser::TraverserOptions::toVelocyPackIndexes(VPackBuilder& bu
builder.close();
}
void arangodb::traverser::TraverserOptions::buildEngineInfo(VPackBuilder& result) const {
void arangodb::traverser::TraverserOptions::buildEngineInfo(
VPackBuilder& result) const {
result.openObject();
result.add("minDepth", VPackValue(minDepth));
result.add("maxDepth", VPackValue(maxDepth));
@ -386,7 +387,7 @@ void arangodb::traverser::TraverserOptions::buildEngineInfo(VPackBuilder& result
result.add(VPackValue("baseLookupInfos"));
result.openArray();
for (auto const& it: _baseLookupInfos) {
for (auto const& it : _baseLookupInfos) {
it.buildEngineInfo(result);
}
result.close();
@ -432,6 +433,18 @@ void arangodb::traverser::TraverserOptions::buildEngineInfo(VPackBuilder& result
result.close();
}
void TraverserOptions::addDepthLookupInfo(aql::Ast* ast,
std::string const& collectionName,
std::string const& attributeName,
aql::AstNode* condition,
uint64_t depth) {
TRI_ASSERT(_depthLookupInfo.find(depth) == _depthLookupInfo.end());
auto ins = _depthLookupInfo.emplace(depth, std::vector<LookupInfo>());
TRI_ASSERT(ins.second); // The insert should always work
injectLookupInfoInList(ins.first->second, ast, collectionName,
attributeName, condition);
}
bool arangodb::traverser::TraverserOptions::vertexHasFilter(
uint64_t depth) const {
if (_baseVertexExpression != nullptr) {
@ -441,8 +454,8 @@ bool arangodb::traverser::TraverserOptions::vertexHasFilter(
}
bool arangodb::traverser::TraverserOptions::evaluateEdgeExpression(
arangodb::velocypack::Slice edge, StringRef vertexId,
uint64_t depth, size_t cursorId) const {
arangodb::velocypack::Slice edge, StringRef vertexId, uint64_t depth,
size_t cursorId) const {
if (_isCoordinator) {
// The Coordinator never checks conditions. The DBServer is responsible!
return true;
@ -467,7 +480,7 @@ bool arangodb::traverser::TraverserOptions::evaluateEdgeExpression(
TRI_ASSERT(node->numMembers() > 0);
auto dirCmp = node->getMemberUnchecked(node->numMembers() - 1);
TRI_ASSERT(dirCmp->type == aql::NODE_TYPE_OPERATOR_BINARY_EQ);
TRI_ASSERT(dirCmp->type == aql::NODE_TYPE_OPERATOR_BINARY_EQ);
TRI_ASSERT(dirCmp->numMembers() == 2);
auto idNode = dirCmp->getMemberUnchecked(1);
@ -513,10 +526,12 @@ arangodb::traverser::TraverserOptions::nextCursor(ManagedDocumentResult* mmdr,
}
arangodb::traverser::EdgeCursor*
arangodb::traverser::TraverserOptions::nextCursorLocal(ManagedDocumentResult* mmdr,
StringRef vid, uint64_t depth, std::vector<LookupInfo>& list) {
arangodb::traverser::TraverserOptions::nextCursorLocal(
ManagedDocumentResult* mmdr, StringRef vid, uint64_t depth,
std::vector<LookupInfo>& list) {
TRI_ASSERT(mmdr != nullptr);
auto allCursor = std::make_unique<SingleServerEdgeCursor>(mmdr, this, list.size());
auto allCursor =
std::make_unique<SingleServerEdgeCursor>(mmdr, this, list.size());
auto& opCursors = allCursor->getCursors();
for (auto& info : list) {
auto& node = info.indexCondition;
@ -535,8 +550,8 @@ arangodb::traverser::TraverserOptions::nextCursorLocal(ManagedDocumentResult* mm
std::vector<OperationCursor*> csrs;
csrs.reserve(info.idxHandles.size());
for (auto const& it : info.idxHandles) {
csrs.emplace_back(_trx->indexScanForCondition(
it, node, _tmpVar, mmdr, UINT64_MAX, 1000, false));
csrs.emplace_back(_trx->indexScanForCondition(it, node, _tmpVar, mmdr,
UINT64_MAX, 1000, false));
}
opCursors.emplace_back(std::move(csrs));
}
@ -544,8 +559,8 @@ arangodb::traverser::TraverserOptions::nextCursorLocal(ManagedDocumentResult* mm
}
arangodb::traverser::EdgeCursor*
arangodb::traverser::TraverserOptions::nextCursorCoordinator(
StringRef vid, uint64_t depth) {
arangodb::traverser::TraverserOptions::nextCursorCoordinator(StringRef vid,
uint64_t depth) {
TRI_ASSERT(_traverser != nullptr);
auto cursor = std::make_unique<ClusterEdgeCursor>(vid, depth, _traverser);
return cursor.release();
@ -556,7 +571,8 @@ void arangodb::traverser::TraverserOptions::linkTraverser(
_traverser = trav;
}
double arangodb::traverser::TraverserOptions::estimateCost(size_t& nrItems) const {
double arangodb::traverser::TraverserOptions::estimateCost(
size_t& nrItems) const {
size_t count = 1;
double cost = 0;
size_t baseCreateItems = 0;

View File

@ -24,9 +24,9 @@
#ifndef ARANGOD_VOC_BASE_TRAVERSER_OPTIONS_H
#define ARANGOD_VOC_BASE_TRAVERSER_OPTIONS_H 1
#include "Aql/FixedVarExpressionContext.h"
#include "Basics/Common.h"
#include "Basics/StringRef.h"
#include "Aql/FixedVarExpressionContext.h"
#include "Graph/BaseOptions.h"
#include "StorageEngine/TransactionState.h"
#include "Transaction/Methods.h"
@ -60,9 +60,13 @@ class EdgeCursor {
EdgeCursor() {}
virtual ~EdgeCursor() {}
virtual bool next(std::function<void(arangodb::StringRef const&, VPackSlice, size_t)> callback) = 0;
virtual bool next(
std::function<void(arangodb::StringRef const&, VPackSlice, size_t)>
callback) = 0;
virtual void readAll(std::function<void(arangodb::StringRef const&, arangodb::velocypack::Slice, size_t&)>) = 0;
virtual void readAll(
std::function<void(arangodb::StringRef const&,
arangodb::velocypack::Slice, size_t&)>) = 0;
};
struct TraverserOptions : public graph::BaseOptions {
@ -103,7 +107,7 @@ struct TraverserOptions : public graph::BaseOptions {
/// @brief Build a velocypack for cloning in the plan.
void toVelocyPack(arangodb::velocypack::Builder&) const;
/// @brief Build a velocypack for indexes
void toVelocyPackIndexes(arangodb::velocypack::Builder&) const;
@ -111,11 +115,15 @@ struct TraverserOptions : public graph::BaseOptions {
/// for DBServer traverser engines.
void buildEngineInfo(arangodb::velocypack::Builder&) const;
/// @brief Add a lookup info for specific depth
void addDepthLookupInfo(aql::Ast* ast, std::string const& collectionName,
std::string const& attributeName,
aql::AstNode* condition, uint64_t depth);
bool vertexHasFilter(uint64_t) const;
bool evaluateEdgeExpression(arangodb::velocypack::Slice,
StringRef vertexId, uint64_t,
size_t) const;
bool evaluateEdgeExpression(arangodb::velocypack::Slice, StringRef vertexId,
uint64_t, size_t) const;
bool evaluateVertexExpression(arangodb::velocypack::Slice, uint64_t) const;
@ -126,14 +134,11 @@ struct TraverserOptions : public graph::BaseOptions {
double estimateCost(size_t& nrItems) const;
private:
EdgeCursor* nextCursorLocal(ManagedDocumentResult*,
StringRef vid, uint64_t,
EdgeCursor* nextCursorLocal(ManagedDocumentResult*, StringRef vid, uint64_t,
std::vector<LookupInfo>&);
EdgeCursor* nextCursorCoordinator(StringRef vid, uint64_t);
};
}
}
#endif